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