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