Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.graphics; import java.io.*; import java.util.function.*; import java.util.stream.*; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.cairo.*; import org.eclipse.swt.internal.gtk.*; /** * This class is the abstract superclass of all device objects, * such as the Display device and the Printer device. Devices * can have a graphics context (GC) created for them, and they * can be drawn on by sending messages to the associated GC. * * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> */ public abstract class Device implements Drawable { /** * @noreference This field is not intended to be referenced by clients. * @since 3.105 */ protected static final int CHANGE_SCALEFACTOR = 1; /* Settings callbacks */ long gsettingsProc; Callback gsettingsCallback; boolean isConnected = false; long displaySettings; //gsettings Dictionary /** * the handle to the X Display * (Warning: This field is platform dependent) * <p> * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT * public API. It is marked protected only so that it can be shared * within the packages provided by SWT. It is not available on all * platforms and should never be accessed from application code. * </p> * * @noreference This field is not intended to be referenced by clients. */ protected long xDisplay; long shellHandle; /* Debugging */ public static boolean DEBUG; boolean debug = DEBUG; boolean tracking = DEBUG; Error[] errors; Object[] objects; Object trackingLock; /* Disposed flag */ boolean disposed; /* Warning and Error Handlers */ long logProc; Callback logCallback; //NOT DONE - get list of valid names String[] log_domains = { "", "GLib-GObject", "GLib", "GObject", "Pango", "ATK", "GdkPixbuf", "Gdk", "Gtk", "GnomeVFS", "GIO" }; int[] handler_ids = new int[log_domains.length]; int warningLevel; /* X Warning and Error Handlers */ static Callback XErrorCallback, XIOErrorCallback; static long XErrorProc, XIOErrorProc, XNullErrorProc, XNullIOErrorProc; static Device[] Devices = new Device[4]; /* * The following colors are listed in the Windows * Programmer's Reference as the colors in the default * palette. */ Color COLOR_BLACK, COLOR_DARK_RED, COLOR_DARK_GREEN, COLOR_DARK_YELLOW, COLOR_DARK_BLUE; Color COLOR_DARK_MAGENTA, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY, COLOR_RED, COLOR_TRANSPARENT; Color COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE; /* System Font */ Font systemFont; /* Device dpi */ Point dpi; /*Device Scale Factor in percentage*/ /** * @noreference This field is not intended to be referenced by clients. * @since 3.105 */ protected int scaleFactor; long emptyTab; /* * TEMPORARY CODE. When a graphics object is * created and the device parameter is null, * the current Display is used. This presents * a problem because SWT graphics does not * reference classes in SWT widgets. The correct * fix is to remove this feature. Unfortunately, * too many application programs rely on this * feature. */ protected static Device CurrentDevice; protected static Runnable DeviceFinder; static { try { Class.forName("org.eclipse.swt.widgets.Display"); } catch (ClassNotFoundException e) { } } /* * TEMPORARY CODE. */ static synchronized Device getDevice() { if (DeviceFinder != null) DeviceFinder.run(); Device device = CurrentDevice; CurrentDevice = null; return device; } /** * Constructs a new instance of this class. * <p> * You must dispose the device when it is no longer required. * </p> * * @see #create * @see #init * * @since 3.1 */ public Device() { this(null); } /** * Constructs a new instance of this class. * <p> * You must dispose the device when it is no longer required. * </p> * * @param data the DeviceData which describes the receiver * * @see #create * @see #init * @see DeviceData */ public Device(DeviceData data) { synchronized (Device.class) { if (data != null) { debug = data.debug; tracking = data.tracking; } if (tracking) { errors = new Error[128]; objects = new Object[128]; trackingLock = new Object(); } create(data); init(); register(this); } } /** * Throws an <code>SWTException</code> if the receiver can not * be accessed by the caller. This may include both checks on * the state of the receiver and more generally on the entire * execution context. This method <em>should</em> be called by * device implementors to enforce the standard SWT invariants. * <p> * Currently, it is an error to invoke any method (other than * <code>isDisposed()</code> and <code>dispose()</code>) on a * device that has had its <code>dispose()</code> method called. * </p><p> * In future releases of SWT, there may be more or fewer error * checks and exceptions may be thrown for different reasons. * </p> * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * </ul> */ protected void checkDevice() { if (disposed) SWT.error(SWT.ERROR_DEVICE_DISPOSED); } /** * Creates the device in the operating system. If the device * does not have a handle, this method may do nothing depending * on the device. * <p> * This method is called before <code>init</code>. * </p><p> * Subclasses are supposed to reimplement this method and not * call the <code>super</code> implementation. * </p> * * @param data the DeviceData which describes the receiver * * @see #init */ protected void create(DeviceData data) { } /** * Disposes of the operating system resources associated with * the receiver. After this method has been invoked, the receiver * will answer <code>true</code> when sent the message * <code>isDisposed()</code>. * * @see #release * @see #destroy * @see #checkDevice */ public void dispose() { synchronized (Device.class) { if (isDisposed()) return; checkDevice(); release(); destroy(); deregister(this); xDisplay = 0; disposed = true; if (tracking) { synchronized (trackingLock) { objects = null; errors = null; trackingLock = null; } } } } void dispose_Object(Object object) { synchronized (trackingLock) { for (int i = 0; i < objects.length; i++) { if (objects[i] == object) { objects[i] = null; errors[i] = null; return; } } } } static synchronized Device findDevice(long xDisplay) { for (int i = 0; i < Devices.length; i++) { Device device = Devices[i]; if (device != null && device.xDisplay == xDisplay) { return device; } } return null; } synchronized static void deregister(Device device) { for (int i = 0; i < Devices.length; i++) { if (device == Devices[i]) Devices[i] = null; } } /** * Destroys the device in the operating system and releases * the device's handle. If the device does not have a handle, * this method may do nothing depending on the device. * <p> * This method is called after <code>release</code>. * </p><p> * Subclasses are supposed to reimplement this method and not * call the <code>super</code> implementation. * </p> * * @see #dispose * @see #release */ protected void destroy() { } /** * Returns a rectangle describing the receiver's size and location. * * @return the bounding rectangle * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> */ public Rectangle getBounds() { checkDevice(); return DPIUtil.autoScaleDown(getBoundsInPixels()); } private Rectangle getBoundsInPixels() { return new Rectangle(0, 0, 0, 0); } /** * Returns a <code>DeviceData</code> based on the receiver. * Modifications made to this <code>DeviceData</code> will not * affect the receiver. * * @return a <code>DeviceData</code> containing the device's data and attributes * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> * * @see DeviceData */ public DeviceData getDeviceData() { checkDevice(); DeviceData data = new DeviceData(); data.debug = debug; data.tracking = tracking; if (tracking) { synchronized (trackingLock) { int count = 0, length = objects.length; for (int i = 0; i < length; i++) { if (objects[i] != null) count++; } int index = 0; data.objects = new Object[count]; data.errors = new Error[count]; for (int i = 0; i < length; i++) { if (objects[i] != null) { data.objects[index] = objects[i]; data.errors[index] = errors[i]; index++; } } } } else { data.objects = new Object[0]; data.errors = new Error[0]; } return data; } /** * Returns a rectangle which describes the area of the * receiver which is capable of displaying data. * * @return the client area * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> * * @see #getBounds */ public Rectangle getClientArea() { return getBounds(); } /** * Returns the bit depth of the screen, which is the number of * bits it takes to represent the number of unique colors that * the screen is currently capable of displaying. This number * will typically be one of 1, 8, 15, 16, 24 or 32. * * @return the depth of the screen * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> */ public int getDepth() { checkDevice(); return 0; } /** * Returns a point whose x coordinate is the logical horizontal * dots per inch of the display, and whose y coordinate * is the logical vertical dots per inch of the display. * * @return the horizontal and vertical DPI * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> */ public Point getDPI() { checkDevice(); return getScreenDPI(); } /** * Returns <code>FontData</code> objects which describe * the fonts that match the given arguments. If the * <code>faceName</code> is null, all fonts will be returned. * * @param faceName the name of the font to look for, or null * @param scalable if true only scalable fonts are returned, otherwise only non-scalable fonts are returned. * @return the matching font data * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> */ public FontData[] getFontList(String faceName, boolean scalable) { checkDevice(); if (!scalable) return new FontData[0]; long[] family = new long[1]; long[] face = new long[1]; long[] families = new long[1]; int[] n_families = new int[1]; long[] faces = new long[1]; int[] n_faces = new int[1]; long context; if (GTK.GTK4) { long fontMap = OS.pango_cairo_font_map_get_default(); context = OS.pango_font_map_create_context(fontMap); } else { context = GDK.gdk_pango_context_get(); } OS.pango_context_list_families(context, families, n_families); int nFds = 0; FontData[] fds = new FontData[faceName != null ? 4 : n_families[0]]; for (int i = 0; i < n_families[0]; i++) { C.memmove(family, families[0] + i * C.PTR_SIZEOF, C.PTR_SIZEOF); boolean match = true; if (faceName != null) { long familyName = OS.pango_font_family_get_name(family[0]); int length = C.strlen(familyName); byte[] buffer = new byte[length]; C.memmove(buffer, familyName, length); String name = new String(Converter.mbcsToWcs(buffer)); match = faceName.equalsIgnoreCase(name); } if (match) { OS.pango_font_family_list_faces(family[0], faces, n_faces); for (int j = 0; j < n_faces[0]; j++) { C.memmove(face, faces[0] + j * C.PTR_SIZEOF, C.PTR_SIZEOF); long fontDesc = OS.pango_font_face_describe(face[0]); Font font = Font.gtk_new(this, fontDesc); FontData data = font.getFontData()[0]; if (nFds == fds.length) { FontData[] newFds = new FontData[fds.length + n_families[0]]; System.arraycopy(fds, 0, newFds, 0, nFds); fds = newFds; } fds[nFds++] = data; OS.pango_font_description_free(fontDesc); } OS.g_free(faces[0]); if (faceName != null) break; } } OS.g_free(families[0]); OS.g_object_unref(context); if (nFds == fds.length) return fds; FontData[] result = new FontData[nFds]; System.arraycopy(fds, 0, result, 0, nFds); return result; } Point getScreenDPI() { int dpi = 96; //default value if (GTK.GTK_VERSION >= OS.VERSION(3, 22, 0)) { long display = GDK.gdk_display_get_default(); long pMonitor = GDK.gdk_display_get_primary_monitor(display); if (pMonitor == 0) { pMonitor = GDK.gdk_display_get_monitor(display, 0); } int widthMM = GDK.gdk_monitor_get_width_mm(pMonitor); if (widthMM == 0) return new Point(dpi, dpi); int scaleFactor = GDK.gdk_monitor_get_scale_factor(pMonitor); GdkRectangle monitorGeometry = new GdkRectangle(); GDK.gdk_monitor_get_geometry(pMonitor, monitorGeometry); dpi = Compatibility.round(254 * monitorGeometry.width * scaleFactor, widthMM * 10); } return new Point(dpi, dpi); } /** * Returns the matching standard color for the given * constant, which should be one of the color constants * specified in class <code>SWT</code>. Any value other * than one of the SWT color constants which is passed * in will result in the color black. This color should * not be freed because it was allocated by the system, * not the application. * * @param id the color constant * @return the matching color * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> * * @see SWT */ public Color getSystemColor(int id) { checkDevice(); switch (id) { case SWT.COLOR_TRANSPARENT: return COLOR_TRANSPARENT; case SWT.COLOR_BLACK: return COLOR_BLACK; case SWT.COLOR_DARK_RED: return COLOR_DARK_RED; case SWT.COLOR_DARK_GREEN: return COLOR_DARK_GREEN; case SWT.COLOR_DARK_YELLOW: return COLOR_DARK_YELLOW; case SWT.COLOR_DARK_BLUE: return COLOR_DARK_BLUE; case SWT.COLOR_DARK_MAGENTA: return COLOR_DARK_MAGENTA; case SWT.COLOR_DARK_CYAN: return COLOR_DARK_CYAN; case SWT.COLOR_GRAY: return COLOR_GRAY; case SWT.COLOR_DARK_GRAY: return COLOR_DARK_GRAY; case SWT.COLOR_RED: return COLOR_RED; case SWT.COLOR_GREEN: return COLOR_GREEN; case SWT.COLOR_YELLOW: return COLOR_YELLOW; case SWT.COLOR_BLUE: return COLOR_BLUE; case SWT.COLOR_MAGENTA: return COLOR_MAGENTA; case SWT.COLOR_CYAN: return COLOR_CYAN; case SWT.COLOR_WHITE: return COLOR_WHITE; } return COLOR_BLACK; } /** * Returns a reasonable font for applications to use. * On some platforms, this will match the "default font" * or "system font" if such can be found. This font * should not be freed because it was allocated by the * system, not the application. * <p> * Typically, applications which want the default look * should simply not set the font on the widgets they * create. Widgets are always created with the correct * default font for the class of user-interface component * they represent. * </p> * * @return a font * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> */ public Font getSystemFont() { checkDevice(); return systemFont; } /** * Returns <code>true</code> if the underlying window system prints out * warning messages on the console, and <code>setWarnings</code> * had previously been called with <code>true</code>. * * @return <code>true</code>if warnings are being handled, and <code>false</code> otherwise * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> */ public boolean getWarnings() { checkDevice(); return warningLevel == 0; } /** * Initializes any internal resources needed by the * device. * <p> * This method is called after <code>create</code>. * </p><p> * If subclasses reimplement this method, they must * call the <code>super</code> implementation. * </p> * * @see #create */ protected void init() { this.dpi = getDPI(); this.scaleFactor = getDeviceZoom(); DPIUtil.setDeviceZoom(scaleFactor); if (debug) { if (xDisplay != 0) { /* Create the warning and error callbacks */ Class<?> clazz = getClass(); synchronized (clazz) { int index = 0; while (index < Devices.length) { if (Devices[index] != null) break; index++; } if (index == Devices.length) { XErrorCallback = new Callback(clazz, "XErrorProc", 2); XNullErrorProc = XErrorCallback.getAddress(); if (XNullErrorProc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); XIOErrorCallback = new Callback(clazz, "XIOErrorProc", 1); XNullIOErrorProc = XIOErrorCallback.getAddress(); if (XNullIOErrorProc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); XErrorProc = OS.XSetErrorHandler(XNullErrorProc); XIOErrorProc = OS.XSetIOErrorHandler(XNullIOErrorProc); } } if (debug) OS.XSynchronize(xDisplay, true); } } /* Create GTK warnings and error callbacks */ if (xDisplay != 0) { logCallback = new Callback(this, "logProc", 4); logProc = logCallback.getAddress(); if (logProc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); /* Set GTK warning and error handlers */ if (debug) { int flags = OS.G_LOG_LEVEL_MASK | OS.G_LOG_FLAG_FATAL | OS.G_LOG_FLAG_RECURSION; for (int i = 0; i < log_domains.length; i++) { byte[] log_domain = Converter.wcsToMbcs(log_domains[i], true); handler_ids[i] = OS.g_log_set_handler(log_domain, flags, logProc, 0); } } } /* Create the standard colors */ COLOR_TRANSPARENT = new Color(this, 0xFF, 0xFF, 0xFF, 0); COLOR_BLACK = new Color(this, 0, 0, 0); COLOR_DARK_RED = new Color(this, 0x80, 0, 0); COLOR_DARK_GREEN = new Color(this, 0, 0x80, 0); COLOR_DARK_YELLOW = new Color(this, 0x80, 0x80, 0); COLOR_DARK_BLUE = new Color(this, 0, 0, 0x80); COLOR_DARK_MAGENTA = new Color(this, 0x80, 0, 0x80); COLOR_DARK_CYAN = new Color(this, 0, 0x80, 0x80); COLOR_GRAY = new Color(this, 0xC0, 0xC0, 0xC0); COLOR_DARK_GRAY = new Color(this, 0x80, 0x80, 0x80); COLOR_RED = new Color(this, 0xFF, 0, 0); COLOR_GREEN = new Color(this, 0, 0xFF, 0); COLOR_YELLOW = new Color(this, 0xFF, 0xFF, 0); COLOR_BLUE = new Color(this, 0, 0, 0xFF); COLOR_MAGENTA = new Color(this, 0xFF, 0, 0xFF); COLOR_CYAN = new Color(this, 0, 0xFF, 0xFF); COLOR_WHITE = new Color(this, 0xFF, 0xFF, 0xFF); emptyTab = OS.pango_tab_array_new(1, false); if (emptyTab == 0) SWT.error(SWT.ERROR_NO_HANDLES); OS.pango_tab_array_set_tab(emptyTab, 0, OS.PANGO_TAB_LEFT, 1); shellHandle = GTK.gtk_window_new(GTK.GTK_WINDOW_TOPLEVEL); if (shellHandle == 0) SWT.error(SWT.ERROR_NO_HANDLES); GTK.gtk_widget_realize(shellHandle); if (GTK.GTK_VERSION >= OS.VERSION(3, 22, 0)) { double sx[] = new double[1]; double sy[] = new double[1]; long gdkResource; long surface; if (GTK.GTK4) { surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_RGB24, 10, 10); } else { gdkResource = GDK.gdk_get_default_root_window(); surface = GDK.gdk_window_create_similar_surface(gdkResource, Cairo.CAIRO_CONTENT_COLOR, 10, 10); } Cairo.cairo_surface_get_device_scale(surface, sx, sy); DPIUtil.setUseCairoAutoScale((sx[0] * 100) == scaleFactor); } /* Initialize the system font slot */ long[] defaultFontArray = new long[1]; long defaultFont; long context = GTK.gtk_widget_get_style_context(shellHandle); if ("ppc64le".equals(System.getProperty("os.arch"))) { defaultFont = GTK.gtk_style_context_get_font(context, GTK.GTK_STATE_FLAG_NORMAL); } else if (GTK.GTK_VERSION >= OS.VERSION(3, 18, 0)) { GTK.gtk_style_context_save(context); GTK.gtk_style_context_set_state(context, GTK.GTK_STATE_FLAG_NORMAL); if (GTK.GTK4) { GTK.gtk_style_context_get(context, GTK.gtk_style_property_font, defaultFontArray, 0); } else { GTK.gtk_style_context_get(context, GTK.GTK_STATE_FLAG_NORMAL, GTK.gtk_style_property_font, defaultFontArray, 0); } GTK.gtk_style_context_restore(context); defaultFont = defaultFontArray[0]; } else { GTK.gtk_style_context_get(context, GTK.GTK_STATE_FLAG_NORMAL, GTK.gtk_style_property_font, defaultFontArray, 0); defaultFont = defaultFontArray[0]; } defaultFont = OS.pango_font_description_copy(defaultFont); Point dpi = getDPI(), screenDPI = getScreenDPI(); if (dpi.y != screenDPI.y) { int size = OS.pango_font_description_get_size(defaultFont); OS.pango_font_description_set_size(defaultFont, size * dpi.y / screenDPI.y); } systemFont = Font.gtk_new(this, defaultFont); overrideThemeValues(); } /** * For functionality & improved looks, we override some CSS theme values with custom values. * * Note about theme load mechanism: * - This method is reached early at start of SWT initialization. * Later, platform.ui will call OS.setDarkThemePreferred(true), which tells Gtk to use dark theme. * This has the implication that the system theme can be 'Adwaita' (light), but later be 'darkened' * by platform.ui. This means that there should not be any color-specific overrides in Adwaita theming * because 'Adwaita' is used for both light and dark theme. * * Note about light/dark system theme: * - If the System theme is Adwaita (light), eclipse can be forced to be dark with setDarkThemePreferred(true). * But if the System theme is Adwaita-dark, eclipse cannot be made 'light'. * * Note that much of eclipse 'dark theme' is done by platform.ui's CSS engine, not by SWT. */ private void overrideThemeValues() { long provider = GTK.gtk_css_provider_new(); BiFunction<String, Boolean, String> load = (path, isResource) -> { try { BufferedReader buffer; if (isResource) { buffer = new BufferedReader(new InputStreamReader(Device.class.getResourceAsStream(path))); } else { buffer = new BufferedReader(new FileReader(new File(path))); } return buffer.lines().collect(Collectors.joining("\n")); } catch (IOException e) { System.err.println("SWT Warning: Failed to load " + (isResource ? "resource: " : "file: ") + path); return ""; } }; StringBuilder combinedCSS = new StringBuilder(); // Load functional CSS fixes. Such as keyboard functionality for some widgets. combinedCSS.append(load.apply(GTK.GTK_VERSION < OS.VERSION(3, 20, 0) ? "/org/eclipse/swt/internal/gtk/swt_functional_gtk_pre_3_20.css" : "/org/eclipse/swt/internal/gtk/swt_functional_gtk_3_20.css", true)); // By default, load CSS theme fixes to overcome things such as excessive padding that breaks SWT otherwise. // Initially designed for Adwaita light/dark theme, but after investigation other themes (like Ubuntu's Ambiance + dark) seem to benefit from this also. // However, a few themes break with these fixes, so we allow them to be turned off by user and allow them to load their own fixes manually instead. // To turn on this flag, add the following vm argument: -Dorg.eclipse.swt.internal.gtk.noThemingFixes // Note: // - Display.create() may override the theme name. See Display.create() ... OS.getThemeName(..). // - These fixes should not contain any color information, otherwise it might break a light/dark variant of the theme. // Color fixes should be put either into the theme itself or via swt user api. if (System.getProperty("org.eclipse.swt.internal.gtk.noThemingFixes") == null) { if (GTK.GTK_VERSION >= OS.VERSION(3, 20, 0)) { combinedCSS .append(load.apply("/org/eclipse/swt/internal/gtk/swt_theming_fixes_gtk_3_20.css", true)); if (GTK.GTK_VERSION >= OS.VERSION(3, 24, 5)) { combinedCSS.append( load.apply("/org/eclipse/swt/internal/gtk/swt_theming_fixes_gtk_3_24_5.css", true)); } } else { combinedCSS.append( load.apply("/org/eclipse/swt/internal/gtk/swt_theming_fixes_gtk_pre_3_20.css", true)); } } // Load CSS from user-defined CSS file. String additionalCSSPath = System.getProperty("org.eclipse.swt.internal.gtk.cssFile"); if (GTK.GTK_VERSION >= OS.VERSION(3, 14, 0) && additionalCSSPath != null) { // Warning: // - gtk css syntax changed in 3.20. If you load custom css, it could break things depending on gtk version on system. // - Also, a lot of custom css/themes are buggy and may result in additional console warnings. combinedCSS.append(load.apply(additionalCSSPath, false)); } if (GTK.GTK4) { long display = GDK.gdk_display_get_default(); if (display == 0 || provider == 0) { System.err.println( "SWT Warning: Override of theme values failed. Reason: could not acquire display or provider."); return; } GTK.gtk_style_context_add_provider_for_display(display, provider, GTK.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } else { long screen = GDK.gdk_screen_get_default(); if (screen == 0 || provider == 0) { System.err.println( "SWT Warning: Override of theme values failed. Reason: could not acquire screen or provider."); return; } GTK.gtk_style_context_add_provider_for_screen(screen, provider, GTK.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } if (GTK.GTK4) { GTK.gtk_css_provider_load_from_data(provider, Converter.wcsToMbcs(combinedCSS.toString(), true), -1); } else { GTK.gtk_css_provider_load_from_data(provider, Converter.wcsToMbcs(combinedCSS.toString(), true), -1, null); } } /** * Invokes platform specific functionality to allocate a new GC handle. * <p> * <b>IMPORTANT:</b> This method is <em>not</em> part of the public * API for <code>Device</code>. It is marked public only so that it * can be shared within the packages provided by SWT. It is not * available on all platforms, and should never be called from * application code. * </p> * * @param data the platform specific GC data * @return the platform specific GC handle * * @noreference This method is not intended to be referenced by clients. */ @Override public abstract long internal_new_GC(GCData data); /** * Invokes platform specific functionality to dispose a GC handle. * <p> * <b>IMPORTANT:</b> This method is <em>not</em> part of the public * API for <code>Device</code>. It is marked public only so that it * can be shared within the packages provided by SWT. It is not * available on all platforms, and should never be called from * application code. * </p> * * @param hDC the platform specific GC handle * @param data the platform specific GC data * * @noreference This method is not intended to be referenced by clients. */ @Override public abstract void internal_dispose_GC(long hDC, GCData data); /** * Returns <code>true</code> if the device has been disposed, * and <code>false</code> otherwise. * <p> * This method gets the dispose state for the device. * When a device has been disposed, it is an error to * invoke any other method using the device. * * @return <code>true</code> when the device is disposed and <code>false</code> otherwise */ public boolean isDisposed() { synchronized (Device.class) { return disposed; } } /** * Loads the font specified by a file. The font will be * present in the list of fonts available to the application. * * @param path the font file path * @return whether the font was successfully loaded * * @exception SWTException <ul> * <li>ERROR_NULL_ARGUMENT - if path is null</li> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> * * @see Font * * @since 3.3 */ public boolean loadFont(String path) { checkDevice(); if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); byte[] buffer = Converter.wcsToMbcs(path, true); return OS.FcConfigAppFontAddFile(0, buffer); } long logProc(long log_domain, long log_level, long message, long user_data) { if (DEBUG) { new Error().printStackTrace(); } if (warningLevel == 0) { if (DEBUG || debug) { new Error().printStackTrace(); } OS.g_log_default_handler(log_domain, (int) log_level, message, 0); } return 0; } void new_Object(Object object) { synchronized (trackingLock) { for (int i = 0; i < objects.length; i++) { if (objects[i] == null) { objects[i] = object; errors[i] = new Error(); return; } } Object[] newObjects = new Object[objects.length + 128]; System.arraycopy(objects, 0, newObjects, 0, objects.length); newObjects[objects.length] = object; objects = newObjects; Error[] newErrors = new Error[errors.length + 128]; System.arraycopy(errors, 0, newErrors, 0, errors.length); newErrors[errors.length] = new Error(); errors = newErrors; } } static synchronized void register(Device device) { for (int i = 0; i < Devices.length; i++) { if (Devices[i] == null) { Devices[i] = device; return; } } Device[] newDevices = new Device[Devices.length + 4]; System.arraycopy(Devices, 0, newDevices, 0, Devices.length); newDevices[Devices.length] = device; Devices = newDevices; } /** * Releases any internal resources back to the operating * system and clears all fields except the device handle. * <p> * When a device is destroyed, resources that were acquired * on behalf of the programmer need to be returned to the * operating system. For example, if the device allocated a * font to be used as the system font, this font would be * freed in <code>release</code>. Also,to assist the garbage * collector and minimize the amount of memory that is not * reclaimed when the programmer keeps a reference to a * disposed device, all fields except the handle are zero'd. * The handle is needed by <code>destroy</code>. * </p> * This method is called before <code>destroy</code>. * <p> * If subclasses reimplement this method, they must * call the <code>super</code> implementation. * </p> * * @see #dispose * @see #destroy */ protected void release() { if (shellHandle != 0) GTK.gtk_widget_destroy(shellHandle); shellHandle = 0; /* Dispose the default font */ if (systemFont != null) systemFont.dispose(); systemFont = null; if (COLOR_BLACK != null) COLOR_BLACK.dispose(); if (COLOR_DARK_RED != null) COLOR_DARK_RED.dispose(); if (COLOR_DARK_GREEN != null) COLOR_DARK_GREEN.dispose(); if (COLOR_DARK_YELLOW != null) COLOR_DARK_YELLOW.dispose(); if (COLOR_DARK_BLUE != null) COLOR_DARK_BLUE.dispose(); if (COLOR_DARK_MAGENTA != null) COLOR_DARK_MAGENTA.dispose(); if (COLOR_DARK_CYAN != null) COLOR_DARK_CYAN.dispose(); if (COLOR_GRAY != null) COLOR_GRAY.dispose(); if (COLOR_DARK_GRAY != null) COLOR_DARK_GRAY.dispose(); if (COLOR_RED != null) COLOR_RED.dispose(); if (COLOR_GREEN != null) COLOR_GREEN.dispose(); if (COLOR_YELLOW != null) COLOR_YELLOW.dispose(); if (COLOR_BLUE != null) COLOR_BLUE.dispose(); if (COLOR_MAGENTA != null) COLOR_MAGENTA.dispose(); if (COLOR_CYAN != null) COLOR_CYAN.dispose(); if (COLOR_WHITE != null) COLOR_WHITE.dispose(); COLOR_BLACK = COLOR_DARK_RED = COLOR_DARK_GREEN = COLOR_DARK_YELLOW = COLOR_DARK_BLUE = COLOR_DARK_MAGENTA = COLOR_DARK_CYAN = COLOR_GRAY = COLOR_DARK_GRAY = COLOR_RED = COLOR_GREEN = COLOR_YELLOW = COLOR_BLUE = COLOR_MAGENTA = COLOR_CYAN = COLOR_WHITE = null; if (emptyTab != 0) OS.pango_tab_array_free(emptyTab); emptyTab = 0; /* Free the GTK error and warning handler */ if (xDisplay != 0) { for (int i = 0; i < handler_ids.length; i++) { if (handler_ids[i] != 0) { byte[] log_domain = Converter.wcsToMbcs(log_domains[i], true); OS.g_log_remove_handler(log_domain, handler_ids[i]); handler_ids[i] = 0; } } logCallback.dispose(); logCallback = null; handler_ids = null; log_domains = null; logProc = 0; } /* Dispose the settings callback */ if (gsettingsCallback != null) { gsettingsCallback.dispose(); gsettingsCallback = null; } gsettingsProc = 0; } /** * If the underlying window system supports printing warning messages * to the console, setting warnings to <code>false</code> prevents these * messages from being printed. If the argument is <code>true</code> then * message printing is not blocked. * * @param warnings <code>true</code>if warnings should be printed, and <code>false</code> otherwise * * @exception SWTException <ul> * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void setWarnings(boolean warnings) { checkDevice(); if (warnings) { if (--warningLevel == 0) { if (debug) return; if (logProc != 0) { for (int i = 0; i < handler_ids.length; i++) { if (handler_ids[i] != 0) { byte[] log_domain = Converter.wcsToMbcs(log_domains[i], true); OS.g_log_remove_handler(log_domain, handler_ids[i]); handler_ids[i] = 0; } } } } } else { if (warningLevel++ == 0) { if (debug) return; if (logProc != 0) { int flags = OS.G_LOG_LEVEL_MASK | OS.G_LOG_FLAG_FATAL | OS.G_LOG_FLAG_RECURSION; for (int i = 0; i < log_domains.length; i++) { byte[] log_domain = Converter.wcsToMbcs(log_domains[i], true); handler_ids[i] = OS.g_log_set_handler(log_domain, flags, logProc, 0); } } } } } static long XErrorProc(long xDisplay, long xErrorEvent) { Device device = findDevice(xDisplay); if (device != null) { if (device.warningLevel == 0) { if (DEBUG || device.debug) { new SWTError().printStackTrace(); } OS.Call(XErrorProc, xDisplay, xErrorEvent); } } else { if (DEBUG) new SWTError().printStackTrace(); OS.Call(XErrorProc, xDisplay, xErrorEvent); } return 0; } static long XIOErrorProc(long xDisplay) { Device device = findDevice(xDisplay); if (device != null) { if (DEBUG || device.debug) { new SWTError().printStackTrace(); } } else { if (DEBUG) new SWTError().printStackTrace(); } OS.Call(XIOErrorProc, xDisplay, 0); return 0; } /** * Returns DPI in x direction. In the modern monitors DPI for * X and Y directions is same. * * @return the horizontal DPI */ int _getDPIx() { return scaleFactor * 96 / 100; } /** * Gets the scaling factor from the device and calculates the zoom level. * @return zoom in percentage * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. * @since 3.105 */ protected int getDeviceZoom() { /* * We can hard-code 96 as gdk_screen_get_resolution will always return -1 * if gdk_screen_set_resolution has not been called. */ int dpi = 96; if (GTK.GTK_VERSION >= OS.VERSION(3, 22, 0)) { long display = GDK.gdk_display_get_default(); long monitor = GDK.gdk_display_get_monitor_at_point(display, 0, 0); int scale = GDK.gdk_monitor_get_scale_factor(monitor); dpi = dpi * scale; } else { long screen = GDK.gdk_screen_get_default(); dpi = (int) GDK.gdk_screen_get_resolution(screen); if (dpi <= 0) dpi = 96; // gdk_screen_get_resolution returns -1 in case of error int monitor_num = GDK.gdk_screen_get_monitor_at_point(screen, 0, 0); int scale = GDK.gdk_screen_get_monitor_scale_factor(screen, monitor_num); dpi = dpi * scale; } return DPIUtil.mapDPIToZoom(dpi); } /** * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. * @since 3.105 */ protected long gsettingsProc(long gobject, long arg1, long user_data) { switch ((int) user_data) { case CHANGE_SCALEFACTOR: this.scaleFactor = getDeviceZoom(); DPIUtil.setDeviceZoom(scaleFactor); } return 0; } }