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