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