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