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