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