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