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