1 /* 2 * Copyright (c) 2011, 2020, 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 sun.lwawt.macosx; 27 28 import java.awt.AWTError; 29 import java.awt.AWTException; 30 import java.awt.CheckboxMenuItem; 31 import java.awt.Color; 32 import java.awt.Component; 33 import java.awt.Cursor; 34 import java.awt.Desktop; 35 import java.awt.Dialog; 36 import java.awt.Dimension; 37 import java.awt.Event; 38 import java.awt.EventQueue; 39 import java.awt.FileDialog; 40 import java.awt.Frame; 41 import java.awt.GraphicsConfiguration; 42 import java.awt.GraphicsDevice; 43 import java.awt.GraphicsEnvironment; 44 import java.awt.HeadlessException; 45 import java.awt.Image; 46 import java.awt.Insets; 47 import java.awt.Menu; 48 import java.awt.MenuBar; 49 import java.awt.MenuItem; 50 import java.awt.Point; 51 import java.awt.PopupMenu; 52 import java.awt.RenderingHints; 53 import java.awt.SystemTray; 54 import java.awt.Taskbar; 55 import java.awt.Toolkit; 56 import java.awt.TrayIcon; 57 import java.awt.Window; 58 import java.awt.datatransfer.Clipboard; 59 import java.awt.dnd.DragGestureEvent; 60 import java.awt.dnd.DragGestureListener; 61 import java.awt.dnd.DragGestureRecognizer; 62 import java.awt.dnd.DragSource; 63 import java.awt.dnd.DropTarget; 64 import java.awt.dnd.InvalidDnDOperationException; 65 import java.awt.dnd.MouseDragGestureRecognizer; 66 import java.awt.dnd.peer.DragSourceContextPeer; 67 import java.awt.event.InputEvent; 68 import java.awt.event.InvocationEvent; 69 import java.awt.event.KeyEvent; 70 import java.awt.font.TextAttribute; 71 import java.awt.im.InputMethodHighlight; 72 import java.awt.im.spi.InputMethodDescriptor; 73 import java.awt.peer.CheckboxMenuItemPeer; 74 import java.awt.peer.DesktopPeer; 75 import java.awt.peer.DialogPeer; 76 import java.awt.peer.FileDialogPeer; 77 import java.awt.peer.FontPeer; 78 import java.awt.peer.MenuBarPeer; 79 import java.awt.peer.MenuItemPeer; 80 import java.awt.peer.MenuPeer; 81 import java.awt.peer.PopupMenuPeer; 82 import java.awt.peer.RobotPeer; 83 import java.awt.peer.SystemTrayPeer; 84 import java.awt.peer.TaskbarPeer; 85 import java.awt.peer.TrayIconPeer; 86 import java.lang.reflect.InvocationTargetException; 87 import java.lang.reflect.UndeclaredThrowableException; 88 import java.net.MalformedURLException; 89 import java.net.URL; 90 import java.security.AccessController; 91 import java.security.PrivilegedAction; 92 import java.util.HashMap; 93 import java.util.Locale; 94 import java.util.Map; 95 import java.util.MissingResourceException; 96 import java.util.Objects; 97 import java.util.ResourceBundle; 98 import java.util.concurrent.Callable; 99 100 import javax.swing.UIManager; 101 102 import com.apple.laf.AquaMenuBarUI; 103 import sun.awt.AWTAccessor; 104 import sun.awt.AppContext; 105 import sun.awt.CGraphicsDevice; 106 import sun.awt.LightweightFrame; 107 import sun.awt.PlatformGraphicsInfo; 108 import sun.awt.SunToolkit; 109 import sun.awt.datatransfer.DataTransferer; 110 import sun.awt.dnd.SunDragSourceContextPeer; 111 import sun.awt.util.ThreadGroupUtils; 112 import sun.java2d.opengl.OGLRenderQueue; 113 import sun.lwawt.LWComponentPeer; 114 import sun.lwawt.LWCursorManager; 115 import sun.lwawt.LWToolkit; 116 import sun.lwawt.LWWindowPeer; 117 import sun.lwawt.LWWindowPeer.PeerType; 118 import sun.lwawt.PlatformComponent; 119 import sun.lwawt.PlatformDropTarget; 120 import sun.lwawt.PlatformWindow; 121 import sun.lwawt.SecurityWarningWindow; 122 123 @SuppressWarnings("serial") // JDK implementation class 124 final class NamedCursor extends Cursor { 125 NamedCursor(String name) { 126 super(name); 127 } 128 } 129 130 /** 131 * Mac OS X Cocoa-based AWT Toolkit. 132 */ 133 public final class LWCToolkit extends LWToolkit { 134 // While it is possible to enumerate all mouse devices 135 // and query them for the number of buttons, the code 136 // that does it is rather complex. Instead, we opt for 137 // the easy way and just support up to 5 mouse buttons, 138 // like Windows. 139 private static final int BUTTONS = 5; 140 141 private static native void initIDs(); 142 private static native void initAppkit(ThreadGroup appKitThreadGroup, boolean headless); 143 private static CInputMethodDescriptor sInputMethodDescriptor; 144 145 static { 146 System.err.flush(); 147 148 ResourceBundle platformResources = java.security.AccessController.doPrivileged( 149 new java.security.PrivilegedAction<ResourceBundle>() { 150 @Override 151 public ResourceBundle run() { 152 ResourceBundle platformResources = null; 153 try { 154 platformResources = ResourceBundle.getBundle("sun.awt.resources.awtosx"); 155 } catch (MissingResourceException e) { 156 // No resource file; defaults will be used. 157 } 158 159 System.loadLibrary("awt"); 160 System.loadLibrary("fontmanager"); 161 162 return platformResources; 163 } 164 }); 165 166 if (!GraphicsEnvironment.isHeadless() && 167 !PlatformGraphicsInfo.isInAquaSession()) 168 { 169 throw new AWTError("WindowServer is not available"); 170 } 171 172 AWTAccessor.getToolkitAccessor().setPlatformResources(platformResources); 173 174 if (!GraphicsEnvironment.isHeadless()) { 175 initIDs(); 176 } 177 inAWT = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 178 @Override 179 public Boolean run() { 180 return !Boolean.parseBoolean(System.getProperty("javafx.embed.singleThread", "false")); 181 } 182 }); 183 } 184 185 /* 186 * If true we operate in normal mode and nested runloop is executed in JavaRunLoopMode 187 * If false we operate in singleThreaded FX/AWT interop mode and nested loop uses NSDefaultRunLoopMode 188 */ 189 private static final boolean inAWT; 190 191 public LWCToolkit() { 192 final String extraButtons = "sun.awt.enableExtraMouseButtons"; 193 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 194 areExtraMouseButtonsEnabled = 195 Boolean.parseBoolean(System.getProperty(extraButtons, "true")); 196 //set system property if not yet assigned 197 System.setProperty(extraButtons, ""+areExtraMouseButtonsEnabled); 198 initAppkit(ThreadGroupUtils.getRootThreadGroup(), 199 GraphicsEnvironment.isHeadless()); 200 return null; 201 }); 202 } 203 204 /* 205 * System colors with default initial values, overwritten by toolkit if system values differ and are available. 206 */ 207 private static final int NUM_APPLE_COLORS = 3; 208 public static final int KEYBOARD_FOCUS_COLOR = 0; 209 public static final int INACTIVE_SELECTION_BACKGROUND_COLOR = 1; 210 public static final int INACTIVE_SELECTION_FOREGROUND_COLOR = 2; 211 private static int[] appleColors = { 212 0xFF808080, // keyboardFocusColor = Color.gray; 213 0xFFC0C0C0, // secondarySelectedControlColor 214 0xFF303030, // controlDarkShadowColor 215 }; 216 217 private native void loadNativeColors(final int[] systemColors, final int[] appleColors); 218 219 @Override 220 protected void loadSystemColors(final int[] systemColors) { 221 if (systemColors == null) return; 222 loadNativeColors(systemColors, appleColors); 223 } 224 225 @SuppressWarnings("serial") // JDK implementation class 226 private static class AppleSpecificColor extends Color { 227 private final int index; 228 AppleSpecificColor(int index) { 229 super(appleColors[index]); 230 this.index = index; 231 } 232 233 @Override 234 public int getRGB() { 235 return appleColors[index]; 236 } 237 } 238 239 /** 240 * Returns Apple specific colors that we may expose going forward. 241 */ 242 public static Color getAppleColor(int color) { 243 return new AppleSpecificColor(color); 244 } 245 246 // This is only called from native code. 247 static void systemColorsChanged() { 248 EventQueue.invokeLater(() -> { 249 AccessController.doPrivileged( (PrivilegedAction<Object>) () -> { 250 AWTAccessor.getSystemColorAccessor().updateSystemColors(); 251 return null; 252 }); 253 }); 254 } 255 256 public static LWCToolkit getLWCToolkit() { 257 return (LWCToolkit)Toolkit.getDefaultToolkit(); 258 } 259 260 @Override 261 protected PlatformWindow createPlatformWindow(PeerType peerType) { 262 if (peerType == PeerType.EMBEDDED_FRAME) { 263 return new CPlatformEmbeddedFrame(); 264 } else if (peerType == PeerType.VIEW_EMBEDDED_FRAME) { 265 return new CViewPlatformEmbeddedFrame(); 266 } else if (peerType == PeerType.LW_FRAME) { 267 return new CPlatformLWWindow(); 268 } else { 269 assert (peerType == PeerType.SIMPLEWINDOW 270 || peerType == PeerType.DIALOG 271 || peerType == PeerType.FRAME); 272 return new CPlatformWindow(); 273 } 274 } 275 276 LWWindowPeer createEmbeddedFrame(CEmbeddedFrame target) { 277 PlatformComponent platformComponent = createPlatformComponent(); 278 PlatformWindow platformWindow = createPlatformWindow(PeerType.EMBEDDED_FRAME); 279 return createDelegatedPeer(target, platformComponent, platformWindow, PeerType.EMBEDDED_FRAME); 280 } 281 282 LWWindowPeer createEmbeddedFrame(CViewEmbeddedFrame target) { 283 PlatformComponent platformComponent = createPlatformComponent(); 284 PlatformWindow platformWindow = createPlatformWindow(PeerType.VIEW_EMBEDDED_FRAME); 285 return createDelegatedPeer(target, platformComponent, platformWindow, PeerType.VIEW_EMBEDDED_FRAME); 286 } 287 288 private CPrinterDialogPeer createCPrinterDialog(CPrinterDialog target) { 289 PlatformComponent platformComponent = createPlatformComponent(); 290 PlatformWindow platformWindow = createPlatformWindow(PeerType.DIALOG); 291 CPrinterDialogPeer peer = new CPrinterDialogPeer(target, platformComponent, platformWindow); 292 targetCreatedPeer(target, peer); 293 return peer; 294 } 295 296 @Override 297 public DialogPeer createDialog(Dialog target) { 298 if (target instanceof CPrinterDialog) { 299 return createCPrinterDialog((CPrinterDialog)target); 300 } 301 return super.createDialog(target); 302 } 303 304 @Override 305 protected SecurityWarningWindow createSecurityWarning(Window ownerWindow, 306 LWWindowPeer ownerPeer) { 307 return new CWarningWindow(ownerWindow, ownerPeer); 308 } 309 310 @Override 311 protected PlatformComponent createPlatformComponent() { 312 return new CPlatformComponent(); 313 } 314 315 @Override 316 protected PlatformComponent createLwPlatformComponent() { 317 return new CPlatformLWComponent(); 318 } 319 320 @Override 321 protected FileDialogPeer createFileDialogPeer(FileDialog target) { 322 return new CFileDialog(target); 323 } 324 325 @Override 326 public MenuPeer createMenu(Menu target) { 327 MenuPeer peer = new CMenu(target); 328 targetCreatedPeer(target, peer); 329 return peer; 330 } 331 332 @Override 333 public MenuBarPeer createMenuBar(MenuBar target) { 334 MenuBarPeer peer = new CMenuBar(target); 335 targetCreatedPeer(target, peer); 336 return peer; 337 } 338 339 @Override 340 public MenuItemPeer createMenuItem(MenuItem target) { 341 MenuItemPeer peer = new CMenuItem(target); 342 targetCreatedPeer(target, peer); 343 return peer; 344 } 345 346 @Override 347 public CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) { 348 CheckboxMenuItemPeer peer = new CCheckboxMenuItem(target); 349 targetCreatedPeer(target, peer); 350 return peer; 351 } 352 353 @Override 354 public PopupMenuPeer createPopupMenu(PopupMenu target) { 355 PopupMenuPeer peer = new CPopupMenu(target); 356 targetCreatedPeer(target, peer); 357 return peer; 358 } 359 360 @Override 361 public SystemTrayPeer createSystemTray(SystemTray target) { 362 return new CSystemTray(); 363 } 364 365 @Override 366 public TrayIconPeer createTrayIcon(TrayIcon target) { 367 TrayIconPeer peer = new CTrayIcon(target); 368 targetCreatedPeer(target, peer); 369 return peer; 370 } 371 372 @Override 373 public DesktopPeer createDesktopPeer(Desktop target) { 374 return new CDesktopPeer(); 375 } 376 377 @Override 378 public TaskbarPeer createTaskbarPeer(Taskbar target) { 379 return new CTaskbarPeer(); 380 } 381 382 @Override 383 public LWCursorManager getCursorManager() { 384 return CCursorManager.getInstance(); 385 } 386 387 @Override 388 public Cursor createCustomCursor(final Image cursor, final Point hotSpot, 389 final String name) 390 throws IndexOutOfBoundsException, HeadlessException { 391 return new CCustomCursor(cursor, hotSpot, name); 392 } 393 394 @Override 395 public Dimension getBestCursorSize(final int preferredWidth, 396 final int preferredHeight) 397 throws HeadlessException { 398 return CCustomCursor.getBestCursorSize(preferredWidth, preferredHeight); 399 } 400 401 @Override 402 protected void platformCleanup() { 403 // TODO Auto-generated method stub 404 } 405 406 @Override 407 protected void platformInit() { 408 // TODO Auto-generated method stub 409 } 410 411 @Override 412 protected void platformRunMessage() { 413 // TODO Auto-generated method stub 414 } 415 416 @Override 417 protected void platformShutdown() { 418 // TODO Auto-generated method stub 419 } 420 421 class OSXPlatformFont extends sun.awt.PlatformFont 422 { 423 OSXPlatformFont(String name, int style) 424 { 425 super(name, style); 426 } 427 @Override 428 protected char getMissingGlyphCharacter() 429 { 430 // Follow up for real implementation 431 return (char)0xfff8; // see http://developer.apple.com/fonts/LastResortFont/ 432 } 433 } 434 @Override 435 public FontPeer getFontPeer(String name, int style) { 436 return new OSXPlatformFont(name, style); 437 } 438 439 @Override 440 protected void initializeDesktopProperties() { 441 super.initializeDesktopProperties(); 442 Map <Object, Object> fontHints = new HashMap<>(); 443 fontHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); 444 desktopProperties.put(SunToolkit.DESKTOPFONTHINTS, fontHints); 445 desktopProperties.put("awt.mouse.numButtons", BUTTONS); 446 desktopProperties.put("awt.multiClickInterval", getMultiClickTime()); 447 448 // These DnD properties must be set, otherwise Swing ends up spewing NPEs 449 // all over the place. The values came straight off of MToolkit. 450 desktopProperties.put("DnD.Autoscroll.initialDelay", Integer.valueOf(50)); 451 desktopProperties.put("DnD.Autoscroll.interval", Integer.valueOf(50)); 452 desktopProperties.put("DnD.Autoscroll.cursorHysteresis", Integer.valueOf(5)); 453 454 desktopProperties.put("DnD.isDragImageSupported", Boolean.TRUE); 455 456 // Register DnD cursors 457 desktopProperties.put("DnD.Cursor.CopyDrop", new NamedCursor("DnD.Cursor.CopyDrop")); 458 desktopProperties.put("DnD.Cursor.MoveDrop", new NamedCursor("DnD.Cursor.MoveDrop")); 459 desktopProperties.put("DnD.Cursor.LinkDrop", new NamedCursor("DnD.Cursor.LinkDrop")); 460 desktopProperties.put("DnD.Cursor.CopyNoDrop", new NamedCursor("DnD.Cursor.CopyNoDrop")); 461 desktopProperties.put("DnD.Cursor.MoveNoDrop", new NamedCursor("DnD.Cursor.MoveNoDrop")); 462 desktopProperties.put("DnD.Cursor.LinkNoDrop", new NamedCursor("DnD.Cursor.LinkNoDrop")); 463 } 464 465 @Override 466 protected boolean syncNativeQueue(long timeout) { 467 if (SunDragSourceContextPeer.isDragDropInProgress() 468 || EventQueue.isDispatchThread()) { 469 // The java code started the DnD, but the native drag may still not 470 // start, the last attempt to flush the native events, 471 // also do not block EDT for a long time 472 timeout = 50; 473 } 474 return nativeSyncQueue(timeout); 475 } 476 477 @Override 478 public native void beep(); 479 480 @Override 481 public int getScreenResolution() throws HeadlessException { 482 return (int) ((CGraphicsDevice) GraphicsEnvironment 483 .getLocalGraphicsEnvironment().getDefaultScreenDevice()) 484 .getXResolution(); 485 } 486 487 @Override 488 public Insets getScreenInsets(final GraphicsConfiguration gc) { 489 GraphicsDevice gd = gc.getDevice(); 490 if (!(gd instanceof CGraphicsDevice)) { 491 return super.getScreenInsets(gc); 492 } 493 return ((CGraphicsDevice)gd).getScreenInsets(); 494 } 495 496 @Override 497 public void sync() { 498 // flush the OGL pipeline (this is a no-op if OGL is not enabled) 499 OGLRenderQueue.sync(); 500 // setNeedsDisplay() selector was sent to the appropriate CALayer so now 501 // we have to flush the native selectors queue. 502 flushNativeSelectors(); 503 } 504 505 @Override 506 public RobotPeer createRobot(GraphicsDevice screen) throws AWTException { 507 if (screen instanceof CGraphicsDevice) { 508 return new CRobot((CGraphicsDevice) screen); 509 } 510 return super.createRobot(screen); 511 } 512 513 private native boolean isCapsLockOn(); 514 515 /* 516 * NOTE: Among the keys this method is supposed to check, 517 * only Caps Lock works as a true locking key with OS X. 518 * There is no Scroll Lock key on modern Apple keyboards, 519 * and with a PC keyboard plugged in Scroll Lock is simply 520 * ignored: no LED lights up if you press it. 521 * The key located at the same position on Apple keyboards 522 * as Num Lock on PC keyboards is called Clear, doesn't lock 523 * anything and is used for entirely different purpose. 524 */ 525 @Override 526 public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationException { 527 switch (keyCode) { 528 case KeyEvent.VK_NUM_LOCK: 529 case KeyEvent.VK_SCROLL_LOCK: 530 case KeyEvent.VK_KANA_LOCK: 531 throw new UnsupportedOperationException("Toolkit.getLockingKeyState"); 532 533 case KeyEvent.VK_CAPS_LOCK: 534 return isCapsLockOn(); 535 536 default: 537 throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState"); 538 } 539 } 540 541 //Is it allowed to generate events assigned to extra mouse buttons. 542 //Set to true by default. 543 private static boolean areExtraMouseButtonsEnabled = true; 544 545 @Override 546 public boolean areExtraMouseButtonsEnabled() throws HeadlessException { 547 return areExtraMouseButtonsEnabled; 548 } 549 550 @Override 551 public int getNumberOfButtons(){ 552 return BUTTONS; 553 } 554 555 /** 556 * Returns the double-click time interval in ms. 557 */ 558 private static native int getMultiClickTime(); 559 560 @Override 561 public boolean isTraySupported() { 562 return true; 563 } 564 565 @Override 566 public DataTransferer getDataTransferer() { 567 return CDataTransferer.getInstanceImpl(); 568 } 569 570 @Override 571 public boolean isAlwaysOnTopSupported() { 572 return true; 573 } 574 575 private static final String APPKIT_THREAD_NAME = "AppKit Thread"; 576 577 // Intended to be called from the LWCToolkit.m only. 578 private static void installToolkitThreadInJava() { 579 Thread.currentThread().setName(APPKIT_THREAD_NAME); 580 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 581 Thread.currentThread().setContextClassLoader(null); 582 return null; 583 }); 584 } 585 586 @Override 587 public boolean isWindowOpacitySupported() { 588 return true; 589 } 590 591 @Override 592 public boolean isFrameStateSupported(int state) throws HeadlessException { 593 switch (state) { 594 case Frame.NORMAL: 595 case Frame.ICONIFIED: 596 case Frame.MAXIMIZED_BOTH: 597 return true; 598 default: 599 return false; 600 } 601 } 602 603 @Override 604 @Deprecated(since = "10") 605 public int getMenuShortcutKeyMask() { 606 return Event.META_MASK; 607 } 608 609 @Override 610 public int getMenuShortcutKeyMaskEx() { 611 return InputEvent.META_DOWN_MASK; 612 } 613 614 @Override 615 public Image getImage(final String filename) { 616 final Image nsImage = checkForNSImage(filename); 617 if (nsImage != null) { 618 return nsImage; 619 } 620 621 if (imageCached(filename)) { 622 return super.getImage(filename); 623 } 624 625 String filename2x = getScaledImageName(filename); 626 return (imageExists(filename2x)) 627 ? getImageWithResolutionVariant(filename, filename2x) 628 : super.getImage(filename); 629 } 630 631 @Override 632 public Image getImage(URL url) { 633 634 if (imageCached(url)) { 635 return super.getImage(url); 636 } 637 638 URL url2x = getScaledImageURL(url); 639 return (imageExists(url2x)) 640 ? getImageWithResolutionVariant(url, url2x) : super.getImage(url); 641 } 642 643 private static final String nsImagePrefix = "NSImage://"; 644 private Image checkForNSImage(final String imageName) { 645 if (imageName == null) return null; 646 if (!imageName.startsWith(nsImagePrefix)) return null; 647 return CImage.getCreator().createImageFromName(imageName.substring(nsImagePrefix.length())); 648 } 649 650 // Thread-safe Object.equals() called from native 651 public static boolean doEquals(final Object a, final Object b, Component c) { 652 if (a == b) return true; 653 654 final boolean[] ret = new boolean[1]; 655 656 try { invokeAndWait(new Runnable() { @Override 657 public void run() { synchronized(ret) { 658 ret[0] = a.equals(b); 659 }}}, c); } catch (Exception e) { e.printStackTrace(); } 660 661 synchronized(ret) { return ret[0]; } 662 } 663 664 public static <T> T invokeAndWait(final Callable<T> callable, 665 Component component) throws Exception { 666 final CallableWrapper<T> wrapper = new CallableWrapper<>(callable); 667 invokeAndWait(wrapper, component); 668 return wrapper.getResult(); 669 } 670 671 static final class CallableWrapper<T> implements Runnable { 672 final Callable<T> callable; 673 T object; 674 Exception e; 675 676 CallableWrapper(final Callable<T> callable) { 677 this.callable = callable; 678 } 679 680 @Override 681 public void run() { 682 try { 683 object = callable.call(); 684 } catch (final Exception e) { 685 this.e = e; 686 } 687 } 688 689 public T getResult() throws Exception { 690 if (e != null) throw e; 691 return object; 692 } 693 } 694 695 /** 696 * Kicks an event over to the appropriate event queue and waits for it to 697 * finish To avoid deadlocking, we manually run the NSRunLoop while waiting 698 * Any selector invoked using ThreadUtilities performOnMainThread will be 699 * processed in doAWTRunLoop The InvocationEvent will call 700 * LWCToolkit.stopAWTRunLoop() when finished, which will stop our manual 701 * run loop. Does not dispatch native events while in the loop 702 */ 703 public static void invokeAndWait(Runnable runnable, Component component) 704 throws InvocationTargetException { 705 Objects.requireNonNull(component, "Null component provided to invokeAndWait"); 706 707 long mediator = createAWTRunLoopMediator(); 708 InvocationEvent invocationEvent = 709 new InvocationEvent(component, 710 runnable, 711 () -> { 712 if (mediator != 0) { 713 stopAWTRunLoop(mediator); 714 } 715 }, 716 true); 717 718 AppContext appContext = SunToolkit.targetToAppContext(component); 719 SunToolkit.postEvent(appContext, invocationEvent); 720 // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock 721 SunToolkit.flushPendingEvents(appContext); 722 doAWTRunLoop(mediator, false); 723 724 checkException(invocationEvent); 725 } 726 727 public static void invokeLater(Runnable event, Component component) 728 throws InvocationTargetException { 729 Objects.requireNonNull(component, "Null component provided to invokeLater"); 730 731 InvocationEvent invocationEvent = new InvocationEvent(component, event); 732 733 AppContext appContext = SunToolkit.targetToAppContext(component); 734 SunToolkit.postEvent(SunToolkit.targetToAppContext(component), invocationEvent); 735 // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock 736 SunToolkit.flushPendingEvents(appContext); 737 738 checkException(invocationEvent); 739 } 740 741 /** 742 * Checks if exception occurred while {@code InvocationEvent} was processed and rethrows it as 743 * an {@code InvocationTargetException} 744 * 745 * @param event the event to check for an exception 746 * @throws InvocationTargetException if exception occurred when event was processed 747 */ 748 private static void checkException(InvocationEvent event) throws InvocationTargetException { 749 Throwable eventException = event.getException(); 750 if (eventException == null) return; 751 752 if (eventException instanceof UndeclaredThrowableException) { 753 eventException = ((UndeclaredThrowableException)eventException).getUndeclaredThrowable(); 754 } 755 throw new InvocationTargetException(eventException); 756 } 757 758 /** 759 * Schedules a {@code Runnable} execution on the Appkit thread after a delay 760 * @param r a {@code Runnable} to execute 761 * @param delay a delay in milliseconds 762 */ 763 static native void performOnMainThreadAfterDelay(Runnable r, long delay); 764 765 // DnD support 766 767 @Override 768 public DragSourceContextPeer createDragSourceContextPeer( 769 DragGestureEvent dge) throws InvalidDnDOperationException { 770 final LightweightFrame f = SunToolkit.getLightweightFrame(dge.getComponent()); 771 if (f != null) { 772 return f.createDragSourceContextPeer(dge); 773 } 774 775 return CDragSourceContextPeer.createDragSourceContextPeer(dge); 776 } 777 778 @Override 779 @SuppressWarnings("unchecked") 780 public <T extends DragGestureRecognizer> T createDragGestureRecognizer( 781 Class<T> abstractRecognizerClass, DragSource ds, Component c, 782 int srcActions, DragGestureListener dgl) { 783 final LightweightFrame f = SunToolkit.getLightweightFrame(c); 784 if (f != null) { 785 return f.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl); 786 } 787 788 DragGestureRecognizer dgr = null; 789 790 // Create a new mouse drag gesture recognizer if we have a class match: 791 if (MouseDragGestureRecognizer.class.equals(abstractRecognizerClass)) 792 dgr = new CMouseDragGestureRecognizer(ds, c, srcActions, dgl); 793 794 return (T)dgr; 795 } 796 797 @Override 798 protected PlatformDropTarget createDropTarget(DropTarget dropTarget, 799 Component component, 800 LWComponentPeer<?, ?> peer) { 801 return new CDropTarget(dropTarget, component, peer); 802 } 803 804 // InputMethodSupport Method 805 /** 806 * Returns the default keyboard locale of the underlying operating system 807 */ 808 @Override 809 public Locale getDefaultKeyboardLocale() { 810 Locale locale = CInputMethod.getNativeLocale(); 811 812 if (locale == null) { 813 return super.getDefaultKeyboardLocale(); 814 } 815 816 return locale; 817 } 818 819 public static boolean isLocaleUSInternationalPC(Locale locale) { 820 return (locale != null ? 821 locale.toString().equals("_US_UserDefined_15000") : false); 822 } 823 824 public static boolean isCharModifierKeyInUSInternationalPC(char ch) { 825 // 5 characters: APOSTROPHE, QUOTATION MARK, ACCENT GRAVE, SMALL TILDE, 826 // CIRCUMFLEX ACCENT 827 final char[] modifierKeys = {'\'', '"', '`', '\u02DC', '\u02C6'}; 828 for (char modKey : modifierKeys) { 829 if (modKey == ch) { 830 return true; 831 } 832 } 833 return false; 834 } 835 836 @Override 837 public InputMethodDescriptor getInputMethodAdapterDescriptor() { 838 if (sInputMethodDescriptor == null) 839 sInputMethodDescriptor = new CInputMethodDescriptor(); 840 841 return sInputMethodDescriptor; 842 } 843 844 /** 845 * Returns a map of visual attributes for thelevel description 846 * of the given input method highlight, or null if no mapping is found. 847 * The style field of the input method highlight is ignored. The map 848 * returned is unmodifiable. 849 * @param highlight input method highlight 850 * @return style attribute map, or null 851 * @since 1.3 852 */ 853 @Override 854 public Map<TextAttribute, ?> mapInputMethodHighlight(InputMethodHighlight highlight) { 855 return CInputMethod.mapInputMethodHighlight(highlight); 856 } 857 858 /** 859 * Returns key modifiers used by Swing to set up a focus accelerator key 860 * stroke. 861 */ 862 @Override 863 @SuppressWarnings("deprecation") 864 public int getFocusAcceleratorKeyMask() { 865 return InputEvent.CTRL_MASK | InputEvent.ALT_MASK; 866 } 867 868 /** 869 * Tests whether specified key modifiers mask can be used to enter a 870 * printable character. 871 */ 872 @Override 873 @SuppressWarnings("deprecation") 874 public boolean isPrintableCharacterModifiersMask(int mods) { 875 return ((mods & (InputEvent.META_MASK | InputEvent.CTRL_MASK)) == 0); 876 } 877 878 /** 879 * Returns whether popup is allowed to be shown above the task bar. 880 */ 881 @Override 882 public boolean canPopupOverlapTaskBar() { 883 return false; 884 } 885 886 /* 887 * Returns true if the application (one of its windows) owns keyboard focus. 888 */ 889 native boolean isApplicationActive(); 890 891 /** 892 * Returns true if AWT toolkit is embedded, false otherwise. 893 * 894 * @return true if AWT toolkit is embedded, false otherwise 895 */ 896 public static native boolean isEmbedded(); 897 898 /* 899 * Activates application ignoring other apps. 900 */ 901 public native void activateApplicationIgnoringOtherApps(); 902 903 /************************ 904 * Native methods section 905 ************************/ 906 907 static native long createAWTRunLoopMediator(); 908 /** 909 * Method to run a nested run-loop. The nested loop is spinned in the javaRunLoop mode, so selectors sent 910 * by [JNFRunLoop performOnMainThreadWaiting] are processed. 911 * @param mediator a native pointer to the mediator object created by createAWTRunLoopMediator 912 * @param processEvents if true - dispatches event while in the nested loop. Used in DnD. 913 * Additional attention is needed when using this feature as we short-circuit normal event 914 * processing which could break Appkit. 915 * (One known example is when the window is resized with the mouse) 916 * 917 * if false - all events come after exit form the nested loop 918 */ 919 static void doAWTRunLoop(long mediator, boolean processEvents) { 920 doAWTRunLoopImpl(mediator, processEvents, inAWT); 921 } 922 private static native void doAWTRunLoopImpl(long mediator, boolean processEvents, boolean inAWT); 923 static native void stopAWTRunLoop(long mediator); 924 925 private native boolean nativeSyncQueue(long timeout); 926 927 /** 928 * Just spin a single empty block synchronously. 929 */ 930 static native void flushNativeSelectors(); 931 932 @Override 933 public Clipboard createPlatformClipboard() { 934 return new CClipboard("System"); 935 } 936 937 @Override 938 public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType exclusionType) { 939 return (exclusionType == null) || 940 (exclusionType == Dialog.ModalExclusionType.NO_EXCLUDE) || 941 (exclusionType == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) || 942 (exclusionType == Dialog.ModalExclusionType.TOOLKIT_EXCLUDE); 943 } 944 945 @Override 946 public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) { 947 //TODO: FileDialog blocks excluded windows... 948 //TODO: Test: 2 file dialogs, separate AppContexts: a) Dialog 1 blocked, shouldn't be. Frame 4 blocked (shouldn't be). 949 return (modalityType == null) || 950 (modalityType == Dialog.ModalityType.MODELESS) || 951 (modalityType == Dialog.ModalityType.DOCUMENT_MODAL) || 952 (modalityType == Dialog.ModalityType.APPLICATION_MODAL) || 953 (modalityType == Dialog.ModalityType.TOOLKIT_MODAL); 954 } 955 956 @Override 957 public boolean isWindowShapingSupported() { 958 return true; 959 } 960 961 @Override 962 public boolean isWindowTranslucencySupported() { 963 return true; 964 } 965 966 @Override 967 public boolean isTranslucencyCapable(GraphicsConfiguration gc) { 968 return true; 969 } 970 971 @Override 972 public boolean isSwingBackbufferTranslucencySupported() { 973 return true; 974 } 975 976 @Override 977 public boolean enableInputMethodsForTextComponent() { 978 return true; 979 } 980 981 private static URL getScaledImageURL(URL url) { 982 try { 983 String scaledImagePath = getScaledImageName(url.getPath()); 984 return scaledImagePath == null ? null : new URL(url.getProtocol(), 985 url.getHost(), url.getPort(), scaledImagePath); 986 } catch (MalformedURLException e) { 987 return null; 988 } 989 } 990 991 private static String getScaledImageName(String path) { 992 if (!isValidPath(path)) { 993 return null; 994 } 995 996 int slash = path.lastIndexOf('/'); 997 String name = (slash < 0) ? path : path.substring(slash + 1); 998 999 if (name.contains("@2x")) { 1000 return null; 1001 } 1002 1003 int dot = name.lastIndexOf('.'); 1004 String name2x = (dot < 0) ? name + "@2x" 1005 : name.substring(0, dot) + "@2x" + name.substring(dot); 1006 return (slash < 0) ? name2x : path.substring(0, slash + 1) + name2x; 1007 } 1008 1009 private static boolean isValidPath(String path) { 1010 return path != null && 1011 !path.isEmpty() && 1012 !path.endsWith("/") && 1013 !path.endsWith("."); 1014 } 1015 1016 @Override 1017 protected PlatformWindow getPlatformWindowUnderMouse() { 1018 return CPlatformWindow.nativeGetTopmostPlatformWindowUnderMouse(); 1019 } 1020 1021 @Override 1022 public void updateScreenMenuBarUI() { 1023 if (AquaMenuBarUI.getScreenMenuBarProperty()) { 1024 UIManager.put("MenuBarUI", "com.apple.laf.AquaMenuBarUI"); 1025 } else { 1026 UIManager.put("MenuBarUI", null); 1027 } 1028 } 1029 }