1 /*
   2  * Copyright (c) 2010, 2013, 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.io.File;
  29 import java.io.InputStream;
  30 import java.nio.ByteBuffer;
  31 import java.nio.IntBuffer;
  32 import java.security.AccessController;
  33 import java.security.PrivilegedAction;
  34 import java.util.ArrayList;
  35 import java.util.Arrays;
  36 import java.util.Collections;
  37 import java.util.HashMap;
  38 import java.util.List;
  39 import java.util.Map;
  40 import java.util.Set;
  41 import java.util.concurrent.CountDownLatch;
  42 import java.util.concurrent.Future;
  43 import java.util.concurrent.TimeUnit;
  44 import java.util.concurrent.atomic.AtomicBoolean;
  45 import javafx.application.ConditionalFeature;
  46 import javafx.geometry.Dimension2D;
  47 import javafx.scene.image.Image;
  48 import javafx.scene.input.Dragboard;
  49 import javafx.scene.input.InputMethodRequests;
  50 import javafx.scene.input.KeyCode;
  51 import javafx.scene.input.KeyEvent;
  52 import javafx.scene.input.TransferMode;
  53 import javafx.scene.paint.Color;
  54 import javafx.scene.paint.CycleMethod;
  55 import javafx.scene.paint.ImagePattern;
  56 import javafx.scene.paint.LinearGradient;
  57 import javafx.scene.paint.RadialGradient;
  58 import javafx.scene.paint.Stop;
  59 import javafx.scene.shape.ClosePath;
  60 import javafx.scene.shape.CubicCurveTo;
  61 import javafx.scene.shape.FillRule;
  62 import javafx.scene.shape.LineTo;
  63 import javafx.scene.shape.MoveTo;
  64 import javafx.scene.shape.PathElement;
  65 import javafx.scene.shape.QuadCurveTo;
  66 import javafx.scene.shape.SVGPath;
  67 import javafx.stage.FileChooser;
  68 import javafx.stage.Modality;
  69 import javafx.stage.StageStyle;
  70 import com.sun.glass.ui.Application;
  71 import com.sun.glass.ui.Clipboard;
  72 import com.sun.glass.ui.ClipboardAssistance;
  73 import com.sun.glass.ui.CommonDialogs;
  74 import com.sun.glass.ui.CommonDialogs.FileChooserResult;
  75 import com.sun.glass.ui.EventLoop;
  76 import com.sun.glass.ui.Screen;
  77 import com.sun.glass.ui.Timer;
  78 import com.sun.glass.ui.View;
  79 import com.sun.glass.ui.Window;
  80 import com.sun.javafx.PlatformUtil;
  81 import com.sun.javafx.embed.HostInterface;
  82 import com.sun.javafx.geom.Path2D;
  83 import com.sun.javafx.geom.PathIterator;
  84 import com.sun.javafx.geom.Shape;
  85 import com.sun.javafx.geom.transform.BaseTransform;
  86 import com.sun.javafx.perf.PerformanceTracker;
  87 import com.sun.javafx.runtime.async.AbstractRemoteResource;
  88 import com.sun.javafx.runtime.async.AsyncOperationListener;
  89 import com.sun.javafx.scene.text.HitInfo;
  90 import com.sun.javafx.scene.text.TextLayoutFactory;
  91 import com.sun.javafx.sg.PGArc;
  92 import com.sun.javafx.sg.PGCanvas;
  93 import com.sun.javafx.sg.PGCircle;
  94 import com.sun.javafx.sg.PGCubicCurve;
  95 import com.sun.javafx.sg.PGEllipse;
  96 import com.sun.javafx.sg.PGExternalNode;
  97 import com.sun.javafx.sg.PGGroup;
  98 import com.sun.javafx.sg.PGImageView;
  99 import com.sun.javafx.sg.PGLine;
 100 import com.sun.javafx.sg.PGPath;
 101 import com.sun.javafx.sg.PGPolygon;
 102 import com.sun.javafx.sg.PGPolyline;
 103 import com.sun.javafx.sg.PGQuadCurve;
 104 import com.sun.javafx.sg.PGRectangle;
 105 import com.sun.javafx.sg.PGRegion;
 106 import com.sun.javafx.sg.PGSVGPath;
 107 import com.sun.javafx.sg.PGShape.StrokeLineCap;
 108 import com.sun.javafx.sg.PGShape.StrokeLineJoin;
 109 import com.sun.javafx.sg.PGShape.StrokeType;
 110 import com.sun.javafx.sg.PGText;
 111 import com.sun.javafx.sg.prism.NGArc;
 112 import com.sun.javafx.sg.prism.NGCanvas;
 113 import com.sun.javafx.sg.prism.NGCircle;
 114 import com.sun.javafx.sg.prism.NGCubicCurve;
 115 import com.sun.javafx.sg.prism.NGEllipse;
 116 import com.sun.javafx.sg.prism.NGGroup;
 117 import com.sun.javafx.sg.prism.NGImageView;
 118 import com.sun.javafx.sg.prism.NGLine;
 119 import com.sun.javafx.sg.prism.NGNode;
 120 import com.sun.javafx.sg.prism.NGPath;
 121 import com.sun.javafx.sg.prism.NGPolygon;
 122 import com.sun.javafx.sg.prism.NGPolyline;
 123 import com.sun.javafx.sg.prism.NGQuadCurve;
 124 import com.sun.javafx.sg.prism.NGRectangle;
 125 import com.sun.javafx.sg.prism.NGRegion;
 126 import com.sun.javafx.sg.prism.NGSVGPath;
 127 import com.sun.javafx.sg.prism.NGText;
 128 import com.sun.javafx.tk.FileChooserType;
 129 import com.sun.javafx.tk.FontLoader;
 130 import com.sun.javafx.tk.ImageLoader;
 131 import com.sun.javafx.tk.PlatformImage;
 132 import com.sun.javafx.tk.ScreenConfigurationAccessor;
 133 import com.sun.javafx.tk.TKClipboard;
 134 import com.sun.javafx.tk.TKDragGestureListener;
 135 import com.sun.javafx.tk.TKDragSourceListener;
 136 import com.sun.javafx.tk.TKDropTargetListener;
 137 import com.sun.javafx.tk.TKScene;
 138 import com.sun.javafx.tk.TKScreenConfigurationListener;
 139 import com.sun.javafx.tk.TKStage;
 140 import com.sun.javafx.tk.TKSystemMenu;
 141 import com.sun.javafx.tk.Toolkit;
 142 import com.sun.javafx.tk.desktop.AppletWindow;
 143 import com.sun.javafx.tk.desktop.DesktopToolkit;
 144 import com.sun.javafx.tk.desktop.MasterTimer;
 145 import com.sun.prism.BasicStroke;
 146 import com.sun.prism.Graphics;
 147 import com.sun.prism.GraphicsPipeline;
 148 import com.sun.prism.PixelFormat;
 149 import com.sun.prism.RTTexture;
 150 import com.sun.prism.RenderingContext;
 151 import com.sun.prism.ResourceFactory;
 152 import com.sun.prism.ResourceFactoryListener;
 153 import com.sun.prism.Texture.WrapMode;
 154 import com.sun.prism.camera.PrismCameraImpl;
 155 import com.sun.prism.camera.PrismParallelCameraImpl;
 156 import com.sun.prism.camera.PrismPerspectiveCameraImpl;
 157 import com.sun.prism.impl.Disposer;
 158 import com.sun.prism.impl.PrismSettings;
 159 import com.sun.prism.render.CompletionListener;
 160 import com.sun.prism.render.RenderJob;
 161 import com.sun.prism.render.ToolkitInterface;
 162 import com.sun.scenario.DelayedRunnable;
 163 import com.sun.scenario.animation.AbstractMasterTimer;
 164 import com.sun.scenario.effect.FilterContext;
 165 import com.sun.scenario.effect.Filterable;
 166 import com.sun.scenario.effect.impl.prism.PrFilterContext;
 167 import com.sun.scenario.effect.impl.prism.PrImage;
 168 
 169 import static com.sun.javafx.logging.PulseLogger.*;
 170 import com.sun.javafx.sg.PGAmbientLight;
 171 import com.sun.javafx.sg.PGBox;
 172 import com.sun.javafx.sg.PGCylinder;
 173 import com.sun.javafx.sg.PGLightBase;
 174 import com.sun.javafx.sg.PGMeshView;
 175 import com.sun.javafx.sg.PGParallelCamera;
 176 import com.sun.javafx.sg.PGPerspectiveCamera;
 177 import com.sun.javafx.sg.PGPhongMaterial;
 178 import com.sun.javafx.sg.PGPointLight;
 179 import com.sun.javafx.sg.PGSphere;
 180 import com.sun.javafx.sg.PGTriangleMesh;
 181 import com.sun.javafx.sg.prism.NGAmbientLight;
 182 import com.sun.javafx.sg.prism.NGBox;
 183 import com.sun.javafx.sg.prism.NGCylinder;
 184 import com.sun.javafx.sg.prism.NGMeshView;
 185 import com.sun.javafx.sg.prism.NGParallelCamera;
 186 import com.sun.javafx.sg.prism.NGPerspectiveCamera;
 187 import com.sun.javafx.sg.prism.NGPhongMaterial;
 188 import com.sun.javafx.sg.prism.NGPointLight;
 189 import com.sun.javafx.sg.prism.NGSphere;
 190 import com.sun.javafx.sg.prism.NGTriangleMesh;
 191 import com.sun.javafx.sg.prism.NGExternalNode;
 192 
 193 public final class QuantumToolkit extends DesktopToolkit implements ToolkitInterface {
 194 
 195     public static final boolean verbose =
 196             AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
 197                 @Override public Boolean run() {
 198                     return Boolean.getBoolean("quantum.verbose");
 199                 }
 200             });
 201 
 202     public static final boolean pulseDebug =
 203             AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
 204                 @Override public Boolean run() {
 205                     return Boolean.getBoolean("quantum.pulse");
 206                 }
 207             });
 208 
 209     public static final boolean multithreaded =
 210             AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
 211                 @Override public Boolean run() {
 212                     // If it is not specified, or it is true, then it should
 213                     // be true. Otherwise it should be false.
 214                     String value = System.getProperty("quantum.multithreaded");
 215                     value = value == null ? "" : value.trim();
 216                     final boolean result = "".equals(value) || Boolean.parseBoolean(value);
 217                     if (verbose) {
 218                         System.out.println(result ? "Multi-Threading Enabled" : "Multi-Threading Disabled");
 219                     }
 220                     return result;
 221                 }
 222             });
 223 
 224     private static boolean debug =
 225             AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
 226                 @Override public Boolean run() {
 227                     return Boolean.getBoolean("quantum.debug");
 228                 }
 229             });
 230 
 231     private static Integer pulseHZ =
 232             AccessController.doPrivileged(new PrivilegedAction<Integer>() {
 233                 @Override public Integer run() {
 234                     return Integer.getInteger("javafx.animation.pulse");
 235                 }
 236             });
 237     
 238     static final boolean liveResize =
 239             AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
 240                 @Override public Boolean run() {
 241                     boolean isSWT = "swt".equals(System.getProperty("glass.platform"));
 242                     String result = PlatformUtil.isMac() && !isSWT ? "true" : "false";
 243                     return result.equals(System.getProperty("javafx.live.resize", "true"));
 244                 }
 245             });
 246     
 247     static final boolean drawInPaint =
 248             AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
 249                 @Override public Boolean run() {
 250                     boolean isSWT = "swt".equals(System.getProperty("glass.platform"));
 251                     String result = PlatformUtil.isMac() && isSWT ? "true" : "false";
 252                     return result.equals(System.getProperty("javafx.draw.in.paint", "true"));}
 253             });
 254 
 255     private AtomicBoolean           toolkitRunning = new AtomicBoolean(false);
 256     private AtomicBoolean           animationRunning = new AtomicBoolean(false);
 257     private AtomicBoolean           nextPulseRequested = new AtomicBoolean(false);
 258     private AtomicBoolean           pulseRunning = new AtomicBoolean(false);
 259     private CountDownLatch          launchLatch = new CountDownLatch(1);
 260     
 261     final int                       PULSE_INTERVAL = (int)(TimeUnit.SECONDS.toMillis(1L) / getRefreshRate());
 262     final int                       FULLSPEED_INTERVAL = 1;     // ms
 263     boolean                         nativeSystemVsync = false;
 264     private float                   _maxPixelScale;
 265     private Runnable                pulseRunnable, userRunnable, timerRunnable;
 266     private Timer                   pulseTimer = null;
 267     private Thread                  shutdownHook = null;
 268     private PaintCollector          collector;
 269     private QuantumRenderer         renderer;
 270     private GraphicsPipeline        pipeline;
 271 
 272     private ClassLoader             ccl;
 273 
 274     private HashMap<Object,EventLoop> eventLoopMap = null;
 275 
 276     @Override public boolean init() {
 277         /*
 278          * The below no longer applies with for jfx 8.0 on jdk 8.0. But leaving
 279          * the check and comments to avoid breaking jfx running with older
 280          * versions of jdk.
 281          *
 282          * AWT headful trips up glass, and is used in various places throughout
 283          * prism, such as J2DFontFactory and BufferedImageTools, but only
 284          * required AWT in a headless capacity.
 285          *
 286          * AWT headful interferes with glass and is used in various places throughout
 287          * prism, such as J2DFontFactory and BufferedImageTools, but the only
 288          * required AWT is in a headless capacity.
 289          *
 290          * It is assumed that we don't need AWT headful, and if it is needed
 291          * then AWT is already initialized before this property is set.  In that
 292          * case the code below is harmless.
 293          *
 294          * Further Consideration and Dependencies:
 295          * - This fix introduces a order dependency in Swing/AWT and FX interop
 296          * mode on Mac. Now Swing/AWT must be initialization before FX.
 297          *
 298          * - On Mac this causes the warning message "Process manager already
 299          * initialized -- can't fully enable headless mode."
 300          *
 301          * - On Windows AWT headful does not cause problems.
 302          *
 303          * - This Mac specific fix should be removed as soon as possible.
 304          * Once RT-12452 has been implemented, then we should be able to do
 305          * just that.  Introducing a long term restriction on Mac only, is worse
 306          * then adding universal platform restriction.
 307          */
 308         if (PlatformUtil.isMac()) {
 309             String version = AccessController.doPrivileged(new PrivilegedAction<String>() {
 310                 @Override public String run() {
 311                     return System.getProperty("os.version");
 312                 }
 313             });
 314             if (version.startsWith("10.4") || version.startsWith("10.5")) {
 315                 throw new RuntimeException("JavaFX requires Mac OSX 10.6 or higher to run");
 316             }
 317             String javaVersion = AccessController.doPrivileged(new PrivilegedAction<String>() {
 318                 @Override public String run() {
 319                     return System.getProperty("java.version");
 320                 }
 321             });
 322             // We only support 1.6 and higher no need to check older versions
 323             if (javaVersion.startsWith("1.6") || javaVersion.startsWith("1.7")) {
 324                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
 325                     @Override public Void run() {
 326                         System.setProperty("java.awt.headless", "true");
 327                         return null;
 328                     }
 329                 });
 330             }
 331         }
 332         /* 
 333          * Glass Mac, X11 need Application.setDeviceDetails to happen prior to Glass Application.Run
 334          */
 335         renderer = QuantumRenderer.getInstance();
 336         collector = PaintCollector.createInstance(this);
 337         pipeline = GraphicsPipeline.getPipeline();
 338         if (PrismSettings.shutdownHook) {
 339             shutdownHook = new Thread("Glass/Prism Shutdown Hook") {
 340                 @Override public void run() {
 341                     dispose();
 342                 }
 343             };
 344             AccessController.doPrivileged(new PrivilegedAction<Void>() {
 345                 @Override public Void run() {
 346                     Runtime.getRuntime().addShutdownHook(shutdownHook);
 347                     return null;
 348                 }
 349             });
 350         }
 351         return true;
 352     }
 353 
 354     /**
 355      * This method is invoked by PlatformImpl. It is typically called on the main
 356      * thread, NOT the JavaFX Application Thread. The userStartupRunnable will
 357      * be invoked on the JavaFX Application Thread.
 358      *
 359      * @param userStartupRunnable A runnable invoked on the JavaFX Application Thread
 360      *                            that allows the system to perform some startup
 361      *                            functionality after the toolkit has been initialized.
 362      */
 363     @Override public void startup(final Runnable userStartupRunnable) {
 364         // Save the context class loader of the launcher thread
 365         ccl = Thread.currentThread().getContextClassLoader();
 366 
 367         try {
 368             this.userRunnable = userStartupRunnable;
 369 
 370             Application.run(new Runnable () {
 371                 public void run () {
 372                     // Ensure that the toolkit can only be started here 
 373                     runToolkit();
 374                 }
 375             });
 376         } catch (RuntimeException ex) {
 377             if (verbose) {
 378                 ex.printStackTrace();
 379             }
 380             throw ex;
 381         } catch (Throwable t) {
 382             if (verbose) {
 383                 t.printStackTrace();
 384             }
 385             throw new RuntimeException(t);
 386         }
 387 
 388         try {
 389             launchLatch.await();
 390         } catch (InterruptedException ie) {
 391             ie.printStackTrace();
 392         }
 393     }
 394 
 395     // restart the toolkit if previously terminated
 396     private void assertToolkitRunning() {
 397         // not implemented
 398     }
 399 
 400     // Called by Glass from Application.run()
 401     void runToolkit() {
 402         Thread user = Thread.currentThread();
 403 
 404         if (toolkitRunning.getAndSet(true) == false) {
 405             user.setName("JavaFX Application Thread");
 406 
 407             // Set context class loader to the same as the thread that called startup
 408             user.setContextClassLoader(ccl);
 409 
 410             /* set uncaught handler for Glass native thread */
 411             user.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
 412                 @Override public void uncaughtException(Thread t, Throwable th) {
 413                     System.out.println(t.getName() + " uncaught: " + th.getClass().getName());
 414                     th.printStackTrace();
 415                 }
 416             });
 417             setFxUserThread(user);
 418 
 419             /*
 420              *  Glass Application instance is now valid - create the ResourceFactory
 421              *  on the render thread
 422              */
 423             renderer.createResourceFactory();
 424 
 425             pulseRunnable = new Runnable() { 
 426                 @Override public void run() {
 427                     QuantumToolkit.this.pulse();
 428                 }
 429             };
 430             timerRunnable = new Runnable() { 
 431                 @Override public void run() {
 432                     try {
 433                         QuantumToolkit.this.postPulse();
 434                     } catch (Throwable th) {
 435                         th.printStackTrace(System.err);
 436                         // } catch (RuntimeException re) {
 437                         // ignore spurious Glass timer events while exiting...
 438                     }
 439                 }
 440             };
 441             pulseTimer = Application.GetApplication().createTimer(timerRunnable);
 442 
 443             Application.GetApplication().setEventHandler(new Application.EventHandler() {
 444                 @Override public void handleQuitAction(Application app, long time) {
 445                     GlassStage.requestClosingAllWindows();
 446                 }
 447             });
 448         }
 449         launchLatch.countDown();
 450         try {
 451             Application.invokeAndWait(this.userRunnable);
 452             
 453             if (getMasterTimer().isFullspeed()) {
 454                 /* 
 455                  * FULLSPEED_INTVERVAL workaround
 456                  *
 457                  * Application.invokeLater(pulseRunnable);
 458                  */
 459                 pulseTimer.start(FULLSPEED_INTERVAL);
 460             } else {
 461                 nativeSystemVsync = Screen.getVideoRefreshPeriod() != 0.0;
 462                 if (nativeSystemVsync) {
 463                     // system supports vsync
 464                     pulseTimer.start();
 465                 } else {
 466                     // rely on millisecond resolution timer to provide
 467                     // nominal pulse sync and use pulse hinting on 
 468                     // synchronous pipelines to fine tune the interval
 469                     pulseTimer.start(PULSE_INTERVAL);
 470                 }
 471             }
 472         } catch (Throwable th) {
 473             th.printStackTrace(System.err);
 474         } finally {
 475             if (PrismSettings.verbose) {
 476                 System.err.println(" vsync: " + PrismSettings.isVsyncEnabled +
 477                                    " vpipe: " + pipeline.isVsyncSupported());
 478             }
 479             PerformanceTracker.logEvent("Toolkit.startup - finished");
 480         }
 481     }
 482     
 483     boolean hasNativeSystemVsync() {
 484         return nativeSystemVsync;
 485     }
 486 
 487     boolean isVsyncEnabled() {
 488         return (PrismSettings.isVsyncEnabled &&
 489                 pipeline.isVsyncSupported());
 490     }
 491     
 492     @Override public void checkFxUserThread() {
 493         super.checkFxUserThread();
 494         renderer.checkRendererIdle();
 495     }
 496 
 497     protected static Thread getFxUserThread() {
 498         return Toolkit.getFxUserThread();
 499     }
 500     
 501     /* com.sun.javafx.tk.ToolkitInterface */
 502 
 503     @Override public Future addRenderJob(RenderJob r) {
 504         return (renderer.submitRenderJob(r));
 505     }
 506    
 507     void postPulse() {
 508         if (toolkitRunning.get() &&
 509             (animationRunning.get() || nextPulseRequested.get() || collector.hasDirty()) &&
 510             !setPulseRunning()) {
 511             
 512             Application.invokeLater(pulseRunnable);
 513             
 514             if (debug) {
 515                 System.err.println("QT.postPulse@(" + System.nanoTime() + "): " + pulseString());
 516             }
 517         } else if (debug) {
 518             System.err.println("QT.postPulse#(" + System.nanoTime() + ") DROP: " + pulseString());
 519         }
 520     }
 521     
 522     private String pulseString() {
 523         return ((toolkitRunning.get() ? "T" : "t") +
 524                 (animationRunning.get() ? "A" : "a") +
 525                 (pulseRunning.get() ? "P" : "p") + 
 526                 (nextPulseRequested.get() ? "N" : "n") +
 527                 (collector.hasDirty() ? "D" : "d"));
 528     }
 529     
 530     private boolean setPulseRunning() {
 531         return (pulseRunning.getAndSet(true));
 532     }
 533     
 534     private void endPulseRunning() {
 535         pulseRunning.set(false);
 536         if (debug) {
 537             System.err.println("QT.endPulse: " + System.nanoTime());
 538         }
 539     }
 540 
 541     protected void pulse() {
 542         pulse(true);
 543     }
 544     
 545     void pulse(boolean collect) {
 546         try {
 547             long start = PULSE_LOGGING_ENABLED ? System.currentTimeMillis() : 0;
 548             if (PULSE_LOGGING_ENABLED) {
 549                 PULSE_LOGGER.pulseStart();
 550             }
 551             
 552             if (toolkitRunning.get() == false) {
 553                 return;
 554             }
 555             try {
 556                 nextPulseRequested.set(false);
 557                 if (animationRunnable != null) {
 558                     animationRunning.set(true);
 559                     animationRunnable.run();
 560                 } else {
 561                     animationRunning.set(false);
 562                 }
 563                 firePulse();
 564                 if (collect) collector.renderAll();
 565                 endPulseRunning();
 566             } catch (Throwable th) {
 567                 th.printStackTrace(System.err);
 568             }
 569         } finally {
 570             if (PULSE_LOGGING_ENABLED) {
 571                 PULSE_LOGGER.pulseEnd();
 572             }
 573         }
 574     }
 575 
 576     void vsyncHint() {
 577         if (isVsyncEnabled()) {
 578             if (debug) {
 579                 System.err.println("QT.vsyncHint: postPulse: " + System.nanoTime());
 580             }
 581             postPulse();
 582         }
 583     }
 584     
 585     @Override  public AppletWindow createAppletWindow(long parent, String serverName) {
 586         GlassAppletWindow parentWindow = new GlassAppletWindow(parent, serverName);
 587         // Make this the parent window for all future Stages
 588         WindowStage.setAppletWindow(parentWindow);
 589         return parentWindow;
 590     }
 591     
 592     @Override public void closeAppletWindow() {
 593         GlassAppletWindow gaw = WindowStage.getAppletWindow();
 594         if (null != gaw) {
 595             gaw.dispose();
 596             WindowStage.setAppletWindow(null);
 597             // any further strong refs will be in the applet itself
 598         }
 599     }
 600     
 601     @Override public TKStage createTKStage(StageStyle stageStyle) {
 602         assertToolkitRunning();
 603         return new WindowStage(verbose, stageStyle).init(systemMenu);
 604     }
 605 
 606     @Override public TKStage createTKStage(StageStyle stageStyle, 
 607             boolean primary, Modality modality, TKStage owner, boolean rtl) {
 608         assertToolkitRunning();
 609         WindowStage stage = new WindowStage(verbose, stageStyle, primary, modality, owner);
 610         stage.setRTL(rtl);
 611         stage.init(systemMenu);
 612         return stage;
 613     }
 614 
 615     @Override public Object enterNestedEventLoop(Object key) {
 616         checkFxUserThread();
 617 
 618         if (key == null) {
 619             throw new NullPointerException();
 620         }
 621         if (eventLoopMap == null) {
 622             eventLoopMap = new HashMap<Object, EventLoop>();
 623         }
 624         if (eventLoopMap.containsKey(key)) {
 625             throw new IllegalArgumentException(
 626                     "Key already associated with a running event loop: " + key);
 627         }
 628         EventLoop eventLoop = Application.GetApplication().createEventLoop();
 629         eventLoopMap.put(key, eventLoop);
 630 
 631         return eventLoop.enter();
 632     }
 633 
 634     @Override public void exitNestedEventLoop(Object key, Object rval) {
 635         checkFxUserThread();
 636 
 637         if (key == null) {
 638             throw new NullPointerException();
 639         }
 640         if (eventLoopMap == null || !eventLoopMap.containsKey(key)) {
 641             throw new IllegalArgumentException(
 642                     "Key not associated with a running event loop: " + key);
 643         }
 644         EventLoop eventLoop = eventLoopMap.get(key);
 645         eventLoopMap.remove(key);
 646         eventLoop.leave(rval);
 647     }
 648 
 649     @Override public TKStage createTKPopupStage(StageStyle stageStyle, Object owner) {
 650         assertToolkitRunning();
 651         return new PopupStage(verbose, owner).init(systemMenu);
 652     }
 653 
 654     @Override public TKStage createTKEmbeddedStage(HostInterface host) {
 655         assertToolkitRunning();
 656         return new EmbeddedStage(host);
 657     }
 658 
 659     private static ScreenConfigurationAccessor screenAccessor =
 660         new ScreenConfigurationAccessor() {
 661             @Override public int getMinX(Object obj) {
 662                return ((Screen)obj).getX();
 663             }
 664             @Override public int getMinY(Object obj) {
 665                 return ((Screen)obj).getY();
 666             }
 667             @Override public int getWidth(Object obj) {
 668                 return ((Screen)obj).getWidth();
 669             }
 670             @Override public int getHeight(Object obj) {
 671                 return ((Screen)obj).getHeight();
 672             }
 673             @Override public int getVisualMinX(Object obj) {
 674                 return ((Screen)obj).getVisibleX();
 675             }
 676             @Override public int getVisualMinY(Object obj) {
 677                 return ((Screen)obj).getVisibleY();
 678             }
 679             @Override public int getVisualWidth(Object obj) {
 680                 return ((Screen)obj).getVisibleWidth();
 681             }
 682             @Override public int getVisualHeight(Object obj) {
 683                 return ((Screen)obj).getVisibleHeight();
 684             }
 685             @Override public float getDPI(Object obj) {
 686                 return ((Screen)obj).getResolutionX();
 687             }
 688         };
 689 
 690     @Override public ScreenConfigurationAccessor 
 691                     setScreenConfigurationListener(final TKScreenConfigurationListener listener) {
 692         Screen.setEventHandler(new Screen.EventHandler() {
 693             @Override public void handleSettingsChanged() {
 694                 notifyScreenListener(listener);
 695             }
 696         });
 697         return screenAccessor;
 698     }
 699 
 700     private static void notifyScreenListener(TKScreenConfigurationListener listener) {
 701         listener.screenConfigurationChanged();
 702     }
 703 
 704     @Override public Object getPrimaryScreen() {
 705         return Screen.getMainScreen();
 706     }
 707 
 708     @Override public List<?> getScreens() {
 709         return Screen.getScreens();
 710     }
 711 
 712     public float getMaxPixelScale() {
 713         if (_maxPixelScale == 0) {
 714             for (Object o : getScreens()) {
 715                 _maxPixelScale = Math.max(_maxPixelScale, ((Screen) o).getScale());
 716             }
 717         }
 718         return _maxPixelScale;
 719     }
 720 
 721     @Override public ImageLoader loadImage(String url, int width, int height, boolean preserveRatio, boolean smooth) {
 722         return new PrismImageLoader2(url, width, height, preserveRatio, getMaxPixelScale(), smooth);
 723     }
 724 
 725     @Override public ImageLoader loadImage(InputStream stream, int width, int height,
 726                                            boolean preserveRatio, boolean smooth) {
 727         return new PrismImageLoader2(stream, width, height, preserveRatio, smooth);
 728     }
 729 
 730     @Override public AbstractRemoteResource<? extends ImageLoader> loadImageAsync(
 731             AsyncOperationListener listener, String url,
 732             int width, int height, boolean preserveRatio, boolean smooth) {
 733         return new PrismImageLoader2.AsyncImageLoader(listener, url, width, height, preserveRatio, smooth);
 734     }
 735 
 736     @Override public void defer(Runnable runnable) {
 737         if (!toolkitRunning.get()) {
 738             throw new IllegalStateException("Attempt to call defer when toolkit not running");
 739         }
 740         Application.invokeLater(runnable);
 741     }
 742 
 743     @Override public void exit() {
 744             notifyShutdownHooks();
 745             pulseTimer.stop();
 746 
 747             AbstractPainter.renderLock.lock();
 748             try {
 749                 //TODO - should update glass scene view state
 750                 //TODO - doesn't matter because we are exiting
 751                 Application app = Application.GetApplication();
 752                 app.terminate();
 753             } finally {
 754                 AbstractPainter.renderLock.unlock();
 755             }
 756 
 757             dispose();
 758 
 759             super.exit();
 760 }
 761 
 762     public void dispose() {
 763         if (toolkitRunning.compareAndSet(true, false)) {
 764             renderer.stopRenderer();
 765 
 766             if (PrismSettings.shutdownHook) {
 767                 try {
 768                     Runtime.getRuntime().removeShutdownHook(shutdownHook);
 769                 } catch (IllegalStateException ignore) {
 770                     // throw when shutdown hook already removed
 771                 }
 772             }
 773         }
 774     }
 775 
 776     @Override public boolean isForwardTraversalKey(KeyEvent e) {
 777         return (e.getCode() == KeyCode.TAB)
 778                    && (e.getEventType() == KeyEvent.KEY_PRESSED)
 779                    && !e.isShiftDown();
 780     }
 781 
 782     @Override public boolean isBackwardTraversalKey(KeyEvent e) {
 783         return (e.getCode() == KeyCode.TAB)
 784                    && (e.getEventType() == KeyEvent.KEY_PRESSED)
 785                    && e.isShiftDown();
 786     }
 787 
 788     private Map contextMap = Collections.synchronizedMap(new HashMap());
 789     @Override public Map<Object, Object> getContextMap() {
 790         return contextMap;
 791     }
 792 
 793     @Override public int getRefreshRate() {
 794         if (pulseHZ == null) {
 795             return 60;
 796         } else {
 797             return pulseHZ.intValue();
 798         }
 799     }
 800 
 801     private DelayedRunnable animationRunnable;
 802     @Override public void setAnimationRunnable(DelayedRunnable animationRunnable) {
 803         if (animationRunnable != null) {
 804             animationRunning.set(true);
 805         }
 806         this.animationRunnable = animationRunnable;
 807     }
 808 
 809     @Override public void requestNextPulse() {
 810         nextPulseRequested.set(true);
 811     }
 812     
 813     @Override public void waitFor(Task t) {
 814         if (t.isFinished()) {
 815             return;
 816         }
 817     }
 818 
 819     @Override protected Object createColorPaint(Color color) {
 820         return new com.sun.prism.paint.Color(
 821                 (float)color.getRed(), (float)color.getGreen(),
 822                 (float)color.getBlue(), (float)color.getOpacity());
 823     }
 824 
 825     private com.sun.prism.paint.Color toPrismColor(Color color) {
 826         return (com.sun.prism.paint.Color) Toolkit.getPaintAccessor().getPlatformPaint(color);
 827     }
 828 
 829     private List<com.sun.prism.paint.Stop> convertStops(List<Stop> paintStops) {
 830         List<com.sun.prism.paint.Stop> stops =
 831             new ArrayList<com.sun.prism.paint.Stop>(paintStops.size());
 832         for (Stop s : paintStops) {
 833             stops.add(new com.sun.prism.paint.Stop(toPrismColor(s.getColor()),
 834                                                    (float) s.getOffset()));
 835         }
 836         return stops;
 837     }
 838 
 839     @Override protected Object createLinearGradientPaint(LinearGradient paint) {
 840         int cmi = com.sun.prism.paint.Gradient.REPEAT;
 841         CycleMethod cycleMethod = paint.getCycleMethod();
 842         if (cycleMethod == CycleMethod.NO_CYCLE) {
 843             cmi = com.sun.prism.paint.Gradient.PAD;
 844         } else if (cycleMethod == CycleMethod.REFLECT) {
 845             cmi = com.sun.prism.paint.Gradient.REFLECT;
 846         }
 847         // TODO: extract colors/offsets and pass them in directly...
 848         List<com.sun.prism.paint.Stop> stops = convertStops(paint.getStops());
 849         return new com.sun.prism.paint.LinearGradient(
 850             (float)paint.getStartX(), (float)paint.getStartY(), (float)paint.getEndX(), (float)paint.getEndY(),
 851             null, paint.isProportional(), cmi, stops);
 852     }
 853 
 854     @Override
 855     protected Object createRadialGradientPaint(RadialGradient paint) {
 856         float cx = (float)paint.getCenterX();
 857         float cy = (float)paint.getCenterY();
 858         float fa = (float)paint.getFocusAngle();
 859         float fd = (float)paint.getFocusDistance();
 860 
 861         int cmi = 0;
 862         if (paint.getCycleMethod() == CycleMethod.NO_CYCLE) {
 863             cmi = com.sun.prism.paint.Gradient.PAD;
 864         } else if (paint.getCycleMethod() == CycleMethod.REFLECT) {
 865             cmi = com.sun.prism.paint.Gradient.REFLECT;
 866         } else {
 867             cmi = com.sun.prism.paint.Gradient.REPEAT;
 868         }
 869 
 870         // TODO: extract colors/offsets and pass them in directly...
 871         List<com.sun.prism.paint.Stop> stops = convertStops(paint.getStops());
 872         return new com.sun.prism.paint.RadialGradient(cx, cy, fa, fd,
 873                 (float)paint.getRadius(), null, paint.isProportional(), cmi, stops);
 874     }
 875 
 876     @Override
 877     protected Object createImagePatternPaint(ImagePattern paint) {
 878         if (paint.getImage() == null) {
 879             return com.sun.prism.paint.Color.TRANSPARENT;
 880         } else {
 881             return new com.sun.prism.paint.ImagePattern((com.sun.prism.Image) paint.getImage().impl_getPlatformImage(),
 882                     (float)paint.getX(),
 883                     (float)paint.getY(),
 884                     (float)paint.getWidth(),
 885                     (float)paint.getHeight(),
 886                     paint.isProportional(),
 887                     Toolkit.getPaintAccessor().isMutable(paint));
 888         }
 889     }
 890 
 891     static BasicStroke tmpStroke = new BasicStroke();
 892     private void initStroke(StrokeType pgtype, double strokewidth,
 893                             StrokeLineCap pgcap,
 894                             StrokeLineJoin pgjoin, float miterLimit,
 895                             float[] dashArray, float dashOffset)
 896     {
 897         int type;
 898         if (pgtype == StrokeType.CENTERED) {
 899             type = BasicStroke.TYPE_CENTERED;
 900         } else if (pgtype == StrokeType.INSIDE) {
 901             type = BasicStroke.TYPE_INNER;
 902         } else {
 903             type = BasicStroke.TYPE_OUTER;
 904         }
 905 
 906         int cap;
 907         if (pgcap == StrokeLineCap.BUTT) {
 908             cap = BasicStroke.CAP_BUTT;
 909         } else if (pgcap == StrokeLineCap.SQUARE) {
 910             cap = BasicStroke.CAP_SQUARE;
 911         } else {
 912             cap = BasicStroke.CAP_ROUND;
 913         }
 914 
 915         int join;
 916         if (pgjoin == StrokeLineJoin.BEVEL) {
 917             join = BasicStroke.JOIN_BEVEL;
 918         } else if (pgjoin == StrokeLineJoin.MITER) {
 919             join = BasicStroke.JOIN_MITER;
 920         } else {
 921             join = BasicStroke.JOIN_ROUND;
 922         }
 923 
 924         tmpStroke.set(type, (float) strokewidth, cap, join, miterLimit);
 925         if ((dashArray != null) && (dashArray.length > 0)) {
 926             tmpStroke.set(dashArray, dashOffset);
 927         } else {
 928             tmpStroke.set((float[])null, 0);
 929         }
 930     }
 931 
 932     @Override
 933     public void accumulateStrokeBounds(Shape shape, float bbox[],
 934                                        StrokeType pgtype,
 935                                        double strokewidth,
 936                                        StrokeLineCap pgcap,
 937                                        StrokeLineJoin pgjoin,
 938                                        float miterLimit,
 939                                        BaseTransform tx)
 940     {
 941 
 942         initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit, null, 0);
 943         if (tx.isTranslateOrIdentity()) {
 944             tmpStroke.accumulateShapeBounds(bbox, shape, tx);
 945         } else {
 946             Shape.accumulate(bbox, tmpStroke.createStrokedShape(shape), tx);
 947         }
 948     }
 949 
 950     @Override
 951     public boolean strokeContains(Shape shape, double x, double y,
 952                                   StrokeType pgtype,
 953                                   double strokewidth,
 954                                   StrokeLineCap pgcap,
 955                                   StrokeLineJoin pgjoin,
 956                                   float miterLimit)
 957     {
 958         initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit, null, 0);
 959         // TODO: The contains testing could be done directly without creating a Shape
 960         return tmpStroke.createStrokedShape(shape).contains((float) x, (float) y);
 961     }
 962 
 963     @Override
 964     public Shape createStrokedShape(Shape shape,
 965                                     StrokeType pgtype,
 966                                     double strokewidth,
 967                                     StrokeLineCap pgcap,
 968                                     StrokeLineJoin pgjoin,
 969                                     float miterLimit,
 970                                     float[] dashArray,
 971                                     float dashOffset) {
 972         initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit,
 973                    dashArray, dashOffset);
 974         return tmpStroke.createStrokedShape(shape);
 975     }
 976 
 977     @Override public Dimension2D getBestCursorSize(int preferredWidth, int preferredHeight) {
 978         return CursorUtils.getBestCursorSize(preferredWidth, preferredHeight);
 979     }
 980 
 981     @Override public int getMaximumCursorColors() {
 982         return 2;
 983     }
 984 
 985     @Override public int getKeyCodeForChar(String character) {
 986         return (character.length() == 1)
 987                 ? com.sun.glass.events.KeyEvent.getKeyCodeForChar(
 988                           character.charAt(0))
 989                 : com.sun.glass.events.KeyEvent.VK_UNDEFINED;
 990     }
 991 
 992     @Override public PathElement[] convertShapeToFXPath(Object shape) {
 993         if (shape == null) {
 994             return new PathElement[0];
 995         }
 996         List<PathElement> elements = new ArrayList();
 997         // iterate over the shape and turn it into a series of path
 998         // elements
 999         com.sun.javafx.geom.Shape geomShape = (com.sun.javafx.geom.Shape) shape;
1000         PathIterator itr = geomShape.getPathIterator(null);
1001         PathIteratorHelper helper = new PathIteratorHelper(itr);
1002         PathIteratorHelper.Struct struct = new PathIteratorHelper.Struct();
1003 
1004         while (!helper.isDone()) {
1005             // true if WIND_EVEN_ODD, false if WIND_NON_ZERO
1006             boolean windEvenOdd = helper.getWindingRule() == PathIterator.WIND_EVEN_ODD;
1007             int type = helper.currentSegment(struct);
1008             PathElement el;
1009             if (type == PathIterator.SEG_MOVETO) {
1010                 el = new MoveTo(struct.f0, struct.f1);
1011             } else if (type == PathIterator.SEG_LINETO) {
1012                 el = new LineTo(struct.f0, struct.f1);
1013             } else if (type == PathIterator.SEG_QUADTO) {
1014                 el = new QuadCurveTo(
1015                     struct.f0,
1016                     struct.f1,
1017                     struct.f2,
1018                     struct.f3);
1019             } else if (type == PathIterator.SEG_CUBICTO) {
1020                 el = new CubicCurveTo (
1021                     struct.f0,
1022                     struct.f1,
1023                     struct.f2,
1024                     struct.f3,
1025                     struct.f4,
1026                     struct.f5);
1027             } else if (type == PathIterator.SEG_CLOSE) {
1028                 el = new ClosePath();
1029             } else {
1030                 throw new IllegalStateException("Invalid element type: " + type);
1031             }
1032             helper.next();
1033             elements.add(el);
1034         }
1035 
1036         return elements.toArray(new PathElement[elements.size()]);
1037     }
1038 
1039     @Override public HitInfo convertHitInfoToFX(Object hit) {
1040         Integer textHitPos = (Integer) hit;
1041         HitInfo hitInfo = new HitInfo();
1042         hitInfo.setCharIndex(textHitPos.intValue());
1043         hitInfo.setLeading(true);
1044         return hitInfo;
1045     }
1046 
1047     @Override public Filterable toFilterable(Image img) {
1048         return PrImage.create((com.sun.prism.Image) img.impl_getPlatformImage());
1049     }
1050 
1051     @Override public FilterContext getFilterContext(Object config) {
1052         if (config == null || ((config instanceof com.sun.glass.ui.Screen) == false)) {
1053             return PrFilterContext.getDefaultInstance();
1054         }
1055         Screen screen = (Screen)config;
1056         return PrFilterContext.getInstance(screen);
1057     }
1058 
1059     @Override public AbstractMasterTimer getMasterTimer() {
1060         return MasterTimer.getInstance();
1061     }
1062 
1063     @Override public FontLoader getFontLoader() {
1064         return com.sun.javafx.font.PrismFontLoader.getInstance();
1065     }
1066 
1067     @Override public TextLayoutFactory getTextLayoutFactory() {
1068         return com.sun.javafx.text.TextLayoutFactory.getFactory();
1069     }
1070 
1071     @Override public PGArc createPGArc() {
1072         return new NGArc();
1073     }
1074 
1075     @Override public PGCircle createPGCircle() {
1076         return new NGCircle();
1077     }
1078 
1079     @Override public PGCubicCurve createPGCubicCurve() {
1080         return new NGCubicCurve();
1081     }
1082 
1083     @Override public PGEllipse createPGEllipse() {
1084         return new NGEllipse();
1085     }
1086 
1087     @Override public PGLine createPGLine() {
1088         return new NGLine();
1089     }
1090 
1091     @Override public PGPath createPGPath() {
1092         return new NGPath();
1093     }
1094 
1095     @Override public PGSVGPath createPGSVGPath() {
1096         return new NGSVGPath();
1097     }
1098 
1099     @Override public PGPolygon createPGPolygon() {
1100         return new NGPolygon();
1101     }
1102 
1103     @Override public PGPolyline createPGPolyline() {
1104         return new NGPolyline();
1105     }
1106 
1107     @Override public PGQuadCurve createPGQuadCurve() {
1108         return new NGQuadCurve();
1109     }
1110 
1111     @Override public PGRectangle createPGRectangle() {
1112         return new NGRectangle();
1113     }
1114 
1115     @Override public PGImageView createPGImageView() {
1116         return new NGImageView();
1117     }
1118 
1119     @Override public PGGroup createPGGroup() {
1120         return new NGGroup();
1121     }
1122 
1123     @Override public PGRegion createPGRegion() {
1124         return new NGRegion();
1125     }
1126 
1127     @Override public PGCanvas createPGCanvas() {
1128         return new NGCanvas();
1129     }
1130 
1131     @Override public PGText createPGText() {
1132         return new NGText();
1133     }
1134 
1135     @Override public PGExternalNode createPGExternalNode() {
1136         return new NGExternalNode();
1137     }
1138 
1139     @Override public Object createSVGPathObject(SVGPath svgpath) {
1140         int windingRule = svgpath.getFillRule() == FillRule.NON_ZERO ? PathIterator.WIND_NON_ZERO : PathIterator.WIND_EVEN_ODD;
1141         Path2D path = new Path2D(windingRule);
1142         path.appendSVGPath(svgpath.getContent());
1143         return path;
1144     }
1145 
1146     @Override public Path2D createSVGPath2D(SVGPath svgpath) {
1147         int windingRule = svgpath.getFillRule() == FillRule.NON_ZERO ? PathIterator.WIND_NON_ZERO : PathIterator.WIND_EVEN_ODD;
1148         Path2D path = new Path2D(windingRule);
1149         path.appendSVGPath(svgpath.getContent());
1150         return path;
1151     }
1152 
1153     @Override public boolean imageContains(Object image, float x, float y) {
1154         if (image == null) {
1155             return false;
1156         }
1157 
1158         com.sun.prism.Image pImage = (com.sun.prism.Image)image;
1159         int intX = (int)x + pImage.getMinX();
1160         int intY = (int)y + pImage.getMinY();
1161 
1162         if (pImage.getPixelFormat().isOpaque()) {
1163             return true;
1164         }
1165 
1166         if (pImage.getPixelFormat() == PixelFormat.INT_ARGB_PRE) {
1167             IntBuffer ib = (IntBuffer) pImage.getPixelBuffer();
1168             int index = intX + intY * pImage.getRowLength();
1169             if (index >= ib.limit()) {
1170                 return false;
1171             } else {
1172                 return (ib.get(index) & 0xff000000) != 0;
1173             }
1174         } else if (pImage.getPixelFormat() == PixelFormat.BYTE_BGRA_PRE) {
1175             ByteBuffer bb = (ByteBuffer) pImage.getPixelBuffer();
1176             int index = intX * pImage.getBytesPerPixelUnit() + intY * pImage.getScanlineStride() + 3;
1177             if (index >= bb.limit()) {
1178                 return false;
1179             } else {
1180                 return (bb.get(index) & 0xff) != 0;
1181             }
1182         } else if (pImage.getPixelFormat() == PixelFormat.BYTE_ALPHA) {
1183             ByteBuffer bb = (ByteBuffer) pImage.getPixelBuffer();
1184             int index = intX * pImage.getBytesPerPixelUnit() + intY * pImage.getScanlineStride();
1185             if (index >= bb.limit()) {
1186                 return false;
1187             } else {
1188                 return (bb.get(index) & 0xff) != 0;
1189             }
1190         }
1191         return true;
1192     }
1193 
1194     @Override
1195     public boolean isSupported(ConditionalFeature feature) {
1196         switch (feature) {
1197             case SCENE3D:
1198                 return GraphicsPipeline.getPipeline().is3DSupported();
1199             case EFFECT:
1200                 return GraphicsPipeline.getPipeline().isEffectSupported();
1201             case SHAPE_CLIP:
1202                 return true;
1203             case INPUT_METHOD:
1204                 return false;     // TODO: RT-28495, implement input method query
1205             case TRANSPARENT_WINDOW:
1206                 return Application.GetApplication().supportsTransparentWindows();
1207             case UNIFIED_WINDOW:
1208                 return Application.GetApplication().supportsUnifiedWindows();
1209             case TWO_LEVEL_FOCUS:
1210                 return Application.GetApplication().hasTwoLevelFocus();
1211             case VIRTUAL_KEYBOARD:
1212                 return Application.GetApplication().hasVirtualKeyboard();
1213             case INPUT_TOUCH:
1214                 return Application.GetApplication().hasTouch();
1215             case INPUT_MULTITOUCH:
1216                 return Application.GetApplication().hasMultiTouch();
1217             case INPUT_POINTER:
1218                 return Application.GetApplication().hasPointer();
1219             default:
1220                 return false;
1221         }
1222     }
1223 
1224     static TransferMode clipboardActionToTransferMode(final int action) {
1225         switch (action) {
1226             case Clipboard.ACTION_NONE:
1227                 return null;
1228             case Clipboard.ACTION_COPY:
1229             //IE drop action for URL copy
1230             case Clipboard.ACTION_COPY | Clipboard.ACTION_REFERENCE:
1231                 return TransferMode.COPY;
1232             case Clipboard.ACTION_MOVE:
1233             //IE drop action for URL move
1234             case Clipboard.ACTION_MOVE | Clipboard.ACTION_REFERENCE:
1235                 return TransferMode.MOVE;
1236             case Clipboard.ACTION_REFERENCE:
1237                 return TransferMode.LINK;
1238             case Clipboard.ACTION_ANY:
1239                 return TransferMode.COPY; // select a reasonable trasnfer mode as workaround until RT-22840
1240         }
1241         return null;
1242     }
1243 
1244     private QuantumClipboard clipboard;
1245     @Override public TKClipboard getSystemClipboard() {
1246         if (clipboard == null) {
1247             clipboard = QuantumClipboard.getClipboardInstance(new ClipboardAssistance(com.sun.glass.ui.Clipboard.SYSTEM));
1248         }
1249         return clipboard;
1250     }
1251 
1252     private GlassSystemMenu systemMenu = new GlassSystemMenu();
1253     @Override public TKSystemMenu getSystemMenu() {
1254         return systemMenu;
1255     }
1256 
1257     @Override public TKClipboard getNamedClipboard(String name) {
1258         return null;
1259     }
1260 
1261     @Override public void startDrag(TKScene scene, Set<TransferMode> tm, TKDragSourceListener l, Dragboard dragboard) {
1262         if (dragboard == null) {
1263             throw new IllegalArgumentException("dragboard should not be null");
1264         }
1265 
1266         GlassScene view = (GlassScene)scene;
1267         view.setTKDragSourceListener(l);
1268 
1269         QuantumClipboard gc = (QuantumClipboard)dragboard.impl_getPeer();
1270         gc.setSupportedTransferMode(tm);
1271         gc.flush();
1272 
1273         // flush causes a modal DnD event loop, when we return, close the clipboard
1274         gc.close();
1275     }
1276 
1277     @Override public void enableDrop(TKScene s, TKDropTargetListener l) {
1278 
1279         assert s instanceof GlassScene;
1280 
1281         GlassScene view = (GlassScene)s;
1282         view.setTKDropTargetListener(l);
1283     }
1284 
1285     @Override public void registerDragGestureListener(TKScene s, Set<TransferMode> tm, TKDragGestureListener l) {
1286 
1287         assert s instanceof GlassScene;
1288 
1289         GlassScene view = (GlassScene)s;
1290         view.setTKDragGestureListener(l);
1291     }
1292 
1293     /* com.sun.javafx.tk.desktop.DesktopToolkit */
1294     @Override public boolean isAppletDragSupported() {
1295         return false;
1296     }
1297 
1298     @Override
1299     public void installInputMethodRequests(TKScene scene, InputMethodRequests requests) {
1300 
1301         assert scene instanceof GlassScene;
1302 
1303         GlassScene view = (GlassScene)scene;
1304         view.setInputMethodRequests(requests);
1305     }
1306 
1307     // 3D API support for FX 8 
1308     @Override
1309     public PGBox createPGBox() {
1310         return new NGBox();
1311     }
1312 
1313     @Override
1314     public PGCylinder createPGCylinder() {
1315         return new NGCylinder();
1316     }
1317 
1318     @Override
1319     public PGSphere createPGSphere() {
1320         return new NGSphere();
1321     }
1322 
1323     @Override
1324     public PGTriangleMesh createPGTriangleMesh() {
1325         return new NGTriangleMesh();
1326     }
1327 
1328     @Override
1329     public PGMeshView createPGMeshView() {
1330         return new NGMeshView();
1331     }
1332 
1333     @Override
1334     public PGPhongMaterial createPGPhongMaterial() {
1335         return new NGPhongMaterial();
1336     }
1337 
1338     @Override
1339     public PGAmbientLight createPGAmbientLight() {
1340         NGAmbientLight light = new NGAmbientLight();
1341         lightsInScene.add(light);
1342         return light;
1343     }
1344 
1345     @Override
1346     public PGPointLight createPGPointLight() {
1347         //        return new NGPointLight();
1348         NGPointLight light = new NGPointLight();
1349         lightsInScene.add(light);
1350         return light;
1351     }
1352 
1353     /**
1354      * TODO: 3D - we need a better way of keeping track of lights in
1355      * the scene. This does not account for lights being deleted or for
1356      * light scoping.
1357      */
1358     private List<PGLightBase> lightsInScene = new ArrayList<PGLightBase>();
1359     public List<PGLightBase> getLightsInScene() { return lightsInScene; }
1360     private boolean lightsDirty = true;
1361     public boolean isLightsDirty() { return lightsDirty; }
1362     public void setLightsDirty(boolean lightsDirty) { this.lightsDirty = lightsDirty; }
1363 
1364     @Override
1365     public PGParallelCamera createPGParallelCamera() {
1366         return new NGParallelCamera();
1367     }
1368 
1369     @Override
1370     public PGPerspectiveCamera createPGPerspectiveCamera(boolean fixedEyePosition) {
1371         return new NGPerspectiveCamera(fixedEyePosition);
1372     }
1373     
1374     static class QuantumImage implements com.sun.javafx.tk.ImageLoader, ResourceFactoryListener {
1375 
1376         // cache rt here
1377         private com.sun.prism.RTTexture rt;
1378         private com.sun.prism.Image image;
1379         private ResourceFactory rf;
1380         
1381         QuantumImage(com.sun.prism.Image image) { 
1382             this.image = image;
1383         }
1384         
1385         RTTexture getRT(int w, int h, ResourceFactory rfNew) {
1386             boolean rttOk = rt != null && rf == rfNew && 
1387                     rt.getContentWidth() == w && rt.getContentHeight() == h;
1388             
1389             if (!rttOk) {
1390                 if (rt != null) {
1391                     rt.dispose();
1392                 }
1393                 if (rf != null) {
1394                     rf.removeFactoryListener(this);
1395                     rf = null;
1396                 }
1397                 rt = rfNew.createRTTexture(w, h, WrapMode.CLAMP_TO_ZERO);
1398                 if (rt != null) {
1399                     rf = rfNew;
1400                     rf.addFactoryListener(this);
1401                 }
1402             }
1403             
1404             return rt;
1405         }
1406         
1407         void dispose() {
1408             if (rt != null) {
1409                 rt.dispose();
1410                 rt = null;
1411             }
1412         }
1413         
1414         void setImage(com.sun.prism.Image img) {
1415             image = img;
1416         }
1417         
1418         @Override
1419         public boolean getError() { return image == null; }
1420         @Override
1421         public int getFrameCount() { return 1; }
1422         @Override
1423         public PlatformImage getFrame(int index) { return image; }
1424         @Override
1425         public int getFrameDelay(int index) { return 0; }
1426         @Override
1427         public int getWidth() { return image.getWidth(); }
1428         @Override
1429         public int getHeight() { return image.getHeight(); }
1430         @Override 
1431         public void factoryReset() { dispose(); }
1432         @Override 
1433         public void factoryReleased() { dispose(); }
1434     }
1435 
1436     @Override public ImageLoader loadPlatformImage(Object platformImage) {
1437         if (platformImage instanceof QuantumImage) {
1438             return (QuantumImage)platformImage;
1439         }
1440         
1441         if (platformImage instanceof com.sun.prism.Image) {
1442             return new QuantumImage((com.sun.prism.Image) platformImage);
1443         }     
1444         
1445         throw new UnsupportedOperationException("unsupported class for loadPlatformImage");
1446     }
1447 
1448     @Override
1449     public PlatformImage createPlatformImage(int w, int h) {
1450         ByteBuffer bytebuf = ByteBuffer.allocate(w * h * 4);
1451         return com.sun.prism.Image.fromByteBgraPreData(bytebuf, w, h);
1452     }
1453 
1454     @Override
1455     public Object renderToImage(ImageRenderingContext p) {
1456         Object saveImage = p.platformImage;
1457         final ImageRenderingContext params = p;
1458         final com.sun.prism.paint.Paint currentPaint = p.platformPaint instanceof com.sun.prism.paint.Paint ? 
1459                 (com.sun.prism.paint.Paint)p.platformPaint : null;
1460         
1461         RenderJob re = new RenderJob(new Runnable() {
1462 
1463             private com.sun.prism.paint.Color getClearColor() {
1464                 if (currentPaint == null) {
1465                     return com.sun.prism.paint.Color.WHITE;
1466                 } else if (currentPaint.getType() == com.sun.prism.paint.Paint.Type.COLOR) {
1467                     return (com.sun.prism.paint.Color) currentPaint;
1468                 } else if (currentPaint.isOpaque()) {
1469                     return com.sun.prism.paint.Color.TRANSPARENT;
1470                 } else {
1471                     return com.sun.prism.paint.Color.WHITE;
1472                 }
1473             }
1474 
1475             private void draw(Graphics g, int x, int y, int w, int h) {
1476                 g.setDepthBuffer(params.depthBuffer);
1477 
1478                 g.clear(getClearColor());
1479                 if (currentPaint != null && 
1480                         currentPaint.getType() != com.sun.prism.paint.Paint.Type.COLOR) {
1481                     g.getRenderTarget().setOpaque(currentPaint.isOpaque());
1482                     g.setPaint(currentPaint);
1483                     g.fillQuad(0, 0, w, h);
1484                 }
1485 
1486                 // Set up transform
1487                 if (x != 0 || y != 0) {
1488                     g.translate(-x, -y);
1489                 }
1490                 if (params.transform != null) {
1491                     g.transform(params.transform);
1492                 }
1493 
1494                 if (params.root != null) {
1495                     if (params.camera instanceof PrismCameraImpl) {
1496                         g.setCamera((PrismCameraImpl)params.camera);
1497                     }
1498                     
1499                     NGNode ngNode = (NGNode)params.root;
1500                     ngNode.render(g);
1501                 }
1502 
1503             }
1504             
1505             @Override
1506             public void run() {
1507 
1508                 ResourceFactory rf = GraphicsPipeline.getDefaultResourceFactory();
1509 
1510                 if (!rf.isDeviceReady()) {
1511                     return;
1512                 }
1513 
1514                 int x = params.x;
1515                 int y = params.y;
1516                 int w = params.width;
1517                 int h = params.height;
1518 
1519                 if (w <= 0 || h <= 0) {
1520                     return;
1521                 }
1522 
1523                 RenderingContext context = rf.createRenderingContext(null);
1524                 try {
1525                     
1526                     context.begin();
1527 
1528                     QuantumImage pImage = (params.platformImage instanceof QuantumImage) ?
1529                             (QuantumImage)params.platformImage : new QuantumImage(null);
1530                     
1531                     com.sun.prism.RTTexture rt = pImage.getRT(w, h, rf);
1532                     
1533                     if (rt == null) {
1534                         return;
1535                     }
1536                     
1537                     Graphics g = rt.createGraphics();
1538 
1539                     draw(g, x, y, w, h);
1540 
1541                     int[] pixels = pImage.rt.getPixels();
1542                     
1543                     if (pixels != null) {
1544                         pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(pixels, w, h));
1545                     } else {
1546                         IntBuffer ib = IntBuffer.allocate(w*h);
1547                         if (pImage.rt.readPixels(ib, pImage.rt.getContentX(),
1548                                 pImage.rt.getContentY(), w, h))
1549                         {
1550                             pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(ib, w, h));
1551                         } else {
1552                             pImage.dispose();
1553                             pImage = null;
1554                         }
1555                     }
1556 
1557                     params.platformImage = pImage;
1558 
1559                 } catch (Throwable t) {
1560                     t.printStackTrace(System.err);
1561                 } finally {
1562                     Disposer.cleanUp();
1563                     context.end();
1564                 }
1565             }
1566         });
1567 
1568         final CountDownLatch latch = new CountDownLatch(1);
1569         re.setCompletionListener(new CompletionListener() {
1570             @Override public void done(final RenderJob job) {
1571                 latch.countDown();
1572             }
1573         });
1574         addRenderJob(re);
1575 
1576         do {
1577             try {
1578                 latch.await();
1579                 break;
1580             } catch (InterruptedException ex) {
1581                 ex.printStackTrace();
1582             }
1583         } while (true);
1584 
1585         Object image = params.platformImage;
1586         params.platformImage = saveImage;
1587 
1588         return image;
1589     }
1590     
1591     @Override
1592     public List<File> showFileChooser(final TKStage ownerWindow,
1593                                       final String title,
1594                                       final File initialDirectory,
1595                                       final String initialFileName,
1596                                       final FileChooserType fileChooserType,
1597                                       final List<FileChooser.ExtensionFilter>
1598                                               extensionFilters) {
1599         WindowStage blockedStage = null;
1600         try {
1601             // NOTE: we block the owner of the owner deliberately.
1602             //       The native system blocks the nearest owner itself.
1603             //       Otherwise sheets on Mac are unusable.
1604             blockedStage = blockOwnerStage(ownerWindow);
1605             
1606             FileChooserResult result = CommonDialogs.showFileChooser(
1607                     (ownerWindow instanceof WindowStage)
1608                             ? ((WindowStage) ownerWindow).getPlatformWindow()
1609                             : null,
1610                     initialDirectory,
1611                     initialFileName,
1612                     title,
1613                     (fileChooserType == FileChooserType.SAVE)
1614                             ? CommonDialogs.Type.SAVE
1615                             : CommonDialogs.Type.OPEN,
1616                     (fileChooserType == FileChooserType.OPEN_MULTIPLE),
1617                     convertExtensionFilters(extensionFilters));
1618 
1619             String extension = result.getExtensionFilter() == null ?
1620                 null : result.getExtensionFilter().getExtensions().get(0);
1621 
1622             if (fileChooserType == FileChooserType.OPEN || extension == null || extension.endsWith("*")) { 
1623                 return result.getFiles();
1624             } else {
1625                 final List<File> list = new ArrayList<File>(result.getFiles().size());
1626                 if (extension.startsWith("*")) {
1627                     extension = extension.substring(1, extension.length());
1628                 }
1629 
1630                 for (File f : result.getFiles()) {
1631                     String filename = f.getAbsolutePath();
1632 
1633                     if (filename.endsWith(extension)) {
1634                         list.add(f);
1635                     } else {
1636                         list.add(new File(filename + extension));
1637                     }
1638                 }
1639                 return list;
1640             }
1641         } finally {
1642             if (blockedStage != null) {
1643                 blockedStage.setEnabled(true);
1644             }
1645         }
1646     }
1647 
1648     @Override
1649     public File showDirectoryChooser(final TKStage ownerWindow,
1650                                      final String title,
1651                                      final File initialDirectory) {
1652         WindowStage blockedStage = null;
1653         try {
1654             // NOTE: we block the owner of the owner deliberately.
1655             //       The native system blocks the nearest owner itself.
1656             //       Otherwise sheets on Mac are unusable.
1657             blockedStage = blockOwnerStage(ownerWindow);
1658 
1659             return CommonDialogs.showFolderChooser(
1660                     (ownerWindow instanceof WindowStage)
1661                             ? ((WindowStage) ownerWindow).getPlatformWindow()
1662                             : null,
1663                     initialDirectory, title);
1664         } finally {
1665             if (blockedStage != null) {
1666                 blockedStage.setEnabled(true);
1667             }
1668         }
1669     }
1670 
1671     private WindowStage blockOwnerStage(final TKStage stage) {
1672         if (stage instanceof WindowStage) {
1673             final TKStage ownerStage = ((WindowStage) stage).getOwner();
1674             if (ownerStage instanceof WindowStage) {
1675                 final WindowStage ownerWindowStage = (WindowStage) ownerStage;
1676                 ownerWindowStage.setEnabled(false);
1677                 return ownerWindowStage;
1678             }
1679         }
1680 
1681         return null;
1682     }
1683 
1684     private static List<CommonDialogs.ExtensionFilter>
1685             convertExtensionFilters(final List<FileChooser.ExtensionFilter>
1686                                             extensionFilters) {
1687         final CommonDialogs.ExtensionFilter[] glassExtensionFilters =
1688                 new CommonDialogs.ExtensionFilter[extensionFilters.size()];
1689 
1690         int i = 0;
1691         for (final FileChooser.ExtensionFilter extensionFilter:
1692                  extensionFilters) {
1693             glassExtensionFilters[i++] =
1694                     new CommonDialogs.ExtensionFilter(
1695                             extensionFilter.getDescription(),
1696                             extensionFilter.getExtensions());
1697         }
1698 
1699         return Arrays.asList(glassExtensionFilters);
1700     }
1701 
1702     @Override
1703     public long getMultiClickTime() {
1704         return View.getMultiClickTime();
1705     }
1706 
1707     @Override
1708     public int getMultiClickMaxX() {
1709         return View.getMultiClickMaxX();
1710     }   
1711 
1712     @Override
1713     public int getMultiClickMaxY() {
1714         return View.getMultiClickMaxY();
1715     }
1716 }