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