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