Path: blob/master/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java
41152 views
/*1* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.awt;2526import java.awt.RenderingHints;27import static java.awt.RenderingHints.*;28import java.awt.color.ColorSpace;29import java.awt.image.*;30import java.security.AccessController;31import java.security.PrivilegedAction;3233import sun.security.action.GetIntegerAction;34import com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection;35import sun.java2d.opengl.OGLRenderQueue;36import sun.security.action.GetPropertyAction;3738public abstract class UNIXToolkit extends SunToolkit39{40/** All calls into GTK should be synchronized on this lock */41public static final Object GTK_LOCK = new Object();4243private static final int[] BAND_OFFSETS = { 0, 1, 2 };44private static final int[] BAND_OFFSETS_ALPHA = { 0, 1, 2, 3 };45private static final int DEFAULT_DATATRANSFER_TIMEOUT = 10000;4647// Allowed GTK versions48public enum GtkVersions {49ANY(0),50GTK2(Constants.GTK2_MAJOR_NUMBER),51GTK3(Constants.GTK3_MAJOR_NUMBER);5253static class Constants {54static final int GTK2_MAJOR_NUMBER = 2;55static final int GTK3_MAJOR_NUMBER = 3;56}5758final int number;5960GtkVersions(int number) {61this.number = number;62}6364public static GtkVersions getVersion(int number) {65switch (number) {66case Constants.GTK2_MAJOR_NUMBER:67return GTK2;68case Constants.GTK3_MAJOR_NUMBER:69return GTK3;70default:71return ANY;72}73}7475// major GTK version number76public int getNumber() {77return number;78}79};8081private Boolean nativeGTKAvailable;82private Boolean nativeGTKLoaded;83private BufferedImage tmpImage = null;8485public static int getDatatransferTimeout() {86@SuppressWarnings("removal")87Integer dt = AccessController.doPrivileged(88new GetIntegerAction("sun.awt.datatransfer.timeout"));89if (dt == null || dt <= 0) {90return DEFAULT_DATATRANSFER_TIMEOUT;91} else {92return dt;93}94}9596@Override97public String getDesktop() {98String gnome = "gnome";99@SuppressWarnings("removal")100String gsi = AccessController.doPrivileged(101(PrivilegedAction<String>) ()102-> System.getenv("GNOME_DESKTOP_SESSION_ID"));103if (gsi != null) {104return gnome;105}106107@SuppressWarnings("removal")108String desktop = AccessController.doPrivileged(109(PrivilegedAction<String>) ()110-> System.getenv("XDG_CURRENT_DESKTOP"));111return (desktop != null && desktop.toLowerCase().contains(gnome))112? gnome : null;113}114115/**116* Returns true if the native GTK libraries are capable of being117* loaded and are expected to work properly, false otherwise. Note118* that this method will not leave the native GTK libraries loaded if119* they haven't already been loaded. This allows, for example, Swing's120* GTK L&F to test for the presence of native GTK support without121* leaving the native libraries loaded. To attempt long-term loading122* of the native GTK libraries, use the loadGTK() method instead.123*/124@Override125public boolean isNativeGTKAvailable() {126synchronized (GTK_LOCK) {127if (nativeGTKLoaded != null) {128// We've already attempted to load GTK, so just return the129// status of that attempt.130return nativeGTKLoaded;131132} else if (nativeGTKAvailable != null) {133// We've already checked the availability of the native GTK134// libraries, so just return the status of that attempt.135return nativeGTKAvailable;136137} else {138boolean success = check_gtk(getEnabledGtkVersion().getNumber());139nativeGTKAvailable = success;140return success;141}142}143}144145/**146* Loads the GTK libraries, if necessary. The first time this method147* is called, it will attempt to load the native GTK library. If148* successful, it leaves the library open and returns true; otherwise,149* the library is left closed and returns false. On future calls to150* this method, the status of the first attempt is returned (a simple151* lightweight boolean check, no native calls required).152*/153public boolean loadGTK() {154synchronized (GTK_LOCK) {155if (nativeGTKLoaded == null) {156nativeGTKLoaded = load_gtk(getEnabledGtkVersion().getNumber(),157isGtkVerbose());158}159}160return nativeGTKLoaded;161}162163/**164* Overridden to handle GTK icon loading165*/166@Override167protected Object lazilyLoadDesktopProperty(String name) {168if (name.startsWith("gtk.icon.")) {169return lazilyLoadGTKIcon(name);170}171return super.lazilyLoadDesktopProperty(name);172}173174/**175* Load a native Gtk stock icon.176*177* @param longname a desktop property name. This contains icon name, size178* and orientation, e.g. {@code "gtk.icon.gtk-add.4.rtl"}179* @return an {@code Image} for the icon, or {@code null} if the180* icon could not be loaded181*/182protected Object lazilyLoadGTKIcon(String longname) {183// Check if we have already loaded it.184Object result = desktopProperties.get(longname);185if (result != null) {186return result;187}188189// We need to have at least gtk.icon.<stock_id>.<size>.<orientation>190String[] str = longname.split("\\.");191if (str.length != 5) {192return null;193}194195// Parse out the stock icon size we are looking for.196int size = 0;197try {198size = Integer.parseInt(str[3]);199} catch (NumberFormatException nfe) {200return null;201}202203// Direction.204TextDirection dir = ("ltr".equals(str[4]) ? TextDirection.LTR :205TextDirection.RTL);206207// Load the stock icon.208BufferedImage img = getStockIcon(-1, str[2], size, dir.ordinal(), null);209if (img != null) {210// Create the desktop property for the icon.211setDesktopProperty(longname, img);212}213return img;214}215216/**217* Returns a BufferedImage which contains the Gtk icon requested. If no218* such icon exists or an error occurs loading the icon the result will219* be null.220*221* @param filename222* @return The icon or null if it was not found or loaded.223*/224public BufferedImage getGTKIcon(final String filename) {225if (!loadGTK()) {226return null;227228} else {229// Call the native method to load the icon.230synchronized (GTK_LOCK) {231if (!load_gtk_icon(filename)) {232tmpImage = null;233}234}235}236// Return local image the callback loaded the icon into.237return tmpImage;238}239240/**241* Returns a BufferedImage which contains the Gtk stock icon requested.242* If no such stock icon exists the result will be null.243*244* @param widgetType one of WidgetType values defined in GTKNativeEngine or245* -1 for system default stock icon.246* @param stockId String which defines the stock id of the gtk item.247* For a complete list reference the API at www.gtk.org for StockItems.248* @param iconSize One of the GtkIconSize values defined in GTKConstants249* @param direction One of the TextDirection values defined in250* GTKConstants251* @param detail Render detail that is passed to the native engine (feel252* free to pass null)253* @return The stock icon or null if it was not found or loaded.254*/255public BufferedImage getStockIcon(final int widgetType, final String stockId,256final int iconSize, final int direction,257final String detail) {258if (!loadGTK()) {259return null;260261} else {262// Call the native method to load the icon.263synchronized (GTK_LOCK) {264if (!load_stock_icon(widgetType, stockId, iconSize, direction, detail)) {265tmpImage = null;266}267}268}269// Return local image the callback loaded the icon into.270return tmpImage; // set by loadIconCallback271}272273/**274* This method is used by JNI as a callback from load_stock_icon.275* Image data is passed back to us via this method and loaded into the276* local BufferedImage and then returned via getStockIcon.277*278* Do NOT call this method directly.279*/280public void loadIconCallback(byte[] data, int width, int height,281int rowStride, int bps, int channels, boolean alpha) {282// Reset the stock image to null.283tmpImage = null;284285// Create a new BufferedImage based on the data returned from the286// JNI call.287DataBuffer dataBuf = new DataBufferByte(data, (rowStride * height));288// Maybe test # channels to determine band offsets?289WritableRaster raster = Raster.createInterleavedRaster(dataBuf,290width, height, rowStride, channels,291(alpha ? BAND_OFFSETS_ALPHA : BAND_OFFSETS), null);292ColorModel colorModel = new ComponentColorModel(293ColorSpace.getInstance(ColorSpace.CS_sRGB), alpha, false,294ColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);295296// Set the local image so we can return it later from297// getStockIcon().298tmpImage = new BufferedImage(colorModel, raster, false, null);299}300301private static native boolean check_gtk(int version);302private static native boolean load_gtk(int version, boolean verbose);303private static native boolean unload_gtk();304private native boolean load_gtk_icon(String filename);305private native boolean load_stock_icon(int widget_type, String stock_id,306int iconSize, int textDirection, String detail);307308private native void nativeSync();309private static native int get_gtk_version();310311@Override312public void sync() {313// flush the X11 buffer314nativeSync();315// now flush the OGL pipeline (this is a no-op if OGL is not enabled)316OGLRenderQueue.sync();317}318319/*320* This returns the value for the desktop property "awt.font.desktophints"321* It builds this by querying the Gnome desktop properties to return322* them as platform independent hints.323* This requires that the Gnome properties have already been gathered.324*/325public static final String FONTCONFIGAAHINT = "fontconfig/Antialias";326327@Override328protected RenderingHints getDesktopAAHints() {329330Object aaValue = getDesktopProperty("gnome.Xft/Antialias");331332if (aaValue == null) {333/* On a KDE desktop running KWin the rendering hint will334* have been set as property "fontconfig/Antialias".335* No need to parse further in this case.336*/337aaValue = getDesktopProperty(FONTCONFIGAAHINT);338if (aaValue != null) {339return new RenderingHints(KEY_TEXT_ANTIALIASING, aaValue);340} else {341return null; // no Gnome or KDE Desktop properties available.342}343}344345/* 0 means off, 1 means some ON. What would any other value mean?346* If we require "1" to enable AA then some new value would cause347* us to default to "OFF". I don't think that's the best guess.348* So if its !=0 then lets assume AA.349*/350boolean aa = ((aaValue instanceof Number)351&& ((Number) aaValue).intValue() != 0);352Object aaHint;353if (aa) {354String subpixOrder =355(String)getDesktopProperty("gnome.Xft/RGBA");356357if (subpixOrder == null || subpixOrder.equals("none")) {358aaHint = VALUE_TEXT_ANTIALIAS_ON;359} else if (subpixOrder.equals("rgb")) {360aaHint = VALUE_TEXT_ANTIALIAS_LCD_HRGB;361} else if (subpixOrder.equals("bgr")) {362aaHint = VALUE_TEXT_ANTIALIAS_LCD_HBGR;363} else if (subpixOrder.equals("vrgb")) {364aaHint = VALUE_TEXT_ANTIALIAS_LCD_VRGB;365} else if (subpixOrder.equals("vbgr")) {366aaHint = VALUE_TEXT_ANTIALIAS_LCD_VBGR;367} else {368/* didn't recognise the string, but AA is requested */369aaHint = VALUE_TEXT_ANTIALIAS_ON;370}371} else {372aaHint = VALUE_TEXT_ANTIALIAS_DEFAULT;373}374return new RenderingHints(KEY_TEXT_ANTIALIASING, aaHint);375}376377private native boolean gtkCheckVersionImpl(int major, int minor,378int micro);379380/**381* Returns {@code true} if the GTK+ library is compatible with the given382* version.383*384* @param major385* The required major version.386* @param minor387* The required minor version.388* @param micro389* The required micro version.390* @return {@code true} if the GTK+ library is compatible with the given391* version.392*/393public boolean checkGtkVersion(int major, int minor, int micro) {394if (loadGTK()) {395return gtkCheckVersionImpl(major, minor, micro);396}397return false;398}399400public static GtkVersions getEnabledGtkVersion() {401@SuppressWarnings("removal")402String version = AccessController.doPrivileged(403new GetPropertyAction("jdk.gtk.version"));404if (version == null) {405return GtkVersions.ANY;406} else if (version.startsWith("2")) {407return GtkVersions.GTK2;408} else if("3".equals(version) ){409return GtkVersions.GTK3;410}411return GtkVersions.ANY;412}413414public static GtkVersions getGtkVersion() {415return GtkVersions.getVersion(get_gtk_version());416}417418@SuppressWarnings("removal")419public static boolean isGtkVerbose() {420return AccessController.doPrivileged((PrivilegedAction<Boolean>)()421-> Boolean.getBoolean("jdk.gtk.verbose"));422}423}424425426