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