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