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