1 /* 2 * Copyright (c) 2004, 2018, 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 package sun.awt; 26 27 import java.awt.RenderingHints; 28 import static java.awt.RenderingHints.*; 29 import java.awt.color.ColorSpace; 30 import java.awt.image.*; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import sun.security.action.GetIntegerAction; 34 import com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection; 35 import sun.java2d.opengl.OGLRenderQueue; 36 import sun.security.action.GetPropertyAction; 37 38 public abstract class UNIXToolkit extends SunToolkit 39 { 40 /** All calls into GTK should be synchronized on this lock */ 41 public static final Object GTK_LOCK = new Object(); 42 43 private static final int[] BAND_OFFSETS = { 0, 1, 2 }; 44 private static final int[] BAND_OFFSETS_ALPHA = { 0, 1, 2, 3 }; 45 private static final int DEFAULT_DATATRANSFER_TIMEOUT = 10000; 46 47 // Allowed GTK versions 48 public enum GtkVersions { 49 ANY(0), 50 GTK2(Constants.GTK2_MAJOR_NUMBER), 51 GTK3(Constants.GTK3_MAJOR_NUMBER); 52 53 static class Constants { 54 static final int GTK2_MAJOR_NUMBER = 2; 55 static final int GTK3_MAJOR_NUMBER = 3; 56 } 57 58 final int number; 59 GtkVersions(int number)60 GtkVersions(int number) { 61 this.number = number; 62 } 63 getVersion(int number)64 public static GtkVersions getVersion(int number) { 65 switch (number) { 66 case Constants.GTK2_MAJOR_NUMBER: 67 return GTK2; 68 case Constants.GTK3_MAJOR_NUMBER: 69 return GTK3; 70 default: 71 return ANY; 72 } 73 } 74 75 // major GTK version number getNumber()76 public int getNumber() { 77 return number; 78 } 79 }; 80 81 private Boolean nativeGTKAvailable; 82 private Boolean nativeGTKLoaded; 83 private BufferedImage tmpImage = null; 84 getDatatransferTimeout()85 public static int getDatatransferTimeout() { 86 Integer dt = (Integer)AccessController.doPrivileged( 87 new GetIntegerAction("sun.awt.datatransfer.timeout")); 88 if (dt == null || dt <= 0) { 89 return DEFAULT_DATATRANSFER_TIMEOUT; 90 } else { 91 return dt; 92 } 93 } 94 95 /** 96 * Returns true if the native GTK libraries are capable of being 97 * loaded and are expected to work properly, false otherwise. Note 98 * that this method will not leave the native GTK libraries loaded if 99 * they haven't already been loaded. This allows, for example, Swing's 100 * GTK L&F to test for the presence of native GTK support without 101 * leaving the native libraries loaded. To attempt long-term loading 102 * of the native GTK libraries, use the loadGTK() method instead. 103 */ 104 @Override isNativeGTKAvailable()105 public boolean isNativeGTKAvailable() { 106 synchronized (GTK_LOCK) { 107 if (nativeGTKLoaded != null) { 108 // We've already attempted to load GTK, so just return the 109 // status of that attempt. 110 return nativeGTKLoaded; 111 112 } else if (nativeGTKAvailable != null) { 113 // We've already checked the availability of the native GTK 114 // libraries, so just return the status of that attempt. 115 return nativeGTKAvailable; 116 117 } else { 118 boolean success = check_gtk(getEnabledGtkVersion().getNumber()); 119 nativeGTKAvailable = success; 120 return success; 121 } 122 } 123 } 124 125 /** 126 * Loads the GTK libraries, if necessary. The first time this method 127 * is called, it will attempt to load the native GTK library. If 128 * successful, it leaves the library open and returns true; otherwise, 129 * the library is left closed and returns false. On future calls to 130 * this method, the status of the first attempt is returned (a simple 131 * lightweight boolean check, no native calls required). 132 */ loadGTK()133 public boolean loadGTK() { 134 synchronized (GTK_LOCK) { 135 if (nativeGTKLoaded == null) { 136 nativeGTKLoaded = load_gtk(getEnabledGtkVersion().getNumber(), 137 isGtkVerbose()); 138 } 139 } 140 return nativeGTKLoaded; 141 } 142 143 /** 144 * Overridden to handle GTK icon loading 145 */ lazilyLoadDesktopProperty(String name)146 protected Object lazilyLoadDesktopProperty(String name) { 147 if (name.startsWith("gtk.icon.")) { 148 return lazilyLoadGTKIcon(name); 149 } 150 return super.lazilyLoadDesktopProperty(name); 151 } 152 153 /** 154 * Load a native Gtk stock icon. 155 * 156 * @param longname a desktop property name. This contains icon name, size 157 * and orientation, e.g. <code>"gtk.icon.gtk-add.4.rtl"</code> 158 * @return an <code>Image</code> for the icon, or <code>null</code> if the 159 * icon could not be loaded 160 */ lazilyLoadGTKIcon(String longname)161 protected Object lazilyLoadGTKIcon(String longname) { 162 // Check if we have already loaded it. 163 Object result = desktopProperties.get(longname); 164 if (result != null) { 165 return result; 166 } 167 168 // We need to have at least gtk.icon.<stock_id>.<size>.<orientation> 169 String str[] = longname.split("\\."); 170 if (str.length != 5) { 171 return null; 172 } 173 174 // Parse out the stock icon size we are looking for. 175 int size = 0; 176 try { 177 size = Integer.parseInt(str[3]); 178 } catch (NumberFormatException nfe) { 179 return null; 180 } 181 182 // Direction. 183 TextDirection dir = ("ltr".equals(str[4]) ? TextDirection.LTR : 184 TextDirection.RTL); 185 186 // Load the stock icon. 187 BufferedImage img = getStockIcon(-1, str[2], size, dir.ordinal(), null); 188 if (img != null) { 189 // Create the desktop property for the icon. 190 setDesktopProperty(longname, img); 191 } 192 return img; 193 } 194 195 /** 196 * Returns a BufferedImage which contains the Gtk icon requested. If no 197 * such icon exists or an error occurs loading the icon the result will 198 * be null. 199 * 200 * @param filename 201 * @return The icon or null if it was not found or loaded. 202 */ getGTKIcon(final String filename)203 public BufferedImage getGTKIcon(final String filename) { 204 if (!loadGTK()) { 205 return null; 206 207 } else { 208 // Call the native method to load the icon. 209 synchronized (GTK_LOCK) { 210 if (!load_gtk_icon(filename)) { 211 tmpImage = null; 212 } 213 } 214 } 215 // Return local image the callback loaded the icon into. 216 return tmpImage; 217 } 218 219 /** 220 * Returns a BufferedImage which contains the Gtk stock icon requested. 221 * If no such stock icon exists the result will be null. 222 * 223 * @param widgetType one of WidgetType values defined in GTKNativeEngine or 224 * -1 for system default stock icon. 225 * @param stockId String which defines the stock id of the gtk item. 226 * For a complete list reference the API at www.gtk.org for StockItems. 227 * @param iconSize One of the GtkIconSize values defined in GTKConstants 228 * @param textDirection One of the TextDirection values defined in 229 * GTKConstants 230 * @param detail Render detail that is passed to the native engine (feel 231 * free to pass null) 232 * @return The stock icon or null if it was not found or loaded. 233 */ getStockIcon(final int widgetType, final String stockId, final int iconSize, final int direction, final String detail)234 public BufferedImage getStockIcon(final int widgetType, final String stockId, 235 final int iconSize, final int direction, 236 final String detail) { 237 if (!loadGTK()) { 238 return null; 239 240 } else { 241 // Call the native method to load the icon. 242 synchronized (GTK_LOCK) { 243 if (!load_stock_icon(widgetType, stockId, iconSize, direction, detail)) { 244 tmpImage = null; 245 } 246 } 247 } 248 // Return local image the callback loaded the icon into. 249 return tmpImage; // set by loadIconCallback 250 } 251 252 /** 253 * This method is used by JNI as a callback from load_stock_icon. 254 * Image data is passed back to us via this method and loaded into the 255 * local BufferedImage and then returned via getStockIcon. 256 * 257 * Do NOT call this method directly. 258 */ loadIconCallback(byte[] data, int width, int height, int rowStride, int bps, int channels, boolean alpha)259 public void loadIconCallback(byte[] data, int width, int height, 260 int rowStride, int bps, int channels, boolean alpha) { 261 // Reset the stock image to null. 262 tmpImage = null; 263 264 // Create a new BufferedImage based on the data returned from the 265 // JNI call. 266 DataBuffer dataBuf = new DataBufferByte(data, (rowStride * height)); 267 // Maybe test # channels to determine band offsets? 268 WritableRaster raster = Raster.createInterleavedRaster(dataBuf, 269 width, height, rowStride, channels, 270 (alpha ? BAND_OFFSETS_ALPHA : BAND_OFFSETS), null); 271 ColorModel colorModel = new ComponentColorModel( 272 ColorSpace.getInstance(ColorSpace.CS_sRGB), alpha, false, 273 ColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE); 274 275 // Set the local image so we can return it later from 276 // getStockIcon(). 277 tmpImage = new BufferedImage(colorModel, raster, false, null); 278 } 279 check_gtk(int version)280 private static native boolean check_gtk(int version); load_gtk(int version, boolean verbose)281 private static native boolean load_gtk(int version, boolean verbose); unload_gtk()282 private static native boolean unload_gtk(); load_gtk_icon(String filename)283 private native boolean load_gtk_icon(String filename); load_stock_icon(int widget_type, String stock_id, int iconSize, int textDirection, String detail)284 private native boolean load_stock_icon(int widget_type, String stock_id, 285 int iconSize, int textDirection, String detail); 286 nativeSync()287 private native void nativeSync(); get_gtk_version()288 private static native int get_gtk_version(); 289 290 @Override sync()291 public void sync() { 292 // flush the X11 buffer 293 nativeSync(); 294 // now flush the OGL pipeline (this is a no-op if OGL is not enabled) 295 OGLRenderQueue.sync(); 296 } 297 298 /* 299 * This returns the value for the desktop property "awt.font.desktophints" 300 * It builds this by querying the Gnome desktop properties to return 301 * them as platform independent hints. 302 * This requires that the Gnome properties have already been gathered. 303 */ 304 public static final String FONTCONFIGAAHINT = "fontconfig/Antialias"; 305 306 @Override getDesktopAAHints()307 protected RenderingHints getDesktopAAHints() { 308 309 Object aaValue = getDesktopProperty("gnome.Xft/Antialias"); 310 311 if (aaValue == null) { 312 /* On a KDE desktop running KWin the rendering hint will 313 * have been set as property "fontconfig/Antialias". 314 * No need to parse further in this case. 315 */ 316 aaValue = getDesktopProperty(FONTCONFIGAAHINT); 317 if (aaValue != null) { 318 return new RenderingHints(KEY_TEXT_ANTIALIASING, aaValue); 319 } else { 320 return null; // no Gnome or KDE Desktop properties available. 321 } 322 } 323 324 /* 0 means off, 1 means some ON. What would any other value mean? 325 * If we require "1" to enable AA then some new value would cause 326 * us to default to "OFF". I don't think that's the best guess. 327 * So if its !=0 then lets assume AA. 328 */ 329 boolean aa = ((aaValue instanceof Number) 330 && ((Number) aaValue).intValue() != 0); 331 Object aaHint; 332 if (aa) { 333 String subpixOrder = 334 (String)getDesktopProperty("gnome.Xft/RGBA"); 335 336 if (subpixOrder == null || subpixOrder.equals("none")) { 337 aaHint = VALUE_TEXT_ANTIALIAS_ON; 338 } else if (subpixOrder.equals("rgb")) { 339 aaHint = VALUE_TEXT_ANTIALIAS_LCD_HRGB; 340 } else if (subpixOrder.equals("bgr")) { 341 aaHint = VALUE_TEXT_ANTIALIAS_LCD_HBGR; 342 } else if (subpixOrder.equals("vrgb")) { 343 aaHint = VALUE_TEXT_ANTIALIAS_LCD_VRGB; 344 } else if (subpixOrder.equals("vbgr")) { 345 aaHint = VALUE_TEXT_ANTIALIAS_LCD_VBGR; 346 } else { 347 /* didn't recognise the string, but AA is requested */ 348 aaHint = VALUE_TEXT_ANTIALIAS_ON; 349 } 350 } else { 351 aaHint = VALUE_TEXT_ANTIALIAS_DEFAULT; 352 } 353 return new RenderingHints(KEY_TEXT_ANTIALIASING, aaHint); 354 } 355 gtkCheckVersionImpl(int major, int minor, int micro)356 private native boolean gtkCheckVersionImpl(int major, int minor, 357 int micro); 358 359 /** 360 * Returns {@code true} if the GTK+ library is compatible with the given 361 * version. 362 * 363 * @param major 364 * The required major version. 365 * @param minor 366 * The required minor version. 367 * @param micro 368 * The required micro version. 369 * @return {@code true} if the GTK+ library is compatible with the given 370 * version. 371 */ checkGtkVersion(int major, int minor, int micro)372 public boolean checkGtkVersion(int major, int minor, int micro) { 373 if (loadGTK()) { 374 return gtkCheckVersionImpl(major, minor, micro); 375 } 376 return false; 377 } 378 getEnabledGtkVersion()379 public static GtkVersions getEnabledGtkVersion() { 380 String version = AccessController.doPrivileged( 381 new GetPropertyAction("jdk.gtk.version")); 382 if (version == null) { 383 return GtkVersions.ANY; 384 } else if (version.startsWith("2")) { 385 return GtkVersions.GTK2; 386 } else if("3".equals(version) ){ 387 return GtkVersions.GTK3; 388 } 389 return GtkVersions.ANY; 390 } 391 getGtkVersion()392 public static GtkVersions getGtkVersion() { 393 return GtkVersions.getVersion(get_gtk_version()); 394 } 395 isGtkVerbose()396 public static boolean isGtkVerbose() { 397 return AccessController.doPrivileged((PrivilegedAction<Boolean>)() 398 -> Boolean.getBoolean("jdk.gtk.verbose")); 399 } 400 } 401