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