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