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