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.DataBuffer; 34 import java.awt.image.DataBufferInt; 35 import java.awt.image.ImageObserver; 36 import java.awt.image.ImageProducer; 37 import java.awt.image.Raster; 38 import java.awt.peer.FramePeer; 39 import java.awt.peer.KeyboardFocusManagerPeer; 40 import java.awt.peer.MouseInfoPeer; 41 import java.awt.peer.SystemTrayPeer; 42 import java.awt.peer.TrayIconPeer; 43 import java.io.File; 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.lang.reflect.InvocationTargetException; 47 import java.net.URL; 48 import java.security.AccessController; 49 import java.util.Collections; 50 import java.util.Iterator; 51 import java.util.Locale; 52 import java.util.Map; 53 import java.util.Vector; 54 import java.util.WeakHashMap; 55 import java.util.concurrent.TimeUnit; 56 import java.util.concurrent.locks.Condition; 57 import java.util.concurrent.locks.ReentrantLock; 58 59 import sun.awt.im.InputContext; 60 import sun.awt.image.ByteArrayImageSource; 61 import sun.awt.image.FileImageSource; 62 import sun.awt.image.ImageRepresentation; 63 import java.awt.image.MultiResolutionImage; 64 import sun.awt.image.MultiResolutionToolkitImage; 65 import sun.awt.image.ToolkitImage; 66 import sun.awt.image.URLImageSource; 67 import sun.font.FontDesignMetrics; 68 import sun.net.util.URLUtil; 69 import sun.security.action.GetBooleanAction; 70 import sun.security.action.GetPropertyAction; 71 import sun.util.logging.PlatformLogger; 72 73 import static java.awt.RenderingHints.*; 74 75 public abstract class SunToolkit extends Toolkit 76 implements ComponentFactory, InputMethodSupport, KeyboardFocusManagerPeerProvider { 77 78 // 8014718: logging has been removed from SunToolkit 79 80 /* Load debug settings for native code */ 81 static { 82 if (AccessController.doPrivileged(new GetBooleanAction("sun.awt.nativedebug"))) { 83 DebugSettings.init(); 84 } 85 }; 86 87 /** 88 * Special mask for the UngrabEvent events, in addition to the 89 * public masks defined in AWTEvent. Should be used as the mask 90 * value for Toolkit.addAWTEventListener. 91 */ 92 public static final int GRAB_EVENT_MASK = 0x80000000; 93 94 /* The key to put()/get() the PostEventQueue into/from the AppContext. 95 */ 96 private static final String POST_EVENT_QUEUE_KEY = "PostEventQueue"; 97 98 /** 99 * Number of buttons. 100 * By default it's taken from the system. If system value does not 101 * fit into int type range, use our own MAX_BUTTONS_SUPPORT value. 102 */ 103 protected static int numberOfButtons = 0; 104 105 106 /* XFree standard mention 24 buttons as maximum: 107 * http://www.xfree86.org/current/mouse.4.html 108 * We workaround systems supporting more than 24 buttons. 109 * Otherwise, we have to use long type values as masks 110 * which leads to API change. 111 * InputEvent.BUTTON_DOWN_MASK may contain only 21 masks due to 112 * the 4-bytes limit for the int type. (CR 6799099) 113 * One more bit is reserved for FIRST_HIGH_BIT. 114 */ 115 public static final int MAX_BUTTONS_SUPPORTED = 20; 116 117 /** 118 * Creates and initializes EventQueue instance for the specified 119 * AppContext. 120 * Note that event queue must be created from createNewAppContext() 121 * only in order to ensure that EventQueue constructor obtains 122 * the correct AppContext. 123 * @param appContext AppContext to associate with the event queue 124 */ 125 private static void initEQ(AppContext appContext) { 126 EventQueue eventQueue = new EventQueue(); 127 appContext.put(AppContext.EVENT_QUEUE_KEY, eventQueue); 128 129 PostEventQueue postEventQueue = new PostEventQueue(eventQueue); 130 appContext.put(POST_EVENT_QUEUE_KEY, postEventQueue); 131 } 132 133 public SunToolkit() { 134 } 135 136 public boolean useBufferPerWindow() { 137 return false; 138 } 139 140 public abstract FramePeer createLightweightFrame(LightweightFrame target) 141 throws HeadlessException; 142 143 public abstract TrayIconPeer createTrayIcon(TrayIcon target) 144 throws HeadlessException, AWTException; 145 146 public abstract SystemTrayPeer createSystemTray(SystemTray target); 147 148 public abstract boolean isTraySupported(); 149 150 @Override 151 public abstract KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() 152 throws HeadlessException; 153 154 /** 155 * The AWT lock is typically only used on Unix platforms to synchronize 156 * access to Xlib, OpenGL, etc. However, these methods are implemented 157 * in SunToolkit so that they can be called from shared code (e.g. 158 * from the OGL pipeline) or from the X11 pipeline regardless of whether 159 * XToolkit or MToolkit is currently in use. There are native macros 160 * (such as AWT_LOCK) defined in awt.h, so if the implementation of these 161 * methods is changed, make sure it is compatible with the native macros. 162 * 163 * Note: The following methods (awtLock(), awtUnlock(), etc) should be 164 * used in place of: 165 * synchronized (getAWTLock()) { 166 * ... 167 * } 168 * 169 * By factoring these methods out specially, we are able to change the 170 * implementation of these methods (e.g. use more advanced locking 171 * mechanisms) without impacting calling code. 172 * 173 * Sample usage: 174 * private void doStuffWithXlib() { 175 * assert !SunToolkit.isAWTLockHeldByCurrentThread(); 176 * SunToolkit.awtLock(); 177 * try { 178 * ... 179 * XlibWrapper.XDoStuff(); 180 * } finally { 181 * SunToolkit.awtUnlock(); 182 * } 183 * } 184 */ 185 186 private static final ReentrantLock AWT_LOCK = new ReentrantLock(); 187 private static final Condition AWT_LOCK_COND = AWT_LOCK.newCondition(); 188 189 public static final void awtLock() { 190 AWT_LOCK.lock(); 191 } 192 193 public static final boolean awtTryLock() { 194 return AWT_LOCK.tryLock(); 195 } 196 197 public static final void awtUnlock() { 198 AWT_LOCK.unlock(); 199 } 200 201 public static final void awtLockWait() 202 throws InterruptedException 203 { 204 AWT_LOCK_COND.await(); 205 } 206 207 public static final void awtLockWait(long timeout) 208 throws InterruptedException 209 { 210 AWT_LOCK_COND.await(timeout, TimeUnit.MILLISECONDS); 211 } 212 213 public static final void awtLockNotify() { 214 AWT_LOCK_COND.signal(); 215 } 216 217 public static final void awtLockNotifyAll() { 218 AWT_LOCK_COND.signalAll(); 219 } 220 221 public static final boolean isAWTLockHeldByCurrentThread() { 222 return AWT_LOCK.isHeldByCurrentThread(); 223 } 224 225 /* 226 * Create a new AppContext, along with its EventQueue, for a 227 * new ThreadGroup. Browser code, for example, would use this 228 * method to create an AppContext & EventQueue for an Applet. 229 */ 230 public static AppContext createNewAppContext() { 231 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 232 return createNewAppContext(threadGroup); 233 } 234 235 static final AppContext createNewAppContext(ThreadGroup threadGroup) { 236 // Create appContext before initialization of EventQueue, so all 237 // the calls to AppContext.getAppContext() from EventQueue ctor 238 // return correct values 239 AppContext appContext = new AppContext(threadGroup); 240 initEQ(appContext); 241 242 return appContext; 243 } 244 245 static void wakeupEventQueue(EventQueue q, boolean isShutdown){ 246 AWTAccessor.getEventQueueAccessor().wakeup(q, isShutdown); 247 } 248 249 /* 250 * Fetch the peer associated with the given target (as specified 251 * in the peer creation method). This can be used to determine 252 * things like what the parent peer is. If the target is null 253 * or the target can't be found (either because the a peer was 254 * never created for it or the peer was disposed), a null will 255 * be returned. 256 */ 257 protected static Object targetToPeer(Object target) { 258 if (target != null && !GraphicsEnvironment.isHeadless()) { 259 return AWTAutoShutdown.getInstance().getPeer(target); 260 } 261 return null; 262 } 263 264 protected static void targetCreatedPeer(Object target, Object peer) { 265 if (target != null && peer != null && 266 !GraphicsEnvironment.isHeadless()) 267 { 268 AWTAutoShutdown.getInstance().registerPeer(target, peer); 269 } 270 } 271 272 protected static void targetDisposedPeer(Object target, Object peer) { 273 if (target != null && peer != null && 274 !GraphicsEnvironment.isHeadless()) 275 { 276 AWTAutoShutdown.getInstance().unregisterPeer(target, peer); 277 } 278 } 279 280 // Maps from non-Component/MenuComponent to AppContext. 281 // WeakHashMap<Component,AppContext> 282 private static final Map<Object, AppContext> appContextMap = 283 Collections.synchronizedMap(new WeakIdentityHashMap<Object, AppContext>()); 284 285 /** 286 * Sets the appContext field of target. If target is not a Component or 287 * MenuComponent, this returns false. 288 */ 289 private static boolean setAppContext(Object target, 290 AppContext context) { 291 if (target instanceof Component) { 292 AWTAccessor.getComponentAccessor(). 293 setAppContext((Component)target, context); 294 } else if (target instanceof MenuComponent) { 295 AWTAccessor.getMenuComponentAccessor(). 296 setAppContext((MenuComponent)target, context); 297 } else { 298 return false; 299 } 300 return true; 301 } 302 303 /** 304 * Returns the appContext field for target. If target is not a 305 * Component or MenuComponent this returns null. 306 */ 307 private static AppContext getAppContext(Object target) { 308 if (target instanceof Component) { 309 return AWTAccessor.getComponentAccessor(). 310 getAppContext((Component)target); 311 } else if (target instanceof MenuComponent) { 312 return AWTAccessor.getMenuComponentAccessor(). 313 getAppContext((MenuComponent)target); 314 } else { 315 return null; 316 } 317 } 318 319 /* 320 * Fetch the AppContext associated with the given target. 321 * This can be used to determine things like which EventQueue 322 * to use for posting events to a Component. If the target is 323 * null or the target can't be found, a null with be returned. 324 */ 325 public static AppContext targetToAppContext(Object target) { 326 if (target == null) { 327 return null; 328 } 329 AppContext context = getAppContext(target); 330 if (context == null) { 331 // target is not a Component/MenuComponent, try the 332 // appContextMap. 333 context = appContextMap.get(target); 334 } 335 return context; 336 } 337 338 /** 339 * Sets the synchronous status of focus requests on lightweight 340 * components in the specified window to the specified value. 341 * If the boolean parameter is {@code true} then the focus 342 * requests on lightweight components will be performed 343 * synchronously, if it is {@code false}, then asynchronously. 344 * By default, all windows have their lightweight request status 345 * set to asynchronous. 346 * <p> 347 * The application can only set the status of lightweight focus 348 * requests to synchronous for any of its windows if it doesn't 349 * perform focus transfers between different heavyweight containers. 350 * In this case the observable focus behaviour is the same as with 351 * asynchronous status. 352 * <p> 353 * If the application performs focus transfer between different 354 * heavyweight containers and sets the lightweight focus request 355 * status to synchronous for any of its windows, then further focus 356 * behaviour is unspecified. 357 * <p> 358 * @param changed the window for which the lightweight focus request 359 * status should be set 360 * @param status the value of lightweight focus request status 361 */ 362 363 public static void setLWRequestStatus(Window changed,boolean status){ 364 AWTAccessor.getWindowAccessor().setLWRequestStatus(changed, status); 365 }; 366 367 public static void checkAndSetPolicy(Container cont) { 368 FocusTraversalPolicy defaultPolicy = KeyboardFocusManager. 369 getCurrentKeyboardFocusManager(). 370 getDefaultFocusTraversalPolicy(); 371 372 cont.setFocusTraversalPolicy(defaultPolicy); 373 } 374 375 private static FocusTraversalPolicy createLayoutPolicy() { 376 FocusTraversalPolicy policy = null; 377 try { 378 Class<?> layoutPolicyClass = 379 Class.forName("javax.swing.LayoutFocusTraversalPolicy"); 380 policy = (FocusTraversalPolicy)layoutPolicyClass.newInstance(); 381 } 382 catch (ClassNotFoundException e) { 383 assert false; 384 } 385 catch (InstantiationException e) { 386 assert false; 387 } 388 catch (IllegalAccessException e) { 389 assert false; 390 } 391 392 return policy; 393 } 394 395 /* 396 * Insert a mapping from target to AppContext, for later retrieval 397 * via targetToAppContext() above. 398 */ 399 public static void insertTargetMapping(Object target, AppContext appContext) { 400 if (!setAppContext(target, appContext)) { 401 // Target is not a Component/MenuComponent, use the private Map 402 // instead. 403 appContextMap.put(target, appContext); 404 } 405 } 406 407 /* 408 * Post an AWTEvent to the Java EventQueue, using the PostEventQueue 409 * to avoid possibly calling client code (EventQueueSubclass.postEvent()) 410 * on the toolkit (AWT-Windows/AWT-Motif) thread. This function should 411 * not be called under another lock since it locks the EventQueue. 412 * See bugids 4632918, 4526597. 413 */ 414 public static void postEvent(AppContext appContext, AWTEvent event) { 415 if (event == null) { 416 throw new NullPointerException(); 417 } 418 419 AWTAccessor.SequencedEventAccessor sea = AWTAccessor.getSequencedEventAccessor(); 420 if (sea != null && sea.isSequencedEvent(event)) { 421 AWTEvent nested = sea.getNested(event); 422 if (nested.getID() == WindowEvent.WINDOW_LOST_FOCUS && 423 nested instanceof TimedWindowEvent) 424 { 425 TimedWindowEvent twe = (TimedWindowEvent)nested; 426 ((SunToolkit)Toolkit.getDefaultToolkit()). 427 setWindowDeactivationTime((Window)twe.getSource(), twe.getWhen()); 428 } 429 } 430 431 // All events posted via this method are system-generated. 432 // Placing the following call here reduces considerably the 433 // number of places throughout the toolkit that would 434 // otherwise have to be modified to precisely identify 435 // system-generated events. 436 setSystemGenerated(event); 437 AppContext eventContext = targetToAppContext(event.getSource()); 438 if (eventContext != null && !eventContext.equals(appContext)) { 439 throw new RuntimeException("Event posted on wrong app context : " + event); 440 } 441 PostEventQueue postEventQueue = 442 (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY); 443 if (postEventQueue != null) { 444 postEventQueue.postEvent(event); 445 } 446 } 447 448 /* 449 * Post AWTEvent of high priority. 450 */ 451 public static void postPriorityEvent(final AWTEvent e) { 452 PeerEvent pe = new PeerEvent(Toolkit.getDefaultToolkit(), new Runnable() { 453 @Override 454 public void run() { 455 AWTAccessor.getAWTEventAccessor().setPosted(e); 456 ((Component)e.getSource()).dispatchEvent(e); 457 } 458 }, PeerEvent.ULTIMATE_PRIORITY_EVENT); 459 postEvent(targetToAppContext(e.getSource()), pe); 460 } 461 462 /* 463 * Flush any pending events which haven't been posted to the AWT 464 * EventQueue yet. 465 */ 466 public static void flushPendingEvents() { 467 AppContext appContext = AppContext.getAppContext(); 468 flushPendingEvents(appContext); 469 } 470 471 /* 472 * Flush the PostEventQueue for the right AppContext. 473 * The default flushPendingEvents only flushes the thread-local context, 474 * which is not always correct, c.f. 3746956 475 */ 476 public static void flushPendingEvents(AppContext appContext) { 477 PostEventQueue postEventQueue = 478 (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY); 479 if (postEventQueue != null) { 480 postEventQueue.flush(); 481 } 482 } 483 484 /* 485 * Execute a chunk of code on the Java event handler thread for the 486 * given target. Does not wait for the execution to occur before 487 * returning to the caller. 488 */ 489 public static void executeOnEventHandlerThread(Object target, 490 Runnable runnable) { 491 executeOnEventHandlerThread(new PeerEvent(target, runnable, PeerEvent.PRIORITY_EVENT)); 492 } 493 494 /* 495 * Fixed 5064013: the InvocationEvent time should be equals 496 * the time of the ActionEvent 497 */ 498 @SuppressWarnings("serial") 499 public static void executeOnEventHandlerThread(Object target, 500 Runnable runnable, 501 final long when) { 502 executeOnEventHandlerThread( 503 new PeerEvent(target, runnable, PeerEvent.PRIORITY_EVENT) { 504 @Override 505 public long getWhen() { 506 return when; 507 } 508 }); 509 } 510 511 /* 512 * Execute a chunk of code on the Java event handler thread for the 513 * given target. Does not wait for the execution to occur before 514 * returning to the caller. 515 */ 516 public static void executeOnEventHandlerThread(PeerEvent peerEvent) { 517 postEvent(targetToAppContext(peerEvent.getSource()), peerEvent); 518 } 519 520 /* 521 * Execute a chunk of code on the Java event handler thread. The 522 * method takes into account provided AppContext and sets 523 * {@code SunToolkit.getDefaultToolkit()} as a target of the 524 * event. See 6451487 for detailes. 525 * Does not wait for the execution to occur before returning to 526 * the caller. 527 */ 528 public static void invokeLaterOnAppContext( 529 AppContext appContext, Runnable dispatcher) 530 { 531 postEvent(appContext, 532 new PeerEvent(Toolkit.getDefaultToolkit(), dispatcher, 533 PeerEvent.PRIORITY_EVENT)); 534 } 535 536 /* 537 * Execute a chunk of code on the Java event handler thread for the 538 * given target. Waits for the execution to occur before returning 539 * to the caller. 540 */ 541 public static void executeOnEDTAndWait(Object target, Runnable runnable) 542 throws InterruptedException, InvocationTargetException 543 { 544 if (EventQueue.isDispatchThread()) { 545 throw new Error("Cannot call executeOnEDTAndWait from any event dispatcher thread"); 546 } 547 548 class AWTInvocationLock {} 549 Object lock = new AWTInvocationLock(); 550 551 PeerEvent event = new PeerEvent(target, runnable, lock, true, PeerEvent.PRIORITY_EVENT); 552 553 synchronized (lock) { 554 executeOnEventHandlerThread(event); 555 while(!event.isDispatched()) { 556 lock.wait(); 557 } 558 } 559 560 Throwable eventThrowable = event.getThrowable(); 561 if (eventThrowable != null) { 562 throw new InvocationTargetException(eventThrowable); 563 } 564 } 565 566 /* 567 * Returns true if the calling thread is the event dispatch thread 568 * contained within AppContext which associated with the given target. 569 * Use this call to ensure that a given task is being executed 570 * (or not being) on the event dispatch thread for the given target. 571 */ 572 public static boolean isDispatchThreadForAppContext(Object target) { 573 AppContext appContext = targetToAppContext(target); 574 EventQueue eq = (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY); 575 576 AWTAccessor.EventQueueAccessor accessor = AWTAccessor.getEventQueueAccessor(); 577 return accessor.isDispatchThreadImpl(eq); 578 } 579 580 @Override 581 public Dimension getScreenSize() { 582 return new Dimension(getScreenWidth(), getScreenHeight()); 583 } 584 protected abstract int getScreenWidth(); 585 protected abstract int getScreenHeight(); 586 587 @Override 588 @SuppressWarnings("deprecation") 589 public FontMetrics getFontMetrics(Font font) { 590 return FontDesignMetrics.getMetrics(font); 591 } 592 593 @Override 594 @SuppressWarnings("deprecation") 595 public String[] getFontList() { 596 String[] hardwiredFontList = { 597 Font.DIALOG, Font.SANS_SERIF, Font.SERIF, Font.MONOSPACED, 598 Font.DIALOG_INPUT 599 600 // -- Obsolete font names from 1.0.2. It was decided that 601 // -- getFontList should not return these old names: 602 // "Helvetica", "TimesRoman", "Courier", "ZapfDingbats" 603 }; 604 return hardwiredFontList; 605 } 606 607 /** 608 * Disables erasing of background on the canvas before painting if 609 * this is supported by the current toolkit. It is recommended to 610 * call this method early, before the Canvas becomes displayable, 611 * because some Toolkit implementations do not support changing 612 * this property once the Canvas becomes displayable. 613 */ 614 public void disableBackgroundErase(Canvas canvas) { 615 disableBackgroundEraseImpl(canvas); 616 } 617 618 /** 619 * Disables the native erasing of the background on the given 620 * component before painting if this is supported by the current 621 * toolkit. This only has an effect for certain components such as 622 * Canvas, Panel and Window. It is recommended to call this method 623 * early, before the Component becomes displayable, because some 624 * Toolkit implementations do not support changing this property 625 * once the Component becomes displayable. 626 */ 627 public void disableBackgroundErase(Component component) { 628 disableBackgroundEraseImpl(component); 629 } 630 631 private void disableBackgroundEraseImpl(Component component) { 632 AWTAccessor.getComponentAccessor().setBackgroundEraseDisabled(component, true); 633 } 634 635 /** 636 * Returns the value of "sun.awt.noerasebackground" property. Default 637 * value is {@code false}. 638 */ 639 public static boolean getSunAwtNoerasebackground() { 640 return AccessController.doPrivileged(new GetBooleanAction("sun.awt.noerasebackground")); 641 } 642 643 /** 644 * Returns the value of "sun.awt.erasebackgroundonresize" property. Default 645 * value is {@code false}. 646 */ 647 public static boolean getSunAwtErasebackgroundonresize() { 648 return AccessController.doPrivileged(new GetBooleanAction("sun.awt.erasebackgroundonresize")); 649 } 650 651 652 @SuppressWarnings("deprecation") 653 static final SoftCache fileImgCache = new SoftCache(); 654 655 @SuppressWarnings("deprecation") 656 static final SoftCache urlImgCache = new SoftCache(); 657 658 static Image getImageFromHash(Toolkit tk, URL url) { 659 checkPermissions(url); 660 synchronized (urlImgCache) { 661 String key = url.toString(); 662 Image img = (Image)urlImgCache.get(key); 663 if (img == null) { 664 try { 665 img = tk.createImage(new URLImageSource(url)); 666 urlImgCache.put(key, img); 667 } catch (Exception e) { 668 } 669 } 670 return img; 671 } 672 } 673 674 static Image getImageFromHash(Toolkit tk, 675 String filename) { 676 checkPermissions(filename); 677 synchronized (fileImgCache) { 678 Image img = (Image)fileImgCache.get(filename); 679 if (img == null) { 680 try { 681 img = tk.createImage(new FileImageSource(filename)); 682 fileImgCache.put(filename, img); 683 } catch (Exception e) { 684 } 685 } 686 return img; 687 } 688 } 689 690 @Override 691 public Image getImage(String filename) { 692 return getImageFromHash(this, filename); 693 } 694 695 @Override 696 public Image getImage(URL url) { 697 return getImageFromHash(this, url); 698 } 699 700 protected Image getImageWithResolutionVariant(String fileName, 701 String resolutionVariantName) { 702 synchronized (fileImgCache) { 703 Image image = getImageFromHash(this, fileName); 704 if (image instanceof MultiResolutionImage) { 705 return image; 706 } 707 Image resolutionVariant = getImageFromHash(this, resolutionVariantName); 708 image = createImageWithResolutionVariant(image, resolutionVariant); 709 fileImgCache.put(fileName, image); 710 return image; 711 } 712 } 713 714 protected Image getImageWithResolutionVariant(URL url, 715 URL resolutionVariantURL) { 716 synchronized (urlImgCache) { 717 Image image = getImageFromHash(this, url); 718 if (image instanceof MultiResolutionImage) { 719 return image; 720 } 721 Image resolutionVariant = getImageFromHash(this, resolutionVariantURL); 722 image = createImageWithResolutionVariant(image, resolutionVariant); 723 String key = url.toString(); 724 urlImgCache.put(key, image); 725 return image; 726 } 727 } 728 729 730 @Override 731 public Image createImage(String filename) { 732 checkPermissions(filename); 733 return createImage(new FileImageSource(filename)); 734 } 735 736 @Override 737 public Image createImage(URL url) { 738 checkPermissions(url); 739 return createImage(new URLImageSource(url)); 740 } 741 742 @Override 743 public Image createImage(byte[] data, int offset, int length) { 744 return createImage(new ByteArrayImageSource(data, offset, length)); 745 } 746 747 @Override 748 public Image createImage(ImageProducer producer) { 749 return new ToolkitImage(producer); 750 } 751 752 public static Image createImageWithResolutionVariant(Image image, 753 Image resolutionVariant) { 754 return new MultiResolutionToolkitImage(image, resolutionVariant); 755 } 756 757 @Override 758 public int checkImage(Image img, int w, int h, ImageObserver o) { 759 if (!(img instanceof ToolkitImage)) { 760 return ImageObserver.ALLBITS; 761 } 762 763 ToolkitImage tkimg = (ToolkitImage)img; 764 int repbits; 765 if (w == 0 || h == 0) { 766 repbits = ImageObserver.ALLBITS; 767 } else { 768 repbits = tkimg.getImageRep().check(o); 769 } 770 return (tkimg.check(o) | repbits) & checkResolutionVariant(img, w, h, o); 771 } 772 773 @Override 774 public boolean prepareImage(Image img, int w, int h, ImageObserver o) { 775 if (w == 0 || h == 0) { 776 return true; 777 } 778 779 // Must be a ToolkitImage 780 if (!(img instanceof ToolkitImage)) { 781 return true; 782 } 783 784 ToolkitImage tkimg = (ToolkitImage)img; 785 if (tkimg.hasError()) { 786 if (o != null) { 787 o.imageUpdate(img, ImageObserver.ERROR|ImageObserver.ABORT, 788 -1, -1, -1, -1); 789 } 790 return false; 791 } 792 ImageRepresentation ir = tkimg.getImageRep(); 793 return ir.prepare(o) & prepareResolutionVariant(img, w, h, o); 794 } 795 796 private int checkResolutionVariant(Image img, int w, int h, ImageObserver o) { 797 ToolkitImage rvImage = getResolutionVariant(img); 798 int rvw = getRVSize(w); 799 int rvh = getRVSize(h); 800 // Ignore the resolution variant in case of error 801 return (rvImage == null || rvImage.hasError()) ? 0xFFFF : 802 checkImage(rvImage, rvw, rvh, MultiResolutionToolkitImage. 803 getResolutionVariantObserver( 804 img, o, w, h, rvw, rvh, true)); 805 } 806 807 private boolean prepareResolutionVariant(Image img, int w, int h, 808 ImageObserver o) { 809 810 ToolkitImage rvImage = getResolutionVariant(img); 811 int rvw = getRVSize(w); 812 int rvh = getRVSize(h); 813 // Ignore the resolution variant in case of error 814 return rvImage == null || rvImage.hasError() || prepareImage( 815 rvImage, rvw, rvh, 816 MultiResolutionToolkitImage.getResolutionVariantObserver( 817 img, o, w, h, rvw, rvh, true)); 818 } 819 820 private static int getRVSize(int size){ 821 return size == -1 ? -1 : 2 * size; 822 } 823 824 private static ToolkitImage getResolutionVariant(Image image) { 825 if (image instanceof MultiResolutionToolkitImage) { 826 Image resolutionVariant = ((MultiResolutionToolkitImage) image). 827 getResolutionVariant(); 828 if (resolutionVariant instanceof ToolkitImage) { 829 return (ToolkitImage) resolutionVariant; 830 } 831 } 832 return null; 833 } 834 835 protected static boolean imageCached(String fileName) { 836 return fileImgCache.containsKey(fileName); 837 } 838 839 protected static boolean imageCached(URL url) { 840 String key = url.toString(); 841 return urlImgCache.containsKey(key); 842 } 843 844 protected static boolean imageExists(String filename) { 845 if (filename != null) { 846 checkPermissions(filename); 847 return new File(filename).exists(); 848 } 849 return false; 850 } 851 852 @SuppressWarnings("try") 853 protected static boolean imageExists(URL url) { 854 if (url != null) { 855 checkPermissions(url); 856 try (InputStream is = url.openStream()) { 857 return true; 858 }catch(IOException e){ 859 return false; 860 } 861 } 862 return false; 863 } 864 865 private static void checkPermissions(String filename) { 866 SecurityManager security = System.getSecurityManager(); 867 if (security != null) { 868 security.checkRead(filename); 869 } 870 } 871 872 private static void checkPermissions(URL url) { 873 SecurityManager sm = System.getSecurityManager(); 874 if (sm != null) { 875 try { 876 java.security.Permission perm = 877 URLUtil.getConnectPermission(url); 878 if (perm != null) { 879 try { 880 sm.checkPermission(perm); 881 } catch (SecurityException se) { 882 // fallback to checkRead/checkConnect for pre 1.2 883 // security managers 884 if ((perm instanceof java.io.FilePermission) && 885 perm.getActions().indexOf("read") != -1) { 886 sm.checkRead(perm.getName()); 887 } else if ((perm instanceof 888 java.net.SocketPermission) && 889 perm.getActions().indexOf("connect") != -1) { 890 sm.checkConnect(url.getHost(), url.getPort()); 891 } else { 892 throw se; 893 } 894 } 895 } 896 } catch (java.io.IOException ioe) { 897 sm.checkConnect(url.getHost(), url.getPort()); 898 } 899 } 900 } 901 902 /** 903 * Scans {@code imageList} for best-looking image of specified dimensions. 904 * Image can be scaled and/or padded with transparency. 905 */ 906 public static BufferedImage getScaledIconImage(java.util.List<Image> imageList, int width, int height) { 907 if (width == 0 || height == 0) { 908 return null; 909 } 910 Image bestImage = null; 911 int bestWidth = 0; 912 int bestHeight = 0; 913 double bestSimilarity = 3; //Impossibly high value 914 double bestScaleFactor = 0; 915 for (Iterator<Image> i = imageList.iterator();i.hasNext();) { 916 //Iterate imageList looking for best matching image. 917 //'Similarity' measure is defined as good scale factor and small insets. 918 //best possible similarity is 0 (no scale, no insets). 919 //It's found while the experiments that good-looking result is achieved 920 //with scale factors x1, x3/4, x2/3, xN, x1/N. 921 Image im = i.next(); 922 if (im == null) { 923 continue; 924 } 925 if (im instanceof ToolkitImage) { 926 ImageRepresentation ir = ((ToolkitImage)im).getImageRep(); 927 ir.reconstruct(ImageObserver.ALLBITS); 928 } 929 int iw; 930 int ih; 931 try { 932 iw = im.getWidth(null); 933 ih = im.getHeight(null); 934 } catch (Exception e){ 935 continue; 936 } 937 if (iw > 0 && ih > 0) { 938 //Calc scale factor 939 double scaleFactor = Math.min((double)width / (double)iw, 940 (double)height / (double)ih); 941 //Calculate scaled image dimensions 942 //adjusting scale factor to nearest "good" value 943 int adjw = 0; 944 int adjh = 0; 945 double scaleMeasure = 1; //0 - best (no) scale, 1 - impossibly bad 946 if (scaleFactor >= 2) { 947 //Need to enlarge image more than twice 948 //Round down scale factor to multiply by integer value 949 scaleFactor = Math.floor(scaleFactor); 950 adjw = iw * (int)scaleFactor; 951 adjh = ih * (int)scaleFactor; 952 scaleMeasure = 1.0 - 0.5 / scaleFactor; 953 } else if (scaleFactor >= 1) { 954 //Don't scale 955 scaleFactor = 1.0; 956 adjw = iw; 957 adjh = ih; 958 scaleMeasure = 0; 959 } else if (scaleFactor >= 0.75) { 960 //Multiply by 3/4 961 scaleFactor = 0.75; 962 adjw = iw * 3 / 4; 963 adjh = ih * 3 / 4; 964 scaleMeasure = 0.3; 965 } else if (scaleFactor >= 0.6666) { 966 //Multiply by 2/3 967 scaleFactor = 0.6666; 968 adjw = iw * 2 / 3; 969 adjh = ih * 2 / 3; 970 scaleMeasure = 0.33; 971 } else { 972 //Multiply size by 1/scaleDivider 973 //where scaleDivider is minimum possible integer 974 //larger than 1/scaleFactor 975 double scaleDivider = Math.ceil(1.0 / scaleFactor); 976 scaleFactor = 1.0 / scaleDivider; 977 adjw = (int)Math.round((double)iw / scaleDivider); 978 adjh = (int)Math.round((double)ih / scaleDivider); 979 scaleMeasure = 1.0 - 1.0 / scaleDivider; 980 } 981 double similarity = ((double)width - (double)adjw) / (double)width + 982 ((double)height - (double)adjh) / (double)height + //Large padding is bad 983 scaleMeasure; //Large rescale is bad 984 if (similarity < bestSimilarity) { 985 bestSimilarity = similarity; 986 bestScaleFactor = scaleFactor; 987 bestImage = im; 988 bestWidth = adjw; 989 bestHeight = adjh; 990 } 991 if (similarity == 0) break; 992 } 993 } 994 if (bestImage == null) { 995 //No images were found, possibly all are broken 996 return null; 997 } 998 BufferedImage bimage = 999 new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 1000 Graphics2D g = bimage.createGraphics(); 1001 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 1002 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 1003 try { 1004 int x = (width - bestWidth) / 2; 1005 int y = (height - bestHeight) / 2; 1006 g.drawImage(bestImage, x, y, bestWidth, bestHeight, null); 1007 } finally { 1008 g.dispose(); 1009 } 1010 return bimage; 1011 } 1012 1013 public static DataBufferInt getScaledIconData(java.util.List<Image> imageList, int width, int height) { 1014 BufferedImage bimage = getScaledIconImage(imageList, width, height); 1015 if (bimage == null) { 1016 return null; 1017 } 1018 Raster raster = bimage.getRaster(); 1019 DataBuffer buffer = raster.getDataBuffer(); 1020 return (DataBufferInt)buffer; 1021 } 1022 1023 @Override 1024 protected EventQueue getSystemEventQueueImpl() { 1025 return getSystemEventQueueImplPP(); 1026 } 1027 1028 // Package private implementation 1029 static EventQueue getSystemEventQueueImplPP() { 1030 return getSystemEventQueueImplPP(AppContext.getAppContext()); 1031 } 1032 1033 public static EventQueue getSystemEventQueueImplPP(AppContext appContext) { 1034 EventQueue theEventQueue = 1035 (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY); 1036 return theEventQueue; 1037 } 1038 1039 /** 1040 * Give native peers the ability to query the native container 1041 * given a native component (eg the direct parent may be lightweight). 1042 */ 1043 public static Container getNativeContainer(Component c) { 1044 return Toolkit.getNativeContainer(c); 1045 } 1046 1047 /** 1048 * Gives native peers the ability to query the closest HW component. 1049 * If the given component is heavyweight, then it returns this. Otherwise, 1050 * it goes one level up in the hierarchy and tests next component. 1051 */ 1052 public static Component getHeavyweightComponent(Component c) { 1053 while (c != null && AWTAccessor.getComponentAccessor().isLightweight(c)) { 1054 c = AWTAccessor.getComponentAccessor().getParent(c); 1055 } 1056 return c; 1057 } 1058 1059 /** 1060 * Returns key modifiers used by Swing to set up a focus accelerator key stroke. 1061 */ 1062 public int getFocusAcceleratorKeyMask() { 1063 return InputEvent.ALT_MASK; 1064 } 1065 1066 /** 1067 * Tests whether specified key modifiers mask can be used to enter a printable 1068 * character. This is a default implementation of this method, which reflects 1069 * the way things work on Windows: here, pressing ctrl + alt allows user to enter 1070 * characters from the extended character set (like euro sign or math symbols) 1071 */ 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 = false; 1526 private boolean queueEmpty = false; 1527 private final Object waitLock = "Wait Lock"; 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 boolean queueWasEmpty = isEQEmpty(); 1545 queueEmpty = false; 1546 eventDispatched = false; 1547 synchronized(waitLock) { 1548 postEvent(AppContext.getAppContext(), 1549 new PeerEvent(getSystemEventQueueImpl(), null, PeerEvent.LOW_PRIORITY_EVENT) { 1550 @Override 1551 public void dispatch() { 1552 // Here we block EDT. It could have some 1553 // events, it should have dispatched them by 1554 // now. So native requests could have been 1555 // generated. First, dispatch them. Then, 1556 // flush Java events again. 1557 int iters = 0; 1558 while (iters < MIN_ITERS) { 1559 syncNativeQueue(timeout); 1560 iters++; 1561 } 1562 while (syncNativeQueue(timeout) && iters < MAX_ITERS) { 1563 iters++; 1564 } 1565 flushPendingEvents(); 1566 1567 synchronized(waitLock) { 1568 queueEmpty = isEQEmpty(); 1569 eventDispatched = true; 1570 waitLock.notifyAll(); 1571 } 1572 } 1573 }); 1574 try { 1575 while (!eventDispatched) { 1576 waitLock.wait(); 1577 } 1578 } catch (InterruptedException ie) { 1579 return false; 1580 } 1581 } 1582 1583 try { 1584 Thread.sleep(MINIMAL_EDELAY); 1585 } catch (InterruptedException ie) { 1586 throw new RuntimeException("Interrupted"); 1587 } 1588 1589 flushPendingEvents(); 1590 1591 // Lock to force write-cache flush for queueEmpty. 1592 synchronized (waitLock) { 1593 return !(queueEmpty && isEQEmpty() && queueWasEmpty); 1594 } 1595 } 1596 1597 /** 1598 * Grabs the mouse input for the given window. The window must be 1599 * visible. The window or its children do not receive any 1600 * additional mouse events besides those targeted to them. All 1601 * other events will be dispatched as before - to the respective 1602 * targets. This Window will receive UngrabEvent when automatic 1603 * ungrab is about to happen. The event can be listened to by 1604 * installing AWTEventListener with WINDOW_EVENT_MASK. See 1605 * UngrabEvent class for the list of conditions when ungrab is 1606 * about to happen. 1607 * @see UngrabEvent 1608 */ 1609 public abstract void grab(Window w); 1610 1611 /** 1612 * Forces ungrab. No event will be sent. 1613 */ 1614 public abstract void ungrab(Window w); 1615 1616 1617 /** 1618 * Locates the splash screen library in a platform dependent way and closes 1619 * the splash screen. Should be invoked on first top-level frame display. 1620 * @see java.awt.SplashScreen 1621 * @since 1.6 1622 */ 1623 public static native void closeSplashScreen(); 1624 1625 /* The following methods and variables are to support retrieving 1626 * desktop text anti-aliasing settings 1627 */ 1628 1629 /* Need an instance method because setDesktopProperty(..) is protected. */ 1630 private void fireDesktopFontPropertyChanges() { 1631 setDesktopProperty(SunToolkit.DESKTOPFONTHINTS, 1632 SunToolkit.getDesktopFontHints()); 1633 } 1634 1635 private static boolean checkedSystemAAFontSettings; 1636 private static boolean useSystemAAFontSettings; 1637 private static boolean lastExtraCondition = true; 1638 private static RenderingHints desktopFontHints; 1639 1640 /* Since Swing is the reason for this "extra condition" logic its 1641 * worth documenting it in some detail. 1642 * First, a goal is for Swing and applications to both retrieve and 1643 * use the same desktop property value so that there is complete 1644 * consistency between the settings used by JDK's Swing implementation 1645 * and 3rd party custom Swing components, custom L&Fs and any general 1646 * text rendering that wants to be consistent with these. 1647 * But by default on Solaris & Linux Swing will not use AA text over 1648 * remote X11 display (unless Xrender can be used which is TBD and may not 1649 * always be available anyway) as that is a noticeable performance hit. 1650 * So there needs to be a way to express that extra condition so that 1651 * it is seen by all clients of the desktop property API. 1652 * If this were the only condition it could be handled here as it would 1653 * be the same for any L&F and could reasonably be considered to be 1654 * a static behaviour of those systems. 1655 * But GTK currently has an additional test based on locale which is 1656 * not applied by Metal. So mixing GTK in a few locales with Metal 1657 * would mean the last one wins. 1658 * This could be stored per-app context which would work 1659 * for different applets, but wouldn't help for a single application 1660 * using GTK and some other L&F concurrently. 1661 * But it is expected this will be addressed within GTK and the font 1662 * system so is a temporary and somewhat unlikely harmless corner case. 1663 */ 1664 public static void setAAFontSettingsCondition(boolean extraCondition) { 1665 if (extraCondition != lastExtraCondition) { 1666 lastExtraCondition = extraCondition; 1667 if (checkedSystemAAFontSettings) { 1668 /* Someone already asked for this info, under a different 1669 * condition. 1670 * We'll force re-evaluation instead of replicating the 1671 * logic, then notify any listeners of any change. 1672 */ 1673 checkedSystemAAFontSettings = false; 1674 Toolkit tk = Toolkit.getDefaultToolkit(); 1675 if (tk instanceof SunToolkit) { 1676 ((SunToolkit)tk).fireDesktopFontPropertyChanges(); 1677 } 1678 } 1679 } 1680 } 1681 1682 /* "false", "off", ""default" aren't explicitly tested, they 1683 * just fall through to produce a null return which all are equated to 1684 * "false". 1685 */ 1686 private static RenderingHints getDesktopAAHintsByName(String hintname) { 1687 Object aaHint = null; 1688 hintname = hintname.toLowerCase(Locale.ENGLISH); 1689 if (hintname.equals("on")) { 1690 aaHint = VALUE_TEXT_ANTIALIAS_ON; 1691 } else if (hintname.equals("gasp")) { 1692 aaHint = VALUE_TEXT_ANTIALIAS_GASP; 1693 } else if (hintname.equals("lcd") || hintname.equals("lcd_hrgb")) { 1694 aaHint = VALUE_TEXT_ANTIALIAS_LCD_HRGB; 1695 } else if (hintname.equals("lcd_hbgr")) { 1696 aaHint = VALUE_TEXT_ANTIALIAS_LCD_HBGR; 1697 } else if (hintname.equals("lcd_vrgb")) { 1698 aaHint = VALUE_TEXT_ANTIALIAS_LCD_VRGB; 1699 } else if (hintname.equals("lcd_vbgr")) { 1700 aaHint = VALUE_TEXT_ANTIALIAS_LCD_VBGR; 1701 } 1702 if (aaHint != null) { 1703 RenderingHints map = new RenderingHints(null); 1704 map.put(KEY_TEXT_ANTIALIASING, aaHint); 1705 return map; 1706 } else { 1707 return null; 1708 } 1709 } 1710 1711 /* This method determines whether to use the system font settings, 1712 * or ignore them if a L&F has specified they should be ignored, or 1713 * to override both of these with a system property specified value. 1714 * If the toolkit isn't a SunToolkit, (eg may be headless) then that 1715 * system property isn't applied as desktop properties are considered 1716 * to be inapplicable in that case. In that headless case although 1717 * this method will return "true" the toolkit will return a null map. 1718 */ 1719 private static boolean useSystemAAFontSettings() { 1720 if (!checkedSystemAAFontSettings) { 1721 useSystemAAFontSettings = true; /* initially set this true */ 1722 String systemAAFonts = null; 1723 Toolkit tk = Toolkit.getDefaultToolkit(); 1724 if (tk instanceof SunToolkit) { 1725 systemAAFonts = 1726 AccessController.doPrivileged( 1727 new GetPropertyAction("awt.useSystemAAFontSettings")); 1728 } 1729 if (systemAAFonts != null) { 1730 useSystemAAFontSettings = 1731 Boolean.valueOf(systemAAFonts).booleanValue(); 1732 /* If it is anything other than "true", then it may be 1733 * a hint name , or it may be "off, "default", etc. 1734 */ 1735 if (!useSystemAAFontSettings) { 1736 desktopFontHints = getDesktopAAHintsByName(systemAAFonts); 1737 } 1738 } 1739 /* If its still true, apply the extra condition */ 1740 if (useSystemAAFontSettings) { 1741 useSystemAAFontSettings = lastExtraCondition; 1742 } 1743 checkedSystemAAFontSettings = true; 1744 } 1745 return useSystemAAFontSettings; 1746 } 1747 1748 /* A variable defined for the convenience of JDK code */ 1749 public static final String DESKTOPFONTHINTS = "awt.font.desktophints"; 1750 1751 /* Overridden by subclasses to return platform/desktop specific values */ 1752 protected RenderingHints getDesktopAAHints() { 1753 return null; 1754 } 1755 1756 /* Subclass desktop property loading methods call this which 1757 * in turn calls the appropriate subclass implementation of 1758 * getDesktopAAHints() when system settings are being used. 1759 * Its public rather than protected because subclasses may delegate 1760 * to a helper class. 1761 */ 1762 public static RenderingHints getDesktopFontHints() { 1763 if (useSystemAAFontSettings()) { 1764 Toolkit tk = Toolkit.getDefaultToolkit(); 1765 if (tk instanceof SunToolkit) { 1766 Object map = ((SunToolkit)tk).getDesktopAAHints(); 1767 return (RenderingHints)map; 1768 } else { /* Headless Toolkit */ 1769 return null; 1770 } 1771 } else if (desktopFontHints != null) { 1772 /* cloning not necessary as the return value is cloned later, but 1773 * its harmless. 1774 */ 1775 return (RenderingHints)(desktopFontHints.clone()); 1776 } else { 1777 return null; 1778 } 1779 } 1780 1781 1782 public abstract boolean isDesktopSupported(); 1783 public abstract boolean isTaskbarSupported(); 1784 1785 /* 1786 * consumeNextKeyTyped() method is not currently used, 1787 * however Swing could use it in the future. 1788 */ 1789 public static synchronized void consumeNextKeyTyped(KeyEvent keyEvent) { 1790 try { 1791 AWTAccessor.getDefaultKeyboardFocusManagerAccessor().consumeNextKeyTyped( 1792 (DefaultKeyboardFocusManager)KeyboardFocusManager. 1793 getCurrentKeyboardFocusManager(), 1794 keyEvent); 1795 } catch (ClassCastException cce) { 1796 cce.printStackTrace(); 1797 } 1798 } 1799 1800 protected static void dumpPeers(final PlatformLogger aLog) { 1801 AWTAutoShutdown.getInstance().dumpPeers(aLog); 1802 } 1803 1804 /** 1805 * Returns the {@code Window} ancestor of the component {@code comp}. 1806 * @return Window ancestor of the component or component by itself if it is Window; 1807 * null, if component is not a part of window hierarchy 1808 */ 1809 public static Window getContainingWindow(Component comp) { 1810 while (comp != null && !(comp instanceof Window)) { 1811 comp = comp.getParent(); 1812 } 1813 return (Window)comp; 1814 } 1815 1816 private static Boolean sunAwtDisableMixing = null; 1817 1818 /** 1819 * Returns the value of "sun.awt.disableMixing" property. Default 1820 * value is {@code false}. 1821 */ 1822 public static synchronized boolean getSunAwtDisableMixing() { 1823 if (sunAwtDisableMixing == null) { 1824 sunAwtDisableMixing = AccessController.doPrivileged( 1825 new GetBooleanAction("sun.awt.disableMixing")); 1826 } 1827 return sunAwtDisableMixing.booleanValue(); 1828 } 1829 1830 /** 1831 * Returns true if the native GTK libraries are available. The 1832 * default implementation returns false, but UNIXToolkit overrides this 1833 * method to provide a more specific answer. 1834 */ 1835 public boolean isNativeGTKAvailable() { 1836 return false; 1837 } 1838 1839 private static final Object DEACTIVATION_TIMES_MAP_KEY = new Object(); 1840 1841 public synchronized void setWindowDeactivationTime(Window w, long time) { 1842 AppContext ctx = getAppContext(w); 1843 if (ctx == null) { 1844 return; 1845 } 1846 @SuppressWarnings("unchecked") 1847 WeakHashMap<Window, Long> map = (WeakHashMap<Window, Long>)ctx.get(DEACTIVATION_TIMES_MAP_KEY); 1848 if (map == null) { 1849 map = new WeakHashMap<Window, Long>(); 1850 ctx.put(DEACTIVATION_TIMES_MAP_KEY, map); 1851 } 1852 map.put(w, time); 1853 } 1854 1855 public synchronized long getWindowDeactivationTime(Window w) { 1856 AppContext ctx = getAppContext(w); 1857 if (ctx == null) { 1858 return -1; 1859 } 1860 @SuppressWarnings("unchecked") 1861 WeakHashMap<Window, Long> map = (WeakHashMap<Window, Long>)ctx.get(DEACTIVATION_TIMES_MAP_KEY); 1862 if (map == null) { 1863 return -1; 1864 } 1865 Long time = map.get(w); 1866 return time == null ? -1 : time; 1867 } 1868 1869 // Cosntant alpha 1870 public boolean isWindowOpacitySupported() { 1871 return false; 1872 } 1873 1874 // Shaping 1875 public boolean isWindowShapingSupported() { 1876 return false; 1877 } 1878 1879 // Per-pixel alpha 1880 public boolean isWindowTranslucencySupported() { 1881 return false; 1882 } 1883 1884 public boolean isTranslucencyCapable(GraphicsConfiguration gc) { 1885 return false; 1886 } 1887 1888 /** 1889 * Returns true if swing backbuffer should be translucent. 1890 */ 1891 public boolean isSwingBackbufferTranslucencySupported() { 1892 return false; 1893 } 1894 1895 /** 1896 * Returns whether or not a containing top level window for the passed 1897 * component is 1898 * {@link GraphicsDevice.WindowTranslucency#PERPIXEL_TRANSLUCENT PERPIXEL_TRANSLUCENT}. 1899 * 1900 * @param c a Component which toplevel's to check 1901 * @return {@code true} if the passed component is not null and has a 1902 * containing toplevel window which is opaque (so per-pixel translucency 1903 * is not enabled), {@code false} otherwise 1904 * @see GraphicsDevice.WindowTranslucency#PERPIXEL_TRANSLUCENT 1905 */ 1906 public static boolean isContainingTopLevelOpaque(Component c) { 1907 Window w = getContainingWindow(c); 1908 return w != null && w.isOpaque(); 1909 } 1910 1911 /** 1912 * Returns whether or not a containing top level window for the passed 1913 * component is 1914 * {@link GraphicsDevice.WindowTranslucency#TRANSLUCENT TRANSLUCENT}. 1915 * 1916 * @param c a Component which toplevel's to check 1917 * @return {@code true} if the passed component is not null and has a 1918 * containing toplevel window which has opacity less than 1919 * 1.0f (which means that it is translucent), {@code false} otherwise 1920 * @see GraphicsDevice.WindowTranslucency#TRANSLUCENT 1921 */ 1922 public static boolean isContainingTopLevelTranslucent(Component c) { 1923 Window w = getContainingWindow(c); 1924 return w != null && w.getOpacity() < 1.0f; 1925 } 1926 1927 /** 1928 * Returns whether the native system requires using the peer.updateWindow() 1929 * method to update the contents of a non-opaque window, or if usual 1930 * painting procedures are sufficient. The default return value covers 1931 * the X11 systems. On MS Windows this method is overriden in WToolkit 1932 * to return true. 1933 */ 1934 public boolean needUpdateWindow() { 1935 return false; 1936 } 1937 1938 /** 1939 * Descendants of the SunToolkit should override and put their own logic here. 1940 */ 1941 public int getNumberOfButtons(){ 1942 return 3; 1943 } 1944 1945 /** 1946 * Checks that the given object implements/extends the given 1947 * interface/class. 1948 * 1949 * Note that using the instanceof operator causes a class to be loaded. 1950 * Using this method doesn't load a class and it can be used instead of 1951 * the instanceof operator for performance reasons. 1952 * 1953 * @param obj Object to be checked 1954 * @param type The name of the interface/class. Must be 1955 * fully-qualified interface/class name. 1956 * @return true, if this object implements/extends the given 1957 * interface/class, false, otherwise, or if obj or type is null 1958 */ 1959 public static boolean isInstanceOf(Object obj, String type) { 1960 if (obj == null) return false; 1961 if (type == null) return false; 1962 1963 return isInstanceOf(obj.getClass(), type); 1964 } 1965 1966 private static boolean isInstanceOf(Class<?> cls, String type) { 1967 if (cls == null) return false; 1968 1969 if (cls.getName().equals(type)) { 1970 return true; 1971 } 1972 1973 for (Class<?> c : cls.getInterfaces()) { 1974 if (c.getName().equals(type)) { 1975 return true; 1976 } 1977 } 1978 return isInstanceOf(cls.getSuperclass(), type); 1979 } 1980 1981 protected static LightweightFrame getLightweightFrame(Component c) { 1982 for (; c != null; c = c.getParent()) { 1983 if (c instanceof LightweightFrame) { 1984 return (LightweightFrame)c; 1985 } 1986 if (c instanceof Window) { 1987 // Don't traverse owner windows 1988 return null; 1989 } 1990 } 1991 return null; 1992 } 1993 1994 /////////////////////////////////////////////////////////////////////////// 1995 // 1996 // The following methods help set and identify whether a particular 1997 // AWTEvent object was produced by the system or by user code. As of this 1998 // writing the only consumer is the Java Plug-In, although this information 1999 // could be useful to more clients and probably should be formalized in 2000 // the public API. 2001 // 2002 /////////////////////////////////////////////////////////////////////////// 2003 2004 public static void setSystemGenerated(AWTEvent e) { 2005 AWTAccessor.getAWTEventAccessor().setSystemGenerated(e); 2006 } 2007 2008 public static boolean isSystemGenerated(AWTEvent e) { 2009 return AWTAccessor.getAWTEventAccessor().isSystemGenerated(e); 2010 } 2011 2012 } // class SunToolkit 2013 2014 2015 /* 2016 * PostEventQueue is a Thread that runs in the same AppContext as the 2017 * Java EventQueue. It is a queue of AWTEvents to be posted to the 2018 * Java EventQueue. The toolkit Thread (AWT-Windows/AWT-Motif) posts 2019 * events to this queue, which then calls EventQueue.postEvent(). 2020 * 2021 * We do this because EventQueue.postEvent() may be overridden by client 2022 * code, and we mustn't ever call client code from the toolkit thread. 2023 */ 2024 class PostEventQueue { 2025 private EventQueueItem queueHead = null; 2026 private EventQueueItem queueTail = null; 2027 private final EventQueue eventQueue; 2028 2029 private Thread flushThread = null; 2030 2031 PostEventQueue(EventQueue eq) { 2032 eventQueue = eq; 2033 } 2034 2035 /* 2036 * Continually post pending AWTEvents to the Java EventQueue. The method 2037 * is synchronized to ensure the flush is completed before a new event 2038 * can be posted to this queue. 2039 * 2040 * 7177040: The method couldn't be wholly synchronized because of calls 2041 * of EventQueue.postEvent() that uses pushPopLock, otherwise it could 2042 * potentially lead to deadlock 2043 */ 2044 public void flush() { 2045 2046 Thread newThread = Thread.currentThread(); 2047 2048 try { 2049 EventQueueItem tempQueue; 2050 synchronized (this) { 2051 // Avoid method recursion 2052 if (newThread == flushThread) { 2053 return; 2054 } 2055 // Wait for other threads' flushing 2056 while (flushThread != null) { 2057 wait(); 2058 } 2059 // Skip everything if queue is empty 2060 if (queueHead == null) { 2061 return; 2062 } 2063 // Remember flushing thread 2064 flushThread = newThread; 2065 2066 tempQueue = queueHead; 2067 queueHead = queueTail = null; 2068 } 2069 try { 2070 while (tempQueue != null) { 2071 eventQueue.postEvent(tempQueue.event); 2072 tempQueue = tempQueue.next; 2073 } 2074 } 2075 finally { 2076 // Only the flushing thread can get here 2077 synchronized (this) { 2078 // Forget flushing thread, inform other pending threads 2079 flushThread = null; 2080 notifyAll(); 2081 } 2082 } 2083 } 2084 catch (InterruptedException e) { 2085 // Couldn't allow exception go up, so at least recover the flag 2086 newThread.interrupt(); 2087 } 2088 } 2089 2090 /* 2091 * Enqueue an AWTEvent to be posted to the Java EventQueue. 2092 */ 2093 void postEvent(AWTEvent event) { 2094 EventQueueItem item = new EventQueueItem(event); 2095 2096 synchronized (this) { 2097 if (queueHead == null) { 2098 queueHead = queueTail = item; 2099 } else { 2100 queueTail.next = item; 2101 queueTail = item; 2102 } 2103 } 2104 SunToolkit.wakeupEventQueue(eventQueue, event.getSource() == AWTAutoShutdown.getInstance()); 2105 } 2106 } // class PostEventQueue