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