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