1 /* 2 * Copyright (c) 1997, 2016, 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.awt; 27 28 import java.awt.*; 29 import java.awt.event.InputEvent; 30 import java.awt.event.KeyEvent; 31 import java.awt.event.WindowEvent; 32 import java.awt.image.BufferedImage; 33 import java.awt.image.ColorModel; 34 import java.awt.image.DataBuffer; 35 import java.awt.image.DataBufferInt; 36 import java.awt.image.ImageObserver; 37 import java.awt.image.ImageProducer; 38 import java.awt.image.Raster; 39 import java.awt.peer.FramePeer; 40 import java.awt.peer.KeyboardFocusManagerPeer; 41 import java.awt.peer.MouseInfoPeer; 42 import java.awt.peer.SystemTrayPeer; 43 import java.awt.peer.TrayIconPeer; 44 import java.io.File; 45 import java.io.IOException; 46 import java.io.InputStream; 47 import java.lang.reflect.InvocationTargetException; 48 import java.net.URL; 49 import java.security.AccessController; 50 import java.util.ArrayList; 51 import java.util.Collections; 52 import java.util.Iterator; 53 import java.util.Locale; 54 import java.util.Map; 55 import java.util.Vector; 56 import java.util.WeakHashMap; 57 import java.util.concurrent.TimeUnit; 58 import java.util.concurrent.locks.Condition; 59 import java.util.concurrent.locks.ReentrantLock; 60 61 import sun.awt.im.InputContext; 62 import sun.awt.image.ByteArrayImageSource; 63 import sun.awt.image.FileImageSource; 64 import sun.awt.image.ImageRepresentation; 65 import java.awt.image.MultiResolutionImage; 66 import sun.awt.image.MultiResolutionToolkitImage; 67 import sun.awt.image.ToolkitImage; 68 import sun.awt.image.URLImageSource; 69 import sun.font.FontDesignMetrics; 70 import sun.net.util.URLUtil; 71 import sun.security.action.GetBooleanAction; 72 import sun.security.action.GetPropertyAction; 73 import sun.util.logging.PlatformLogger; 74 75 import static java.awt.RenderingHints.*; 76 77 public abstract class SunToolkit extends Toolkit 78 implements ComponentFactory, InputMethodSupport, KeyboardFocusManagerPeerProvider { 79 80 // 8014718: logging has been removed from SunToolkit 81 82 /* Load debug settings for native code */ 83 static { 84 if (AccessController.doPrivileged(new GetBooleanAction("sun.awt.nativedebug"))) { 85 DebugSettings.init(); 86 } 87 }; 88 89 /** 90 * Special mask for the UngrabEvent events, in addition to the 91 * public masks defined in AWTEvent. Should be used as the mask 92 * value for Toolkit.addAWTEventListener. 93 */ 94 public static final int GRAB_EVENT_MASK = 0x80000000; 95 96 /* The key to put()/get() the PostEventQueue into/from the AppContext. 97 */ 98 private static final String POST_EVENT_QUEUE_KEY = "PostEventQueue"; 99 100 /** 101 * Number of buttons. 102 * By default it's taken from the system. If system value does not 103 * fit into int type range, use our own MAX_BUTTONS_SUPPORT value. 104 */ 105 protected static int numberOfButtons = 0; 106 107 108 /* XFree standard mention 24 buttons as maximum: 109 * http://www.xfree86.org/current/mouse.4.html 110 * We workaround systems supporting more than 24 buttons. 111 * Otherwise, we have to use long type values as masks 112 * which leads to API change. 113 * InputEvent.BUTTON_DOWN_MASK may contain only 21 masks due to 114 * the 4-bytes limit for the int type. (CR 6799099) 115 * One more bit is reserved for FIRST_HIGH_BIT. 116 */ 117 public static final int MAX_BUTTONS_SUPPORTED = 20; 118 119 /** 120 * Creates and initializes EventQueue instance for the specified 121 * AppContext. 122 * Note that event queue must be created from createNewAppContext() 123 * only in order to ensure that EventQueue constructor obtains 124 * the correct AppContext. 125 * @param appContext AppContext to associate with the event queue 126 */ 127 private static void initEQ(AppContext appContext) { 128 EventQueue eventQueue = new EventQueue(); 129 appContext.put(AppContext.EVENT_QUEUE_KEY, eventQueue); 130 131 PostEventQueue postEventQueue = new PostEventQueue(eventQueue); 132 appContext.put(POST_EVENT_QUEUE_KEY, postEventQueue); 133 } 134 135 public SunToolkit() { 136 } 137 138 public boolean useBufferPerWindow() { 139 return false; 140 } 141 142 public abstract FramePeer createLightweightFrame(LightweightFrame target) 143 throws HeadlessException; 144 145 public abstract TrayIconPeer createTrayIcon(TrayIcon target) 146 throws HeadlessException, AWTException; 147 148 public abstract SystemTrayPeer createSystemTray(SystemTray target); 149 150 public abstract boolean isTraySupported(); 151 152 @Override 153 public abstract KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() 154 throws HeadlessException; 155 156 /** 157 * The AWT lock is typically only used on Unix platforms to synchronize 158 * access to Xlib, OpenGL, etc. However, these methods are implemented 159 * in SunToolkit so that they can be called from shared code (e.g. 160 * from the OGL pipeline) or from the X11 pipeline regardless of whether 161 * XToolkit or MToolkit is currently in use. There are native macros 162 * (such as AWT_LOCK) defined in awt.h, so if the implementation of these 163 * methods is changed, make sure it is compatible with the native macros. 164 * 165 * Note: The following methods (awtLock(), awtUnlock(), etc) should be 166 * used in place of: 167 * synchronized (getAWTLock()) { 168 * ... 169 * } 170 * 171 * By factoring these methods out specially, we are able to change the 172 * implementation of these methods (e.g. use more advanced locking 173 * mechanisms) without impacting calling code. 174 * 175 * Sample usage: 176 * private void doStuffWithXlib() { 177 * assert !SunToolkit.isAWTLockHeldByCurrentThread(); 178 * SunToolkit.awtLock(); 179 * try { 180 * ... 181 * XlibWrapper.XDoStuff(); 182 * } finally { 183 * SunToolkit.awtUnlock(); 184 * } 185 * } 186 */ 187 188 private static final ReentrantLock AWT_LOCK = new ReentrantLock(); 189 private static final Condition AWT_LOCK_COND = AWT_LOCK.newCondition(); 190 191 public static final void awtLock() { 192 AWT_LOCK.lock(); 193 } 194 195 public static final boolean awtTryLock() { 196 return AWT_LOCK.tryLock(); 197 } 198 199 public static final void awtUnlock() { 200 AWT_LOCK.unlock(); 201 } 202 203 public static final void awtLockWait() 204 throws InterruptedException 205 { 206 AWT_LOCK_COND.await(); 207 } 208 209 public static final void awtLockWait(long timeout) 210 throws InterruptedException 211 { 212 AWT_LOCK_COND.await(timeout, TimeUnit.MILLISECONDS); 213 } 214 215 public static final void awtLockNotify() { 216 AWT_LOCK_COND.signal(); 217 } 218 219 public static final void awtLockNotifyAll() { 220 AWT_LOCK_COND.signalAll(); 221 } 222 223 public static final boolean isAWTLockHeldByCurrentThread() { 224 return AWT_LOCK.isHeldByCurrentThread(); 225 } 226 227 /* 228 * Create a new AppContext, along with its EventQueue, for a 229 * new ThreadGroup. Browser code, for example, would use this 230 * method to create an AppContext & EventQueue for an Applet. 231 */ 232 public static AppContext createNewAppContext() { 233 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 234 return createNewAppContext(threadGroup); 235 } 236 237 static final AppContext createNewAppContext(ThreadGroup threadGroup) { 238 // Create appContext before initialization of EventQueue, so all 239 // the calls to AppContext.getAppContext() from EventQueue ctor 240 // return correct values 241 AppContext appContext = new AppContext(threadGroup); 242 initEQ(appContext); 243 244 return appContext; 245 } 246 247 static void wakeupEventQueue(EventQueue q, boolean isShutdown){ 248 AWTAccessor.getEventQueueAccessor().wakeup(q, isShutdown); 249 } 250 251 /* 252 * Fetch the peer associated with the given target (as specified 253 * in the peer creation method). This can be used to determine 254 * things like what the parent peer is. If the target is null 255 * or the target can't be found (either because the a peer was 256 * never created for it or the peer was disposed), a null will 257 * be returned. 258 */ 259 protected static Object targetToPeer(Object target) { 260 if (target != null && !GraphicsEnvironment.isHeadless()) { 261 return AWTAutoShutdown.getInstance().getPeer(target); 262 } 263 return null; 264 } 265 266 protected static void targetCreatedPeer(Object target, Object peer) { 267 if (target != null && peer != null && 268 !GraphicsEnvironment.isHeadless()) 269 { 270 AWTAutoShutdown.getInstance().registerPeer(target, peer); 271 } 272 } 273 274 protected static void targetDisposedPeer(Object target, Object peer) { 275 if (target != null && peer != null && 276 !GraphicsEnvironment.isHeadless()) 277 { 278 AWTAutoShutdown.getInstance().unregisterPeer(target, peer); 279 } 280 } 281 282 // Maps from non-Component/MenuComponent to AppContext. 283 // WeakHashMap<Component,AppContext> 284 private static final Map<Object, AppContext> appContextMap = 285 Collections.synchronizedMap(new WeakIdentityHashMap<Object, AppContext>()); 286 287 /** 288 * Sets the appContext field of target. If target is not a Component or 289 * MenuComponent, this returns false. 290 */ 291 private static boolean setAppContext(Object target, 292 AppContext context) { 293 if (target instanceof Component) { 294 AWTAccessor.getComponentAccessor(). 295 setAppContext((Component)target, context); 296 } else if (target instanceof MenuComponent) { 297 AWTAccessor.getMenuComponentAccessor(). 298 setAppContext((MenuComponent)target, context); 299 } else { 300 return false; 301 } 302 return true; 303 } 304 305 /** 306 * Returns the appContext field for target. If target is not a 307 * Component or MenuComponent this returns null. 308 */ 309 private static AppContext getAppContext(Object target) { 310 if (target instanceof Component) { 311 return AWTAccessor.getComponentAccessor(). 312 getAppContext((Component)target); 313 } else if (target instanceof MenuComponent) { 314 return AWTAccessor.getMenuComponentAccessor(). 315 getAppContext((MenuComponent)target); 316 } else { 317 return null; 318 } 319 } 320 321 /* 322 * Fetch the AppContext associated with the given target. 323 * This can be used to determine things like which EventQueue 324 * to use for posting events to a Component. If the target is 325 * null or the target can't be found, a null with be returned. 326 */ 327 public static AppContext targetToAppContext(Object target) { 328 if (target == null) { 329 return null; 330 } 331 AppContext context = getAppContext(target); 332 if (context == null) { 333 // target is not a Component/MenuComponent, try the 334 // appContextMap. 335 context = appContextMap.get(target); 336 } 337 return context; 338 } 339 340 /** 341 * Sets the synchronous status of focus requests on lightweight 342 * components in the specified window to the specified value. 343 * If the boolean parameter is {@code true} then the focus 344 * requests on lightweight components will be performed 345 * synchronously, if it is {@code false}, then asynchronously. 346 * By default, all windows have their lightweight request status 347 * set to asynchronous. 348 * <p> 349 * The application can only set the status of lightweight focus 350 * requests to synchronous for any of its windows if it doesn't 351 * perform focus transfers between different heavyweight containers. 352 * In this case the observable focus behaviour is the same as with 353 * asynchronous status. 354 * <p> 355 * If the application performs focus transfer between different 356 * heavyweight containers and sets the lightweight focus request 357 * status to synchronous for any of its windows, then further focus 358 * behaviour is unspecified. 359 * <p> 360 * @param changed the window for which the lightweight focus request 361 * status should be set 362 * @param status the value of lightweight focus request status 363 */ 364 365 public static void setLWRequestStatus(Window changed,boolean status){ 366 AWTAccessor.getWindowAccessor().setLWRequestStatus(changed, status); 367 }; 368 369 public static void checkAndSetPolicy(Container cont) { 370 FocusTraversalPolicy defaultPolicy = KeyboardFocusManager. 371 getCurrentKeyboardFocusManager(). 372 getDefaultFocusTraversalPolicy(); 373 374 cont.setFocusTraversalPolicy(defaultPolicy); 375 } 376 377 /* 378 * Insert a mapping from target to AppContext, for later retrieval 379 * via targetToAppContext() above. 380 */ 381 public static void insertTargetMapping(Object target, AppContext appContext) { 382 if (!setAppContext(target, appContext)) { 383 // Target is not a Component/MenuComponent, use the private Map 384 // instead. 385 appContextMap.put(target, appContext); 386 } 387 } 388 389 /* 390 * Post an AWTEvent to the Java EventQueue, using the PostEventQueue 391 * to avoid possibly calling client code (EventQueueSubclass.postEvent()) 392 * on the toolkit (AWT-Windows/AWT-Motif) thread. This function should 393 * not be called under another lock since it locks the EventQueue. 394 * See bugids 4632918, 4526597. 395 */ 396 public static void postEvent(AppContext appContext, AWTEvent event) { 397 if (event == null) { 398 throw new NullPointerException(); 399 } 400 401 AWTAccessor.SequencedEventAccessor sea = AWTAccessor.getSequencedEventAccessor(); 402 if (sea != null && sea.isSequencedEvent(event)) { 403 AWTEvent nested = sea.getNested(event); 404 if (nested.getID() == WindowEvent.WINDOW_LOST_FOCUS && 405 nested instanceof TimedWindowEvent) 406 { 407 TimedWindowEvent twe = (TimedWindowEvent)nested; 408 ((SunToolkit)Toolkit.getDefaultToolkit()). 409 setWindowDeactivationTime((Window)twe.getSource(), twe.getWhen()); 410 } 411 } 412 413 // All events posted via this method are system-generated. 414 // Placing the following call here reduces considerably the 415 // number of places throughout the toolkit that would 416 // otherwise have to be modified to precisely identify 417 // system-generated events. 418 setSystemGenerated(event); 419 AppContext eventContext = targetToAppContext(event.getSource()); 420 if (eventContext != null && !eventContext.equals(appContext)) { 421 throw new RuntimeException("Event posted on wrong app context : " + event); 422 } 423 PostEventQueue postEventQueue = 424 (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY); 425 if (postEventQueue != null) { 426 postEventQueue.postEvent(event); 427 } 428 } 429 430 /* 431 * Post AWTEvent of high priority. 432 */ 433 public static void postPriorityEvent(final AWTEvent e) { 434 PeerEvent pe = new PeerEvent(Toolkit.getDefaultToolkit(), new Runnable() { 435 @Override 436 public void run() { 437 AWTAccessor.getAWTEventAccessor().setPosted(e); 438 ((Component)e.getSource()).dispatchEvent(e); 439 } 440 }, PeerEvent.ULTIMATE_PRIORITY_EVENT); 441 postEvent(targetToAppContext(e.getSource()), pe); 442 } 443 444 /* 445 * Flush any pending events which haven't been posted to the AWT 446 * EventQueue yet. 447 */ 448 public static void flushPendingEvents() { 449 AppContext appContext = AppContext.getAppContext(); 450 flushPendingEvents(appContext); 451 } 452 453 /* 454 * Flush the PostEventQueue for the right AppContext. 455 * The default flushPendingEvents only flushes the thread-local context, 456 * which is not always correct, c.f. 3746956 457 */ 458 public static void flushPendingEvents(AppContext appContext) { 459 PostEventQueue postEventQueue = 460 (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY); 461 if (postEventQueue != null) { 462 postEventQueue.flush(); 463 } 464 } 465 466 /* 467 * Execute a chunk of code on the Java event handler thread for the 468 * given target. Does not wait for the execution to occur before 469 * returning to the caller. 470 */ 471 public static void executeOnEventHandlerThread(Object target, 472 Runnable runnable) { 473 executeOnEventHandlerThread(new PeerEvent(target, runnable, PeerEvent.PRIORITY_EVENT)); 474 } 475 476 /* 477 * Fixed 5064013: the InvocationEvent time should be equals 478 * the time of the ActionEvent 479 */ 480 @SuppressWarnings("serial") 481 public static void executeOnEventHandlerThread(Object target, 482 Runnable runnable, 483 final long when) { 484 executeOnEventHandlerThread( 485 new PeerEvent(target, runnable, PeerEvent.PRIORITY_EVENT) { 486 @Override 487 public long getWhen() { 488 return when; 489 } 490 }); 491 } 492 493 /* 494 * Execute a chunk of code on the Java event handler thread for the 495 * given target. Does not wait for the execution to occur before 496 * returning to the caller. 497 */ 498 public static void executeOnEventHandlerThread(PeerEvent peerEvent) { 499 postEvent(targetToAppContext(peerEvent.getSource()), peerEvent); 500 } 501 502 /* 503 * Execute a chunk of code on the Java event handler thread. The 504 * method takes into account provided AppContext and sets 505 * {@code SunToolkit.getDefaultToolkit()} as a target of the 506 * event. See 6451487 for detailes. 507 * Does not wait for the execution to occur before returning to 508 * the caller. 509 */ 510 public static void invokeLaterOnAppContext( 511 AppContext appContext, Runnable dispatcher) 512 { 513 postEvent(appContext, 514 new PeerEvent(Toolkit.getDefaultToolkit(), dispatcher, 515 PeerEvent.PRIORITY_EVENT)); 516 } 517 518 /* 519 * Execute a chunk of code on the Java event handler thread for the 520 * given target. Waits for the execution to occur before returning 521 * to the caller. 522 */ 523 public static void executeOnEDTAndWait(Object target, Runnable runnable) 524 throws InterruptedException, InvocationTargetException 525 { 526 if (EventQueue.isDispatchThread()) { 527 throw new Error("Cannot call executeOnEDTAndWait from any event dispatcher thread"); 528 } 529 530 class AWTInvocationLock {} 531 Object lock = new AWTInvocationLock(); 532 533 PeerEvent event = new PeerEvent(target, runnable, lock, true, PeerEvent.PRIORITY_EVENT); 534 535 synchronized (lock) { 536 executeOnEventHandlerThread(event); 537 while(!event.isDispatched()) { 538 lock.wait(); 539 } 540 } 541 542 Throwable eventThrowable = event.getThrowable(); 543 if (eventThrowable != null) { 544 throw new InvocationTargetException(eventThrowable); 545 } 546 } 547 548 /* 549 * Returns true if the calling thread is the event dispatch thread 550 * contained within AppContext which associated with the given target. 551 * Use this call to ensure that a given task is being executed 552 * (or not being) on the event dispatch thread for the given target. 553 */ 554 public static boolean isDispatchThreadForAppContext(Object target) { 555 AppContext appContext = targetToAppContext(target); 556 EventQueue eq = (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY); 557 558 AWTAccessor.EventQueueAccessor accessor = AWTAccessor.getEventQueueAccessor(); 559 return accessor.isDispatchThreadImpl(eq); 560 } 561 562 @Override 563 public Dimension getScreenSize() { 564 return GraphicsEnvironment.getLocalGraphicsEnvironment() 565 .getDefaultScreenDevice().getDefaultConfiguration() 566 .getBounds().getSize(); 567 } 568 569 @Override 570 public ColorModel getColorModel() throws HeadlessException { 571 return GraphicsEnvironment.getLocalGraphicsEnvironment() 572 .getDefaultScreenDevice().getDefaultConfiguration() 573 .getColorModel(); 574 } 575 576 @Override 577 @SuppressWarnings("deprecation") 578 public FontMetrics getFontMetrics(Font font) { 579 return FontDesignMetrics.getMetrics(font); 580 } 581 582 @Override 583 @SuppressWarnings("deprecation") 584 public String[] getFontList() { 585 String[] hardwiredFontList = { 586 Font.DIALOG, Font.SANS_SERIF, Font.SERIF, Font.MONOSPACED, 587 Font.DIALOG_INPUT 588 589 // -- Obsolete font names from 1.0.2. It was decided that 590 // -- getFontList should not return these old names: 591 // "Helvetica", "TimesRoman", "Courier", "ZapfDingbats" 592 }; 593 return hardwiredFontList; 594 } 595 596 /** 597 * Disables erasing of background on the canvas before painting if 598 * this is supported by the current toolkit. It is recommended to 599 * call this method early, before the Canvas becomes displayable, 600 * because some Toolkit implementations do not support changing 601 * this property once the Canvas becomes displayable. 602 */ 603 public void disableBackgroundErase(Canvas canvas) { 604 disableBackgroundEraseImpl(canvas); 605 } 606 607 /** 608 * Disables the native erasing of the background on the given 609 * component before painting if this is supported by the current 610 * toolkit. This only has an effect for certain components such as 611 * Canvas, Panel and Window. It is recommended to call this method 612 * early, before the Component becomes displayable, because some 613 * Toolkit implementations do not support changing this property 614 * once the Component becomes displayable. 615 */ 616 public void disableBackgroundErase(Component component) { 617 disableBackgroundEraseImpl(component); 618 } 619 620 private void disableBackgroundEraseImpl(Component component) { 621 AWTAccessor.getComponentAccessor().setBackgroundEraseDisabled(component, true); 622 } 623 624 /** 625 * Returns the value of "sun.awt.noerasebackground" property. Default 626 * value is {@code false}. 627 */ 628 public static boolean getSunAwtNoerasebackground() { 629 return AccessController.doPrivileged(new GetBooleanAction("sun.awt.noerasebackground")); 630 } 631 632 /** 633 * Returns the value of "sun.awt.erasebackgroundonresize" property. Default 634 * value is {@code false}. 635 */ 636 public static boolean getSunAwtErasebackgroundonresize() { 637 return AccessController.doPrivileged(new GetBooleanAction("sun.awt.erasebackgroundonresize")); 638 } 639 640 641 @SuppressWarnings("deprecation") 642 static final SoftCache fileImgCache = new SoftCache(); 643 644 @SuppressWarnings("deprecation") 645 static final SoftCache urlImgCache = new SoftCache(); 646 647 static Image getImageFromHash(Toolkit tk, URL url) { 648 checkPermissions(url); 649 synchronized (urlImgCache) { 650 String key = url.toString(); 651 Image img = (Image)urlImgCache.get(key); 652 if (img == null) { 653 try { 654 img = tk.createImage(new URLImageSource(url)); 655 urlImgCache.put(key, img); 656 } catch (Exception e) { 657 } 658 } 659 return img; 660 } 661 } 662 663 static Image getImageFromHash(Toolkit tk, 664 String filename) { 665 checkPermissions(filename); 666 synchronized (fileImgCache) { 667 Image img = (Image)fileImgCache.get(filename); 668 if (img == null) { 669 try { 670 img = tk.createImage(new FileImageSource(filename)); 671 fileImgCache.put(filename, img); 672 } catch (Exception e) { 673 } 674 } 675 return img; 676 } 677 } 678 679 @Override 680 public Image getImage(String filename) { 681 return getImageFromHash(this, filename); 682 } 683 684 @Override 685 public Image getImage(URL url) { 686 return getImageFromHash(this, url); 687 } 688 689 protected Image getImageWithResolutionVariant(String fileName, 690 String resolutionVariantName) { 691 synchronized (fileImgCache) { 692 Image image = getImageFromHash(this, fileName); 693 if (image instanceof MultiResolutionImage) { 694 return image; 695 } 696 Image resolutionVariant = getImageFromHash(this, resolutionVariantName); 697 image = createImageWithResolutionVariant(image, resolutionVariant); 698 fileImgCache.put(fileName, image); 699 return image; 700 } 701 } 702 703 protected Image getImageWithResolutionVariant(URL url, 704 URL resolutionVariantURL) { 705 synchronized (urlImgCache) { 706 Image image = getImageFromHash(this, url); 707 if (image instanceof MultiResolutionImage) { 708 return image; 709 } 710 Image resolutionVariant = getImageFromHash(this, resolutionVariantURL); 711 image = createImageWithResolutionVariant(image, resolutionVariant); 712 String key = url.toString(); 713 urlImgCache.put(key, image); 714 return image; 715 } 716 } 717 718 719 @Override 720 public Image createImage(String filename) { 721 checkPermissions(filename); 722 return createImage(new FileImageSource(filename)); 723 } 724 725 @Override 726 public Image createImage(URL url) { 727 checkPermissions(url); 728 return createImage(new URLImageSource(url)); 729 } 730 731 @Override 732 public Image createImage(byte[] data, int offset, int length) { 733 return createImage(new ByteArrayImageSource(data, offset, length)); 734 } 735 736 @Override 737 public Image createImage(ImageProducer producer) { 738 return new ToolkitImage(producer); 739 } 740 741 public static Image createImageWithResolutionVariant(Image image, 742 Image resolutionVariant) { 743 return new MultiResolutionToolkitImage(image, resolutionVariant); 744 } 745 746 @Override 747 public int checkImage(Image img, int w, int h, ImageObserver o) { 748 if (!(img instanceof ToolkitImage)) { 749 return ImageObserver.ALLBITS; 750 } 751 752 ToolkitImage tkimg = (ToolkitImage)img; 753 int repbits; 754 if (w == 0 || h == 0) { 755 repbits = ImageObserver.ALLBITS; 756 } else { 757 repbits = tkimg.getImageRep().check(o); 758 } 759 return (tkimg.check(o) | repbits) & checkResolutionVariant(img, w, h, o); 760 } 761 762 @Override 763 public boolean prepareImage(Image img, int w, int h, ImageObserver o) { 764 if (w == 0 || h == 0) { 765 return true; 766 } 767 768 // Must be a ToolkitImage 769 if (!(img instanceof ToolkitImage)) { 770 return true; 771 } 772 773 ToolkitImage tkimg = (ToolkitImage)img; 774 if (tkimg.hasError()) { 775 if (o != null) { 776 o.imageUpdate(img, ImageObserver.ERROR|ImageObserver.ABORT, 777 -1, -1, -1, -1); 778 } 779 return false; 780 } 781 ImageRepresentation ir = tkimg.getImageRep(); 782 return ir.prepare(o) & prepareResolutionVariant(img, w, h, o); 783 } 784 785 private int checkResolutionVariant(Image img, int w, int h, ImageObserver o) { 786 ToolkitImage rvImage = getResolutionVariant(img); 787 int rvw = getRVSize(w); 788 int rvh = getRVSize(h); 789 // Ignore the resolution variant in case of error 790 return (rvImage == null || rvImage.hasError()) ? 0xFFFF : 791 checkImage(rvImage, rvw, rvh, MultiResolutionToolkitImage. 792 getResolutionVariantObserver( 793 img, o, w, h, rvw, rvh, true)); 794 } 795 796 private boolean prepareResolutionVariant(Image img, int w, int h, 797 ImageObserver o) { 798 799 ToolkitImage rvImage = getResolutionVariant(img); 800 int rvw = getRVSize(w); 801 int rvh = getRVSize(h); 802 // Ignore the resolution variant in case of error 803 return rvImage == null || rvImage.hasError() || prepareImage( 804 rvImage, rvw, rvh, 805 MultiResolutionToolkitImage.getResolutionVariantObserver( 806 img, o, w, h, rvw, rvh, true)); 807 } 808 809 private static int getRVSize(int size){ 810 return size == -1 ? -1 : 2 * size; 811 } 812 813 private static ToolkitImage getResolutionVariant(Image image) { 814 if (image instanceof MultiResolutionToolkitImage) { 815 Image resolutionVariant = ((MultiResolutionToolkitImage) image). 816 getResolutionVariant(); 817 if (resolutionVariant instanceof ToolkitImage) { 818 return (ToolkitImage) resolutionVariant; 819 } 820 } 821 return null; 822 } 823 824 protected static boolean imageCached(String fileName) { 825 return fileImgCache.containsKey(fileName); 826 } 827 828 protected static boolean imageCached(URL url) { 829 String key = url.toString(); 830 return urlImgCache.containsKey(key); 831 } 832 833 protected static boolean imageExists(String filename) { 834 if (filename != null) { 835 checkPermissions(filename); 836 return new File(filename).exists(); 837 } 838 return false; 839 } 840 841 @SuppressWarnings("try") 842 protected static boolean imageExists(URL url) { 843 if (url != null) { 844 checkPermissions(url); 845 try (InputStream is = url.openStream()) { 846 return true; 847 }catch(IOException e){ 848 return false; 849 } 850 } 851 return false; 852 } 853 854 private static void checkPermissions(String filename) { 855 SecurityManager security = System.getSecurityManager(); 856 if (security != null) { 857 security.checkRead(filename); 858 } 859 } 860 861 private static void checkPermissions(URL url) { 862 SecurityManager sm = System.getSecurityManager(); 863 if (sm != null) { 864 try { 865 java.security.Permission perm = 866 URLUtil.getConnectPermission(url); 867 if (perm != null) { 868 try { 869 sm.checkPermission(perm); 870 } catch (SecurityException se) { 871 // fallback to checkRead/checkConnect for pre 1.2 872 // security managers 873 if ((perm instanceof java.io.FilePermission) && 874 perm.getActions().indexOf("read") != -1) { 875 sm.checkRead(perm.getName()); 876 } else if ((perm instanceof 877 java.net.SocketPermission) && 878 perm.getActions().indexOf("connect") != -1) { 879 sm.checkConnect(url.getHost(), url.getPort()); 880 } else { 881 throw se; 882 } 883 } 884 } 885 } catch (java.io.IOException ioe) { 886 sm.checkConnect(url.getHost(), url.getPort()); 887 } 888 } 889 } 890 891 /** 892 * Scans {@code imageList} for best-looking image of specified dimensions. 893 * Image can be scaled and/or padded with transparency. 894 */ 895 public static BufferedImage getScaledIconImage(java.util.List<Image> imageList, int width, int height) { 896 if (width == 0 || height == 0) { 897 return null; 898 } 899 java.util.List<Image> multiResAndnormalImages = new ArrayList<>(imageList.size()); 900 for (Image image : imageList) { 901 if ((image instanceof MultiResolutionImage)) { 902 Image im = ((MultiResolutionImage) image).getResolutionVariant(width, height); 903 multiResAndnormalImages.add(im); 904 } else { 905 multiResAndnormalImages.add(image); 906 } 907 } 908 Image bestImage = null; 909 int bestWidth = 0; 910 int bestHeight = 0; 911 double bestSimilarity = 3; //Impossibly high value 912 double bestScaleFactor = 0; 913 for (Iterator<Image> i = multiResAndnormalImages.iterator();i.hasNext();) { 914 //Iterate imageList looking for best matching image. 915 //'Similarity' measure is defined as good scale factor and small insets. 916 //best possible similarity is 0 (no scale, no insets). 917 //It's found while the experiments that good-looking result is achieved 918 //with scale factors x1, x3/4, x2/3, xN, x1/N. 919 Image im = i.next(); 920 if (im == null) { 921 continue; 922 } 923 if (im instanceof ToolkitImage) { 924 ImageRepresentation ir = ((ToolkitImage)im).getImageRep(); 925 ir.reconstruct(ImageObserver.ALLBITS); 926 } 927 int iw; 928 int ih; 929 try { 930 iw = im.getWidth(null); 931 ih = im.getHeight(null); 932 } catch (Exception e){ 933 continue; 934 } 935 if (iw > 0 && ih > 0) { 936 //Calc scale factor 937 double scaleFactor = Math.min((double)width / (double)iw, 938 (double)height / (double)ih); 939 //Calculate scaled image dimensions 940 //adjusting scale factor to nearest "good" value 941 int adjw = 0; 942 int adjh = 0; 943 double scaleMeasure = 1; //0 - best (no) scale, 1 - impossibly bad 944 if (scaleFactor >= 2) { 945 //Need to enlarge image more than twice 946 //Round down scale factor to multiply by integer value 947 scaleFactor = Math.floor(scaleFactor); 948 adjw = iw * (int)scaleFactor; 949 adjh = ih * (int)scaleFactor; 950 scaleMeasure = 1.0 - 0.5 / scaleFactor; 951 } else if (scaleFactor >= 1) { 952 //Don't scale 953 scaleFactor = 1.0; 954 adjw = iw; 955 adjh = ih; 956 scaleMeasure = 0; 957 } else if (scaleFactor >= 0.75) { 958 //Multiply by 3/4 959 scaleFactor = 0.75; 960 adjw = iw * 3 / 4; 961 adjh = ih * 3 / 4; 962 scaleMeasure = 0.3; 963 } else if (scaleFactor >= 0.6666) { 964 //Multiply by 2/3 965 scaleFactor = 0.6666; 966 adjw = iw * 2 / 3; 967 adjh = ih * 2 / 3; 968 scaleMeasure = 0.33; 969 } else { 970 //Multiply size by 1/scaleDivider 971 //where scaleDivider is minimum possible integer 972 //larger than 1/scaleFactor 973 double scaleDivider = Math.ceil(1.0 / scaleFactor); 974 scaleFactor = 1.0 / scaleDivider; 975 adjw = (int)Math.round((double)iw / scaleDivider); 976 adjh = (int)Math.round((double)ih / scaleDivider); 977 scaleMeasure = 1.0 - 1.0 / scaleDivider; 978 } 979 double similarity = ((double)width - (double)adjw) / (double)width + 980 ((double)height - (double)adjh) / (double)height + //Large padding is bad 981 scaleMeasure; //Large rescale is bad 982 if (similarity < bestSimilarity) { 983 bestSimilarity = similarity; 984 bestScaleFactor = scaleFactor; 985 bestImage = im; 986 bestWidth = adjw; 987 bestHeight = adjh; 988 } 989 if (similarity == 0) break; 990 } 991 } 992 if (bestImage == null) { 993 //No images were found, possibly all are broken 994 return null; 995 } 996 BufferedImage bimage = 997 new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 998 Graphics2D g = bimage.createGraphics(); 999 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 1000 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 1001 try { 1002 int x = (width - bestWidth) / 2; 1003 int y = (height - bestHeight) / 2; 1004 g.drawImage(bestImage, x, y, bestWidth, bestHeight, null); 1005 } finally { 1006 g.dispose(); 1007 } 1008 return bimage; 1009 } 1010 1011 public static DataBufferInt getScaledIconData(java.util.List<Image> imageList, int width, int height) { 1012 BufferedImage bimage = getScaledIconImage(imageList, width, height); 1013 if (bimage == null) { 1014 return null; 1015 } 1016 Raster raster = bimage.getRaster(); 1017 DataBuffer buffer = raster.getDataBuffer(); 1018 return (DataBufferInt)buffer; 1019 } 1020 1021 @Override 1022 protected EventQueue getSystemEventQueueImpl() { 1023 return getSystemEventQueueImplPP(); 1024 } 1025 1026 // Package private implementation 1027 static EventQueue getSystemEventQueueImplPP() { 1028 return getSystemEventQueueImplPP(AppContext.getAppContext()); 1029 } 1030 1031 public static EventQueue getSystemEventQueueImplPP(AppContext appContext) { 1032 EventQueue theEventQueue = 1033 (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY); 1034 return theEventQueue; 1035 } 1036 1037 /** 1038 * Give native peers the ability to query the native container 1039 * given a native component (eg the direct parent may be lightweight). 1040 */ 1041 public static Container getNativeContainer(Component c) { 1042 return Toolkit.getNativeContainer(c); 1043 } 1044 1045 /** 1046 * Gives native peers the ability to query the closest HW component. 1047 * If the given component is heavyweight, then it returns this. Otherwise, 1048 * it goes one level up in the hierarchy and tests next component. 1049 */ 1050 public static Component getHeavyweightComponent(Component c) { 1051 while (c != null && AWTAccessor.getComponentAccessor().isLightweight(c)) { 1052 c = AWTAccessor.getComponentAccessor().getParent(c); 1053 } 1054 return c; 1055 } 1056 1057 /** 1058 * Returns key modifiers used by Swing to set up a focus accelerator key stroke. 1059 */ 1060 @SuppressWarnings("deprecation") 1061 public int getFocusAcceleratorKeyMask() { 1062 return InputEvent.ALT_MASK; 1063 } 1064 1065 /** 1066 * Tests whether specified key modifiers mask can be used to enter a printable 1067 * character. This is a default implementation of this method, which reflects 1068 * the way things work on Windows: here, pressing ctrl + alt allows user to enter 1069 * characters from the extended character set (like euro sign or math symbols) 1070 */ 1071 @SuppressWarnings("deprecation") 1072 public boolean isPrintableCharacterModifiersMask(int mods) { 1073 return ((mods & InputEvent.ALT_MASK) == (mods & InputEvent.CTRL_MASK)); 1074 } 1075 1076 /** 1077 * Returns whether popup is allowed to be shown above the task bar. 1078 * This is a default implementation of this method, which checks 1079 * corresponding security permission. 1080 */ 1081 public boolean canPopupOverlapTaskBar() { 1082 boolean result = true; 1083 try { 1084 SecurityManager sm = System.getSecurityManager(); 1085 if (sm != null) { 1086 sm.checkPermission(AWTPermissions.SET_WINDOW_ALWAYS_ON_TOP_PERMISSION); 1087 } 1088 } catch (SecurityException se) { 1089 // There is no permission to show popups over the task bar 1090 result = false; 1091 } 1092 return result; 1093 } 1094 1095 /** 1096 * Returns a new input method window, with behavior as specified in 1097 * {@link java.awt.im.spi.InputMethodContext#createInputMethodWindow}. 1098 * If the inputContext is not null, the window should return it from its 1099 * getInputContext() method. The window needs to implement 1100 * sun.awt.im.InputMethodWindow. 1101 * <p> 1102 * SunToolkit subclasses can override this method to return better input 1103 * method windows. 1104 */ 1105 @Override 1106 public Window createInputMethodWindow(String title, InputContext context) { 1107 return new sun.awt.im.SimpleInputMethodWindow(title, context); 1108 } 1109 1110 /** 1111 * Returns whether enableInputMethods should be set to true for peered 1112 * TextComponent instances on this platform. False by default. 1113 */ 1114 @Override 1115 public boolean enableInputMethodsForTextComponent() { 1116 return false; 1117 } 1118 1119 private static Locale startupLocale = null; 1120 1121 /** 1122 * Returns the locale in which the runtime was started. 1123 */ 1124 public static Locale getStartupLocale() { 1125 if (startupLocale == null) { 1126 String language, region, country, variant; 1127 language = AccessController.doPrivileged( 1128 new GetPropertyAction("user.language", "en")); 1129 // for compatibility, check for old user.region property 1130 region = AccessController.doPrivileged( 1131 new GetPropertyAction("user.region")); 1132 if (region != null) { 1133 // region can be of form country, country_variant, or _variant 1134 int i = region.indexOf('_'); 1135 if (i >= 0) { 1136 country = region.substring(0, i); 1137 variant = region.substring(i + 1); 1138 } else { 1139 country = region; 1140 variant = ""; 1141 } 1142 } else { 1143 country = AccessController.doPrivileged( 1144 new GetPropertyAction("user.country", "")); 1145 variant = AccessController.doPrivileged( 1146 new GetPropertyAction("user.variant", "")); 1147 } 1148 startupLocale = new Locale(language, country, variant); 1149 } 1150 return startupLocale; 1151 } 1152 1153 /** 1154 * Returns the default keyboard locale of the underlying operating system 1155 */ 1156 @Override 1157 public Locale getDefaultKeyboardLocale() { 1158 return getStartupLocale(); 1159 } 1160 1161 /** 1162 * Returns whether default toolkit needs the support of the xembed 1163 * from embedding host(if any). 1164 * @return {@code true}, if XEmbed is needed, {@code false} otherwise 1165 */ 1166 public static boolean needsXEmbed() { 1167 String noxembed = AccessController. 1168 doPrivileged(new GetPropertyAction("sun.awt.noxembed", "false")); 1169 if ("true".equals(noxembed)) { 1170 return false; 1171 } 1172 1173 Toolkit tk = Toolkit.getDefaultToolkit(); 1174 if (tk instanceof SunToolkit) { 1175 // SunToolkit descendants should override this method to specify 1176 // concrete behavior 1177 return ((SunToolkit)tk).needsXEmbedImpl(); 1178 } else { 1179 // Non-SunToolkit doubtly might support XEmbed 1180 return false; 1181 } 1182 } 1183 1184 /** 1185 * Returns whether this toolkit needs the support of the xembed 1186 * from embedding host(if any). 1187 * @return {@code true}, if XEmbed is needed, {@code false} otherwise 1188 */ 1189 protected boolean needsXEmbedImpl() { 1190 return false; 1191 } 1192 1193 private static Dialog.ModalExclusionType DEFAULT_MODAL_EXCLUSION_TYPE = null; 1194 1195 /** 1196 * Returns whether the XEmbed server feature is requested by 1197 * developer. If true, Toolkit should return an 1198 * XEmbed-server-enabled CanvasPeer instead of the ordinary CanvasPeer. 1199 */ 1200 protected final boolean isXEmbedServerRequested() { 1201 return AccessController.doPrivileged(new GetBooleanAction("sun.awt.xembedserver")); 1202 } 1203 1204 /** 1205 * Returns whether the modal exclusion API is supported by the current toolkit. 1206 * When it isn't supported, calling {@code setModalExcluded} has no 1207 * effect, and {@code isModalExcluded} returns false for all windows. 1208 * 1209 * @return true if modal exclusion is supported by the toolkit, false otherwise 1210 * 1211 * @see sun.awt.SunToolkit#setModalExcluded(java.awt.Window) 1212 * @see sun.awt.SunToolkit#isModalExcluded(java.awt.Window) 1213 * 1214 * @since 1.5 1215 */ 1216 public static boolean isModalExcludedSupported() 1217 { 1218 Toolkit tk = Toolkit.getDefaultToolkit(); 1219 return tk.isModalExclusionTypeSupported(DEFAULT_MODAL_EXCLUSION_TYPE); 1220 } 1221 /* 1222 * Default implementation for isModalExcludedSupportedImpl(), returns false. 1223 * 1224 * @see sun.awt.windows.WToolkit#isModalExcludeSupportedImpl 1225 * @see sun.awt.X11.XToolkit#isModalExcludeSupportedImpl 1226 * 1227 * @since 1.5 1228 */ 1229 protected boolean isModalExcludedSupportedImpl() 1230 { 1231 return false; 1232 } 1233 1234 /* 1235 * Sets this window to be excluded from being modally blocked. When the 1236 * toolkit supports modal exclusion and this method is called, input 1237 * events, focus transfer and z-order will continue to work for the 1238 * window, it's owned windows and child components, even in the 1239 * presence of a modal dialog. 1240 * For details on which {@code Window}s are normally blocked 1241 * by modal dialog, see {@link java.awt.Dialog}. 1242 * Invoking this method when the modal exclusion API is not supported by 1243 * the current toolkit has no effect. 1244 * @param window Window to be marked as not modally blocked 1245 * @see java.awt.Dialog 1246 * @see java.awt.Dialog#setModal(boolean) 1247 * @see sun.awt.SunToolkit#isModalExcludedSupported 1248 * @see sun.awt.SunToolkit#isModalExcluded(java.awt.Window) 1249 */ 1250 public static void setModalExcluded(Window window) 1251 { 1252 if (DEFAULT_MODAL_EXCLUSION_TYPE == null) { 1253 DEFAULT_MODAL_EXCLUSION_TYPE = Dialog.ModalExclusionType.APPLICATION_EXCLUDE; 1254 } 1255 window.setModalExclusionType(DEFAULT_MODAL_EXCLUSION_TYPE); 1256 } 1257 1258 /* 1259 * Returns whether the specified window is blocked by modal dialogs. 1260 * If the modal exclusion API isn't supported by the current toolkit, 1261 * it returns false for all windows. 1262 * 1263 * @param window Window to test for modal exclusion 1264 * 1265 * @return true if the window is modal excluded, false otherwise. If 1266 * the modal exclusion isn't supported by the current Toolkit, false 1267 * is returned 1268 * 1269 * @see sun.awt.SunToolkit#isModalExcludedSupported 1270 * @see sun.awt.SunToolkit#setModalExcluded(java.awt.Window) 1271 * 1272 * @since 1.5 1273 */ 1274 public static boolean isModalExcluded(Window window) 1275 { 1276 if (DEFAULT_MODAL_EXCLUSION_TYPE == null) { 1277 DEFAULT_MODAL_EXCLUSION_TYPE = Dialog.ModalExclusionType.APPLICATION_EXCLUDE; 1278 } 1279 return window.getModalExclusionType().compareTo(DEFAULT_MODAL_EXCLUSION_TYPE) >= 0; 1280 } 1281 1282 /** 1283 * Overridden in XToolkit and WToolkit 1284 */ 1285 @Override 1286 public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) { 1287 return (modalityType == Dialog.ModalityType.MODELESS) || 1288 (modalityType == Dialog.ModalityType.APPLICATION_MODAL); 1289 } 1290 1291 /** 1292 * Overridden in XToolkit and WToolkit 1293 */ 1294 @Override 1295 public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType exclusionType) { 1296 return (exclusionType == Dialog.ModalExclusionType.NO_EXCLUDE); 1297 } 1298 1299 /////////////////////////////////////////////////////////////////////////// 1300 // 1301 // The following is used by the Java Plug-in to coordinate dialog modality 1302 // between containing applications (browsers, ActiveX containers etc) and 1303 // the AWT. 1304 // 1305 /////////////////////////////////////////////////////////////////////////// 1306 1307 private ModalityListenerList modalityListeners = new ModalityListenerList(); 1308 1309 public void addModalityListener(ModalityListener listener) { 1310 modalityListeners.add(listener); 1311 } 1312 1313 public void removeModalityListener(ModalityListener listener) { 1314 modalityListeners.remove(listener); 1315 } 1316 1317 public void notifyModalityPushed(Dialog dialog) { 1318 notifyModalityChange(ModalityEvent.MODALITY_PUSHED, dialog); 1319 } 1320 1321 public void notifyModalityPopped(Dialog dialog) { 1322 notifyModalityChange(ModalityEvent.MODALITY_POPPED, dialog); 1323 } 1324 1325 final void notifyModalityChange(int id, Dialog source) { 1326 ModalityEvent ev = new ModalityEvent(source, modalityListeners, id); 1327 ev.dispatch(); 1328 } 1329 1330 static class ModalityListenerList implements ModalityListener { 1331 1332 Vector<ModalityListener> listeners = new Vector<ModalityListener>(); 1333 1334 void add(ModalityListener listener) { 1335 listeners.addElement(listener); 1336 } 1337 1338 void remove(ModalityListener listener) { 1339 listeners.removeElement(listener); 1340 } 1341 1342 @Override 1343 public void modalityPushed(ModalityEvent ev) { 1344 Iterator<ModalityListener> it = listeners.iterator(); 1345 while (it.hasNext()) { 1346 it.next().modalityPushed(ev); 1347 } 1348 } 1349 1350 @Override 1351 public void modalityPopped(ModalityEvent ev) { 1352 Iterator<ModalityListener> it = listeners.iterator(); 1353 while (it.hasNext()) { 1354 it.next().modalityPopped(ev); 1355 } 1356 } 1357 } // end of class ModalityListenerList 1358 1359 /////////////////////////////////////////////////////////////////////////// 1360 // End Plug-in code 1361 /////////////////////////////////////////////////////////////////////////// 1362 1363 public static boolean isLightweightOrUnknown(Component comp) { 1364 if (comp.isLightweight() 1365 || !(getDefaultToolkit() instanceof SunToolkit)) 1366 { 1367 return true; 1368 } 1369 return !(comp instanceof Button 1370 || comp instanceof Canvas 1371 || comp instanceof Checkbox 1372 || comp instanceof Choice 1373 || comp instanceof Label 1374 || comp instanceof java.awt.List 1375 || comp instanceof Panel 1376 || comp instanceof Scrollbar 1377 || comp instanceof ScrollPane 1378 || comp instanceof TextArea 1379 || comp instanceof TextField 1380 || comp instanceof Window); 1381 } 1382 1383 @SuppressWarnings("serial") 1384 public static class OperationTimedOut extends RuntimeException { 1385 public OperationTimedOut(String msg) { 1386 super(msg); 1387 } 1388 public OperationTimedOut() { 1389 } 1390 } 1391 1392 @SuppressWarnings("serial") 1393 public static class InfiniteLoop extends RuntimeException { 1394 } 1395 1396 @SuppressWarnings("serial") 1397 public static class IllegalThreadException extends RuntimeException { 1398 public IllegalThreadException(String msg) { 1399 super(msg); 1400 } 1401 public IllegalThreadException() { 1402 } 1403 } 1404 1405 public static final int DEFAULT_WAIT_TIME = 10000; 1406 private static final int MAX_ITERS = 20; 1407 private static final int MIN_ITERS = 0; 1408 private static final int MINIMAL_EDELAY = 0; 1409 1410 /** 1411 * Parameterless version of realsync which uses default timout (see DEFAUL_WAIT_TIME). 1412 */ 1413 public void realSync() throws OperationTimedOut, InfiniteLoop { 1414 realSync(DEFAULT_WAIT_TIME); 1415 } 1416 1417 /** 1418 * Forces toolkit to synchronize with the native windowing 1419 * sub-system, flushing all pending work and waiting for all the 1420 * events to be processed. This method guarantees that after 1421 * return no additional Java events will be generated, unless 1422 * cause by user. Obviously, the method cannot be used on the 1423 * event dispatch thread (EDT). In case it nevertheless gets 1424 * invoked on this thread, the method throws the 1425 * IllegalThreadException runtime exception. 1426 * 1427 * <p> This method allows to write tests without explicit timeouts 1428 * or wait for some event. Example: 1429 * <pre>{@code 1430 * Frame f = ...; 1431 * f.setVisible(true); 1432 * ((SunToolkit)Toolkit.getDefaultToolkit()).realSync(); 1433 * }</pre> 1434 * 1435 * <p> After realSync, {@code f} will be completely visible 1436 * on the screen, its getLocationOnScreen will be returning the 1437 * right result and it will be the focus owner. 1438 * 1439 * <p> Another example: 1440 * <pre>{@code 1441 * b.requestFocus(); 1442 * ((SunToolkit)Toolkit.getDefaultToolkit()).realSync(); 1443 * }</pre> 1444 * 1445 * <p> After realSync, {@code b} will be focus owner. 1446 * 1447 * <p> Notice that realSync isn't guaranteed to work if recurring 1448 * actions occur, such as if during processing of some event 1449 * another request which may generate some events occurs. By 1450 * default, sync tries to perform as much as {@value #MAX_ITERS} 1451 * cycles of event processing, allowing for roughly {@value 1452 * #MAX_ITERS} additional requests. 1453 * 1454 * <p> For example, requestFocus() generates native request, which 1455 * generates one or two Java focus events, which then generate a 1456 * serie of paint events, a serie of Java focus events, which then 1457 * generate a serie of paint events which then are processed - 1458 * three cycles, minimum. 1459 * 1460 * @param timeout the maximum time to wait in milliseconds, negative means "forever". 1461 */ 1462 public void realSync(final long timeout) throws OperationTimedOut, InfiniteLoop 1463 { 1464 if (EventQueue.isDispatchThread()) { 1465 throw new IllegalThreadException("The SunToolkit.realSync() method cannot be used on the event dispatch thread (EDT)."); 1466 } 1467 int bigLoop = 0; 1468 do { 1469 // Let's do sync first 1470 sync(); 1471 1472 // During the wait process, when we were processing incoming 1473 // events, we could have made some new request, which can 1474 // generate new events. Example: MapNotify/XSetInputFocus. 1475 // Therefore, we dispatch them as long as there is something 1476 // to dispatch. 1477 int iters = 0; 1478 while (iters < MIN_ITERS) { 1479 syncNativeQueue(timeout); 1480 iters++; 1481 } 1482 while (syncNativeQueue(timeout) && iters < MAX_ITERS) { 1483 iters++; 1484 } 1485 if (iters >= MAX_ITERS) { 1486 throw new InfiniteLoop(); 1487 } 1488 1489 // native requests were dispatched by X/Window Manager or Windows 1490 // Moreover, we processed them all on Toolkit thread 1491 // Now wait while EDT processes them. 1492 // 1493 // During processing of some events (focus, for example), 1494 // some other events could have been generated. So, after 1495 // waitForIdle, we may end up with full EventQueue 1496 iters = 0; 1497 while (iters < MIN_ITERS) { 1498 waitForIdle(timeout); 1499 iters++; 1500 } 1501 while (waitForIdle(timeout) && iters < MAX_ITERS) { 1502 iters++; 1503 } 1504 if (iters >= MAX_ITERS) { 1505 throw new InfiniteLoop(); 1506 } 1507 1508 bigLoop++; 1509 // Again, for Java events, it was simple to check for new Java 1510 // events by checking event queue, but what if Java events 1511 // resulted in native requests? Therefor, check native events again. 1512 } while ((syncNativeQueue(timeout) || waitForIdle(timeout)) && bigLoop < MAX_ITERS); 1513 } 1514 1515 /** 1516 * Platform toolkits need to implement this method to perform the 1517 * sync of the native queue. The method should wait until native 1518 * requests are processed, all native events are processed and 1519 * corresponding Java events are generated. Should return 1520 * {@code true} if some events were processed, 1521 * {@code false} otherwise. 1522 */ 1523 protected abstract boolean syncNativeQueue(final long timeout); 1524 1525 private boolean eventDispatched; 1526 private boolean queueEmpty; 1527 private final Object waitLock = new Object(); 1528 1529 private boolean isEQEmpty() { 1530 EventQueue queue = getSystemEventQueueImpl(); 1531 return AWTAccessor.getEventQueueAccessor().noEvents(queue); 1532 } 1533 1534 /** 1535 * Waits for the Java event queue to empty. Ensures that all 1536 * events are processed (including paint events), and that if 1537 * recursive events were generated, they are also processed. 1538 * Should return {@code true} if more processing is 1539 * necessary, {@code false} otherwise. 1540 */ 1541 @SuppressWarnings("serial") 1542 protected final boolean waitForIdle(final long timeout) { 1543 flushPendingEvents(); 1544 final boolean queueWasEmpty; 1545 synchronized (waitLock) { 1546 queueWasEmpty = isEQEmpty(); 1547 queueEmpty = false; 1548 eventDispatched = false; 1549 postEvent(AppContext.getAppContext(), 1550 new PeerEvent(getSystemEventQueueImpl(), null, PeerEvent.LOW_PRIORITY_EVENT) { 1551 @Override 1552 public void dispatch() { 1553 // Here we block EDT. It could have some 1554 // events, it should have dispatched them by 1555 // now. So native requests could have been 1556 // generated. First, dispatch them. Then, 1557 // flush Java events again. 1558 int iters = 0; 1559 while (iters < MIN_ITERS) { 1560 syncNativeQueue(timeout); 1561 iters++; 1562 } 1563 while (syncNativeQueue(timeout) && iters < MAX_ITERS) { 1564 iters++; 1565 } 1566 flushPendingEvents(); 1567 1568 synchronized(waitLock) { 1569 queueEmpty = isEQEmpty(); 1570 eventDispatched = true; 1571 waitLock.notifyAll(); 1572 } 1573 } 1574 }); 1575 try { 1576 while (!eventDispatched) { 1577 waitLock.wait(); 1578 } 1579 } catch (InterruptedException ie) { 1580 return false; 1581 } 1582 } 1583 1584 try { 1585 Thread.sleep(MINIMAL_EDELAY); 1586 } catch (InterruptedException ie) { 1587 throw new RuntimeException("Interrupted"); 1588 } 1589 1590 flushPendingEvents(); 1591 1592 // Lock to force write-cache flush for queueEmpty. 1593 synchronized (waitLock) { 1594 return !(queueEmpty && isEQEmpty() && queueWasEmpty); 1595 } 1596 } 1597 1598 /** 1599 * Grabs the mouse input for the given window. The window must be 1600 * visible. The window or its children do not receive any 1601 * additional mouse events besides those targeted to them. All 1602 * other events will be dispatched as before - to the respective 1603 * targets. This Window will receive UngrabEvent when automatic 1604 * ungrab is about to happen. The event can be listened to by 1605 * installing AWTEventListener with WINDOW_EVENT_MASK. See 1606 * UngrabEvent class for the list of conditions when ungrab is 1607 * about to happen. 1608 * @see UngrabEvent 1609 */ 1610 public abstract void grab(Window w); 1611 1612 /** 1613 * Forces ungrab. No event will be sent. 1614 */ 1615 public abstract void ungrab(Window w); 1616 1617 1618 /** 1619 * Locates the splash screen library in a platform dependent way and closes 1620 * the splash screen. Should be invoked on first top-level frame display. 1621 * @see java.awt.SplashScreen 1622 * @since 1.6 1623 */ 1624 public static native void closeSplashScreen(); 1625 1626 /* The following methods and variables are to support retrieving 1627 * desktop text anti-aliasing settings 1628 */ 1629 1630 /* Need an instance method because setDesktopProperty(..) is protected. */ 1631 private void fireDesktopFontPropertyChanges() { 1632 setDesktopProperty(SunToolkit.DESKTOPFONTHINTS, 1633 SunToolkit.getDesktopFontHints()); 1634 } 1635 1636 private static boolean checkedSystemAAFontSettings; 1637 private static boolean useSystemAAFontSettings; 1638 private static boolean lastExtraCondition = true; 1639 private static RenderingHints desktopFontHints; 1640 1641 /* Since Swing is the reason for this "extra condition" logic its 1642 * worth documenting it in some detail. 1643 * First, a goal is for Swing and applications to both retrieve and 1644 * use the same desktop property value so that there is complete 1645 * consistency between the settings used by JDK's Swing implementation 1646 * and 3rd party custom Swing components, custom L&Fs and any general 1647 * text rendering that wants to be consistent with these. 1648 * But by default on Solaris & Linux Swing will not use AA text over 1649 * remote X11 display (unless Xrender can be used which is TBD and may not 1650 * always be available anyway) as that is a noticeable performance hit. 1651 * So there needs to be a way to express that extra condition so that 1652 * it is seen by all clients of the desktop property API. 1653 * If this were the only condition it could be handled here as it would 1654 * be the same for any L&F and could reasonably be considered to be 1655 * a static behaviour of those systems. 1656 * But GTK currently has an additional test based on locale which is 1657 * not applied by Metal. So mixing GTK in a few locales with Metal 1658 * would mean the last one wins. 1659 * This could be stored per-app context which would work 1660 * for different applets, but wouldn't help for a single application 1661 * using GTK and some other L&F concurrently. 1662 * But it is expected this will be addressed within GTK and the font 1663 * system so is a temporary and somewhat unlikely harmless corner case. 1664 */ 1665 public static void setAAFontSettingsCondition(boolean extraCondition) { 1666 if (extraCondition != lastExtraCondition) { 1667 lastExtraCondition = extraCondition; 1668 if (checkedSystemAAFontSettings) { 1669 /* Someone already asked for this info, under a different 1670 * condition. 1671 * We'll force re-evaluation instead of replicating the 1672 * logic, then notify any listeners of any change. 1673 */ 1674 checkedSystemAAFontSettings = false; 1675 Toolkit tk = Toolkit.getDefaultToolkit(); 1676 if (tk instanceof SunToolkit) { 1677 ((SunToolkit)tk).fireDesktopFontPropertyChanges(); 1678 } 1679 } 1680 } 1681 } 1682 1683 /* "false", "off", ""default" aren't explicitly tested, they 1684 * just fall through to produce a null return which all are equated to 1685 * "false". 1686 */ 1687 private static RenderingHints getDesktopAAHintsByName(String hintname) { 1688 Object aaHint = null; 1689 hintname = hintname.toLowerCase(Locale.ENGLISH); 1690 if (hintname.equals("on")) { 1691 aaHint = VALUE_TEXT_ANTIALIAS_ON; 1692 } else if (hintname.equals("gasp")) { 1693 aaHint = VALUE_TEXT_ANTIALIAS_GASP; 1694 } else if (hintname.equals("lcd") || hintname.equals("lcd_hrgb")) { 1695 aaHint = VALUE_TEXT_ANTIALIAS_LCD_HRGB; 1696 } else if (hintname.equals("lcd_hbgr")) { 1697 aaHint = VALUE_TEXT_ANTIALIAS_LCD_HBGR; 1698 } else if (hintname.equals("lcd_vrgb")) { 1699 aaHint = VALUE_TEXT_ANTIALIAS_LCD_VRGB; 1700 } else if (hintname.equals("lcd_vbgr")) { 1701 aaHint = VALUE_TEXT_ANTIALIAS_LCD_VBGR; 1702 } 1703 if (aaHint != null) { 1704 RenderingHints map = new RenderingHints(null); 1705 map.put(KEY_TEXT_ANTIALIASING, aaHint); 1706 return map; 1707 } else { 1708 return null; 1709 } 1710 } 1711 1712 /* This method determines whether to use the system font settings, 1713 * or ignore them if a L&F has specified they should be ignored, or 1714 * to override both of these with a system property specified value. 1715 * If the toolkit isn't a SunToolkit, (eg may be headless) then that 1716 * system property isn't applied as desktop properties are considered 1717 * to be inapplicable in that case. In that headless case although 1718 * this method will return "true" the toolkit will return a null map. 1719 */ 1720 private static boolean useSystemAAFontSettings() { 1721 if (!checkedSystemAAFontSettings) { 1722 useSystemAAFontSettings = true; /* initially set this true */ 1723 String systemAAFonts = null; 1724 Toolkit tk = Toolkit.getDefaultToolkit(); 1725 if (tk instanceof SunToolkit) { 1726 systemAAFonts = 1727 AccessController.doPrivileged( 1728 new GetPropertyAction("awt.useSystemAAFontSettings")); 1729 } 1730 if (systemAAFonts != null) { 1731 useSystemAAFontSettings = 1732 Boolean.valueOf(systemAAFonts).booleanValue(); 1733 /* If it is anything other than "true", then it may be 1734 * a hint name , or it may be "off, "default", etc. 1735 */ 1736 if (!useSystemAAFontSettings) { 1737 desktopFontHints = getDesktopAAHintsByName(systemAAFonts); 1738 } 1739 } 1740 /* If its still true, apply the extra condition */ 1741 if (useSystemAAFontSettings) { 1742 useSystemAAFontSettings = lastExtraCondition; 1743 } 1744 checkedSystemAAFontSettings = true; 1745 } 1746 return useSystemAAFontSettings; 1747 } 1748 1749 /* A variable defined for the convenience of JDK code */ 1750 public static final String DESKTOPFONTHINTS = "awt.font.desktophints"; 1751 1752 /* Overridden by subclasses to return platform/desktop specific values */ 1753 protected RenderingHints getDesktopAAHints() { 1754 return null; 1755 } 1756 1757 /* Subclass desktop property loading methods call this which 1758 * in turn calls the appropriate subclass implementation of 1759 * getDesktopAAHints() when system settings are being used. 1760 * Its public rather than protected because subclasses may delegate 1761 * to a helper class. 1762 */ 1763 public static RenderingHints getDesktopFontHints() { 1764 if (useSystemAAFontSettings()) { 1765 Toolkit tk = Toolkit.getDefaultToolkit(); 1766 if (tk instanceof SunToolkit) { 1767 Object map = ((SunToolkit)tk).getDesktopAAHints(); 1768 return (RenderingHints)map; 1769 } else { /* Headless Toolkit */ 1770 return null; 1771 } 1772 } else if (desktopFontHints != null) { 1773 /* cloning not necessary as the return value is cloned later, but 1774 * its harmless. 1775 */ 1776 return (RenderingHints)(desktopFontHints.clone()); 1777 } else { 1778 return null; 1779 } 1780 } 1781 1782 1783 public abstract boolean isDesktopSupported(); 1784 public abstract boolean isTaskbarSupported(); 1785 1786 /* 1787 * consumeNextKeyTyped() method is not currently used, 1788 * however Swing could use it in the future. 1789 */ 1790 public static synchronized void consumeNextKeyTyped(KeyEvent keyEvent) { 1791 try { 1792 AWTAccessor.getDefaultKeyboardFocusManagerAccessor().consumeNextKeyTyped( 1793 (DefaultKeyboardFocusManager)KeyboardFocusManager. 1794 getCurrentKeyboardFocusManager(), 1795 keyEvent); 1796 } catch (ClassCastException cce) { 1797 cce.printStackTrace(); 1798 } 1799 } 1800 1801 protected static void dumpPeers(final PlatformLogger aLog) { 1802 AWTAutoShutdown.getInstance().dumpPeers(aLog); 1803 } 1804 1805 /** 1806 * Returns the {@code Window} ancestor of the component {@code comp}. 1807 * @return Window ancestor of the component or component by itself if it is Window; 1808 * null, if component is not a part of window hierarchy 1809 */ 1810 public static Window getContainingWindow(Component comp) { 1811 while (comp != null && !(comp instanceof Window)) { 1812 comp = comp.getParent(); 1813 } 1814 return (Window)comp; 1815 } 1816 1817 private static Boolean sunAwtDisableMixing = null; 1818 1819 /** 1820 * Returns the value of "sun.awt.disableMixing" property. Default 1821 * value is {@code false}. 1822 */ 1823 public static synchronized boolean getSunAwtDisableMixing() { 1824 if (sunAwtDisableMixing == null) { 1825 sunAwtDisableMixing = AccessController.doPrivileged( 1826 new GetBooleanAction("sun.awt.disableMixing")); 1827 } 1828 return sunAwtDisableMixing.booleanValue(); 1829 } 1830 1831 /** 1832 * Returns true if the native GTK libraries are available. The 1833 * default implementation returns false, but UNIXToolkit overrides this 1834 * method to provide a more specific answer. 1835 */ 1836 public boolean isNativeGTKAvailable() { 1837 return false; 1838 } 1839 1840 private static final Object DEACTIVATION_TIMES_MAP_KEY = new Object(); 1841 1842 public synchronized void setWindowDeactivationTime(Window w, long time) { 1843 AppContext ctx = getAppContext(w); 1844 if (ctx == null) { 1845 return; 1846 } 1847 @SuppressWarnings("unchecked") 1848 WeakHashMap<Window, Long> map = (WeakHashMap<Window, Long>)ctx.get(DEACTIVATION_TIMES_MAP_KEY); 1849 if (map == null) { 1850 map = new WeakHashMap<Window, Long>(); 1851 ctx.put(DEACTIVATION_TIMES_MAP_KEY, map); 1852 } 1853 map.put(w, time); 1854 } 1855 1856 public synchronized long getWindowDeactivationTime(Window w) { 1857 AppContext ctx = getAppContext(w); 1858 if (ctx == null) { 1859 return -1; 1860 } 1861 @SuppressWarnings("unchecked") 1862 WeakHashMap<Window, Long> map = (WeakHashMap<Window, Long>)ctx.get(DEACTIVATION_TIMES_MAP_KEY); 1863 if (map == null) { 1864 return -1; 1865 } 1866 Long time = map.get(w); 1867 return time == null ? -1 : time; 1868 } 1869 1870 public void updateScreenMenuBarUI() { 1871 } 1872 1873 // Cosntant alpha 1874 public boolean isWindowOpacitySupported() { 1875 return false; 1876 } 1877 1878 // Shaping 1879 public boolean isWindowShapingSupported() { 1880 return false; 1881 } 1882 1883 // Per-pixel alpha 1884 public boolean isWindowTranslucencySupported() { 1885 return false; 1886 } 1887 1888 public boolean isTranslucencyCapable(GraphicsConfiguration gc) { 1889 return false; 1890 } 1891 1892 /** 1893 * Returns true if swing backbuffer should be translucent. 1894 */ 1895 public boolean isSwingBackbufferTranslucencySupported() { 1896 return false; 1897 } 1898 1899 /** 1900 * Returns whether or not a containing top level window for the passed 1901 * component is 1902 * {@link GraphicsDevice.WindowTranslucency#PERPIXEL_TRANSLUCENT PERPIXEL_TRANSLUCENT}. 1903 * 1904 * @param c a Component which toplevel's to check 1905 * @return {@code true} if the passed component is not null and has a 1906 * containing toplevel window which is opaque (so per-pixel translucency 1907 * is not enabled), {@code false} otherwise 1908 * @see GraphicsDevice.WindowTranslucency#PERPIXEL_TRANSLUCENT 1909 */ 1910 public static boolean isContainingTopLevelOpaque(Component c) { 1911 Window w = getContainingWindow(c); 1912 return w != null && w.isOpaque(); 1913 } 1914 1915 /** 1916 * Returns whether or not a containing top level window for the passed 1917 * component is 1918 * {@link GraphicsDevice.WindowTranslucency#TRANSLUCENT TRANSLUCENT}. 1919 * 1920 * @param c a Component which toplevel's to check 1921 * @return {@code true} if the passed component is not null and has a 1922 * containing toplevel window which has opacity less than 1923 * 1.0f (which means that it is translucent), {@code false} otherwise 1924 * @see GraphicsDevice.WindowTranslucency#TRANSLUCENT 1925 */ 1926 public static boolean isContainingTopLevelTranslucent(Component c) { 1927 Window w = getContainingWindow(c); 1928 return w != null && w.getOpacity() < 1.0f; 1929 } 1930 1931 /** 1932 * Returns whether the native system requires using the peer.updateWindow() 1933 * method to update the contents of a non-opaque window, or if usual 1934 * painting procedures are sufficient. The default return value covers 1935 * the X11 systems. On MS Windows this method is overriden in WToolkit 1936 * to return true. 1937 */ 1938 public boolean needUpdateWindow() { 1939 return false; 1940 } 1941 1942 /** 1943 * Descendants of the SunToolkit should override and put their own logic here. 1944 */ 1945 public int getNumberOfButtons(){ 1946 return 3; 1947 } 1948 1949 /** 1950 * Checks that the given object implements/extends the given 1951 * interface/class. 1952 * 1953 * Note that using the instanceof operator causes a class to be loaded. 1954 * Using this method doesn't load a class and it can be used instead of 1955 * the instanceof operator for performance reasons. 1956 * 1957 * @param obj Object to be checked 1958 * @param type The name of the interface/class. Must be 1959 * fully-qualified interface/class name. 1960 * @return true, if this object implements/extends the given 1961 * interface/class, false, otherwise, or if obj or type is null 1962 */ 1963 public static boolean isInstanceOf(Object obj, String type) { 1964 if (obj == null) return false; 1965 if (type == null) return false; 1966 1967 return isInstanceOf(obj.getClass(), type); 1968 } 1969 1970 private static boolean isInstanceOf(Class<?> cls, String type) { 1971 if (cls == null) return false; 1972 1973 if (cls.getName().equals(type)) { 1974 return true; 1975 } 1976 1977 for (Class<?> c : cls.getInterfaces()) { 1978 if (c.getName().equals(type)) { 1979 return true; 1980 } 1981 } 1982 return isInstanceOf(cls.getSuperclass(), type); 1983 } 1984 1985 protected static LightweightFrame getLightweightFrame(Component c) { 1986 for (; c != null; c = c.getParent()) { 1987 if (c instanceof LightweightFrame) { 1988 return (LightweightFrame)c; 1989 } 1990 if (c instanceof Window) { 1991 // Don't traverse owner windows 1992 return null; 1993 } 1994 } 1995 return null; 1996 } 1997 1998 /////////////////////////////////////////////////////////////////////////// 1999 // 2000 // The following methods help set and identify whether a particular 2001 // AWTEvent object was produced by the system or by user code. As of this 2002 // writing the only consumer is the Java Plug-In, although this information 2003 // could be useful to more clients and probably should be formalized in 2004 // the public API. 2005 // 2006 /////////////////////////////////////////////////////////////////////////// 2007 2008 public static void setSystemGenerated(AWTEvent e) { 2009 AWTAccessor.getAWTEventAccessor().setSystemGenerated(e); 2010 } 2011 2012 public static boolean isSystemGenerated(AWTEvent e) { 2013 return AWTAccessor.getAWTEventAccessor().isSystemGenerated(e); 2014 } 2015 2016 } // class SunToolkit 2017 2018 2019 /* 2020 * PostEventQueue is a Thread that runs in the same AppContext as the 2021 * Java EventQueue. It is a queue of AWTEvents to be posted to the 2022 * Java EventQueue. The toolkit Thread (AWT-Windows/AWT-Motif) posts 2023 * events to this queue, which then calls EventQueue.postEvent(). 2024 * 2025 * We do this because EventQueue.postEvent() may be overridden by client 2026 * code, and we mustn't ever call client code from the toolkit thread. 2027 */ 2028 class PostEventQueue { 2029 private EventQueueItem queueHead = null; 2030 private EventQueueItem queueTail = null; 2031 private final EventQueue eventQueue; 2032 2033 private Thread flushThread = null; 2034 2035 PostEventQueue(EventQueue eq) { 2036 eventQueue = eq; 2037 } 2038 2039 /* 2040 * Continually post pending AWTEvents to the Java EventQueue. The method 2041 * is synchronized to ensure the flush is completed before a new event 2042 * can be posted to this queue. 2043 * 2044 * 7177040: The method couldn't be wholly synchronized because of calls 2045 * of EventQueue.postEvent() that uses pushPopLock, otherwise it could 2046 * potentially lead to deadlock 2047 */ 2048 public void flush() { 2049 2050 Thread newThread = Thread.currentThread(); 2051 2052 try { 2053 EventQueueItem tempQueue; 2054 synchronized (this) { 2055 // Avoid method recursion 2056 if (newThread == flushThread) { 2057 return; 2058 } 2059 // Wait for other threads' flushing 2060 while (flushThread != null) { 2061 wait(); 2062 } 2063 // Skip everything if queue is empty 2064 if (queueHead == null) { 2065 return; 2066 } 2067 // Remember flushing thread 2068 flushThread = newThread; 2069 2070 tempQueue = queueHead; 2071 queueHead = queueTail = null; 2072 } 2073 try { 2074 while (tempQueue != null) { 2075 eventQueue.postEvent(tempQueue.event); 2076 tempQueue = tempQueue.next; 2077 } 2078 } 2079 finally { 2080 // Only the flushing thread can get here 2081 synchronized (this) { 2082 // Forget flushing thread, inform other pending threads 2083 flushThread = null; 2084 notifyAll(); 2085 } 2086 } 2087 } 2088 catch (InterruptedException e) { 2089 // Couldn't allow exception go up, so at least recover the flag 2090 newThread.interrupt(); 2091 } 2092 } 2093 2094 /* 2095 * Enqueue an AWTEvent to be posted to the Java EventQueue. 2096 */ 2097 void postEvent(AWTEvent event) { 2098 EventQueueItem item = new EventQueueItem(event); 2099 2100 synchronized (this) { 2101 if (queueHead == null) { 2102 queueHead = queueTail = item; 2103 } else { 2104 queueTail.next = item; 2105 queueTail = item; 2106 } 2107 } 2108 SunToolkit.wakeupEventQueue(eventQueue, event.getSource() == AWTAutoShutdown.getInstance()); 2109 } 2110 } // class PostEventQueue