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