/* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt; import java.awt.AWTEvent; import java.awt.AWTException; import java.awt.Button; import java.awt.Canvas; import java.awt.Checkbox; import java.awt.Choice; import java.awt.Component; import java.awt.Container; import java.awt.DefaultKeyboardFocusManager; import java.awt.Dialog; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.FocusTraversalPolicy; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.Image; import java.awt.KeyboardFocusManager; import java.awt.Label; import java.awt.MenuComponent; import java.awt.Panel; import java.awt.RenderingHints; import java.awt.ScrollPane; import java.awt.Scrollbar; import java.awt.SystemTray; import java.awt.TextArea; import java.awt.TextField; import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.Window; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; import java.awt.image.ImageObserver; import java.awt.image.ImageProducer; import java.awt.image.MultiResolutionImage; import java.awt.image.Raster; import java.awt.peer.FramePeer; import java.awt.peer.KeyboardFocusManagerPeer; import java.awt.peer.SystemTrayPeer; import java.awt.peer.TrayIconPeer; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.Vector; import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import sun.awt.im.InputContext; import sun.awt.image.ByteArrayImageSource; import sun.awt.image.FileImageSource; import sun.awt.image.ImageRepresentation; import sun.awt.image.MultiResolutionToolkitImage; import sun.awt.image.ToolkitImage; import sun.awt.image.URLImageSource; import sun.font.FontDesignMetrics; import sun.util.logging.PlatformLogger; import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING; import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_GASP; import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR; import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB; import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR; import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB; import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON; public abstract class SunToolkit extends Toolkit implements ComponentFactory, InputMethodSupport, KeyboardFocusManagerPeerProvider { // 8014718: logging has been removed from SunToolkit /* Load debug settings for native code */ static { initStatic(); } private static void initStatic() { if (Boolean.getBoolean("sun.awt.nativedebug")) { DebugSettings.init(); } touchKeyboardAutoShowIsEnabled = Boolean.parseBoolean( System.getProperty("awt.touchKeyboardAutoShowIsEnabled", "true")); } /** * Special mask for the UngrabEvent events, in addition to the * public masks defined in AWTEvent. Should be used as the mask * value for Toolkit.addAWTEventListener. */ public static final int GRAB_EVENT_MASK = 0x80000000; /** * Number of buttons. * By default it's taken from the system. If system value does not * fit into int type range, use our own MAX_BUTTONS_SUPPORT value. */ protected static int numberOfButtons = 0; /* XFree standard mention 24 buttons as maximum: * http://www.xfree86.org/current/mouse.4.html * We workaround systems supporting more than 24 buttons. * Otherwise, we have to use long type values as masks * which leads to API change. * InputEvent.BUTTON_DOWN_MASK may contain only 21 masks due to * the 4-bytes limit for the int type. (CR 6799099) * One more bit is reserved for FIRST_HIGH_BIT. */ public static final int MAX_BUTTONS_SUPPORTED = 20; public static volatile EventQueue currentEventQueue; private static volatile PostEventQueue postEventQueue; /** * Creates and initializes EventQueue instance. */ private static synchronized void initEQ() { if (currentEventQueue == null) { currentEventQueue = new EventQueue(); postEventQueue = new PostEventQueue(currentEventQueue); } } public SunToolkit() { initEQ(); } public boolean useBufferPerWindow() { return false; } public abstract FramePeer createLightweightFrame(LightweightFrame target) throws HeadlessException; public abstract TrayIconPeer createTrayIcon(TrayIcon target) throws HeadlessException, AWTException; public abstract SystemTrayPeer createSystemTray(SystemTray target); public abstract boolean isTraySupported(); @Override public abstract KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() throws HeadlessException; /** * The AWT lock is typically only used on Unix platforms to synchronize * access to Xlib, OpenGL, etc. However, these methods are implemented * in SunToolkit so that they can be called from shared code (e.g. * from the OGL pipeline) or from the X11 pipeline regardless of whether * XToolkit is currently in use. There are native macros * (such as AWT_LOCK) defined in awt.h, so if the implementation of these * methods is changed, make sure it is compatible with the native macros. * * Note: The following methods (awtLock(), awtUnlock(), etc) should be * used in place of: * synchronized (getAWTLock()) { * ... * } * * By factoring these methods out specially, we are able to change the * implementation of these methods (e.g. use more advanced locking * mechanisms) without impacting calling code. * * Sample usage: * private void doStuffWithXlib() { * assert !SunToolkit.isAWTLockHeldByCurrentThread(); * SunToolkit.awtLock(); * try { * ... * XlibWrapper.XDoStuff(); * } finally { * SunToolkit.awtUnlock(); * } * } */ private static final ReentrantLock AWT_LOCK = new ReentrantLock( Boolean.getBoolean("awt.lock.fair")); private static final Condition AWT_LOCK_COND = AWT_LOCK.newCondition(); public static final void awtLock() { AWT_LOCK.lock(); } public static final boolean awtTryLock() { return AWT_LOCK.tryLock(); } public static final void awtUnlock() { AWT_LOCK.unlock(); } public static final void awtLockWait() throws InterruptedException { AWT_LOCK_COND.await(); } public static final void awtLockWait(long timeout) throws InterruptedException { AWT_LOCK_COND.await(timeout, TimeUnit.MILLISECONDS); } public static final void awtLockNotify() { AWT_LOCK_COND.signal(); } public static final void awtLockNotifyAll() { AWT_LOCK_COND.signalAll(); } public static final boolean isAWTLockHeldByCurrentThread() { return AWT_LOCK.isHeldByCurrentThread(); } static void wakeupEventQueue(EventQueue q, boolean isShutdown){ AWTAccessor.getEventQueueAccessor().wakeup(q, isShutdown); } /* * Fetch the peer associated with the given target (as specified * in the peer creation method). This can be used to determine * things like what the parent peer is. If the target is null * or the target can't be found (either because the peer was * never created for it or the peer was disposed), a null will * be returned. */ protected static Object targetToPeer(Object target) { if (target != null && !GraphicsEnvironment.isHeadless()) { return AWTAutoShutdown.getInstance().getPeer(target); } return null; } protected static void targetCreatedPeer(Object target, Object peer) { if (target != null && peer != null && !GraphicsEnvironment.isHeadless()) { AWTAutoShutdown.getInstance().registerPeer(target, peer); } } protected static void targetDisposedPeer(Object target, Object peer) { if (target != null && peer != null && !GraphicsEnvironment.isHeadless()) { AWTAutoShutdown.getInstance().unregisterPeer(target, peer); } } /** * Sets the synchronous status of focus requests on lightweight * components in the specified window to the specified value. * If the boolean parameter is {@code true} then the focus * requests on lightweight components will be performed * synchronously, if it is {@code false}, then asynchronously. * By default, all windows have their lightweight request status * set to asynchronous. *
* The application can only set the status of lightweight focus * requests to synchronous for any of its windows if it doesn't * perform focus transfers between different heavyweight containers. * In this case the observable focus behaviour is the same as with * asynchronous status. *
* If the application performs focus transfer between different * heavyweight containers and sets the lightweight focus request * status to synchronous for any of its windows, then further focus * behaviour is unspecified. *
* @param changed the window for which the lightweight focus request
* status should be set
* @param status the value of lightweight focus request status
*/
public static void setLWRequestStatus(Window changed,boolean status){
AWTAccessor.getWindowAccessor().setLWRequestStatus(changed, status);
}
public static void checkAndSetPolicy(Container cont) {
FocusTraversalPolicy defaultPolicy = KeyboardFocusManager.
getCurrentKeyboardFocusManager().
getDefaultFocusTraversalPolicy();
cont.setFocusTraversalPolicy(defaultPolicy);
}
/*
* Post an AWTEvent to the Java EventQueue, using the PostEventQueue
* to avoid possibly calling client code (EventQueueSubclass.postEvent())
* on the toolkit (AWT-Windows/AWT-Motif) thread. This function should
* not be called under another lock since it locks the EventQueue.
* See bugids 4632918, 4526597.
*/
public static void postEvent(AWTEvent event) {
if (event == null) {
throw new NullPointerException();
}
AWTAccessor.SequencedEventAccessor sea = AWTAccessor.getSequencedEventAccessor();
if (sea != null && sea.isSequencedEvent(event)) {
AWTEvent nested = sea.getNested(event);
if (nested.getID() == WindowEvent.WINDOW_LOST_FOCUS &&
nested instanceof TimedWindowEvent)
{
TimedWindowEvent twe = (TimedWindowEvent)nested;
((SunToolkit)Toolkit.getDefaultToolkit()).
setWindowDeactivationTime((Window)twe.getSource(), twe.getWhen());
}
}
// All events posted via this method are system-generated.
// Placing the following call here reduces considerably the
// number of places throughout the toolkit that would
// otherwise have to be modified to precisely identify
// system-generated events.
setSystemGenerated(event);
if (postEventQueue != null) {
postEventQueue.postEvent(event);
}
}
/*
* Post AWTEvent of high priority.
*/
public static void postPriorityEvent(final AWTEvent e) {
PeerEvent pe = new PeerEvent(Toolkit.getDefaultToolkit(), new Runnable() {
@Override
public void run() {
AWTAccessor.getAWTEventAccessor().setPosted(e);
((Component)e.getSource()).dispatchEvent(e);
}
}, PeerEvent.ULTIMATE_PRIORITY_EVENT);
postEvent(pe);
}
/*
* Flush any pending events which haven't been posted to the AWT
* EventQueue yet.
*/
public static void flushPendingEvents() {
if (postEventQueue != null) {
postEventQueue.flush();
}
}
/*
* Execute a chunk of code on the Java event handler thread for the
* given target. Does not wait for the execution to occur before
* returning to the caller.
*/
public static void executeOnEventHandlerThread(Object target,
Runnable runnable) {
executeOnEventHandlerThread(new PeerEvent(target, runnable, PeerEvent.PRIORITY_EVENT));
}
/*
* Fixed 5064013: the InvocationEvent time should be equals
* the time of the ActionEvent
*/
public static void executeOnEventHandlerThread(Object target,
Runnable runnable,
final long when) {
executeOnEventHandlerThread(
new PeerEvent(target, runnable, PeerEvent.PRIORITY_EVENT) {
@Override
public long getWhen() {
return when;
}
});
}
/*
* Execute a chunk of code on the Java event handler thread for the
* given target. Does not wait for the execution to occur before
* returning to the caller.
*/
public static void executeOnEventHandlerThread(PeerEvent peerEvent) {
postEvent(peerEvent);
}
/*
* Execute a chunk of code on the Java event handler thread. The
* method sets {@code SunToolkit.getDefaultToolkit()} as a target of the
* event. See 6451487 for details.
* Does not wait for the execution to occur before returning to
* the caller.
*/
public static void invokeLater(Runnable dispatcher) {
postEvent(
new PeerEvent(Toolkit.getDefaultToolkit(), dispatcher,
PeerEvent.PRIORITY_EVENT));
}
/*
* Execute a chunk of code on the Java event handler thread for the
* given target. Waits for the execution to occur before returning
* to the caller.
*/
public static void executeOnEDTAndWait(Object target, Runnable runnable)
throws InterruptedException, InvocationTargetException
{
if (EventQueue.isDispatchThread()) {
throw new Error("Cannot call executeOnEDTAndWait from any event dispatcher thread");
}
class AWTInvocationLock {}
Object lock = new AWTInvocationLock();
PeerEvent event = new PeerEvent(target, runnable, lock, true, PeerEvent.PRIORITY_EVENT);
synchronized (lock) {
executeOnEventHandlerThread(event);
while(!event.isDispatched()) {
lock.wait();
}
}
Throwable eventThrowable = event.getThrowable();
if (eventThrowable != null) {
throw new InvocationTargetException(eventThrowable);
}
}
@Override
public Dimension getScreenSize() {
return GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration()
.getBounds().getSize();
}
@Override
public ColorModel getColorModel() throws HeadlessException {
return GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration()
.getColorModel();
}
@Override
@SuppressWarnings("deprecation")
public FontMetrics getFontMetrics(Font font) {
return FontDesignMetrics.getMetrics(font);
}
@Override
@SuppressWarnings("deprecation")
public String[] getFontList() {
String[] hardwiredFontList = {
Font.DIALOG, Font.SANS_SERIF, Font.SERIF, Font.MONOSPACED,
Font.DIALOG_INPUT
// -- Obsolete font names from 1.0.2. It was decided that
// -- getFontList should not return these old names:
// "Helvetica", "TimesRoman", "Courier", "ZapfDingbats"
};
return hardwiredFontList;
}
/**
* Disables erasing of background on the canvas before painting if
* this is supported by the current toolkit. It is recommended to
* call this method early, before the Canvas becomes displayable,
* because some Toolkit implementations do not support changing
* this property once the Canvas becomes displayable.
*/
public void disableBackgroundErase(Canvas canvas) {
disableBackgroundEraseImpl(canvas);
}
/**
* Disables the native erasing of the background on the given
* component before painting if this is supported by the current
* toolkit. This only has an effect for certain components such as
* Canvas, Panel and Window. It is recommended to call this method
* early, before the Component becomes displayable, because some
* Toolkit implementations do not support changing this property
* once the Component becomes displayable.
*/
public void disableBackgroundErase(Component component) {
disableBackgroundEraseImpl(component);
}
private void disableBackgroundEraseImpl(Component component) {
AWTAccessor.getComponentAccessor().setBackgroundEraseDisabled(component, true);
}
/**
* Returns the value of "sun.awt.noerasebackground" property. Default
* value is {@code false}.
*/
public static boolean getSunAwtNoerasebackground() {
return Boolean.getBoolean("sun.awt.noerasebackground");
}
/**
* Returns the value of "sun.awt.erasebackgroundonresize" property. Default
* value is {@code false}.
*/
public static boolean getSunAwtErasebackgroundonresize() {
return Boolean.getBoolean("sun.awt.erasebackgroundonresize");
}
@SuppressWarnings("deprecation")
static final SoftCache fileImgCache = new SoftCache();
@SuppressWarnings("deprecation")
static final SoftCache urlImgCache = new SoftCache();
static Image getImageFromHash(Toolkit tk, URL url) {
synchronized (urlImgCache) {
String key = url.toString();
Image img = (Image)urlImgCache.get(key);
if (img == null) {
try {
img = tk.createImage(new URLImageSource(url));
urlImgCache.put(key, img);
} catch (Exception e) {
}
}
return img;
}
}
static Image getImageFromHash(Toolkit tk,
String filename) {
synchronized (fileImgCache) {
Image img = (Image)fileImgCache.get(filename);
if (img == null) {
try {
img = tk.createImage(new FileImageSource(filename));
fileImgCache.put(filename, img);
} catch (Exception e) {
}
}
return img;
}
}
@Override
public Image getImage(String filename) {
return getImageFromHash(this, filename);
}
@Override
public Image getImage(URL url) {
return getImageFromHash(this, url);
}
protected Image getImageWithResolutionVariant(String fileName,
String resolutionVariantName) {
synchronized (fileImgCache) {
Image image = getImageFromHash(this, fileName);
if (image instanceof MultiResolutionImage) {
return image;
}
Image resolutionVariant = getImageFromHash(this, resolutionVariantName);
image = createImageWithResolutionVariant(image, resolutionVariant);
fileImgCache.put(fileName, image);
return image;
}
}
protected Image getImageWithResolutionVariant(URL url,
URL resolutionVariantURL) {
synchronized (urlImgCache) {
Image image = getImageFromHash(this, url);
if (image instanceof MultiResolutionImage) {
return image;
}
Image resolutionVariant = getImageFromHash(this, resolutionVariantURL);
image = createImageWithResolutionVariant(image, resolutionVariant);
String key = url.toString();
urlImgCache.put(key, image);
return image;
}
}
@Override
public Image createImage(String filename) {
return createImage(new FileImageSource(filename));
}
@Override
public Image createImage(URL url) {
return createImage(new URLImageSource(url));
}
@Override
public Image createImage(byte[] data, int offset, int length) {
return createImage(new ByteArrayImageSource(data, offset, length));
}
@Override
public Image createImage(ImageProducer producer) {
return new ToolkitImage(producer);
}
public static Image createImageWithResolutionVariant(Image image,
Image resolutionVariant) {
return new MultiResolutionToolkitImage(image, resolutionVariant);
}
@Override
public int checkImage(Image img, int w, int h, ImageObserver o) {
if (!(img instanceof ToolkitImage)) {
return ImageObserver.ALLBITS;
}
ToolkitImage tkimg = (ToolkitImage)img;
int repbits;
if (w == 0 || h == 0) {
repbits = ImageObserver.ALLBITS;
} else {
repbits = tkimg.getImageRep().check(o);
}
return (tkimg.check(o) | repbits) & checkResolutionVariant(img, w, h, o);
}
@Override
public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
if (w == 0 || h == 0) {
return true;
}
// Must be a ToolkitImage
if (!(img instanceof ToolkitImage)) {
return true;
}
ToolkitImage tkimg = (ToolkitImage)img;
if (tkimg.hasError()) {
if (o != null) {
o.imageUpdate(img, ImageObserver.ERROR|ImageObserver.ABORT,
-1, -1, -1, -1);
}
return false;
}
ImageRepresentation ir = tkimg.getImageRep();
return ir.prepare(o) & prepareResolutionVariant(img, w, h, o);
}
private int checkResolutionVariant(Image img, int w, int h, ImageObserver o) {
ToolkitImage rvImage = getResolutionVariant(img);
int rvw = getRVSize(w);
int rvh = getRVSize(h);
// Ignore the resolution variant in case of error
return (rvImage == null || rvImage.hasError()) ? 0xFFFF :
checkImage(rvImage, rvw, rvh, MultiResolutionToolkitImage.
getResolutionVariantObserver(
img, o, w, h, rvw, rvh, true));
}
private boolean prepareResolutionVariant(Image img, int w, int h,
ImageObserver o) {
ToolkitImage rvImage = getResolutionVariant(img);
int rvw = getRVSize(w);
int rvh = getRVSize(h);
// Ignore the resolution variant in case of error
return rvImage == null || rvImage.hasError() || prepareImage(
rvImage, rvw, rvh,
MultiResolutionToolkitImage.getResolutionVariantObserver(
img, o, w, h, rvw, rvh, true));
}
private static int getRVSize(int size){
return size == -1 ? -1 : 2 * size;
}
private static ToolkitImage getResolutionVariant(Image image) {
if (image instanceof MultiResolutionToolkitImage) {
Image resolutionVariant = ((MultiResolutionToolkitImage) image).
getResolutionVariant();
if (resolutionVariant instanceof ToolkitImage) {
return (ToolkitImage) resolutionVariant;
}
}
return null;
}
protected static boolean imageCached(String fileName) {
return fileImgCache.containsKey(fileName);
}
protected static boolean imageCached(URL url) {
String key = url.toString();
return urlImgCache.containsKey(key);
}
protected static boolean imageExists(String filename) {
if (filename != null) {
return new File(filename).exists();
}
return false;
}
@SuppressWarnings("try")
protected static boolean imageExists(URL url) {
if (url != null) {
try (InputStream is = url.openStream()) {
return true;
}catch(IOException e){
return false;
}
}
return false;
}
/**
* Scans {@code imageList} for best-looking image of specified dimensions.
* Image can be scaled and/or padded with transparency.
*/
public static BufferedImage getScaledIconImage(java.util.List
* SunToolkit subclasses can override this method to return better input
* method windows.
*/
@Override
public Window createInputMethodWindow(String title, InputContext context) {
return new sun.awt.im.SimpleInputMethodWindow(title, context);
}
/**
* Returns whether enableInputMethods should be set to true for peered
* TextComponent instances on this platform. False by default.
*/
@Override
public boolean enableInputMethodsForTextComponent() {
return false;
}
private static Locale startupLocale = null;
/**
* Returns the locale in which the runtime was started.
*/
public static Locale getStartupLocale() {
if (startupLocale == null) {
String language, region, country, variant;
language = System.getProperty("user.language", "en");
// for compatibility, check for old user.region property
region = System.getProperty("user.region");
if (region != null) {
// region can be of form country, country_variant, or _variant
int i = region.indexOf('_');
if (i >= 0) {
country = region.substring(0, i);
variant = region.substring(i + 1);
} else {
country = region;
variant = "";
}
} else {
country = System.getProperty("user.country", "");
variant = System.getProperty("user.variant", "");
}
startupLocale = Locale.of(language, country, variant);
}
return startupLocale;
}
/**
* Returns the default keyboard locale of the underlying operating system
*/
@Override
public Locale getDefaultKeyboardLocale() {
return getStartupLocale();
}
/**
* Returns whether default toolkit needs the support of the xembed
* from embedding host(if any).
* @return {@code true}, if XEmbed is needed, {@code false} otherwise
*/
public static boolean needsXEmbed() {
String noxembed = System.getProperty("sun.awt.noxembed", "false");
if ("true".equals(noxembed)) {
return false;
}
Toolkit tk = Toolkit.getDefaultToolkit();
if (tk instanceof SunToolkit) {
// SunToolkit descendants should override this method to specify
// concrete behavior
return ((SunToolkit)tk).needsXEmbedImpl();
} else {
// Non-SunToolkit doubtly might support XEmbed
return false;
}
}
/**
* Returns whether this toolkit needs the support of the xembed
* from embedding host(if any).
* @return {@code true}, if XEmbed is needed, {@code false} otherwise
*/
protected boolean needsXEmbedImpl() {
return false;
}
private static Dialog.ModalExclusionType DEFAULT_MODAL_EXCLUSION_TYPE = null;
/**
* Returns whether the XEmbed server feature is requested by
* developer. If true, Toolkit should return an
* XEmbed-server-enabled CanvasPeer instead of the ordinary CanvasPeer.
*/
protected final boolean isXEmbedServerRequested() {
return Boolean.getBoolean("sun.awt.xembedserver");
}
/**
* Returns whether the modal exclusion API is supported by the current toolkit.
* When it isn't supported, calling {@code setModalExcluded} has no
* effect, and {@code isModalExcluded} returns false for all windows.
*
* @return true if modal exclusion is supported by the toolkit, false otherwise
*
* @see sun.awt.SunToolkit#setModalExcluded(java.awt.Window)
* @see sun.awt.SunToolkit#isModalExcluded(java.awt.Window)
*
* @since 1.5
*/
public static boolean isModalExcludedSupported()
{
Toolkit tk = Toolkit.getDefaultToolkit();
return tk.isModalExclusionTypeSupported(DEFAULT_MODAL_EXCLUSION_TYPE);
}
/*
* Default implementation for isModalExcludedSupportedImpl(), returns false.
*
* @see sun.awt.windows.WToolkit#isModalExcludeSupportedImpl
* @see sun.awt.X11.XToolkit#isModalExcludeSupportedImpl
*
* @since 1.5
*/
protected boolean isModalExcludedSupportedImpl()
{
return false;
}
/*
* Sets this window to be excluded from being modally blocked. When the
* toolkit supports modal exclusion and this method is called, input
* events, focus transfer and z-order will continue to work for the
* window, it's owned windows and child components, even in the
* presence of a modal dialog.
* For details on which {@code Window}s are normally blocked
* by modal dialog, see {@link java.awt.Dialog}.
* Invoking this method when the modal exclusion API is not supported by
* the current toolkit has no effect.
* @param window Window to be marked as not modally blocked
* @see java.awt.Dialog
* @see java.awt.Dialog#setModal(boolean)
* @see sun.awt.SunToolkit#isModalExcludedSupported
* @see sun.awt.SunToolkit#isModalExcluded(java.awt.Window)
*/
public static void setModalExcluded(Window window)
{
if (DEFAULT_MODAL_EXCLUSION_TYPE == null) {
DEFAULT_MODAL_EXCLUSION_TYPE = Dialog.ModalExclusionType.APPLICATION_EXCLUDE;
}
window.setModalExclusionType(DEFAULT_MODAL_EXCLUSION_TYPE);
}
/*
* Returns whether the specified window is blocked by modal dialogs.
* If the modal exclusion API isn't supported by the current toolkit,
* it returns false for all windows.
*
* @param window Window to test for modal exclusion
*
* @return true if the window is modal excluded, false otherwise. If
* the modal exclusion isn't supported by the current Toolkit, false
* is returned
*
* @see sun.awt.SunToolkit#isModalExcludedSupported
* @see sun.awt.SunToolkit#setModalExcluded(java.awt.Window)
*
* @since 1.5
*/
public static boolean isModalExcluded(Window window)
{
if (DEFAULT_MODAL_EXCLUSION_TYPE == null) {
DEFAULT_MODAL_EXCLUSION_TYPE = Dialog.ModalExclusionType.APPLICATION_EXCLUDE;
}
return window.getModalExclusionType().compareTo(DEFAULT_MODAL_EXCLUSION_TYPE) >= 0;
}
/**
* Overridden in XToolkit and WToolkit
*/
@Override
public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) {
return (modalityType == Dialog.ModalityType.MODELESS) ||
(modalityType == Dialog.ModalityType.APPLICATION_MODAL);
}
/**
* Overridden in XToolkit and WToolkit
*/
@Override
public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType exclusionType) {
return (exclusionType == Dialog.ModalExclusionType.NO_EXCLUDE);
}
///////////////////////////////////////////////////////////////////////////
//
// The following is used by the Java Plug-in to coordinate dialog modality
// between containing applications (browsers, ActiveX containers etc) and
// the AWT.
//
///////////////////////////////////////////////////////////////////////////
private ModalityListenerList modalityListeners = new ModalityListenerList();
public void addModalityListener(ModalityListener listener) {
modalityListeners.add(listener);
}
public void removeModalityListener(ModalityListener listener) {
modalityListeners.remove(listener);
}
public void notifyModalityPushed(Dialog dialog) {
notifyModalityChange(ModalityEvent.MODALITY_PUSHED, dialog);
}
public void notifyModalityPopped(Dialog dialog) {
notifyModalityChange(ModalityEvent.MODALITY_POPPED, dialog);
}
final void notifyModalityChange(int id, Dialog source) {
ModalityEvent ev = new ModalityEvent(source, modalityListeners, id);
ev.dispatch();
}
static class ModalityListenerList implements ModalityListener {
Vector This method allows to write tests without explicit timeouts
* or wait for some event. Example:
* After realSync, {@code f} will be completely visible
* on the screen, its getLocationOnScreen will be returning the
* right result and it will be the focus owner.
*
* Another example:
* After realSync, {@code b} will be focus owner.
*
* Notice that realSync isn't guaranteed to work if recurring
* actions occur, such as if during processing of some event
* another request which may generate some events occurs. By
* default, sync tries to perform as much as {@value #MAX_ITERS}
* cycles of event processing, allowing for roughly {@value
* #MAX_ITERS} additional requests.
*
* For example, requestFocus() generates native request, which
* generates one or two Java focus events, which then generate a
* series of paint events, a series of Java focus events, which then
* generate a series of paint events which then are processed -
* three cycles, minimum.
*
* @param timeout the maximum time to wait in milliseconds, negative means "forever".
*/
public void realSync(final long timeout) {
if (EventQueue.isDispatchThread()) {
throw new IllegalThreadException("The SunToolkit.realSync() method cannot be used on the event dispatch thread (EDT).");
}
try {
// We should wait unconditionally for the first event on EDT
EventQueue.invokeAndWait(() -> {/*dummy implementation*/});
} catch (InterruptedException | InvocationTargetException ignored) {
}
int bigLoop = 0;
long end = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) + timeout;
do {
if (timeout(end) < 0) {
return;
}
// Let's do sync first
sync();
// During the wait process, when we were processing incoming
// events, we could have made some new request, which can
// generate new events. Example: MapNotify/XSetInputFocus.
// Therefore, we dispatch them as long as there is something
// to dispatch.
int iters = 0;
while (iters < MIN_ITERS) {
syncNativeQueue(timeout(end));
iters++;
}
while (syncNativeQueue(timeout(end)) && iters < MAX_ITERS) {
iters++;
}
// native requests were dispatched by X/Window Manager or Windows
// Moreover, we processed them all on Toolkit thread
// Now wait while EDT processes them.
//
// During processing of some events (focus, for example),
// some other events could have been generated. So, after
// waitForIdle, we may end up with full EventQueue
iters = 0;
while (iters < MIN_ITERS) {
waitForIdle(timeout(end));
iters++;
}
while (waitForIdle(end) && iters < MAX_ITERS) {
iters++;
}
bigLoop++;
// Again, for Java events, it was simple to check for new Java
// events by checking event queue, but what if Java events
// resulted in native requests? Therefore, check native events again.
} while ((syncNativeQueue(timeout(end)) || waitForIdle(end))
&& bigLoop < MAX_ITERS);
}
protected long timeout(long end){
return end - TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
}
/**
* Platform toolkits need to implement this method to perform the
* sync of the native queue. The method should wait until native
* requests are processed, all native events are processed and
* corresponding Java events are generated. Should return
* {@code true} if some events were processed,
* {@code false} otherwise.
*/
protected abstract boolean syncNativeQueue(long timeout);
private final Object waitLock = new Object();
private boolean isEQEmpty() {
EventQueue queue = getSystemEventQueueImpl();
return AWTAccessor.getEventQueueAccessor().noEvents(queue);
}
/**
* Waits for the Java event queue to empty. Ensures that all
* events are processed (including paint events), and that if
* recursive events were generated, they are also processed.
* Should return {@code true} if more processing is
* necessary, {@code false} otherwise.
*/
private final boolean waitForIdle(final long end) {
if (timeout(end) <= 0) {
return false;
}
flushPendingEvents();
final boolean queueWasEmpty;
final AtomicBoolean queueEmpty = new AtomicBoolean();
final AtomicBoolean eventDispatched = new AtomicBoolean();
synchronized (waitLock) {
queueWasEmpty = isEQEmpty();
postEvent(new PeerEvent(getSystemEventQueueImpl(), null, PeerEvent.LOW_PRIORITY_EVENT) {
@Override
public void dispatch() {
// Here we block EDT. It could have some
// events, it should have dispatched them by
// now. So native requests could have been
// generated. First, dispatch them. Then,
// flush Java events again.
int iters = 0;
while (iters < MIN_ITERS) {
syncNativeQueue(timeout(end));
iters++;
}
while (syncNativeQueue(timeout(end)) && iters < MAX_ITERS) {
iters++;
}
flushPendingEvents();
synchronized(waitLock) {
queueEmpty.set(isEQEmpty());
eventDispatched.set(true);
waitLock.notifyAll();
}
}
});
try {
while (!eventDispatched.get() && timeout(end) > 0) {
waitLock.wait(timeout(end));
}
} catch (InterruptedException ie) {
return false;
}
}
try {
Thread.sleep(MINIMAL_DELAY);
} catch (InterruptedException ie) {
throw new RuntimeException("Interrupted");
}
flushPendingEvents();
// Lock to force write-cache flush for queueEmpty.
synchronized (waitLock) {
return !(queueEmpty.get() && isEQEmpty() && queueWasEmpty);
}
}
/**
* Grabs the mouse input for the given window. The window must be
* visible. The window or its children do not receive any
* additional mouse events besides those targeted to them. All
* other events will be dispatched as before - to the respective
* targets. This Window will receive UngrabEvent when automatic
* ungrab is about to happen. The event can be listened to by
* installing AWTEventListener with WINDOW_EVENT_MASK. See
* UngrabEvent class for the list of conditions when ungrab is
* about to happen.
* @see UngrabEvent
*/
public abstract void grab(Window w);
/**
* Forces ungrab. No event will be sent.
*/
public abstract void ungrab(Window w);
public void showOrHideTouchKeyboard(Component comp, AWTEvent e) {}
private static boolean touchKeyboardAutoShowIsEnabled;
public static boolean isTouchKeyboardAutoShowEnabled() {
return touchKeyboardAutoShowIsEnabled;
}
/**
* Locates the splash screen library in a platform dependent way and closes
* the splash screen. Should be invoked on first top-level frame display.
* @see java.awt.SplashScreen
* @since 1.6
*/
public static native void closeSplashScreen();
/* The following methods and variables are to support retrieving
* desktop text anti-aliasing settings
*/
/* Need an instance method because setDesktopProperty(..) is protected. */
private void fireDesktopFontPropertyChanges() {
setDesktopProperty(SunToolkit.DESKTOPFONTHINTS,
SunToolkit.getDesktopFontHints());
}
private static boolean checkedSystemAAFontSettings;
private static boolean useSystemAAFontSettings;
private static boolean lastExtraCondition = true;
private static RenderingHints desktopFontHints;
/* Since Swing is the reason for this "extra condition" logic its
* worth documenting it in some detail.
* First, a goal is for Swing and applications to both retrieve and
* use the same desktop property value so that there is complete
* consistency between the settings used by JDK's Swing implementation
* and 3rd party custom Swing components, custom L&Fs and any general
* text rendering that wants to be consistent with these.
* But by default on Solaris & Linux Swing will not use AA text over
* remote X11 display (unless Xrender can be used which is TBD and may not
* always be available anyway) as that is a noticeable performance hit.
* So there needs to be a way to express that extra condition so that
* it is seen by all clients of the desktop property API.
* If this were the only condition it could be handled here as it would
* be the same for any L&F and could reasonably be considered to be
* a static behaviour of those systems.
* But GTK currently has an additional test based on locale which is
* not applied by Metal. So mixing GTK in a few locales with Metal
* would mean the last one wins.
* But it is expected this will be addressed within GTK and the font
* system so is a temporary and somewhat unlikely harmless corner case.
*/
public static void setAAFontSettingsCondition(boolean extraCondition) {
if (extraCondition != lastExtraCondition) {
lastExtraCondition = extraCondition;
if (checkedSystemAAFontSettings) {
/* Someone already asked for this info, under a different
* condition.
* We'll force re-evaluation instead of replicating the
* logic, then notify any listeners of any change.
*/
checkedSystemAAFontSettings = false;
Toolkit tk = Toolkit.getDefaultToolkit();
if (tk instanceof SunToolkit) {
((SunToolkit)tk).fireDesktopFontPropertyChanges();
}
}
}
}
/* "false", "off", ""default" aren't explicitly tested, they
* just fall through to produce a null return which all are equated to
* "false".
*/
private static RenderingHints getDesktopAAHintsByName(String hintname) {
Object aaHint = null;
hintname = hintname.toLowerCase(Locale.ENGLISH);
if (hintname.equals("on")) {
aaHint = VALUE_TEXT_ANTIALIAS_ON;
} else if (hintname.equals("gasp")) {
aaHint = VALUE_TEXT_ANTIALIAS_GASP;
} else if (hintname.equals("lcd") || hintname.equals("lcd_hrgb")) {
aaHint = VALUE_TEXT_ANTIALIAS_LCD_HRGB;
} else if (hintname.equals("lcd_hbgr")) {
aaHint = VALUE_TEXT_ANTIALIAS_LCD_HBGR;
} else if (hintname.equals("lcd_vrgb")) {
aaHint = VALUE_TEXT_ANTIALIAS_LCD_VRGB;
} else if (hintname.equals("lcd_vbgr")) {
aaHint = VALUE_TEXT_ANTIALIAS_LCD_VBGR;
}
if (aaHint != null) {
RenderingHints map = new RenderingHints(null);
map.put(KEY_TEXT_ANTIALIASING, aaHint);
return map;
} else {
return null;
}
}
/* This method determines whether to use the system font settings,
* or ignore them if a L&F has specified they should be ignored, or
* to override both of these with a system property specified value.
* If the toolkit isn't a SunToolkit, (eg may be headless) then that
* system property isn't applied as desktop properties are considered
* to be inapplicable in that case. In that headless case although
* this method will return "true" the toolkit will return a null map.
*/
private static boolean useSystemAAFontSettings() {
if (!checkedSystemAAFontSettings) {
useSystemAAFontSettings = true; /* initially set this true */
String systemAAFonts = null;
Toolkit tk = Toolkit.getDefaultToolkit();
if (tk instanceof SunToolkit) {
systemAAFonts = System.getProperty("awt.useSystemAAFontSettings");
}
if (systemAAFonts != null) {
useSystemAAFontSettings = Boolean.parseBoolean(systemAAFonts);
/* If it is anything other than "true", then it may be
* a hint name , or it may be "off, "default", etc.
*/
if (!useSystemAAFontSettings) {
desktopFontHints = getDesktopAAHintsByName(systemAAFonts);
}
}
/* If its still true, apply the extra condition */
if (useSystemAAFontSettings) {
useSystemAAFontSettings = lastExtraCondition;
}
checkedSystemAAFontSettings = true;
}
return useSystemAAFontSettings;
}
/* A variable defined for the convenience of JDK code */
public static final String DESKTOPFONTHINTS = "awt.font.desktophints";
/* Overridden by subclasses to return platform/desktop specific values */
protected RenderingHints getDesktopAAHints() {
return null;
}
/* Subclass desktop property loading methods call this which
* in turn calls the appropriate subclass implementation of
* getDesktopAAHints() when system settings are being used.
* Its public rather than protected because subclasses may delegate
* to a helper class.
*/
public static RenderingHints getDesktopFontHints() {
if (useSystemAAFontSettings()) {
Toolkit tk = Toolkit.getDefaultToolkit();
if (tk instanceof SunToolkit) {
RenderingHints map = ((SunToolkit)tk).getDesktopAAHints();
return map;
} else { /* Headless Toolkit */
return null;
}
} else if (desktopFontHints != null) {
/* cloning not necessary as the return value is cloned later, but
* its harmless.
*/
return (RenderingHints)(desktopFontHints.clone());
} else {
return null;
}
}
public abstract boolean isDesktopSupported();
public abstract boolean isTaskbarSupported();
/*
* consumeNextKeyTyped() method is not currently used,
* however Swing could use it in the future.
*/
public static synchronized void consumeNextKeyTyped(KeyEvent keyEvent) {
try {
AWTAccessor.getDefaultKeyboardFocusManagerAccessor().consumeNextKeyTyped(
(DefaultKeyboardFocusManager)KeyboardFocusManager.
getCurrentKeyboardFocusManager(),
keyEvent);
} catch (ClassCastException cce) {
cce.printStackTrace();
}
}
protected static void dumpPeers(final PlatformLogger aLog) {
AWTAutoShutdown.getInstance().dumpPeers(aLog);
}
/**
* Returns the {@code Window} ancestor of the component {@code comp}.
* @return Window ancestor of the component or component by itself if it is Window;
* null, if component is not a part of window hierarchy
*/
public static Window getContainingWindow(Component comp) {
while (comp != null && !(comp instanceof Window)) {
comp = comp.getParent();
}
return (Window)comp;
}
private static Boolean sunAwtDisableMixing = null;
/**
* Returns the value of "sun.awt.disableMixing" property. Default
* value is {@code false}.
*/
public static synchronized boolean getSunAwtDisableMixing() {
if (sunAwtDisableMixing == null) {
sunAwtDisableMixing = Boolean.getBoolean("sun.awt.disableMixing");
}
return sunAwtDisableMixing.booleanValue();
}
public String getDesktop() {
return null;
}
/**
* Returns true if the native GTK libraries are available. The
* default implementation returns false, but UNIXToolkit overrides this
* method to provide a more specific answer.
*/
public boolean isNativeGTKAvailable() {
return false;
}
/**
* Checks if the system is running Linux with the Wayland server.
*
* @return true if running on Wayland, false otherwise
*/
public boolean isRunningOnWayland() {
return false;
}
public void dismissPopupOnFocusLostIfNeeded(Window invoker) {}
public void dismissPopupOnFocusLostIfNeededCleanUp(Window invoker) {}
private static WeakHashMap{@code
* Frame f = ...;
* f.setVisible(true);
* ((SunToolkit)Toolkit.getDefaultToolkit()).realSync();
* }
*
* {@code
* b.requestFocus();
* ((SunToolkit)Toolkit.getDefaultToolkit()).realSync();
* }
*
*