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