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