1 /* 2 * Copyright (c) 2010, 2016, 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 com.sun.javafx.embed.HostDragStartListener; 29 import javafx.application.Platform; 30 import javafx.collections.ObservableList; 31 import javafx.event.EventType; 32 import javafx.scene.input.InputMethodEvent; 33 import javafx.scene.input.InputMethodRequests; 34 import javafx.scene.input.InputMethodTextRun; 35 import javafx.scene.input.MouseEvent; 36 import javafx.scene.input.ScrollEvent; 37 import javafx.scene.image.PixelFormat; 38 import java.nio.IntBuffer; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 import com.sun.javafx.cursor.CursorFrame; 42 import com.sun.javafx.embed.AbstractEvents; 43 import com.sun.javafx.embed.EmbeddedSceneDTInterface; 44 import com.sun.javafx.embed.EmbeddedSceneInterface; 45 import com.sun.javafx.embed.HostInterface; 46 import com.sun.javafx.scene.input.KeyCodeMap; 47 import com.sun.javafx.scene.traversal.Direction; 48 import com.sun.javafx.sg.prism.NGNode; 49 import com.sun.javafx.tk.TKClipboard; 50 import com.sun.javafx.tk.Toolkit; 51 import com.sun.prism.paint.Color; 52 import com.sun.prism.paint.Paint; 53 import com.sun.glass.ui.Pixels; 54 import java.nio.ByteOrder; 55 56 final class EmbeddedScene extends GlassScene implements EmbeddedSceneInterface { 57 58 // TODO: synchronize access to embedder from ET and RT 59 private HostInterface host; 60 61 private UploadingPainter painter; 62 private PaintRenderJob paintRenderJob; 63 private float renderScaleX; 64 private float renderScaleY; 65 66 private final EmbeddedSceneDnD embeddedDnD; 67 68 private volatile IntBuffer texBits; 69 private volatile int texLineStride; // pre-scaled 70 private volatile float texScaleFactorX = 1.0f; 71 private volatile float texScaleFactorY = 1.0f; 72 73 private volatile PixelFormat<?> pixelFormat; 74 75 public EmbeddedScene(HostInterface host, boolean depthBuffer, boolean msaa) { 76 super(depthBuffer, msaa); 77 sceneState = new EmbeddedState(this); 78 79 this.host = host; 80 this.embeddedDnD = new EmbeddedSceneDnD(this); 81 82 PaintCollector collector = PaintCollector.getInstance(); 83 painter = new UploadingPainter(this); 84 paintRenderJob = new PaintRenderJob(this, collector.getRendered(), painter); 85 86 int nativeFormat = Pixels.getNativeFormat(); 87 ByteOrder byteorder = ByteOrder.nativeOrder(); 88 89 if (nativeFormat == Pixels.Format.BYTE_BGRA_PRE && 90 byteorder == ByteOrder.LITTLE_ENDIAN) 91 { 92 pixelFormat = PixelFormat.getIntArgbPreInstance(); 93 94 } else if (nativeFormat == Pixels.Format.BYTE_ARGB && 95 byteorder == ByteOrder.BIG_ENDIAN) 96 { 97 pixelFormat = PixelFormat.getIntArgbInstance(); 98 } 99 assert pixelFormat != null; 100 } 101 102 @Override 103 public void dispose() { 104 assert host != null; 105 QuantumToolkit.runWithRenderLock(() -> { 106 host.setEmbeddedScene(null); 107 host = null; 108 updateSceneState(); 109 painter = null; 110 paintRenderJob = null; 111 texBits = null; 112 return null; 113 }); 114 super.dispose(); 115 } 116 117 @Override 118 void setStage(GlassStage stage) { 119 super.setStage(stage); 120 121 assert host != null; // setStage() is called before dispose() 122 host.setEmbeddedScene(stage != null ? this : null); 123 } 124 125 @Override protected boolean isSynchronous() { 126 return false; 127 } 128 129 @Override public void setRoot(NGNode root) { 130 super.setRoot(root); 131 painter.setRoot(root); 132 } 133 134 @Override 135 public TKClipboard createDragboard(boolean isDragSource) { 136 return embeddedDnD.createDragboard(isDragSource); 137 } 138 139 @Override 140 public void enableInputMethodEvents(boolean enable) { 141 if (QuantumToolkit.verbose) { 142 System.err.println("EmbeddedScene.enableInputMethodEvents " + enable); 143 } 144 } 145 146 @Override 147 public void finishInputMethodComposition() { 148 if (QuantumToolkit.verbose) { 149 System.err.println("EmbeddedScene.finishInputMethodComposition"); 150 } 151 } 152 153 @Override 154 public void setPixelScaleFactors(float scalex, float scaley) { 155 renderScaleX = scalex; 156 renderScaleY = scaley; 157 entireSceneNeedsRepaint(); 158 } 159 160 public float getRenderScaleX() { 161 return renderScaleX; 162 } 163 164 public float getRenderScaleY() { 165 return renderScaleY; 166 } 167 168 @Override 169 public PixelFormat<?> getPixelFormat() { 170 return pixelFormat; 171 } 172 173 // Called by EmbeddedPainter on the render thread under renderLock 174 void uploadPixels(Pixels pixels) { 175 texBits = (IntBuffer)pixels.getPixels(); 176 texLineStride = pixels.getWidthUnsafe(); 177 texScaleFactorX = pixels.getScaleXUnsafe(); 178 texScaleFactorY = pixels.getScaleYUnsafe(); 179 if (host != null) { 180 host.repaint(); 181 } 182 } 183 184 // EmbeddedSceneInterface methods 185 186 @Override 187 public void repaint() { 188 Toolkit tk = Toolkit.getToolkit(); 189 tk.addRenderJob(paintRenderJob); 190 } 191 192 @Override 193 public boolean traverseOut(Direction dir) { 194 if (dir == Direction.NEXT) { 195 return host.traverseFocusOut(true); 196 } else if (dir == Direction.PREVIOUS) { 197 return host.traverseFocusOut(false); 198 } 199 return false; 200 } 201 202 @Override 203 public void setSize(final int width, final int height) { 204 Platform.runLater(() -> { 205 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 206 if (sceneListener != null) { 207 sceneListener.changedSize(width, height); 208 } 209 return null; 210 }, getAccessControlContext()); 211 }); 212 } 213 214 /** 215 * @param dest the destination buffer 216 * @param width the logical width of the buffer 217 * @param height the logical height of the buffer 218 * @param scale the scale factor 219 * @return 220 */ 221 @Override 222 public boolean getPixels(final IntBuffer dest, final int width, final int height) { 223 return QuantumToolkit.runWithRenderLock(() -> { 224 int scaledWidth = width; 225 int scaledHeight = height; 226 227 // The dest buffer scale factor is expected to match painter.getPixelScaleFactor(). 228 if (getRenderScaleX() != texScaleFactorX || 229 getRenderScaleY() != texScaleFactorY || 230 texBits == null) 231 { 232 return false; 233 } 234 scaledWidth = Math.round(scaledWidth * texScaleFactorX); 235 scaledHeight = Math.round(scaledHeight * texScaleFactorY); 236 237 dest.rewind(); 238 texBits.rewind(); 239 if (dest.capacity() != texBits.capacity()) { 240 // Calculate the intersection of the dest & src images. 241 int w = Math.min(scaledWidth, texLineStride); 242 int h = Math.min(scaledHeight, texBits.capacity() / texLineStride); 243 244 // Copy the intersection to the dest. 245 // The backed array of the textureBits may not be available, 246 // so not relying on it. 247 int[] linebuf = new int[w]; 248 for (int i = 0; i < h; i++) { 249 texBits.position(i * texLineStride); 250 texBits.get(linebuf, 0, w); 251 dest.position(i * scaledWidth); 252 dest.put(linebuf); 253 } 254 return true; 255 } 256 dest.put(texBits); 257 return true; 258 }); 259 } 260 261 @Override 262 protected Color getClearColor() { 263 if (fillPaint != null && fillPaint.getType() == Paint.Type.COLOR && 264 ((Color)fillPaint).getAlpha() == 0f) 265 { 266 return (Color)fillPaint; 267 } 268 return super.getClearColor(); 269 } 270 271 @Override 272 public void mouseEvent(final int type, final int button, 273 final boolean primaryBtnDown, final boolean middleBtnDown, final boolean secondaryBtnDown, 274 final int x, final int y, final int xAbs, final int yAbs, 275 final boolean shift, final boolean ctrl, final boolean alt, final boolean meta, 276 final boolean popupTrigger) 277 { 278 Platform.runLater(() -> { 279 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 280 if (sceneListener == null) { 281 return null; 282 } 283 // Click events are generated in Scene, so we don't expect them here 284 assert type != AbstractEvents.MOUSEEVENT_CLICKED; 285 EventType<MouseEvent> eventType = AbstractEvents.mouseIDToFXEventID(type); 286 sceneListener.mouseEvent(eventType, x, y, xAbs, yAbs, 287 AbstractEvents.mouseButtonToFXMouseButton(button), 288 popupTrigger, false, // do we know if it's synthesized? RT-20142 289 shift, ctrl, alt, meta, 290 primaryBtnDown, middleBtnDown, secondaryBtnDown); 291 return null; 292 }, getAccessControlContext()); 293 }); 294 } 295 296 @Override 297 public void scrollEvent(final int type, 298 final double scrollX, final double scrollY, 299 final double totalScrollX, final double totalScrollY, 300 double xMultiplier, double yMultiplier, 301 final double x, final double y, 302 final double xAbs, final double yAbs, 303 final boolean shift, final boolean ctrl, final boolean alt, final boolean meta, 304 final boolean inertia) { 305 { 306 Platform.runLater(() -> { 307 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 308 if (sceneListener == null) { 309 return null; 310 } 311 sceneListener.scrollEvent(AbstractEvents.scrollIDToFXEventType(type), scrollX, scrollY, totalScrollX, totalScrollY, xMultiplier, yMultiplier, 312 0, 0, 0, 0, 0, x, y, xAbs, yAbs, shift, ctrl, alt, meta, false, inertia); 313 return null; 314 }, getAccessControlContext()); 315 }); 316 } 317 } 318 319 @Override 320 public void inputMethodEvent(final EventType<InputMethodEvent> type, 321 final ObservableList<InputMethodTextRun> composed, final String committed, 322 final int caretPosition) { 323 Platform.runLater(() -> { 324 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 325 if (sceneListener != null) { 326 sceneListener.inputMethodEvent(type, composed, committed, caretPosition); 327 } 328 return null; 329 }); 330 }); 331 } 332 333 @Override 334 public void menuEvent(final int x, final int y, final int xAbs, final int yAbs, final boolean isKeyboardTrigger) { 335 Platform.runLater(() -> { 336 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 337 if (sceneListener != null) { 338 sceneListener.menuEvent(x, y, xAbs, yAbs, isKeyboardTrigger); 339 } 340 return null; 341 }, getAccessControlContext()); 342 }); 343 } 344 345 @Override 346 public void keyEvent(final int type, final int key, final char[] ch, final int modifiers) { 347 Platform.runLater(() -> { 348 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 349 if (sceneListener != null) { 350 boolean shiftDown = (modifiers & AbstractEvents.MODIFIER_SHIFT) != 0; 351 boolean controlDown = (modifiers & AbstractEvents.MODIFIER_CONTROL) != 0; 352 boolean altDown = (modifiers & AbstractEvents.MODIFIER_ALT) != 0; 353 boolean metaDown = (modifiers & AbstractEvents.MODIFIER_META) != 0; 354 355 String str = new String(ch); 356 String text = str; // TODO: this must be a text like "HOME", "F1", or "A" 357 javafx.scene.input.KeyEvent keyEvent = new javafx.scene.input.KeyEvent( 358 AbstractEvents.keyIDToFXEventType(type), 359 str, text, 360 KeyCodeMap.valueOf(key), 361 shiftDown, controlDown, altDown, metaDown); 362 sceneListener.keyEvent(keyEvent); 363 } 364 return null; 365 }, getAccessControlContext()); 366 }); 367 } 368 369 @Override 370 public void zoomEvent(final int type, final double zoomFactor, final double totalZoomFactor, 371 final double x, final double y, final double screenX, final double screenY, 372 boolean shift, boolean ctrl, boolean alt, boolean meta, boolean inertia) 373 { 374 Platform.runLater(() -> { 375 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 376 if (sceneListener == null) { 377 return null; 378 } 379 sceneListener.zoomEvent(AbstractEvents.zoomIDToFXEventType(type), 380 zoomFactor, totalZoomFactor, 381 x, y, screenX, screenY, 382 shift, ctrl, alt, meta, false, inertia); 383 return null; 384 }, getAccessControlContext()); 385 }); 386 } 387 388 @Override 389 public void rotateEvent(final int type, final double angle, final double totalAngle, 390 final double x, final double y, final double screenX, final double screenY, 391 boolean shift, boolean ctrl, boolean alt, boolean meta, boolean inertia) 392 { 393 Platform.runLater(() -> { 394 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 395 if (sceneListener == null) { 396 return null; 397 } 398 sceneListener.rotateEvent(AbstractEvents.rotateIDToFXEventType(type), 399 angle, totalAngle, 400 x, y, screenX, screenY, 401 shift, ctrl, alt, meta, false, inertia); 402 return null; 403 }, getAccessControlContext()); 404 }); 405 } 406 407 @Override 408 public void swipeEvent(final int type, final double x, final double y, final double screenX, final double screenY, 409 boolean shift, boolean ctrl, boolean alt, boolean meta) 410 { 411 Platform.runLater(() -> { 412 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 413 if (sceneListener == null) { 414 return null; 415 } 416 sceneListener.swipeEvent(AbstractEvents.swipeIDToFXEventType(type), 417 0, x, y, screenX, screenY, 418 shift, ctrl, alt, meta, false); 419 return null; 420 }, getAccessControlContext()); 421 }); 422 } 423 424 @Override 425 public void setCursor(final Object cursor) { 426 super.setCursor(cursor); 427 host.setCursor((CursorFrame) cursor); 428 } 429 430 @Override 431 public void setDragStartListener(HostDragStartListener l) { 432 embeddedDnD.setDragStartListener(l); 433 } 434 435 @Override 436 public EmbeddedSceneDTInterface createDropTarget() { 437 return embeddedDnD.createDropTarget(); 438 } 439 440 @Override 441 public InputMethodRequests getInputMethodRequests() { 442 return inputMethodRequests; 443 } 444 }