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
26package sun.awt;
27
28import java.awt.*;
29import java.awt.event.InputEvent;
30import java.awt.event.KeyEvent;
31import java.awt.event.WindowEvent;
32import java.awt.image.BufferedImage;
33import java.awt.image.ColorModel;
34import java.awt.image.DataBuffer;
35import java.awt.image.DataBufferInt;
36import java.awt.image.ImageObserver;
37import java.awt.image.ImageProducer;
38import java.awt.image.Raster;
39import java.awt.peer.FramePeer;
40import java.awt.peer.KeyboardFocusManagerPeer;
41import java.awt.peer.MouseInfoPeer;
42import java.awt.peer.SystemTrayPeer;
43import java.awt.peer.TrayIconPeer;
44import java.io.File;
45import java.io.IOException;
46import java.io.InputStream;
47import java.lang.reflect.InvocationTargetException;
48import java.net.URL;
49import java.security.AccessController;
50import java.util.ArrayList;
51import java.util.Collections;
52import java.util.Iterator;
53import java.util.Locale;
54import java.util.Map;
55import java.util.Vector;
56import java.util.WeakHashMap;
57import java.util.concurrent.TimeUnit;
58import java.util.concurrent.locks.Condition;
59import java.util.concurrent.locks.ReentrantLock;
60
61import sun.awt.im.InputContext;
62import sun.awt.image.ByteArrayImageSource;
63import sun.awt.image.FileImageSource;
64import sun.awt.image.ImageRepresentation;
65import java.awt.image.MultiResolutionImage;
66import sun.awt.image.MultiResolutionToolkitImage;
67import sun.awt.image.ToolkitImage;
68import sun.awt.image.URLImageSource;
69import sun.font.FontDesignMetrics;
70import sun.net.util.URLUtil;
71import sun.security.action.GetBooleanAction;
72import sun.security.action.GetPropertyAction;
73import sun.util.logging.PlatformLogger;
74
75import static java.awt.RenderingHints.*;
76
77public 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 */
2028class 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
2111