1 /*
   2  * Copyright (c) 2010, 2017, 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;
  27 
  28 import javafx.application.ConditionalFeature;
  29 import javafx.beans.property.ReadOnlyObjectProperty;
  30 import javafx.geometry.Dimension2D;
  31 import javafx.scene.Scene;
  32 import javafx.scene.effect.BlurType;
  33 import javafx.scene.image.Image;
  34 import javafx.scene.image.PixelFormat;
  35 import javafx.scene.image.WritableImage;
  36 import javafx.scene.input.Dragboard;
  37 import javafx.scene.input.InputMethodRequests;
  38 import javafx.scene.input.KeyCode;
  39 import javafx.scene.input.KeyEvent;
  40 import javafx.scene.input.TransferMode;
  41 import javafx.scene.paint.Color;
  42 import javafx.scene.paint.ImagePattern;
  43 import javafx.scene.paint.LinearGradient;
  44 import javafx.scene.paint.Paint;
  45 import javafx.scene.paint.RadialGradient;
  46 import javafx.scene.paint.Stop;
  47 import javafx.scene.shape.PathElement;
  48 import javafx.scene.shape.SVGPath;
  49 import javafx.scene.shape.StrokeLineCap;
  50 import javafx.scene.shape.StrokeLineJoin;
  51 import javafx.scene.shape.StrokeType;
  52 import javafx.stage.FileChooser.ExtensionFilter;
  53 import javafx.stage.Modality;
  54 import javafx.stage.Stage;
  55 import javafx.stage.StageStyle;
  56 import javafx.stage.Window;
  57 import java.io.File;
  58 import java.io.IOException;
  59 import java.io.InputStream;
  60 import java.nio.ByteBuffer;
  61 import java.security.AccessControlContext;
  62 import java.security.AccessController;
  63 import java.security.PrivilegedAction;
  64 import java.util.ArrayList;
  65 import java.util.HashSet;
  66 import java.util.Iterator;
  67 import java.util.List;
  68 import java.util.Map;
  69 import java.util.Set;
  70 import java.util.WeakHashMap;
  71 import java.util.concurrent.CountDownLatch;
  72 import java.util.concurrent.Future;
  73 import com.sun.glass.ui.CommonDialogs.FileChooserResult;
  74 import com.sun.glass.utils.NativeLibLoader;
  75 import com.sun.javafx.PlatformUtil;
  76 import com.sun.javafx.beans.event.AbstractNotifyListener;
  77 import com.sun.javafx.embed.HostInterface;
  78 import com.sun.javafx.geom.Path2D;
  79 import com.sun.javafx.geom.transform.BaseTransform;
  80 import com.sun.javafx.jmx.HighlightRegion;
  81 import com.sun.javafx.perf.PerformanceTracker;
  82 import com.sun.javafx.runtime.VersionInfo;
  83 import com.sun.javafx.runtime.async.AsyncOperation;
  84 import com.sun.javafx.runtime.async.AsyncOperationListener;
  85 import com.sun.javafx.scene.SceneHelper;
  86 import com.sun.javafx.scene.text.TextLayoutFactory;
  87 import com.sun.javafx.sg.prism.NGCamera;
  88 import com.sun.javafx.sg.prism.NGLightBase;
  89 import com.sun.javafx.sg.prism.NGNode;
  90 import com.sun.javafx.util.Utils;
  91 import com.sun.scenario.DelayedRunnable;
  92 import com.sun.scenario.animation.AbstractMasterTimer;
  93 import com.sun.scenario.effect.AbstractShadow.ShadowMode;
  94 import com.sun.scenario.effect.Color4f;
  95 import com.sun.scenario.effect.FilterContext;
  96 import com.sun.scenario.effect.Filterable;
  97 
  98 
  99 public abstract class Toolkit {
 100     private static String tk;
 101     private static Toolkit TOOLKIT;
 102     private static Thread fxUserThread = null;
 103 
 104     private static final String QUANTUM_TOOLKIT     = "com.sun.javafx.tk.quantum.QuantumToolkit";
 105     private static final String DEFAULT_TOOLKIT     = QUANTUM_TOOLKIT;
 106 
 107     private static final Map gradientMap = new WeakHashMap();
 108 
 109     private static final boolean verbose = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.verbose"));
 110 
 111     private static final String[] msLibNames = {
 112         "api-ms-win-core-console-l1-1-0",
 113         "api-ms-win-core-datetime-l1-1-0",
 114         "api-ms-win-core-debug-l1-1-0",
 115         "api-ms-win-core-errorhandling-l1-1-0",
 116         "api-ms-win-core-file-l1-1-0",
 117         "api-ms-win-core-file-l1-2-0",
 118         "api-ms-win-core-file-l2-1-0",
 119         "api-ms-win-core-handle-l1-1-0",
 120         "api-ms-win-core-heap-l1-1-0",
 121         "api-ms-win-core-interlocked-l1-1-0",
 122         "api-ms-win-core-libraryloader-l1-1-0",
 123         "api-ms-win-core-localization-l1-2-0",
 124         "api-ms-win-core-memory-l1-1-0",
 125         "api-ms-win-core-namedpipe-l1-1-0",
 126         "api-ms-win-core-processenvironment-l1-1-0",
 127         "api-ms-win-core-processthreads-l1-1-0",
 128         "api-ms-win-core-processthreads-l1-1-1",
 129         "api-ms-win-core-profile-l1-1-0",
 130         "api-ms-win-core-rtlsupport-l1-1-0",
 131         "api-ms-win-core-string-l1-1-0",
 132         "api-ms-win-core-synch-l1-1-0",
 133         "api-ms-win-core-synch-l1-2-0",
 134         "api-ms-win-core-sysinfo-l1-1-0",
 135         "api-ms-win-core-timezone-l1-1-0",
 136         "api-ms-win-core-util-l1-1-0",
 137         "api-ms-win-crt-conio-l1-1-0",
 138         "api-ms-win-crt-convert-l1-1-0",
 139         "api-ms-win-crt-environment-l1-1-0",
 140         "api-ms-win-crt-filesystem-l1-1-0",
 141         "api-ms-win-crt-heap-l1-1-0",
 142         "api-ms-win-crt-locale-l1-1-0",
 143         "api-ms-win-crt-math-l1-1-0",
 144         "api-ms-win-crt-multibyte-l1-1-0",
 145         "api-ms-win-crt-private-l1-1-0",
 146         "api-ms-win-crt-process-l1-1-0",
 147         "api-ms-win-crt-runtime-l1-1-0",
 148         "api-ms-win-crt-stdio-l1-1-0",
 149         "api-ms-win-crt-string-l1-1-0",
 150         "api-ms-win-crt-time-l1-1-0",
 151         "api-ms-win-crt-utility-l1-1-0",
 152         "ucrtbase",
 153 
 154         // Finally load VS 2017 DLLs in the following order
 155         "vcruntime140",
 156         "msvcp140",
 157         "concrt140"
 158 };
 159 
 160     private static String lookupToolkitClass(String name) {
 161         if ("prism".equalsIgnoreCase(name)) {
 162             return QUANTUM_TOOLKIT;
 163         } else if ("quantum".equalsIgnoreCase(name)) {
 164             return QUANTUM_TOOLKIT;
 165         }
 166         return name;
 167     }
 168 
 169     public static synchronized void loadMSWindowsLibraries() {
 170         for (String libName : msLibNames) {
 171             try {
 172                 NativeLibLoader.loadLibrary(libName);
 173             } catch (Throwable t) {
 174                 if (verbose) {
 175                     System.err.println("Error: failed to load "
 176                             + libName + ".dll : " + t);
 177                 }
 178             }
 179         }
 180     }
 181 
 182     private static String getDefaultToolkit() {
 183         if (PlatformUtil.isWindows()) {
 184             return DEFAULT_TOOLKIT;
 185         } else if (PlatformUtil.isMac()) {
 186             return DEFAULT_TOOLKIT;
 187         } else if (PlatformUtil.isLinux()) {
 188             return DEFAULT_TOOLKIT;
 189         } else if (PlatformUtil.isIOS()) {
 190             return DEFAULT_TOOLKIT;
 191         } else if (PlatformUtil.isAndroid()) {
 192            return DEFAULT_TOOLKIT;
 193         }
 194 
 195         throw new UnsupportedOperationException(System.getProperty("os.name") + " is not supported");
 196     }
 197 
 198     public static synchronized Toolkit getToolkit() {
 199         if (TOOLKIT != null) {
 200             return TOOLKIT;
 201         }
 202 
 203         // This loading of msvcp140.dll and vcruntime140.dll (VS2017) is required on Windows platforms
 204         if (PlatformUtil.isWindows()) {
 205             loadMSWindowsLibraries();
 206         }
 207 
 208         AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
 209             // Get the javafx.version and javafx.runtime.version from a preconstructed
 210             // java class, VersionInfo, created at build time.
 211             VersionInfo.setupSystemProperties();
 212             return null;
 213         });
 214 
 215         boolean userSpecifiedToolkit = true;
 216 
 217         // Check a system property to see if there is a specific toolkit to use.
 218         // This is not a doPriviledged check so that applets cannot use this.
 219         String forcedToolkit = null;
 220         try {
 221             forcedToolkit = System.getProperty("javafx.toolkit");
 222         } catch (SecurityException ex) {}
 223 
 224         if (forcedToolkit == null) {
 225             forcedToolkit = tk;
 226         }
 227         if (forcedToolkit == null) {
 228             userSpecifiedToolkit = false;
 229             forcedToolkit = getDefaultToolkit();
 230         }
 231 
 232         if (forcedToolkit.indexOf('.') == -1) {
 233             // Turn a short name into a fully qualified classname
 234             forcedToolkit = lookupToolkitClass(forcedToolkit);
 235         }
 236 
 237         boolean printToolkit = verbose
 238                 || (userSpecifiedToolkit && !forcedToolkit.endsWith("StubToolkit"));
 239 
 240         try {
 241             Class clz = null;
 242 
 243             try {
 244                 // try our priveledged loader first
 245                 final ClassLoader loader = Toolkit.class.getClassLoader();
 246                 clz = Class.forName(forcedToolkit, false, loader);
 247             } catch (ClassNotFoundException e) {
 248                 // fall back and try the application class loader
 249                 final ClassLoader loader = Thread.currentThread().getContextClassLoader();
 250                 clz = Class.forName(forcedToolkit, false, loader);
 251             }
 252 
 253             // Check that clz is a subclass of Toolkit
 254             if (!Toolkit.class.isAssignableFrom(clz)) {
 255                 throw new IllegalArgumentException("Unrecognized FX Toolkit class: "
 256                         + forcedToolkit);
 257             }
 258 
 259             TOOLKIT = (Toolkit)clz.newInstance();
 260             if (TOOLKIT.init()) {
 261                 if (printToolkit) {
 262                     System.err.println("JavaFX: using " + forcedToolkit);
 263                 }
 264                 return TOOLKIT;
 265             }
 266             TOOLKIT = null;
 267         } catch (Exception any) {
 268             TOOLKIT = null;
 269             any.printStackTrace();
 270         }
 271 
 272         throw new RuntimeException("No toolkit found");
 273     }
 274 
 275     protected static Thread getFxUserThread() {
 276         return fxUserThread;
 277     }
 278 
 279     protected static void setFxUserThread(Thread t) {
 280         if (fxUserThread != null) {
 281             throw new IllegalStateException("Error: FX User Thread already initialized");
 282         }
 283 
 284         fxUserThread = t;
 285     }
 286 
 287     public void checkFxUserThread() {
 288         // Throw exception if not on FX user thread
 289         if (!isFxUserThread()) {
 290             throw new IllegalStateException("Not on FX application thread; currentThread = "
 291                     + Thread.currentThread().getName());
 292         }
 293     }
 294 
 295     // Toolkit can override this if needed
 296     public boolean isFxUserThread() {
 297         return Thread.currentThread() == fxUserThread;
 298     }
 299 
 300     protected Toolkit() {
 301     }
 302 
 303     public abstract boolean init();
 304 
 305     /**
 306      * Indicates whether or not a nested event loop can be started
 307      * from the current thread in the current state. Note that a nested
 308      * event loop is not allowed outside of an event handler.
 309      *
 310      * @return flag indicating whether a nested event loop can be started.
 311      */
 312     public abstract boolean canStartNestedEventLoop();
 313 
 314     /**
 315      * Enter a nested event loop and block until the corresponding
 316      * exitNestedEventLoop call is made.
 317      * The key passed into this method is used to
 318      * uniquely identify the matched enter/exit pair. This method creates a
 319      * new nested event loop and blocks until the corresponding
 320      * exitNestedEventLoop method is called with the same key.
 321      * The return value of this method will be the {@code rval}
 322      * object supplied to the exitNestedEventLoop method call that unblocks it.
 323      *
 324      * @param key the Object that identifies the nested event loop, which
 325      * must not be null
 326      *
 327      * @throws IllegalArgumentException if the specified key is associated
 328      * with a nested event loop that has not yet returned
 329      *
 330      * @throws NullPointerException if the key is null
 331      *
 332      * @throws IllegalStateException if this method is called on a thread
 333      * other than the FX Application thread
 334      *
 335      * @return the value passed into the corresponding call to exitEventLoop
 336      */
 337     public abstract Object enterNestedEventLoop(Object key);
 338 
 339     /**
 340      * Exit a nested event loop and unblock the caller of the
 341      * corresponding enterNestedEventLoop.
 342      * The key passed into this method is used to
 343      * uniquely identify the matched enter/exit pair. This method causes the
 344      * nested event loop that was previously created with the key to exit and
 345      * return control to the caller. If the specified nested event loop is not
 346      * the inner-most loop then it will not return until all other inner loops
 347      * also exit.
 348      *
 349      * @param key the Object that identifies the nested event loop, which
 350      * must not be null
 351      *
 352      * @param rval an Object that is returned to the caller of the
 353      * corresponding enterNestedEventLoop. This may be null.
 354      *
 355      * @throws IllegalArgumentException if the specified key is not associated
 356      * with an active nested event loop
 357      *
 358      * @throws NullPointerException if the key is null
 359      *
 360      * @throws IllegalStateException if this method is called on a thread
 361      * other than the FX Application thread
 362      */
 363     public abstract void exitNestedEventLoop(Object key, Object rval);
 364 
 365     public abstract boolean isNestedLoopRunning();
 366 
 367     public abstract TKStage createTKStage(Window peerWindow, boolean securityDialog, StageStyle stageStyle, boolean primary, Modality modality, TKStage owner, boolean rtl, AccessControlContext acc);
 368 
 369     public abstract TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner, AccessControlContext acc);
 370     public abstract TKStage createTKEmbeddedStage(HostInterface host, AccessControlContext acc);
 371 
 372     /**
 373      * Creates an AppletWindow using the provided window pointer as the parent
 374      * window.
 375      *
 376      * @param parent the native parent which will contain the primary stage
 377      * window(s). Used on Windows/Linux platforms.
 378      *
 379      * @param serverName the name of CARemoteLayerServer which
 380      * will be used to register native layer. Used on Mac platform.
 381      */
 382     public abstract AppletWindow createAppletWindow(long parent, String serverName);
 383 
 384     /**
 385      * Perform cleanup in preparation for applet termination, including closing
 386      * the applet window.
 387      */
 388     public abstract void closeAppletWindow();
 389 
 390     private final Map<TKPulseListener,AccessControlContext> stagePulseListeners =
 391             new WeakHashMap<TKPulseListener,AccessControlContext>();
 392     private final Map<TKPulseListener,AccessControlContext> scenePulseListeners =
 393             new WeakHashMap<TKPulseListener,AccessControlContext>();
 394     private final Map<TKPulseListener,AccessControlContext> postScenePulseListeners =
 395             new WeakHashMap<TKPulseListener,AccessControlContext>();
 396     private final Map<TKListener,AccessControlContext> toolkitListeners =
 397             new WeakHashMap<TKListener,AccessControlContext>();
 398 
 399     // The set of shutdown hooks is strongly held to avoid premature GC.
 400     private final Set<Runnable> shutdownHooks = new HashSet<Runnable>();
 401 
 402     private void runPulse(final TKPulseListener listener,
 403             final AccessControlContext acc) {
 404 
 405         if (acc == null) {
 406             throw new IllegalStateException("Invalid AccessControlContext");
 407         }
 408 
 409         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 410             listener.pulse();
 411             return null;
 412         }, acc);
 413     }
 414 
 415     public void firePulse() {
 416         // Stages need to be notified of pulses before scenes so the Stage can resized
 417         // and those changes propogated to scene before it gets its pulse to update
 418 
 419         // Copy of listener map
 420         final Map<TKPulseListener,AccessControlContext> stagePulseList =
 421                 new WeakHashMap<TKPulseListener,AccessControlContext>();
 422         final Map<TKPulseListener,AccessControlContext> scenePulseList =
 423                 new WeakHashMap<TKPulseListener,AccessControlContext>();
 424         final Map<TKPulseListener,AccessControlContext> postScenePulseList =
 425                 new WeakHashMap<TKPulseListener,AccessControlContext>();
 426 
 427         synchronized (this) {
 428             stagePulseList.putAll(stagePulseListeners);
 429             scenePulseList.putAll(scenePulseListeners);
 430             postScenePulseList.putAll(postScenePulseListeners);
 431         }
 432         for (Map.Entry<TKPulseListener,AccessControlContext> entry : stagePulseList.entrySet()) {
 433             runPulse(entry.getKey(), entry.getValue());
 434         }
 435         for (Map.Entry<TKPulseListener,AccessControlContext> entry : scenePulseList.entrySet()) {
 436             runPulse(entry.getKey(), entry.getValue());
 437         }
 438         for (Map.Entry<TKPulseListener,AccessControlContext> entry : postScenePulseList.entrySet()) {
 439             runPulse(entry.getKey(), entry.getValue());
 440         }
 441 
 442         if (lastTkPulseListener != null) {
 443             runPulse(lastTkPulseListener, lastTkPulseAcc);
 444         }
 445     }
 446     public void addStageTkPulseListener(TKPulseListener listener) {
 447         if (listener == null) {
 448             return;
 449         }
 450         synchronized (this) {
 451             AccessControlContext acc = AccessController.getContext();
 452             stagePulseListeners.put(listener, acc);
 453         }
 454     }
 455     public void removeStageTkPulseListener(TKPulseListener listener) {
 456         synchronized (this) {
 457             stagePulseListeners.remove(listener);
 458         }
 459     }
 460     public void addSceneTkPulseListener(TKPulseListener listener) {
 461         if (listener == null) {
 462             return;
 463         }
 464         synchronized (this) {
 465             AccessControlContext acc = AccessController.getContext();
 466             scenePulseListeners.put(listener, acc);
 467         }
 468     }
 469     public void removeSceneTkPulseListener(TKPulseListener listener) {
 470         synchronized (this) {
 471             scenePulseListeners.remove(listener);
 472         }
 473     }
 474     public void addPostSceneTkPulseListener(TKPulseListener listener) {
 475         if (listener == null) {
 476             return;
 477         }
 478         synchronized (this) {
 479             AccessControlContext acc = AccessController.getContext();
 480             postScenePulseListeners.put(listener, acc);
 481         }
 482     }
 483     public void removePostSceneTkPulseListener(TKPulseListener listener) {
 484         synchronized (this) {
 485             postScenePulseListeners.remove(listener);
 486         }
 487     }
 488 
 489     public void addTkListener(TKListener listener) {
 490         if (listener == null) {
 491             return;
 492         }
 493         AccessControlContext acc = AccessController.getContext();
 494         toolkitListeners.put(listener, acc);
 495     }
 496 
 497     public void removeTkListener(TKListener listener) {
 498         toolkitListeners.remove(listener);
 499     }
 500 
 501     private TKPulseListener lastTkPulseListener = null;
 502     private AccessControlContext lastTkPulseAcc = null;
 503     public void setLastTkPulseListener(TKPulseListener listener) {
 504         lastTkPulseAcc = AccessController.getContext();
 505         lastTkPulseListener = listener;
 506     }
 507 
 508     public void addShutdownHook(Runnable hook) {
 509         if (hook == null) {
 510             return;
 511         }
 512         synchronized (shutdownHooks) {
 513             shutdownHooks.add(hook);
 514         }
 515     }
 516 
 517     public void removeShutdownHook(Runnable hook) {
 518         synchronized (shutdownHooks) {
 519             shutdownHooks.remove(hook);
 520         }
 521     }
 522 
 523     protected void notifyShutdownHooks() {
 524         List<Runnable> hooks;
 525         synchronized (shutdownHooks) {
 526             hooks = new ArrayList<Runnable>(shutdownHooks);
 527             shutdownHooks.clear();
 528         }
 529 
 530         for (Runnable hook : hooks) {
 531             hook.run();
 532         }
 533     }
 534 
 535     public void notifyWindowListeners(final List<TKStage> windows) {
 536         for (Map.Entry<TKListener,AccessControlContext> entry : toolkitListeners.entrySet()) {
 537             final TKListener listener = entry.getKey();
 538             final AccessControlContext acc = entry.getValue();
 539             if (acc == null) {
 540                 throw new IllegalStateException("Invalid AccessControlContext");
 541             }
 542 
 543             AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 544                 listener.changedTopLevelWindows(windows);
 545                 return null;
 546             }, acc);
 547         }
 548     }
 549 
 550     public void notifyLastNestedLoopExited() {
 551         for (TKListener listener: toolkitListeners.keySet()) {
 552             listener.exitedLastNestedLoop();
 553         }
 554     }
 555 
 556     // notify the pulse timer code that we need the next pulse to happen
 557     // this flag is cleared each cycle so subsequent pulses must be requested
 558     public abstract void requestNextPulse();
 559 
 560     public abstract Future addRenderJob(RenderJob rj);
 561 
 562     public abstract ImageLoader loadImage(String url,
 563                                           double width, double height,
 564                                           boolean preserveRatio,
 565                                           boolean smooth);
 566     public abstract ImageLoader loadImage(InputStream stream,
 567                                           double width, double height,
 568                                           boolean preserveRatio,
 569                                           boolean smooth);
 570     public abstract AsyncOperation loadImageAsync(
 571                                           AsyncOperationListener<? extends ImageLoader> listener,
 572                                           String url,
 573                                           double width, double height,
 574                                           boolean preserveRatio,
 575                                           boolean smooth);
 576 
 577     /*
 578      * The loadPlatformImage method supports the following image types:
 579      *   - an object returned by the renderToImage method
 580      *   - an instance of com.sun.prism.Image (in case of prism)
 581      *   - an instance of an external image object, which can be a BufferedImage
 582      * If JavaFX Image had one more constructor Image(ImageLoader),
 583      * we could introduce a different method for external image loading support.
 584      */
 585 
 586     public abstract ImageLoader loadPlatformImage(Object platformImage);
 587 
 588     public abstract PlatformImage createPlatformImage(int w, int h);
 589 
 590     // Indicates the default state of smooth for ImageView and MediaView
 591     // Subclasses may override this to provide a platform-specific default
 592     public boolean getDefaultImageSmooth() { return true; }
 593 
 594     public abstract void startup(Runnable runnable);
 595     public abstract void defer(Runnable runnable);
 596     public void exit() {
 597         fxUserThread = null;
 598     }
 599 
 600     public abstract Map<Object, Object> getContextMap();
 601     public abstract int getRefreshRate();
 602     public abstract void setAnimationRunnable(DelayedRunnable animationRunnable);
 603     public abstract PerformanceTracker getPerformanceTracker();
 604     public abstract PerformanceTracker createPerformanceTracker();
 605 
 606     //to be used for testing only
 607     public abstract void waitFor(Task t);
 608 
 609     private Object checkSingleColor(List<Stop> stops) {
 610         if (stops.size() == 2) {
 611             Color c = stops.get(0).getColor();
 612             if (c.equals(stops.get(1).getColor())) {
 613                 return Toolkit.getPaintAccessor().getPlatformPaint(c);
 614             }
 615         }
 616         return null;
 617     }
 618 
 619     private Object getPaint(LinearGradient paint) {
 620         Object p = gradientMap.get(paint);
 621         if (p != null) {
 622             return p;
 623         }
 624         p = checkSingleColor(paint.getStops());
 625         if (p == null) {
 626             p = createLinearGradientPaint(paint);
 627         }
 628         gradientMap.put(paint, p);
 629         return p;
 630     }
 631 
 632     private Object getPaint(RadialGradient paint) {
 633         Object p = gradientMap.get(paint);
 634         if (p != null) {
 635             return p;
 636         }
 637         p = checkSingleColor(paint.getStops());
 638         if (p == null) {
 639             p = createRadialGradientPaint(paint);
 640         }
 641         gradientMap.put(paint, p);
 642         return p;
 643     }
 644 
 645     public Object getPaint(Paint paint) {
 646         if (paint instanceof Color) {
 647             return createColorPaint((Color) paint);
 648         }
 649 
 650         if (paint instanceof LinearGradient) {
 651             return getPaint((LinearGradient) paint);
 652         }
 653 
 654         if (paint instanceof RadialGradient) {
 655             return getPaint((RadialGradient) paint);
 656         }
 657 
 658         if (paint instanceof ImagePattern) {
 659             return createImagePatternPaint((ImagePattern) paint);
 660         }
 661 
 662         return null;
 663     }
 664 
 665     protected static final double clampStopOffset(double offset) {
 666         return (offset > 1.0) ? 1.0 :
 667                (offset < 0.0) ? 0.0 : offset;
 668     }
 669 
 670     protected abstract Object createColorPaint(Color paint);
 671     protected abstract Object createLinearGradientPaint(LinearGradient paint);
 672     protected abstract Object createRadialGradientPaint(RadialGradient paint);
 673     protected abstract Object createImagePatternPaint(ImagePattern paint);
 674 
 675     public abstract void
 676         accumulateStrokeBounds(com.sun.javafx.geom.Shape shape,
 677                                float bbox[],
 678                                StrokeType type,
 679                                double strokewidth,
 680                                StrokeLineCap cap,
 681                                StrokeLineJoin join,
 682                                float miterLimit,
 683                                BaseTransform tx);
 684 
 685     public abstract boolean
 686         strokeContains(com.sun.javafx.geom.Shape shape,
 687                        double x, double y,
 688                        StrokeType type,
 689                        double strokewidth,
 690                        StrokeLineCap cap,
 691                        StrokeLineJoin join,
 692                        float miterLimit);
 693 
 694     public abstract com.sun.javafx.geom.Shape
 695         createStrokedShape(com.sun.javafx.geom.Shape shape,
 696                            StrokeType type,
 697                            double strokewidth,
 698                            StrokeLineCap cap,
 699                            StrokeLineJoin join,
 700                            float miterLimit,
 701                            float[] dashArray,
 702                            float dashOffset);
 703 
 704     public abstract int getKeyCodeForChar(String character);
 705     public abstract Dimension2D getBestCursorSize(int preferredWidth, int preferredHeight);
 706     public abstract int getMaximumCursorColors();
 707     public abstract PathElement[] convertShapeToFXPath(Object shape);
 708 
 709     public abstract Filterable toFilterable(Image img);
 710     public abstract FilterContext getFilterContext(Object config);
 711 
 712     public abstract boolean isForwardTraversalKey(KeyEvent e);
 713     public abstract boolean isBackwardTraversalKey(KeyEvent e);
 714 
 715     public abstract AbstractMasterTimer getMasterTimer();
 716 
 717     public abstract FontLoader getFontLoader();
 718     public abstract TextLayoutFactory getTextLayoutFactory();
 719 
 720     public abstract Object createSVGPathObject(SVGPath svgpath);
 721     public abstract Path2D createSVGPath2D(SVGPath svgpath);
 722 
 723     /**
 724      * Tests whether the pixel on the given coordinates in the given image
 725      * is non-empty (not fully transparent). Return value is not defined
 726      * for pixels out of the image bounds.
 727      */
 728     public abstract boolean imageContains(Object image, float x, float y);
 729 
 730     public abstract TKClipboard getSystemClipboard();
 731 
 732     public TKClipboard createLocalClipboard() {
 733         return new LocalClipboard();
 734     }
 735 
 736     public abstract TKSystemMenu getSystemMenu();
 737 
 738     public abstract TKClipboard getNamedClipboard(String name);
 739 
 740     public boolean isSupported(ConditionalFeature feature) { return false; }
 741 
 742     public boolean isMSAASupported() { return false; }
 743 
 744     public abstract ScreenConfigurationAccessor setScreenConfigurationListener(TKScreenConfigurationListener listener);
 745 
 746     public abstract Object getPrimaryScreen();
 747 
 748     public abstract List<?> getScreens();
 749 
 750     public abstract ScreenConfigurationAccessor getScreenConfigurationAccessor();
 751 
 752     public abstract void registerDragGestureListener(TKScene s, Set<TransferMode> tm, TKDragGestureListener l);
 753 
 754     /**
 755      * This function is called when a drag originates within a JavaFX application.
 756      * This means that drags that originate in other applications / from the OS
 757      * do not call this function.
 758      * The argument o represents an object used to identify a scene on which
 759      * the drag has started.
 760      */
 761     public abstract void startDrag(TKScene scene, Set<TransferMode> tm, TKDragSourceListener l, Dragboard dragboard);
 762 
 763     // template function which can be implemented by toolkit impls such that they
 764     // can be informed when a drag and drop operation has completed. This allows
 765     // for any cleanup that may need to be done.
 766     public void stopDrag(Dragboard dragboard) {
 767         // no-op
 768     }
 769 
 770     public abstract void enableDrop(TKScene s, TKDropTargetListener l);
 771 
 772     public interface Task {
 773         boolean isFinished();
 774     }
 775 
 776     public Color4f toColor4f(Color color) {
 777         return new Color4f((float)color.getRed(), (float)color.getGreen(), (float)color.getBlue(), (float)color.getOpacity());
 778     }
 779 
 780 
 781     public ShadowMode toShadowMode(BlurType blurType) {
 782         switch (blurType) {
 783             case ONE_PASS_BOX:
 784                 return ShadowMode.ONE_PASS_BOX;
 785             case TWO_PASS_BOX:
 786                 return ShadowMode.TWO_PASS_BOX;
 787             case THREE_PASS_BOX:
 788                 return ShadowMode.THREE_PASS_BOX;
 789             default:
 790                 return ShadowMode.GAUSSIAN;
 791         }
 792     }
 793 
 794     public abstract void installInputMethodRequests(TKScene scene, InputMethodRequests requests);
 795 
 796     /*
 797      * ImageRenderingContext holds the many parameters passed to
 798      * the renderToImage method.
 799      * The use of the parameters is specified by the renderToImage
 800      * method.
 801      * @see #renderToImage
 802      */
 803     public static class ImageRenderingContext {
 804         // Node to be rendered
 805         public NGNode root;
 806 
 807         // Viewport for rendering
 808         public int x;
 809         public int y;
 810         public int width;
 811         public int height;
 812 
 813         // Initial transform for root node
 814         public BaseTransform transform;
 815 
 816         // Rendering parameters either from Scene or SnapShotParams
 817         public boolean depthBuffer;
 818         public Object platformPaint;
 819         public NGCamera camera;
 820         public NGLightBase[] lights;
 821 
 822         // PlatformImage into which to render or null
 823         public Object platformImage;
 824     }
 825 
 826     /*
 827      * This method renders a PG-graph to a platform image object.
 828      * The returned object can be turned into a useable
 829      * scene graph image using the appropriate factor of the
 830      * Image class.
 831      * The scale specified in the params is used to scale the
 832      * entire rendering before any transforms in the nodes are
 833      * applied.
 834      * The width and height specified in the params represent
 835      * the user space dimensions to be rendered.  The returned
 836      * image will be large enough to hold these dimensions
 837      * scaled by the scale parameter.
 838      * The depthBuffer specified in the params is used to determine
 839      * with or without depthBuffer rendering should be performed.
 840      * The root node is the root of a tree of toolkit-specific
 841      * scene graph peer nodes to be rendered and should have
 842      * been previously created by this toolkit.
 843      * The platformPaint specified in the params must be
 844      * generated by the appropriate Toolkit.createPaint method
 845      * and is used to fill the background of the image before
 846      * rendering the scene graph.
 847      * The platformImage specified in the params may be non-null
 848      * and should be a previous return value from this method.
 849      * If it is non-null then it may be reused as the return value
 850      * of this method if it is still valid and large enough to
 851      * hold the requested size.
 852      *
 853      * @param context a ImageRenderingContext instance specifying
 854      *               the various rendering parameters
 855      * @return a platform specific image object
 856      * @see Toolkit.getImageAccessor().fromPlatformImage
 857      */
 858 
 859     public abstract Object renderToImage(ImageRenderingContext context);
 860 
 861     /**
 862      * Returns the key code for the key which is commonly used on the
 863      * corresponding platform as a modifier key in shortcuts. For example
 864      * it is {@code KeyCode.CONTROL} on Windows (Ctrl + C, Ctrl + V ...) and
 865      * {@code KeyCode.META} on MacOS (Cmd + C, Cmd + V ...).
 866      *
 867      * @return the key code for shortcut modifier key
 868      */
 869     public KeyCode getPlatformShortcutKey() {
 870         return PlatformUtil.isMac() ? KeyCode.META : KeyCode.CONTROL;
 871     }
 872 
 873     public abstract FileChooserResult showFileChooser(
 874             TKStage ownerWindow,
 875             String title,
 876             File initialDirectory,
 877             String initialFileName,
 878             FileChooserType fileChooserType,
 879             List<ExtensionFilter> extensionFilters,
 880             ExtensionFilter selectedFilter);
 881 
 882     public abstract File showDirectoryChooser(
 883             TKStage ownerWindow,
 884             String title,
 885             File initialDirectory);
 886 
 887     /*
 888      * Methods for obtaining "double-click" speed value.
 889      */
 890     public abstract long getMultiClickTime();
 891     public abstract int getMultiClickMaxX();
 892     public abstract int getMultiClickMaxY();
 893 
 894     private CountDownLatch pauseScenesLatch = null;
 895 
 896     /*
 897      * Causes all scenes to stop by removing its TKPulseListener from Toolkit.
 898      * It is used by Scenegraph-JMX bean.
 899      */
 900     public void pauseScenes() {
 901         pauseScenesLatch = new CountDownLatch(1);
 902         Window.getWindows().stream().forEach(window -> {
 903             final Scene scene = window.getScene();
 904             if (scene != null) {
 905                 this.removeSceneTkPulseListener(SceneHelper.getScenePulseListener(scene));
 906             }
 907         });
 908         this.getMasterTimer().pause();
 909         SceneHelper.setPaused(true);
 910     }
 911 
 912     /*
 913      * Resume all scenes by registering its TKPulseListener back to Toolkit.
 914      * It is used by Scenegraph-JMX bean.
 915      */
 916     public void resumeScenes() {
 917         SceneHelper.setPaused(false);
 918         this.getMasterTimer().resume();
 919         Window.getWindows().stream().forEach(window -> {
 920             final Scene scene = window.getScene();
 921             if (scene != null) {
 922                 this.addSceneTkPulseListener(SceneHelper.getScenePulseListener(scene));
 923             }
 924         });
 925         pauseScenesLatch.countDown();
 926         pauseScenesLatch = null;
 927     }
 928 
 929     /**
 930      * Used to pause current thread when the Scene-graph is in "PAUSED" mode.
 931      * It is mainly used by {@link javafx.application.Platform#runLater(Runnable)}
 932      * to block the thread and not to pass the runnable to FX event queue.
 933      */
 934     public void pauseCurrentThread() {
 935         final CountDownLatch cdl = pauseScenesLatch;
 936         if (cdl == null) {
 937             return;
 938         }
 939         try {
 940             cdl.await();
 941         } catch (InterruptedException e) { }
 942     }
 943 
 944     private Set<HighlightRegion> highlightRegions;
 945 
 946     /**
 947      * Getter for the set of regions to be highlighted using the JMX tooling
 948      * interface.
 949      *
 950      * @return the set of regions to be highlighted.
 951      */
 952     public Set<HighlightRegion> getHighlightedRegions() {
 953         if (highlightRegions == null) {
 954             highlightRegions = new HashSet<HighlightRegion>();
 955         }
 956         return highlightRegions;
 957     }
 958 
 959     public interface WritableImageAccessor {
 960         public void loadTkImage(WritableImage wimg, Object loader);
 961         public Object getTkImageLoader(WritableImage wimg);
 962     }
 963 
 964     private static WritableImageAccessor writableImageAccessor = null;
 965 
 966     public static void setWritableImageAccessor(WritableImageAccessor accessor) {
 967         writableImageAccessor = accessor;
 968     }
 969 
 970     public static WritableImageAccessor getWritableImageAccessor() {
 971         return writableImageAccessor;
 972     }
 973 
 974     public interface PaintAccessor {
 975         public boolean isMutable(Paint paint);
 976         public Object getPlatformPaint(Paint paint);
 977         public void addListener(Paint paint, AbstractNotifyListener platformChangeListener);
 978         public void removeListener(Paint paint, AbstractNotifyListener platformChangeListener);
 979     }
 980 
 981     private static PaintAccessor paintAccessor = null;
 982 
 983     public static void setPaintAccessor(PaintAccessor accessor) {
 984         paintAccessor = accessor;
 985     }
 986 
 987     public static PaintAccessor getPaintAccessor() {
 988         return paintAccessor;
 989     }
 990 
 991     public interface ImageAccessor {
 992         public boolean isAnimation(Image image);
 993         public ReadOnlyObjectProperty<PlatformImage>getImageProperty(Image image);
 994         public int[] getPreColors(PixelFormat<ByteBuffer> pf);
 995         public int[] getNonPreColors(PixelFormat<ByteBuffer> pf);
 996         public Object getPlatformImage(Image image);
 997         public Image fromPlatformImage(Object image);
 998     }
 999 
1000     private static ImageAccessor imageAccessor;
1001 
1002     static {
1003         // Need to ensure that the Image class is loaded since Toolkit class
1004         // is the provider of getImageAccessor method and sets the accessor.
1005         Utils.forceInit(Image.class);
1006     }
1007 
1008     public static void setImageAccessor(ImageAccessor accessor) {
1009         imageAccessor = accessor;
1010     }
1011 
1012     public static ImageAccessor getImageAccessor() {
1013         return imageAccessor;
1014     }
1015 
1016     public String getThemeName() {
1017         return null;
1018     }
1019 }