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