1 /* 2 * Copyright (c) 2018, 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.embed.swing.newimpl; 27 28 import javafx.geometry.Point2D; 29 import javafx.beans.InvalidationListener; 30 import javafx.beans.value.ChangeListener; 31 import javafx.stage.Window; 32 import javafx.scene.Scene; 33 import javafx.scene.input.KeyEvent; 34 import javafx.scene.input.MouseButton; 35 import javafx.scene.input.ScrollEvent; 36 import java.awt.event.MouseWheelEvent; 37 import javafx.event.EventHandler; 38 import java.awt.EventQueue; 39 import java.awt.Toolkit; 40 import java.lang.ref.WeakReference; 41 import java.lang.reflect.Method; 42 import java.awt.Component; 43 import java.awt.AWTEvent; 44 import java.awt.Cursor; 45 import java.nio.IntBuffer; 46 import java.awt.event.WindowFocusListener; 47 import java.awt.event.MouseEvent; 48 import java.awt.event.WindowEvent; 49 import java.awt.dnd.DragGestureEvent; 50 import java.awt.dnd.DragGestureRecognizer; 51 import java.awt.dnd.DragGestureListener; 52 import java.awt.dnd.DragSource; 53 import java.awt.dnd.DropTarget; 54 import java.awt.dnd.InvalidDnDOperationException; 55 import javax.swing.JComponent; 56 import javax.swing.Timer; 57 import java.security.PrivilegedAction; 58 import java.util.ArrayList; 59 import java.util.List; 60 import java.util.Set; 61 import java.util.concurrent.locks.ReentrantLock; 62 import com.sun.javafx.stage.WindowHelper; 63 import com.sun.javafx.stage.FocusUngrabEvent; 64 import com.sun.javafx.sg.prism.NGNode; 65 import com.sun.javafx.sg.prism.NGExternalNode; 66 import com.sun.javafx.geom.BaseBounds; 67 import com.sun.javafx.geom.transform.BaseTransform; 68 import com.sun.javafx.scene.DirtyBits; 69 import com.sun.javafx.embed.swing.DisposerRecord; 70 import javafx.embed.swing.SwingNode; 71 import com.sun.javafx.PlatformUtil; 72 import com.sun.javafx.scene.NodeHelper; 73 74 import com.sun.javafx.util.Utils; 75 import jdk.swing.interop.LightweightFrameWrapper; 76 import jdk.swing.interop.LightweightContentWrapper; 77 import jdk.swing.interop.DragSourceContextWrapper; 78 import com.sun.javafx.embed.swing.SwingNodeInterop; 79 import com.sun.javafx.embed.swing.FXDnD; 80 import com.sun.javafx.embed.swing.SwingCursors; 81 import com.sun.javafx.embed.swing.SwingNodeHelper; 82 83 public class SwingNodeInteropN extends SwingNodeInterop { 84 85 private volatile LightweightFrameWrapper lwFrame; 86 87 /** 88 * Calls LightweightFrameWrapper.notifyDisplayChanged. 89 * Must be called on EDT only. 90 */ 91 private static OptionalMethod<LightweightFrameWrapper> jlfNotifyDisplayChanged; 92 private static Class lwFrameWrapperClass = null; 93 private static native void overrideNativeWindowHandle(Class lwFrameWrapperClass, 94 LightweightFrameWrapper lwFrame, long handle, 95 Runnable closeWindow); 96 static { 97 jlfNotifyDisplayChanged = new OptionalMethod<>(LightweightFrameWrapper.class, 98 "notifyDisplayChanged", Double.TYPE, Double.TYPE); 99 if (!jlfNotifyDisplayChanged.isSupported()) { 100 jlfNotifyDisplayChanged = new OptionalMethod<>( 101 LightweightFrameWrapper.class,"notifyDisplayChanged", Integer.TYPE); 102 } 103 104 try { 105 lwFrameWrapperClass = Class.forName("jdk.swing.interop.LightweightFrameWrapper"); 106 } catch (Throwable t) {} 107 108 Utils.loadNativeSwingLibrary(); 109 } 110 111 public LightweightFrameWrapper createLightweightFrame() { 112 lwFrame = new LightweightFrameWrapper(); 113 return lwFrame; 114 } 115 116 public LightweightFrameWrapper getLightweightFrame() { return lwFrame; } 117 118 public MouseEvent createMouseEvent(Object frame, 119 int swingID, long swingWhen, int swingModifiers, 120 int relX, int relY, int absX, int absY, 121 int clickCount, boolean swingPopupTrigger, 122 int swingButton) { 123 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 124 return lwFrame.createMouseEvent(lwFrame, swingID, 125 swingWhen, swingModifiers, relX, relY, absX, absY, 126 clickCount, swingPopupTrigger, swingButton); 127 } 128 129 public MouseWheelEvent createMouseWheelEvent(Object frame, 130 int swingModifiers, int x, int y, int wheelRotation) { 131 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 132 return lwFrame.createMouseWheelEvent(lwFrame, 133 swingModifiers, x, y, wheelRotation); 134 } 135 136 public java.awt.event.KeyEvent createKeyEvent(Object frame, 137 int swingID, long swingWhen, 138 int swingModifiers, 139 int swingKeyCode, char swingChar) { 140 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 141 return lwFrame.createKeyEvent(lwFrame, swingID, 142 swingWhen, swingModifiers, swingKeyCode, 143 swingChar); 144 } 145 146 public AWTEvent createUngrabEvent(Object frame) { 147 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 148 return lwFrame.createUngrabEvent(lwFrame); 149 } 150 151 public void overrideNativeWindowHandle(Object frame, long handle, Runnable closeWindow) { 152 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 153 overrideNativeWindowHandle(lwFrameWrapperClass, lwFrame, handle, closeWindow); 154 } 155 156 public void notifyDisplayChanged(Object frame, double scaleX, double scaleY) { 157 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 158 if (jlfNotifyDisplayChanged.isIntegerApi()) { 159 jlfNotifyDisplayChanged.invoke(lwFrame, 160 (int) Math.round(scaleX)); 161 } else { 162 jlfNotifyDisplayChanged.invoke(lwFrame, 163 scaleX, scaleY); 164 } 165 } 166 167 public void setHostBounds(Object frame, int windowX, int windowY, int windowW, int windowH) { 168 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 169 jlfSetHostBounds.invoke(lwFrame, windowX, windowY, windowW, windowH); 170 } 171 172 public void setContent(Object frame, Object cnt) { 173 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 174 LightweightContentWrapper content = (LightweightContentWrapper)cnt; 175 lwFrame.setContent(content); 176 } 177 178 public void setVisible(Object frame, boolean visible) { 179 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 180 lwFrame.setVisible(visible); 181 } 182 183 public void setBounds(Object frame, int frameX, int frameY, int frameW, int frameH) { 184 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 185 lwFrame.setBounds(frameX, frameY, frameW, frameH); 186 } 187 188 public LightweightContentWrapper createSwingNodeContent(JComponent content, SwingNode node) { 189 return (LightweightContentWrapper)new SwingNodeContent(content, node); 190 } 191 192 public DisposerRecord createSwingNodeDisposer(Object frame) { 193 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 194 return new SwingNodeDisposer(lwFrame); 195 } 196 197 private static final class OptionalMethod<T> { 198 private final Method method; 199 private final boolean isIntegerAPI; 200 201 OptionalMethod(Class<T> cls, String name, Class<?>... args) { 202 Method m; 203 try { 204 m = cls.getMethod(name, args); 205 } catch (NoSuchMethodException ignored) { 206 // This means we're running with older JDK, simply skip the call 207 m = null; 208 } catch (Throwable ex) { 209 throw new RuntimeException("Error when calling " + cls.getName() + ".getMethod('" + name + "').", ex); 210 } 211 method = m; 212 isIntegerAPI = args != null && args.length > 0 && 213 args[0] == Integer.TYPE; 214 } 215 216 public boolean isSupported() { 217 return method != null; 218 } 219 220 public boolean isIntegerApi() { 221 return isIntegerAPI; 222 } 223 224 public Object invoke(T object, Object... args) { 225 if (method != null) { 226 try { 227 return method.invoke(object, args); 228 } catch (Throwable ex) { 229 throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex); 230 } 231 } else { 232 return null; 233 } 234 } 235 } 236 237 /** 238 * Calls LightweightFrameWrapper.setHostBounds. 239 * Must be called on EDT only. 240 */ 241 private static final OptionalMethod<LightweightFrameWrapper> jlfSetHostBounds = 242 new OptionalMethod<>(LightweightFrameWrapper.class, "setHostBounds", 243 Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE); 244 245 public void emulateActivation(Object frame, boolean activate) { 246 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 247 lwFrame.emulateActivation(activate); 248 } 249 250 public void disposeFrame(Object frame) { 251 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 252 lwFrame.dispose(); 253 } 254 255 public void addWindowFocusListener(Object frame, WindowFocusListener l) { 256 LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; 257 lwFrame.addWindowFocusListener(l); 258 } 259 260 private static class SwingNodeDisposer implements DisposerRecord { 261 LightweightFrameWrapper lwFrame; 262 263 SwingNodeDisposer(LightweightFrameWrapper ref) { 264 this.lwFrame = ref; 265 } 266 public void dispose() { 267 if (lwFrame != null) { 268 lwFrame.dispose(); 269 lwFrame = null; 270 } 271 } 272 } 273 274 private static class SwingNodeContent extends LightweightContentWrapper { 275 private JComponent comp; 276 private volatile FXDnD dnd; 277 private WeakReference<SwingNode> swingNodeRef; 278 279 SwingNodeContent(JComponent comp, SwingNode swingNode) { 280 this.comp = comp; 281 this.swingNodeRef = new WeakReference<SwingNode>(swingNode); 282 } 283 @Override 284 public JComponent getComponent() { 285 return comp; 286 } 287 @Override 288 public void paintLock() { 289 SwingNode swingNode = swingNodeRef.get(); 290 if (swingNode != null) { 291 SwingNodeHelper.getPaintLock(swingNode).lock(); 292 } 293 } 294 @Override 295 public void paintUnlock() { 296 SwingNode swingNode = swingNodeRef.get(); 297 if (swingNode != null) { 298 SwingNodeHelper.getPaintLock(swingNode).unlock(); 299 } 300 } 301 302 @Override 303 public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) { 304 imageBufferReset(data, x, y, width, height, linestride, 1); 305 } 306 //@Override 307 public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) { 308 SwingNode swingNode = swingNodeRef.get(); 309 if (swingNode != null) { 310 SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scale, scale); 311 } 312 } 313 @Override 314 public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) { 315 SwingNode swingNode = swingNodeRef.get(); 316 if (swingNode != null) { 317 SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scaleX, scaleY); 318 } 319 } 320 @Override 321 public void imageReshaped(int x, int y, int width, int height) { 322 SwingNode swingNode = swingNodeRef.get(); 323 if (swingNode != null) { 324 SwingNodeHelper.setImageBounds(swingNode, x, y, width, height); 325 } 326 } 327 @Override 328 public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) { 329 SwingNode swingNode = swingNodeRef.get(); 330 if (swingNode != null) { 331 SwingNodeHelper.repaintDirtyRegion(swingNode, dirtyX, dirtyY, dirtyWidth, dirtyHeight); 332 } 333 } 334 @Override 335 public void focusGrabbed() { 336 SwingNodeHelper.runOnFxThread(() -> { 337 // On X11 grab is limited to a single XDisplay connection, 338 // so we can't delegate it to another GUI toolkit. 339 if (PlatformUtil.isLinux()) return; 340 341 SwingNode swingNode = swingNodeRef.get(); 342 if (swingNode != null) { 343 Scene scene = swingNode.getScene(); 344 if (scene != null && 345 scene.getWindow() != null && 346 WindowHelper.getPeer(scene.getWindow()) != null) { 347 WindowHelper.getPeer(scene.getWindow()).grabFocus(); 348 SwingNodeHelper.setGrabbed(swingNode, true); 349 } 350 } 351 }); 352 } 353 @Override 354 public void focusUngrabbed() { 355 SwingNodeHelper.runOnFxThread(() -> { 356 SwingNode swingNode = swingNodeRef.get(); 357 if (swingNode != null) { 358 Scene scene = swingNode.getScene(); 359 SwingNodeHelper.ungrabFocus(swingNode, false); 360 } 361 }); 362 } 363 @Override 364 public void preferredSizeChanged(final int width, final int height) { 365 SwingNodeHelper.runOnFxThread(() -> { 366 SwingNode swingNode = swingNodeRef.get(); 367 if (swingNode != null) { 368 SwingNodeHelper.setSwingPrefWidth(swingNode, width); 369 SwingNodeHelper.setSwingPrefHeight(swingNode, height); 370 NodeHelper.notifyLayoutBoundsChanged(swingNode); 371 } 372 }); 373 } 374 @Override 375 public void maximumSizeChanged(final int width, final int height) { 376 SwingNodeHelper.runOnFxThread(() -> { 377 SwingNode swingNode = swingNodeRef.get(); 378 if (swingNode != null) { 379 SwingNodeHelper.setSwingMaxWidth(swingNode, width); 380 SwingNodeHelper.setSwingMaxHeight(swingNode, height); 381 NodeHelper.notifyLayoutBoundsChanged(swingNode); 382 } 383 }); 384 } 385 @Override 386 public void minimumSizeChanged(final int width, final int height) { 387 SwingNodeHelper.runOnFxThread(() -> { 388 SwingNode swingNode = swingNodeRef.get(); 389 if (swingNode != null) { 390 SwingNodeHelper.setSwingMinWidth(swingNode, width); 391 SwingNodeHelper.setSwingMinHeight(swingNode, height); 392 NodeHelper.notifyLayoutBoundsChanged(swingNode); 393 } 394 }); 395 } 396 397 //@Override 398 public void setCursor(Cursor cursor) { 399 SwingNodeHelper.runOnFxThread(() -> { 400 SwingNode swingNode = swingNodeRef.get(); 401 if (swingNode != null) { 402 swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor)); 403 } 404 }); 405 } 406 407 private void initDnD() { 408 // This is a part of AWT API, so the method may be invoked on any thread 409 synchronized (SwingNodeContent.this) { 410 if (this.dnd == null) { 411 SwingNode swingNode = swingNodeRef.get(); 412 if (swingNode != null) { 413 this.dnd = new FXDnD(swingNode); 414 } 415 } 416 } 417 } 418 419 @Override 420 public synchronized <T extends DragGestureRecognizer> T createDragGestureRecognizer( 421 Class<T> abstractRecognizerClass, 422 DragSource ds, Component c, int srcActions, 423 DragGestureListener dgl) 424 { 425 initDnD(); 426 return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl); 427 } 428 429 @Override 430 public DragSourceContextWrapper createDragSourceContext(DragGestureEvent dge) throws InvalidDnDOperationException 431 { 432 initDnD(); 433 return (DragSourceContextWrapper)dnd.createDragSourceContext(dge); 434 } 435 436 @Override 437 public void addDropTarget(DropTarget dt) { 438 initDnD(); 439 dnd.addDropTarget(dt); 440 } 441 442 @Override 443 public void removeDropTarget(DropTarget dt) { 444 initDnD(); 445 dnd.removeDropTarget(dt); 446 } 447 } 448 449 }