1 /* 2 * Copyright (c) 2010, 2013, 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.quantum; 27 28 import javafx.application.ConditionalFeature; 29 import javafx.geometry.Dimension2D; 30 import javafx.scene.image.Image; 31 import javafx.scene.input.Dragboard; 32 import javafx.scene.input.InputMethodRequests; 33 import javafx.scene.input.KeyCode; 34 import javafx.scene.input.KeyEvent; 35 import javafx.scene.input.TransferMode; 36 import javafx.scene.paint.Color; 37 import javafx.scene.paint.CycleMethod; 38 import javafx.scene.paint.ImagePattern; 39 import javafx.scene.paint.LinearGradient; 40 import javafx.scene.paint.RadialGradient; 41 import javafx.scene.paint.Stop; 42 import javafx.scene.shape.ClosePath; 43 import javafx.scene.shape.CubicCurveTo; 44 import javafx.scene.shape.FillRule; 45 import javafx.scene.shape.LineTo; 46 import javafx.scene.shape.MoveTo; 47 import javafx.scene.shape.PathElement; 48 import javafx.scene.shape.QuadCurveTo; 49 import javafx.scene.shape.SVGPath; 50 import javafx.scene.shape.StrokeLineCap; 51 import javafx.scene.shape.StrokeLineJoin; 52 import javafx.scene.shape.StrokeType; 53 import javafx.stage.FileChooser; 54 import javafx.stage.Modality; 55 import javafx.stage.StageStyle; 56 import javafx.stage.Window; 57 import java.io.File; 58 import java.io.InputStream; 59 import java.nio.ByteBuffer; 60 import java.nio.IntBuffer; 61 import java.security.AccessControlContext; 62 import java.security.AccessController; 63 import java.security.PrivilegedAction; 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 import java.util.Collections; 67 import java.util.HashMap; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Set; 71 import java.util.concurrent.CountDownLatch; 72 import java.util.concurrent.Future; 73 import java.util.concurrent.TimeUnit; 74 import java.util.concurrent.atomic.AtomicBoolean; 75 import com.sun.glass.ui.Application; 76 import com.sun.glass.ui.Clipboard; 77 import com.sun.glass.ui.ClipboardAssistance; 78 import com.sun.glass.ui.CommonDialogs; 79 import com.sun.glass.ui.CommonDialogs.FileChooserResult; 80 import com.sun.glass.ui.EventLoop; 81 import com.sun.glass.ui.Screen; 82 import com.sun.glass.ui.Timer; 83 import com.sun.glass.ui.View; 84 import com.sun.javafx.PlatformUtil; 85 import com.sun.javafx.embed.HostInterface; 86 import com.sun.javafx.geom.Path2D; 87 import com.sun.javafx.geom.PathIterator; 88 import com.sun.javafx.geom.Shape; 89 import com.sun.javafx.geom.transform.BaseTransform; 90 import com.sun.javafx.perf.PerformanceTracker; 91 import com.sun.javafx.runtime.async.AbstractRemoteResource; 92 import com.sun.javafx.runtime.async.AsyncOperationListener; 93 import com.sun.javafx.scene.text.HitInfo; 94 import com.sun.javafx.scene.text.TextLayoutFactory; 95 import com.sun.javafx.sg.prism.NGNode; 96 import com.sun.javafx.tk.AppletWindow; 97 import com.sun.javafx.tk.CompletionListener; 98 import com.sun.javafx.tk.FileChooserType; 99 import com.sun.javafx.tk.FontLoader; 100 import com.sun.javafx.tk.ImageLoader; 101 import com.sun.javafx.tk.PlatformImage; 102 import com.sun.javafx.tk.RenderJob; 103 import com.sun.javafx.tk.ScreenConfigurationAccessor; 104 import com.sun.javafx.tk.TKClipboard; 105 import com.sun.javafx.tk.TKDragGestureListener; 106 import com.sun.javafx.tk.TKDragSourceListener; 107 import com.sun.javafx.tk.TKDropTargetListener; 108 import com.sun.javafx.tk.TKScene; 109 import com.sun.javafx.tk.TKScreenConfigurationListener; 110 import com.sun.javafx.tk.TKStage; 111 import com.sun.javafx.tk.TKSystemMenu; 112 import com.sun.javafx.tk.Toolkit; 113 import com.sun.prism.BasicStroke; 114 import com.sun.prism.Graphics; 115 import com.sun.prism.GraphicsPipeline; 116 import com.sun.prism.PixelFormat; 117 import com.sun.prism.RTTexture; 118 import com.sun.prism.ResourceFactory; 119 import com.sun.prism.ResourceFactoryListener; 120 import com.sun.prism.Texture.WrapMode; 121 import com.sun.prism.impl.Disposer; 122 import com.sun.prism.impl.PrismSettings; 123 import com.sun.scenario.DelayedRunnable; 124 import com.sun.scenario.animation.AbstractMasterTimer; 125 import com.sun.scenario.effect.FilterContext; 126 import com.sun.scenario.effect.Filterable; 127 import com.sun.scenario.effect.impl.prism.PrFilterContext; 128 import com.sun.scenario.effect.impl.prism.PrImage; 129 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGER; 130 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED; 131 import com.sun.prism.impl.ManagedResource; 132 133 public final class QuantumToolkit extends Toolkit { 134 135 public static final boolean verbose = 136 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 137 @Override public Boolean run() { 138 return Boolean.getBoolean("quantum.verbose"); 139 } 140 }); 141 142 public static final boolean pulseDebug = 143 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 144 @Override public Boolean run() { 145 return Boolean.getBoolean("quantum.pulse"); 146 } 147 }); 148 149 public static final boolean multithreaded = 150 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 151 @Override public Boolean run() { 152 // If it is not specified, or it is true, then it should 153 // be true. Otherwise it should be false. 154 String value = System.getProperty("quantum.multithreaded"); 155 value = value == null ? "" : value.trim(); 156 final boolean result = "".equals(value) || Boolean.parseBoolean(value); 157 if (verbose) { 158 System.out.println(result ? "Multi-Threading Enabled" : "Multi-Threading Disabled"); 159 } 160 return result; 161 } 162 }); 163 164 private static boolean debug = 165 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 166 @Override public Boolean run() { 167 return Boolean.getBoolean("quantum.debug"); 168 } 169 }); 170 171 private static Integer pulseHZ = 172 AccessController.doPrivileged(new PrivilegedAction<Integer>() { 173 @Override public Integer run() { 174 return Integer.getInteger("javafx.animation.pulse"); 175 } 176 }); 177 178 static final boolean liveResize = 179 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 180 @Override public Boolean run() { 181 boolean isSWT = "swt".equals(System.getProperty("glass.platform")); 182 String result = (PlatformUtil.isMac() || PlatformUtil.isWindows()) && !isSWT ? "true" : "false"; 183 return "true".equals(System.getProperty("javafx.live.resize", result)); 184 } 185 }); 186 187 static final boolean drawInPaint = 188 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 189 @Override public Boolean run() { 190 boolean isSWT = "swt".equals(System.getProperty("glass.platform")); 191 String result = PlatformUtil.isMac() && isSWT ? "true" : "false"; 192 return "true".equals(System.getProperty("javafx.draw.in.paint", result));} 193 }); 194 195 private static boolean singleThreaded = 196 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 197 @Override public Boolean run() { 198 Boolean result = Boolean.getBoolean("quantum.singlethreaded"); 199 if (/*verbose &&*/ result) { 200 System.out.println("Warning: Single GUI Threadiong is enabled, FPS should be slower"); 201 } 202 return result; 203 } 204 }); 205 206 private static boolean noRenderJobs = 207 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 208 @Override public Boolean run() { 209 Boolean result = Boolean.getBoolean("quantum.norenderjobs"); 210 if (/*verbose &&*/ result) { 211 System.out.println("Warning: Quantum will not submit render jobs, nothing should draw"); 212 } 213 return result; 214 } 215 }); 216 217 private AtomicBoolean toolkitRunning = new AtomicBoolean(false); 218 private AtomicBoolean animationRunning = new AtomicBoolean(false); 219 private AtomicBoolean nextPulseRequested = new AtomicBoolean(false); 220 private AtomicBoolean pulseRunning = new AtomicBoolean(false); 221 private CountDownLatch launchLatch = new CountDownLatch(1); 222 223 final int PULSE_INTERVAL = (int)(TimeUnit.SECONDS.toMillis(1L) / getRefreshRate()); 224 final int FULLSPEED_INTERVAL = 1; // ms 225 boolean nativeSystemVsync = false; 226 private float _maxPixelScale; 227 private Runnable pulseRunnable, userRunnable, timerRunnable; 228 private Timer pulseTimer = null; 229 private Thread shutdownHook = null; 230 private PaintCollector collector; 231 private QuantumRenderer renderer; 232 private GraphicsPipeline pipeline; 233 234 private ClassLoader ccl; 235 236 private HashMap<Object,EventLoop> eventLoopMap = null; 237 238 private final PerformanceTracker perfTracker = new PerformanceTrackerImpl(); 239 240 @Override public boolean init() { 241 /* 242 * Glass Mac, X11 need Application.setDeviceDetails to happen prior to Glass Application.Run 243 */ 244 renderer = QuantumRenderer.getInstance(); 245 collector = PaintCollector.createInstance(this); 246 pipeline = GraphicsPipeline.getPipeline(); 247 if (PrismSettings.shutdownHook) { 248 shutdownHook = new Thread("Glass/Prism Shutdown Hook") { 249 @Override public void run() { 250 dispose(); 251 } 252 }; 253 AccessController.doPrivileged(new PrivilegedAction<Void>() { 254 @Override public Void run() { 255 Runtime.getRuntime().addShutdownHook(shutdownHook); 256 return null; 257 } 258 }); 259 } 260 return true; 261 } 262 263 /** 264 * This method is invoked by PlatformImpl. It is typically called on the main 265 * thread, NOT the JavaFX Application Thread. The userStartupRunnable will 266 * be invoked on the JavaFX Application Thread. 267 * 268 * @param userStartupRunnable A runnable invoked on the JavaFX Application Thread 269 * that allows the system to perform some startup 270 * functionality after the toolkit has been initialized. 271 */ 272 @Override public void startup(final Runnable userStartupRunnable) { 273 // Save the context class loader of the launcher thread 274 ccl = Thread.currentThread().getContextClassLoader(); 275 276 try { 277 this.userRunnable = userStartupRunnable; 278 279 Application.run(new Runnable () { 280 public void run () { 281 // Ensure that the toolkit can only be started here 282 runToolkit(); 283 } 284 }); 285 } catch (RuntimeException ex) { 286 if (verbose) { 287 ex.printStackTrace(); 288 } 289 throw ex; 290 } catch (Throwable t) { 291 if (verbose) { 292 t.printStackTrace(); 293 } 294 throw new RuntimeException(t); 295 } 296 297 try { 298 launchLatch.await(); 299 } catch (InterruptedException ie) { 300 ie.printStackTrace(); 301 } 302 } 303 304 // restart the toolkit if previously terminated 305 private void assertToolkitRunning() { 306 // not implemented 307 } 308 309 // Called by Glass from Application.run() 310 void runToolkit() { 311 Thread user = Thread.currentThread(); 312 313 if (!toolkitRunning.getAndSet(true)) { 314 user.setName("JavaFX Application Thread"); 315 // Set context class loader to the same as the thread that called startup 316 user.setContextClassLoader(ccl); 317 setFxUserThread(user); 318 319 /* 320 * Glass Application instance is now valid - create the ResourceFactory 321 * on the render thread 322 */ 323 renderer.createResourceFactory(); 324 325 pulseRunnable = new Runnable() { 326 @Override public void run() { 327 QuantumToolkit.this.pulse(); 328 } 329 }; 330 timerRunnable = new Runnable() { 331 @Override public void run() { 332 try { 333 QuantumToolkit.this.postPulse(); 334 } catch (Throwable th) { 335 th.printStackTrace(System.err); 336 // } catch (RuntimeException re) { 337 // ignore spurious Glass timer events while exiting... 338 } 339 } 340 }; 341 pulseTimer = Application.GetApplication().createTimer(timerRunnable); 342 343 Application.GetApplication().setEventHandler(new Application.EventHandler() { 344 @Override public void handleQuitAction(Application app, long time) { 345 GlassStage.requestClosingAllWindows(); 346 } 347 }); 348 } 349 launchLatch.countDown(); 350 try { 351 Application.invokeAndWait(this.userRunnable); 352 353 if (getMasterTimer().isFullspeed()) { 354 /* 355 * FULLSPEED_INTVERVAL workaround 356 * 357 * Application.invokeLater(pulseRunnable); 358 */ 359 pulseTimer.start(FULLSPEED_INTERVAL); 360 } else { 361 nativeSystemVsync = Screen.getVideoRefreshPeriod() != 0.0; 362 if (nativeSystemVsync) { 363 // system supports vsync 364 pulseTimer.start(); 365 } else { 366 // rely on millisecond resolution timer to provide 367 // nominal pulse sync and use pulse hinting on 368 // synchronous pipelines to fine tune the interval 369 pulseTimer.start(PULSE_INTERVAL); 370 } 371 } 372 } catch (Throwable th) { 373 th.printStackTrace(System.err); 374 } finally { 375 if (PrismSettings.verbose) { 376 System.err.println(" vsync: " + PrismSettings.isVsyncEnabled + 377 " vpipe: " + pipeline.isVsyncSupported()); 378 } 379 PerformanceTracker.logEvent("Toolkit.startup - finished"); 380 } 381 } 382 383 boolean hasNativeSystemVsync() { 384 return nativeSystemVsync; 385 } 386 387 boolean isVsyncEnabled() { 388 return (PrismSettings.isVsyncEnabled && 389 pipeline.isVsyncSupported()); 390 } 391 392 @Override public void checkFxUserThread() { 393 super.checkFxUserThread(); 394 renderer.checkRendererIdle(); 395 } 396 397 protected static Thread getFxUserThread() { 398 return Toolkit.getFxUserThread(); 399 } 400 401 @Override public Future addRenderJob(RenderJob r) { 402 // Do not run any render jobs (this is for benchmarking only) 403 if (noRenderJobs) { 404 CompletionListener listener = r.getCompletionListener(); 405 if (r instanceof PaintRenderJob) { 406 ((PaintRenderJob)r).getScene().setPainting(false); 407 } 408 if (listener != null) { 409 try { 410 listener.done(r); 411 } catch (Throwable th) { 412 th.printStackTrace(); 413 } 414 } 415 return null; 416 } 417 // Run the render job in the UI thread (this is for benchmarking only) 418 if (singleThreaded) { 419 r.run(); 420 return null; 421 } 422 return (renderer.submitRenderJob(r)); 423 } 424 425 void postPulse() { 426 if (toolkitRunning.get() && 427 (animationRunning.get() || nextPulseRequested.get() || collector.hasDirty()) && 428 !setPulseRunning()) { 429 430 Application.invokeLater(pulseRunnable); 431 432 if (debug) { 433 System.err.println("QT.postPulse@(" + System.nanoTime() + "): " + pulseString()); 434 } 435 } else if (debug) { 436 System.err.println("QT.postPulse#(" + System.nanoTime() + ") DROP: " + pulseString()); 437 } 438 } 439 440 private String pulseString() { 441 return ((toolkitRunning.get() ? "T" : "t") + 442 (animationRunning.get() ? "A" : "a") + 443 (pulseRunning.get() ? "P" : "p") + 444 (nextPulseRequested.get() ? "N" : "n") + 445 (collector.hasDirty() ? "D" : "d")); 446 } 447 448 private boolean setPulseRunning() { 449 return (pulseRunning.getAndSet(true)); 450 } 451 452 private void endPulseRunning() { 453 pulseRunning.set(false); 454 if (debug) { 455 System.err.println("QT.endPulse: " + System.nanoTime()); 456 } 457 } 458 459 protected void pulse() { 460 pulse(true); 461 } 462 463 void pulse(boolean collect) { 464 try { 465 if (PULSE_LOGGING_ENABLED) { 466 PULSE_LOGGER.pulseStart(); 467 } 468 469 if (!toolkitRunning.get()) { 470 return; 471 } 472 nextPulseRequested.set(false); 473 if (animationRunnable != null) { 474 animationRunning.set(true); 475 animationRunnable.run(); 476 } else { 477 animationRunning.set(false); 478 } 479 firePulse(); 480 if (collect) collector.renderAll(); 481 } finally { 482 endPulseRunning(); 483 if (PULSE_LOGGING_ENABLED) { 484 PULSE_LOGGER.pulseEnd(); 485 } 486 } 487 } 488 489 void vsyncHint() { 490 if (isVsyncEnabled()) { 491 if (debug) { 492 System.err.println("QT.vsyncHint: postPulse: " + System.nanoTime()); 493 } 494 postPulse(); 495 } 496 } 497 498 @Override public AppletWindow createAppletWindow(long parent, String serverName) { 499 GlassAppletWindow parentWindow = new GlassAppletWindow(parent, serverName); 500 // Make this the parent window for all future Stages 501 WindowStage.setAppletWindow(parentWindow); 502 return parentWindow; 503 } 504 505 @Override public void closeAppletWindow() { 506 GlassAppletWindow gaw = WindowStage.getAppletWindow(); 507 if (null != gaw) { 508 gaw.dispose(); 509 WindowStage.setAppletWindow(null); 510 // any further strong refs will be in the applet itself 511 } 512 } 513 514 @Override public TKStage createTKStage(Window peerWindow, StageStyle stageStyle, 515 boolean primary, Modality modality, TKStage owner, boolean rtl, AccessControlContext acc) { 516 assertToolkitRunning(); 517 WindowStage stage = new WindowStage(peerWindow, stageStyle, modality, owner); 518 stage.setSecurityContext(acc); 519 if (primary) { 520 stage.setIsPrimary(); 521 } 522 stage.setRTL(rtl); 523 stage.init(systemMenu); 524 return stage; 525 } 526 527 @Override public Object enterNestedEventLoop(Object key) { 528 checkFxUserThread(); 529 530 if (key == null) { 531 throw new NullPointerException(); 532 } 533 if (eventLoopMap == null) { 534 eventLoopMap = new HashMap<>(); 535 } 536 if (eventLoopMap.containsKey(key)) { 537 throw new IllegalArgumentException( 538 "Key already associated with a running event loop: " + key); 539 } 540 EventLoop eventLoop = Application.GetApplication().createEventLoop(); 541 eventLoopMap.put(key, eventLoop); 542 543 Object ret = eventLoop.enter(); 544 545 if (!isNestedLoopRunning()) { 546 notifyLastNestedLoopExited(); 547 } 548 549 return ret; 550 } 551 552 @Override public void exitNestedEventLoop(Object key, Object rval) { 553 checkFxUserThread(); 554 555 if (key == null) { 556 throw new NullPointerException(); 557 } 558 if (eventLoopMap == null || !eventLoopMap.containsKey(key)) { 559 throw new IllegalArgumentException( 560 "Key not associated with a running event loop: " + key); 561 } 562 EventLoop eventLoop = eventLoopMap.get(key); 563 eventLoopMap.remove(key); 564 eventLoop.leave(rval); 565 } 566 567 @Override public TKStage createTKPopupStage(Window peerWindow, 568 TKStage owner, 569 AccessControlContext acc) { 570 assertToolkitRunning(); 571 WindowStage stage = new WindowStage(peerWindow, StageStyle.TRANSPARENT, null, owner); 572 stage.setSecurityContext(acc); 573 stage.setIsPopup(); 574 stage.init(systemMenu); 575 return stage; 576 } 577 578 @Override public TKStage createTKEmbeddedStage(HostInterface host, AccessControlContext acc) { 579 assertToolkitRunning(); 580 EmbeddedStage stage = new EmbeddedStage(host); 581 stage.setSecurityContext(acc); 582 return stage; 583 } 584 585 private static ScreenConfigurationAccessor screenAccessor = 586 new ScreenConfigurationAccessor() { 587 @Override public int getMinX(Object obj) { 588 return ((Screen)obj).getX(); 589 } 590 @Override public int getMinY(Object obj) { 591 return ((Screen)obj).getY(); 592 } 593 @Override public int getWidth(Object obj) { 594 return ((Screen)obj).getWidth(); 595 } 596 @Override public int getHeight(Object obj) { 597 return ((Screen)obj).getHeight(); 598 } 599 @Override public int getVisualMinX(Object obj) { 600 return ((Screen)obj).getVisibleX(); 601 } 602 @Override public int getVisualMinY(Object obj) { 603 return ((Screen)obj).getVisibleY(); 604 } 605 @Override public int getVisualWidth(Object obj) { 606 return ((Screen)obj).getVisibleWidth(); 607 } 608 @Override public int getVisualHeight(Object obj) { 609 return ((Screen)obj).getVisibleHeight(); 610 } 611 @Override public float getDPI(Object obj) { 612 return ((Screen)obj).getResolutionX(); 613 } 614 }; 615 616 @Override public ScreenConfigurationAccessor 617 setScreenConfigurationListener(final TKScreenConfigurationListener listener) { 618 Screen.setEventHandler(new Screen.EventHandler() { 619 @Override public void handleSettingsChanged() { 620 notifyScreenListener(listener); 621 } 622 }); 623 return screenAccessor; 624 } 625 626 private static void notifyScreenListener(TKScreenConfigurationListener listener) { 627 listener.screenConfigurationChanged(); 628 } 629 630 @Override public Object getPrimaryScreen() { 631 return Screen.getMainScreen(); 632 } 633 634 @Override public List<?> getScreens() { 635 return Screen.getScreens(); 636 } 637 638 @Override 639 public PerformanceTracker getPerformanceTracker() { 640 return perfTracker; 641 } 642 643 @Override 644 public PerformanceTracker createPerformanceTracker() { 645 return new PerformanceTrackerImpl(); 646 } 647 648 public float getMaxPixelScale() { 649 if (_maxPixelScale == 0) { 650 for (Object o : getScreens()) { 651 _maxPixelScale = Math.max(_maxPixelScale, ((Screen) o).getScale()); 652 } 653 } 654 return _maxPixelScale; 655 } 656 657 @Override public ImageLoader loadImage(String url, int width, int height, boolean preserveRatio, boolean smooth) { 658 return new PrismImageLoader2(url, width, height, preserveRatio, getMaxPixelScale(), smooth); 659 } 660 661 @Override public ImageLoader loadImage(InputStream stream, int width, int height, 662 boolean preserveRatio, boolean smooth) { 663 return new PrismImageLoader2(stream, width, height, preserveRatio, smooth); 664 } 665 666 @Override public AbstractRemoteResource<? extends ImageLoader> loadImageAsync( 667 AsyncOperationListener listener, String url, 668 int width, int height, boolean preserveRatio, boolean smooth) { 669 return new PrismImageLoader2.AsyncImageLoader(listener, url, width, height, preserveRatio, smooth); 670 } 671 672 @Override public void defer(Runnable runnable) { 673 if (!toolkitRunning.get()) { 674 throw new IllegalStateException("Attempt to call defer when toolkit not running"); 675 } 676 Application.invokeLater(runnable); 677 } 678 679 @Override public void exit() { 680 notifyShutdownHooks(); 681 pulseTimer.stop(); 682 683 ViewPainter.renderLock.lock(); 684 try { 685 //TODO - should update glass scene view state 686 //TODO - doesn't matter because we are exiting 687 Application app = Application.GetApplication(); 688 app.terminate(); 689 } finally { 690 ViewPainter.renderLock.unlock(); 691 } 692 693 dispose(); 694 695 super.exit(); 696 } 697 698 public void dispose() { 699 if (toolkitRunning.compareAndSet(true, false)) { 700 renderer.stopRenderer(); 701 702 if (PrismSettings.shutdownHook) { 703 try { 704 Runtime.getRuntime().removeShutdownHook(shutdownHook); 705 } catch (IllegalStateException ignore) { 706 // throw when shutdown hook already removed 707 } 708 } 709 } 710 } 711 712 @Override public boolean isForwardTraversalKey(KeyEvent e) { 713 return (e.getCode() == KeyCode.TAB) 714 && (e.getEventType() == KeyEvent.KEY_PRESSED) 715 && !e.isShiftDown(); 716 } 717 718 @Override public boolean isBackwardTraversalKey(KeyEvent e) { 719 return (e.getCode() == KeyCode.TAB) 720 && (e.getEventType() == KeyEvent.KEY_PRESSED) 721 && e.isShiftDown(); 722 } 723 724 private Map<Object, Object> contextMap = Collections.synchronizedMap(new HashMap<>()); 725 @Override public Map<Object, Object> getContextMap() { 726 return contextMap; 727 } 728 729 @Override public int getRefreshRate() { 730 if (pulseHZ == null) { 731 return 60; 732 } else { 733 return pulseHZ; 734 } 735 } 736 737 private DelayedRunnable animationRunnable; 738 @Override public void setAnimationRunnable(DelayedRunnable animationRunnable) { 739 if (animationRunnable != null) { 740 animationRunning.set(true); 741 } 742 this.animationRunnable = animationRunnable; 743 } 744 745 @Override public void requestNextPulse() { 746 nextPulseRequested.set(true); 747 } 748 749 @Override public void waitFor(Task t) { 750 if (t.isFinished()) { 751 return; 752 } 753 } 754 755 @Override protected Object createColorPaint(Color color) { 756 return new com.sun.prism.paint.Color( 757 (float)color.getRed(), (float)color.getGreen(), 758 (float)color.getBlue(), (float)color.getOpacity()); 759 } 760 761 private com.sun.prism.paint.Color toPrismColor(Color color) { 762 return (com.sun.prism.paint.Color) Toolkit.getPaintAccessor().getPlatformPaint(color); 763 } 764 765 private List<com.sun.prism.paint.Stop> convertStops(List<Stop> paintStops) { 766 List<com.sun.prism.paint.Stop> stops = 767 new ArrayList<>(paintStops.size()); 768 for (Stop s : paintStops) { 769 stops.add(new com.sun.prism.paint.Stop(toPrismColor(s.getColor()), 770 (float) s.getOffset())); 771 } 772 return stops; 773 } 774 775 @Override protected Object createLinearGradientPaint(LinearGradient paint) { 776 int cmi = com.sun.prism.paint.Gradient.REPEAT; 777 CycleMethod cycleMethod = paint.getCycleMethod(); 778 if (cycleMethod == CycleMethod.NO_CYCLE) { 779 cmi = com.sun.prism.paint.Gradient.PAD; 780 } else if (cycleMethod == CycleMethod.REFLECT) { 781 cmi = com.sun.prism.paint.Gradient.REFLECT; 782 } 783 // TODO: extract colors/offsets and pass them in directly... 784 List<com.sun.prism.paint.Stop> stops = convertStops(paint.getStops()); 785 return new com.sun.prism.paint.LinearGradient( 786 (float)paint.getStartX(), (float)paint.getStartY(), (float)paint.getEndX(), (float)paint.getEndY(), 787 null, paint.isProportional(), cmi, stops); 788 } 789 790 @Override 791 protected Object createRadialGradientPaint(RadialGradient paint) { 792 float cx = (float)paint.getCenterX(); 793 float cy = (float)paint.getCenterY(); 794 float fa = (float)paint.getFocusAngle(); 795 float fd = (float)paint.getFocusDistance(); 796 797 int cmi = 0; 798 if (paint.getCycleMethod() == CycleMethod.NO_CYCLE) { 799 cmi = com.sun.prism.paint.Gradient.PAD; 800 } else if (paint.getCycleMethod() == CycleMethod.REFLECT) { 801 cmi = com.sun.prism.paint.Gradient.REFLECT; 802 } else { 803 cmi = com.sun.prism.paint.Gradient.REPEAT; 804 } 805 806 // TODO: extract colors/offsets and pass them in directly... 807 List<com.sun.prism.paint.Stop> stops = convertStops(paint.getStops()); 808 return new com.sun.prism.paint.RadialGradient(cx, cy, fa, fd, 809 (float)paint.getRadius(), null, paint.isProportional(), cmi, stops); 810 } 811 812 @Override 813 protected Object createImagePatternPaint(ImagePattern paint) { 814 if (paint.getImage() == null) { 815 return com.sun.prism.paint.Color.TRANSPARENT; 816 } else { 817 return new com.sun.prism.paint.ImagePattern((com.sun.prism.Image) paint.getImage().impl_getPlatformImage(), 818 (float)paint.getX(), 819 (float)paint.getY(), 820 (float)paint.getWidth(), 821 (float)paint.getHeight(), 822 paint.isProportional(), 823 Toolkit.getPaintAccessor().isMutable(paint)); 824 } 825 } 826 827 static BasicStroke tmpStroke = new BasicStroke(); 828 private void initStroke(StrokeType pgtype, double strokewidth, 829 StrokeLineCap pgcap, 830 StrokeLineJoin pgjoin, float miterLimit, 831 float[] dashArray, float dashOffset) 832 { 833 int type; 834 if (pgtype == StrokeType.CENTERED) { 835 type = BasicStroke.TYPE_CENTERED; 836 } else if (pgtype == StrokeType.INSIDE) { 837 type = BasicStroke.TYPE_INNER; 838 } else { 839 type = BasicStroke.TYPE_OUTER; 840 } 841 842 int cap; 843 if (pgcap == StrokeLineCap.BUTT) { 844 cap = BasicStroke.CAP_BUTT; 845 } else if (pgcap == StrokeLineCap.SQUARE) { 846 cap = BasicStroke.CAP_SQUARE; 847 } else { 848 cap = BasicStroke.CAP_ROUND; 849 } 850 851 int join; 852 if (pgjoin == StrokeLineJoin.BEVEL) { 853 join = BasicStroke.JOIN_BEVEL; 854 } else if (pgjoin == StrokeLineJoin.MITER) { 855 join = BasicStroke.JOIN_MITER; 856 } else { 857 join = BasicStroke.JOIN_ROUND; 858 } 859 860 tmpStroke.set(type, (float) strokewidth, cap, join, miterLimit); 861 if ((dashArray != null) && (dashArray.length > 0)) { 862 tmpStroke.set(dashArray, dashOffset); 863 } else { 864 tmpStroke.set((float[])null, 0); 865 } 866 } 867 868 @Override 869 public void accumulateStrokeBounds(Shape shape, float bbox[], 870 StrokeType pgtype, 871 double strokewidth, 872 StrokeLineCap pgcap, 873 StrokeLineJoin pgjoin, 874 float miterLimit, 875 BaseTransform tx) 876 { 877 878 initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit, null, 0); 879 if (tx.isTranslateOrIdentity()) { 880 tmpStroke.accumulateShapeBounds(bbox, shape, tx); 881 } else { 882 Shape.accumulate(bbox, tmpStroke.createStrokedShape(shape), tx); 883 } 884 } 885 886 @Override 887 public boolean strokeContains(Shape shape, double x, double y, 888 StrokeType pgtype, 889 double strokewidth, 890 StrokeLineCap pgcap, 891 StrokeLineJoin pgjoin, 892 float miterLimit) 893 { 894 initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit, null, 0); 895 // TODO: The contains testing could be done directly without creating a Shape 896 return tmpStroke.createStrokedShape(shape).contains((float) x, (float) y); 897 } 898 899 @Override 900 public Shape createStrokedShape(Shape shape, 901 StrokeType pgtype, 902 double strokewidth, 903 StrokeLineCap pgcap, 904 StrokeLineJoin pgjoin, 905 float miterLimit, 906 float[] dashArray, 907 float dashOffset) { 908 initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit, 909 dashArray, dashOffset); 910 return tmpStroke.createStrokedShape(shape); 911 } 912 913 @Override public Dimension2D getBestCursorSize(int preferredWidth, int preferredHeight) { 914 return CursorUtils.getBestCursorSize(preferredWidth, preferredHeight); 915 } 916 917 @Override public int getMaximumCursorColors() { 918 return 2; 919 } 920 921 @Override public int getKeyCodeForChar(String character) { 922 return (character.length() == 1) 923 ? com.sun.glass.events.KeyEvent.getKeyCodeForChar( 924 character.charAt(0)) 925 : com.sun.glass.events.KeyEvent.VK_UNDEFINED; 926 } 927 928 @Override public PathElement[] convertShapeToFXPath(Object shape) { 929 if (shape == null) { 930 return new PathElement[0]; 931 } 932 List<PathElement> elements = new ArrayList<>(); 933 // iterate over the shape and turn it into a series of path 934 // elements 935 com.sun.javafx.geom.Shape geomShape = (com.sun.javafx.geom.Shape) shape; 936 PathIterator itr = geomShape.getPathIterator(null); 937 PathIteratorHelper helper = new PathIteratorHelper(itr); 938 PathIteratorHelper.Struct struct = new PathIteratorHelper.Struct(); 939 940 while (!helper.isDone()) { 941 // true if WIND_EVEN_ODD, false if WIND_NON_ZERO 942 boolean windEvenOdd = helper.getWindingRule() == PathIterator.WIND_EVEN_ODD; 943 int type = helper.currentSegment(struct); 944 PathElement el; 945 if (type == PathIterator.SEG_MOVETO) { 946 el = new MoveTo(struct.f0, struct.f1); 947 } else if (type == PathIterator.SEG_LINETO) { 948 el = new LineTo(struct.f0, struct.f1); 949 } else if (type == PathIterator.SEG_QUADTO) { 950 el = new QuadCurveTo( 951 struct.f0, 952 struct.f1, 953 struct.f2, 954 struct.f3); 955 } else if (type == PathIterator.SEG_CUBICTO) { 956 el = new CubicCurveTo ( 957 struct.f0, 958 struct.f1, 959 struct.f2, 960 struct.f3, 961 struct.f4, 962 struct.f5); 963 } else if (type == PathIterator.SEG_CLOSE) { 964 el = new ClosePath(); 965 } else { 966 throw new IllegalStateException("Invalid element type: " + type); 967 } 968 helper.next(); 969 elements.add(el); 970 } 971 972 return elements.toArray(new PathElement[elements.size()]); 973 } 974 975 @Override public HitInfo convertHitInfoToFX(Object hit) { 976 Integer textHitPos = (Integer) hit; 977 HitInfo hitInfo = new HitInfo(); 978 hitInfo.setCharIndex(textHitPos); 979 hitInfo.setLeading(true); 980 return hitInfo; 981 } 982 983 @Override public Filterable toFilterable(Image img) { 984 return PrImage.create((com.sun.prism.Image) img.impl_getPlatformImage()); 985 } 986 987 @Override public FilterContext getFilterContext(Object config) { 988 if (config == null || (!(config instanceof com.sun.glass.ui.Screen))) { 989 return PrFilterContext.getDefaultInstance(); 990 } 991 Screen screen = (Screen)config; 992 return PrFilterContext.getInstance(screen); 993 } 994 995 @Override public AbstractMasterTimer getMasterTimer() { 996 return MasterTimer.getInstance(); 997 } 998 999 @Override public FontLoader getFontLoader() { 1000 return com.sun.javafx.font.PrismFontLoader.getInstance(); 1001 } 1002 1003 @Override public TextLayoutFactory getTextLayoutFactory() { 1004 return com.sun.javafx.text.PrismTextLayoutFactory.getFactory(); 1005 } 1006 1007 @Override public Object createSVGPathObject(SVGPath svgpath) { 1008 int windingRule = svgpath.getFillRule() == FillRule.NON_ZERO ? PathIterator.WIND_NON_ZERO : PathIterator.WIND_EVEN_ODD; 1009 Path2D path = new Path2D(windingRule); 1010 path.appendSVGPath(svgpath.getContent()); 1011 return path; 1012 } 1013 1014 @Override public Path2D createSVGPath2D(SVGPath svgpath) { 1015 int windingRule = svgpath.getFillRule() == FillRule.NON_ZERO ? PathIterator.WIND_NON_ZERO : PathIterator.WIND_EVEN_ODD; 1016 Path2D path = new Path2D(windingRule); 1017 path.appendSVGPath(svgpath.getContent()); 1018 return path; 1019 } 1020 1021 @Override public boolean imageContains(Object image, float x, float y) { 1022 if (image == null) { 1023 return false; 1024 } 1025 1026 com.sun.prism.Image pImage = (com.sun.prism.Image)image; 1027 int intX = (int)x + pImage.getMinX(); 1028 int intY = (int)y + pImage.getMinY(); 1029 1030 if (pImage.isOpaque()) { 1031 return true; 1032 } 1033 1034 if (pImage.getPixelFormat() == PixelFormat.INT_ARGB_PRE) { 1035 IntBuffer ib = (IntBuffer) pImage.getPixelBuffer(); 1036 int index = intX + intY * pImage.getRowLength(); 1037 if (index >= ib.limit()) { 1038 return false; 1039 } else { 1040 return (ib.get(index) & 0xff000000) != 0; 1041 } 1042 } else if (pImage.getPixelFormat() == PixelFormat.BYTE_BGRA_PRE) { 1043 ByteBuffer bb = (ByteBuffer) pImage.getPixelBuffer(); 1044 int index = intX * pImage.getBytesPerPixelUnit() + intY * pImage.getScanlineStride() + 3; 1045 if (index >= bb.limit()) { 1046 return false; 1047 } else { 1048 return (bb.get(index) & 0xff) != 0; 1049 } 1050 } else if (pImage.getPixelFormat() == PixelFormat.BYTE_ALPHA) { 1051 ByteBuffer bb = (ByteBuffer) pImage.getPixelBuffer(); 1052 int index = intX * pImage.getBytesPerPixelUnit() + intY * pImage.getScanlineStride(); 1053 if (index >= bb.limit()) { 1054 return false; 1055 } else { 1056 return (bb.get(index) & 0xff) != 0; 1057 } 1058 } 1059 return true; 1060 } 1061 1062 @Override 1063 public boolean isNestedLoopRunning() { 1064 return Application.isNestedLoopRunning(); 1065 } 1066 1067 @Override 1068 public boolean isSupported(ConditionalFeature feature) { 1069 switch (feature) { 1070 case SCENE3D: 1071 return GraphicsPipeline.getPipeline().is3DSupported(); 1072 case EFFECT: 1073 return GraphicsPipeline.getPipeline().isEffectSupported(); 1074 case SHAPE_CLIP: 1075 return true; 1076 case INPUT_METHOD: 1077 return Application.GetApplication().supportsInputMethods(); 1078 case TRANSPARENT_WINDOW: 1079 return Application.GetApplication().supportsTransparentWindows(); 1080 case UNIFIED_WINDOW: 1081 return Application.GetApplication().supportsUnifiedWindows(); 1082 case TWO_LEVEL_FOCUS: 1083 return Application.GetApplication().hasTwoLevelFocus(); 1084 case VIRTUAL_KEYBOARD: 1085 return Application.GetApplication().hasVirtualKeyboard(); 1086 case INPUT_TOUCH: 1087 return Application.GetApplication().hasTouch(); 1088 case INPUT_MULTITOUCH: 1089 return Application.GetApplication().hasMultiTouch(); 1090 case INPUT_POINTER: 1091 return Application.GetApplication().hasPointer(); 1092 default: 1093 return false; 1094 } 1095 } 1096 1097 @Override 1098 public boolean isAntiAliasingSupported() { 1099 return GraphicsPipeline.getPipeline().isAntiAliasingSupported(); 1100 } 1101 1102 static TransferMode clipboardActionToTransferMode(final int action) { 1103 switch (action) { 1104 case Clipboard.ACTION_NONE: 1105 return null; 1106 case Clipboard.ACTION_COPY: 1107 //IE drop action for URL copy 1108 case Clipboard.ACTION_COPY | Clipboard.ACTION_REFERENCE: 1109 return TransferMode.COPY; 1110 case Clipboard.ACTION_MOVE: 1111 //IE drop action for URL move 1112 case Clipboard.ACTION_MOVE | Clipboard.ACTION_REFERENCE: 1113 return TransferMode.MOVE; 1114 case Clipboard.ACTION_REFERENCE: 1115 return TransferMode.LINK; 1116 case Clipboard.ACTION_ANY: 1117 return TransferMode.COPY; // select a reasonable trasnfer mode as workaround until RT-22840 1118 } 1119 return null; 1120 } 1121 1122 private QuantumClipboard clipboard; 1123 @Override public TKClipboard getSystemClipboard() { 1124 if (clipboard == null) { 1125 clipboard = QuantumClipboard.getClipboardInstance(new ClipboardAssistance(com.sun.glass.ui.Clipboard.SYSTEM)); 1126 } 1127 return clipboard; 1128 } 1129 1130 private GlassSystemMenu systemMenu = new GlassSystemMenu(); 1131 @Override public TKSystemMenu getSystemMenu() { 1132 return systemMenu; 1133 } 1134 1135 @Override public TKClipboard getNamedClipboard(String name) { 1136 return null; 1137 } 1138 1139 @Override public void startDrag(TKScene scene, Set<TransferMode> tm, TKDragSourceListener l, Dragboard dragboard) { 1140 if (dragboard == null) { 1141 throw new IllegalArgumentException("dragboard should not be null"); 1142 } 1143 1144 GlassScene view = (GlassScene)scene; 1145 view.setTKDragSourceListener(l); 1146 1147 QuantumClipboard gc = (QuantumClipboard)dragboard.impl_getPeer(); 1148 gc.setSupportedTransferMode(tm); 1149 gc.flush(); 1150 1151 // flush causes a modal DnD event loop, when we return, close the clipboard 1152 gc.close(); 1153 } 1154 1155 @Override public void enableDrop(TKScene s, TKDropTargetListener l) { 1156 1157 assert s instanceof GlassScene; 1158 1159 GlassScene view = (GlassScene)s; 1160 view.setTKDropTargetListener(l); 1161 } 1162 1163 @Override public void registerDragGestureListener(TKScene s, Set<TransferMode> tm, TKDragGestureListener l) { 1164 1165 assert s instanceof GlassScene; 1166 1167 GlassScene view = (GlassScene)s; 1168 view.setTKDragGestureListener(l); 1169 } 1170 1171 @Override 1172 public void installInputMethodRequests(TKScene scene, InputMethodRequests requests) { 1173 1174 assert scene instanceof GlassScene; 1175 1176 GlassScene view = (GlassScene)scene; 1177 view.setInputMethodRequests(requests); 1178 } 1179 1180 static class QuantumImage implements com.sun.javafx.tk.ImageLoader, ResourceFactoryListener { 1181 1182 // cache rt here 1183 private com.sun.prism.RTTexture rt; 1184 private com.sun.prism.Image image; 1185 private ResourceFactory rf; 1186 1187 QuantumImage(com.sun.prism.Image image) { 1188 this.image = image; 1189 } 1190 1191 RTTexture getRT(int w, int h, ResourceFactory rfNew) { 1192 boolean rttOk = rt != null && rf == rfNew && 1193 rt.getContentWidth() == w && rt.getContentHeight() == h; 1194 if (rttOk) { 1195 rt.lock(); 1196 if (rt.isSurfaceLost()) { 1197 rttOk = false; 1198 } 1199 } 1200 1201 if (!rttOk) { 1202 if (rt != null) { 1203 rt.dispose(); 1204 } 1205 if (rf != null) { 1206 rf.removeFactoryListener(this); 1207 rf = null; 1208 } 1209 rt = rfNew.createRTTexture(w, h, WrapMode.CLAMP_TO_ZERO); 1210 if (rt != null) { 1211 rf = rfNew; 1212 rf.addFactoryListener(this); 1213 } 1214 } 1215 1216 return rt; 1217 } 1218 1219 void dispose() { 1220 if (rt != null) { 1221 rt.dispose(); 1222 rt = null; 1223 } 1224 } 1225 1226 void setImage(com.sun.prism.Image img) { 1227 image = img; 1228 } 1229 1230 @Override 1231 public Exception getException() { 1232 return (image == null) 1233 ? new IllegalStateException("Unitialized image") 1234 : null; 1235 } 1236 @Override 1237 public int getFrameCount() { return 1; } 1238 @Override 1239 public PlatformImage getFrame(int index) { return image; } 1240 @Override 1241 public int getFrameDelay(int index) { return 0; } 1242 @Override 1243 public int getWidth() { return image.getWidth(); } 1244 @Override 1245 public int getHeight() { return image.getHeight(); } 1246 @Override 1247 public void factoryReset() { dispose(); } 1248 @Override 1249 public void factoryReleased() { dispose(); } 1250 } 1251 1252 @Override public ImageLoader loadPlatformImage(Object platformImage) { 1253 if (platformImage instanceof QuantumImage) { 1254 return (QuantumImage)platformImage; 1255 } 1256 1257 if (platformImage instanceof com.sun.prism.Image) { 1258 return new QuantumImage((com.sun.prism.Image) platformImage); 1259 } 1260 1261 throw new UnsupportedOperationException("unsupported class for loadPlatformImage"); 1262 } 1263 1264 @Override 1265 public PlatformImage createPlatformImage(int w, int h) { 1266 ByteBuffer bytebuf = ByteBuffer.allocate(w * h * 4); 1267 return com.sun.prism.Image.fromByteBgraPreData(bytebuf, w, h); 1268 } 1269 1270 @Override 1271 public Object renderToImage(ImageRenderingContext p) { 1272 Object saveImage = p.platformImage; 1273 final ImageRenderingContext params = p; 1274 final com.sun.prism.paint.Paint currentPaint = p.platformPaint instanceof com.sun.prism.paint.Paint ? 1275 (com.sun.prism.paint.Paint)p.platformPaint : null; 1276 1277 RenderJob re = new RenderJob(new Runnable() { 1278 1279 private com.sun.prism.paint.Color getClearColor() { 1280 if (currentPaint == null) { 1281 return com.sun.prism.paint.Color.WHITE; 1282 } else if (currentPaint.getType() == com.sun.prism.paint.Paint.Type.COLOR) { 1283 return (com.sun.prism.paint.Color) currentPaint; 1284 } else if (currentPaint.isOpaque()) { 1285 return com.sun.prism.paint.Color.TRANSPARENT; 1286 } else { 1287 return com.sun.prism.paint.Color.WHITE; 1288 } 1289 } 1290 1291 private void draw(Graphics g, int x, int y, int w, int h) { 1292 g.setLights(params.lights); 1293 g.setDepthBuffer(params.depthBuffer); 1294 1295 g.clear(getClearColor()); 1296 if (currentPaint != null && 1297 currentPaint.getType() != com.sun.prism.paint.Paint.Type.COLOR) { 1298 g.getRenderTarget().setOpaque(currentPaint.isOpaque()); 1299 g.setPaint(currentPaint); 1300 g.fillQuad(0, 0, w, h); 1301 } 1302 1303 // Set up transform 1304 if (x != 0 || y != 0) { 1305 g.translate(-x, -y); 1306 } 1307 if (params.transform != null) { 1308 g.transform(params.transform); 1309 } 1310 1311 if (params.root != null) { 1312 if (params.camera != null) { 1313 g.setCamera(params.camera); 1314 } 1315 NGNode ngNode = params.root; 1316 ngNode.render(g); 1317 } 1318 1319 } 1320 1321 @Override 1322 public void run() { 1323 1324 ResourceFactory rf = GraphicsPipeline.getDefaultResourceFactory(); 1325 1326 if (!rf.isDeviceReady()) { 1327 return; 1328 } 1329 1330 int x = params.x; 1331 int y = params.y; 1332 int w = params.width; 1333 int h = params.height; 1334 1335 if (w <= 0 || h <= 0) { 1336 return; 1337 } 1338 1339 boolean errored = false; 1340 try { 1341 QuantumImage pImage = (params.platformImage instanceof QuantumImage) ? 1342 (QuantumImage)params.platformImage : new QuantumImage(null); 1343 1344 com.sun.prism.RTTexture rt = pImage.getRT(w, h, rf); 1345 1346 if (rt == null) { 1347 return; 1348 } 1349 1350 Graphics g = rt.createGraphics(); 1351 1352 draw(g, x, y, w, h); 1353 1354 int[] pixels = pImage.rt.getPixels(); 1355 1356 if (pixels != null) { 1357 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(pixels, w, h)); 1358 } else { 1359 IntBuffer ib = IntBuffer.allocate(w*h); 1360 if (pImage.rt.readPixels(ib, pImage.rt.getContentX(), 1361 pImage.rt.getContentY(), w, h)) 1362 { 1363 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(ib, w, h)); 1364 } else { 1365 pImage.dispose(); 1366 pImage = null; 1367 } 1368 } 1369 1370 rt.unlock(); 1371 1372 params.platformImage = pImage; 1373 1374 } catch (Throwable t) { 1375 errored = true; 1376 t.printStackTrace(System.err); 1377 } finally { 1378 Disposer.cleanUp(); 1379 ManagedResource.freeDisposalRequestedAndCheckResources(errored); 1380 } 1381 } 1382 }); 1383 1384 final CountDownLatch latch = new CountDownLatch(1); 1385 re.setCompletionListener(new CompletionListener() { 1386 @Override public void done(final RenderJob job) { 1387 latch.countDown(); 1388 } 1389 }); 1390 addRenderJob(re); 1391 1392 do { 1393 try { 1394 latch.await(); 1395 break; 1396 } catch (InterruptedException ex) { 1397 ex.printStackTrace(); 1398 } 1399 } while (true); 1400 1401 Object image = params.platformImage; 1402 params.platformImage = saveImage; 1403 1404 return image; 1405 } 1406 1407 @Override 1408 public FileChooserResult showFileChooser(final TKStage ownerWindow, 1409 final String title, 1410 final File initialDirectory, 1411 final String initialFileName, 1412 final FileChooserType fileChooserType, 1413 final List<FileChooser.ExtensionFilter> 1414 extensionFilters, 1415 final FileChooser.ExtensionFilter selectedFilter) { 1416 WindowStage blockedStage = null; 1417 try { 1418 // NOTE: we block the owner of the owner deliberately. 1419 // The native system blocks the nearest owner itself. 1420 // Otherwise sheets on Mac are unusable. 1421 blockedStage = blockOwnerStage(ownerWindow); 1422 1423 return CommonDialogs.showFileChooser( 1424 (ownerWindow instanceof WindowStage) 1425 ? ((WindowStage) ownerWindow).getPlatformWindow() 1426 : null, 1427 initialDirectory, 1428 initialFileName, 1429 title, 1430 (fileChooserType == FileChooserType.SAVE) 1431 ? CommonDialogs.Type.SAVE 1432 : CommonDialogs.Type.OPEN, 1433 (fileChooserType == FileChooserType.OPEN_MULTIPLE), 1434 convertExtensionFilters(extensionFilters), 1435 extensionFilters.indexOf(selectedFilter)); 1436 } finally { 1437 if (blockedStage != null) { 1438 blockedStage.setEnabled(true); 1439 } 1440 } 1441 } 1442 1443 @Override 1444 public File showDirectoryChooser(final TKStage ownerWindow, 1445 final String title, 1446 final File initialDirectory) { 1447 WindowStage blockedStage = null; 1448 try { 1449 // NOTE: we block the owner of the owner deliberately. 1450 // The native system blocks the nearest owner itself. 1451 // Otherwise sheets on Mac are unusable. 1452 blockedStage = blockOwnerStage(ownerWindow); 1453 1454 return CommonDialogs.showFolderChooser( 1455 (ownerWindow instanceof WindowStage) 1456 ? ((WindowStage) ownerWindow).getPlatformWindow() 1457 : null, 1458 initialDirectory, title); 1459 } finally { 1460 if (blockedStage != null) { 1461 blockedStage.setEnabled(true); 1462 } 1463 } 1464 } 1465 1466 private WindowStage blockOwnerStage(final TKStage stage) { 1467 if (stage instanceof WindowStage) { 1468 final TKStage ownerStage = ((WindowStage) stage).getOwner(); 1469 if (ownerStage instanceof WindowStage) { 1470 final WindowStage ownerWindowStage = (WindowStage) ownerStage; 1471 ownerWindowStage.setEnabled(false); 1472 return ownerWindowStage; 1473 } 1474 } 1475 1476 return null; 1477 } 1478 1479 private static List<CommonDialogs.ExtensionFilter> 1480 convertExtensionFilters(final List<FileChooser.ExtensionFilter> 1481 extensionFilters) { 1482 final CommonDialogs.ExtensionFilter[] glassExtensionFilters = 1483 new CommonDialogs.ExtensionFilter[extensionFilters.size()]; 1484 1485 int i = 0; 1486 for (final FileChooser.ExtensionFilter extensionFilter: 1487 extensionFilters) { 1488 glassExtensionFilters[i++] = 1489 new CommonDialogs.ExtensionFilter( 1490 extensionFilter.getDescription(), 1491 extensionFilter.getExtensions()); 1492 } 1493 1494 return Arrays.asList(glassExtensionFilters); 1495 } 1496 1497 @Override 1498 public long getMultiClickTime() { 1499 return View.getMultiClickTime(); 1500 } 1501 1502 @Override 1503 public int getMultiClickMaxX() { 1504 return View.getMultiClickMaxX(); 1505 } 1506 1507 @Override 1508 public int getMultiClickMaxY() { 1509 return View.getMultiClickMaxY(); 1510 } 1511 }