1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.graphics;
15 
16 
17 import java.io.*;
18 import java.util.function.*;
19 import java.util.stream.*;
20 
21 import org.eclipse.swt.*;
22 import org.eclipse.swt.internal.*;
23 import org.eclipse.swt.internal.cairo.*;
24 import org.eclipse.swt.internal.gtk.*;
25 import org.eclipse.swt.internal.gtk3.*;
26 import org.eclipse.swt.internal.gtk4.*;
27 
28 /**
29  * This class is the abstract superclass of all device objects,
30  * such as the Display device and the Printer device. Devices
31  * can have a graphics context (GC) created for them, and they
32  * can be drawn on by sending messages to the associated GC.
33  *
34  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
35  */
36 public abstract class Device implements Drawable {
37 	/**
38 	 * @noreference This field is not intended to be referenced by clients.
39 	 * @since 3.105
40 	 */
41 	protected static final int CHANGE_SCALEFACTOR = 1;
42 	/* Settings callbacks */
43 	long gsettingsProc;
44 	Callback gsettingsCallback;
45 	boolean isConnected = false;
46 	long displaySettings; //gsettings Dictionary
47 
48 	/**
49 	 * the handle to the X Display
50 	 * (Warning: This field is platform dependent)
51 	 * <p>
52 	 * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
53 	 * public API. It is marked protected only so that it can be shared
54 	 * within the packages provided by SWT. It is not available on all
55 	 * platforms and should never be accessed from application code.
56 	 * </p>
57 	 *
58 	 * @noreference This field is not intended to be referenced by clients.
59 	 */
60 	protected long xDisplay;
61 	long shellHandle;
62 
63 	/* Debugging */
64 	public static boolean DEBUG;
65 	boolean debug = DEBUG;
66 	boolean tracking = DEBUG;
67 	Error [] errors;
68 	Object [] objects;
69 	Object trackingLock;
70 
71 	/* Disposed flag */
72 	boolean disposed;
73 
74 	/* Warning and Error Handlers */
75 	long logProc;
76 	Callback logCallback;
77 	//NOT DONE - get list of valid names
78 	String [] log_domains = {"", "GLib-GObject", "GLib", "GObject", "Pango", "ATK", "GdkPixbuf", "Gdk", "Gtk", "GnomeVFS", "GIO"};
79 	int [] handler_ids = new int [log_domains.length];
80 	int warningLevel;
81 
82 	/* X Warning and Error Handlers */
83 	static Callback XErrorCallback, XIOErrorCallback;
84 	static long XErrorProc, XIOErrorProc, XNullErrorProc, XNullIOErrorProc;
85 	static Device[] Devices = new Device[4];
86 
87 	/*
88 	* The following colors are listed in the Windows
89 	* Programmer's Reference as the colors in the default
90 	* palette.
91 	*/
92 	Color COLOR_BLACK, COLOR_DARK_RED, COLOR_DARK_GREEN, COLOR_DARK_YELLOW, COLOR_DARK_BLUE;
93 	Color COLOR_DARK_MAGENTA, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY, COLOR_RED, COLOR_TRANSPARENT;
94 	Color COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE;
95 
96 	/* System Font */
97 	Font systemFont;
98 
99 	/* Device dpi */
100 	Point dpi;
101 
102 	long emptyTab;
103 
104 	/*
105 	* TEMPORARY CODE. When a graphics object is
106 	* created and the device parameter is null,
107 	* the current Display is used. This presents
108 	* a problem because SWT graphics does not
109 	* reference classes in SWT widgets. The correct
110 	* fix is to remove this feature. Unfortunately,
111 	* too many application programs rely on this
112 	* feature.
113 	*/
114 	protected static Device CurrentDevice;
115 	protected static Runnable DeviceFinder;
116 	static {
117 		try {
118 			Class.forName ("org.eclipse.swt.widgets.Display");
119 		} catch (ClassNotFoundException e) {}
120 	}
121 
122 /*
123 * TEMPORARY CODE.
124 */
getDevice()125 static synchronized Device getDevice () {
126 	if (DeviceFinder != null) DeviceFinder.run();
127 	Device device = CurrentDevice;
128 	CurrentDevice = null;
129 	return device;
130 }
131 
132 /**
133  * Constructs a new instance of this class.
134  * <p>
135  * You must dispose the device when it is no longer required.
136  * </p>
137  *
138  * @see #create
139  * @see #init
140  *
141  * @since 3.1
142  */
Device()143 public Device() {
144 	this(null);
145 }
146 
147 /**
148  * Constructs a new instance of this class.
149  * <p>
150  * You must dispose the device when it is no longer required.
151  * </p>
152  *
153  * @param data the DeviceData which describes the receiver
154  *
155  * @see #create
156  * @see #init
157  * @see DeviceData
158  */
Device(DeviceData data)159 public Device(DeviceData data) {
160 	synchronized (Device.class) {
161 		if (data != null) {
162 			debug = data.debug;
163 			tracking = data.tracking;
164 		}
165 		if (tracking) {
166 			startTracking();
167 		}
168 		create (data);
169 		init ();
170 		register (this);
171 	}
172 }
173 
174 /**
175  *
176  * @exception SWTException <ul>
177  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
178  * </ul>
179  * @since 3.115
180  */
isTracking()181 public boolean isTracking() {
182 	checkDevice();
183 	return tracking;
184 }
185 
186 /**
187  * @exception SWTException <ul>
188  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
189  * </ul>
190  * @since 3.115
191  */
setTracking(boolean tracking)192 public void setTracking(boolean tracking) {
193 	checkDevice();
194 	if (tracking == this.tracking) {
195 		return;
196 	}
197 	this.tracking = tracking;
198 	if (tracking) {
199 		startTracking();
200 	} else {
201 		stopTracking();
202 	}
203 }
204 
startTracking()205 private void startTracking() {
206 	errors = new Error [128];
207 	objects = new Object [128];
208 	trackingLock = new Object ();
209 }
210 
stopTracking()211 private void stopTracking() {
212 	synchronized (trackingLock) {
213 		objects = null;
214 		errors = null;
215 		trackingLock = null;
216 	}
217 }
218 
219 /**
220  * Throws an <code>SWTException</code> if the receiver can not
221  * be accessed by the caller. This may include both checks on
222  * the state of the receiver and more generally on the entire
223  * execution context. This method <em>should</em> be called by
224  * device implementors to enforce the standard SWT invariants.
225  * <p>
226  * Currently, it is an error to invoke any method (other than
227  * <code>isDisposed()</code> and <code>dispose()</code>) on a
228  * device that has had its <code>dispose()</code> method called.
229  * </p><p>
230  * In future releases of SWT, there may be more or fewer error
231  * checks and exceptions may be thrown for different reasons.
232  * </p>
233  *
234  * @exception SWTException <ul>
235  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
236  * </ul>
237  */
checkDevice()238 protected void checkDevice () {
239 	if (disposed) SWT.error(SWT.ERROR_DEVICE_DISPOSED);
240 }
241 
242 /**
243  * Creates the device in the operating system.  If the device
244  * does not have a handle, this method may do nothing depending
245  * on the device.
246  * <p>
247  * This method is called before <code>init</code>.
248  * </p><p>
249  * Subclasses are supposed to reimplement this method and not
250  * call the <code>super</code> implementation.
251  * </p>
252  *
253  * @param data the DeviceData which describes the receiver
254  *
255  * @see #init
256  */
create(DeviceData data)257 protected void create (DeviceData data) {
258 }
259 
260 /**
261  * Disposes of the operating system resources associated with
262  * the receiver. After this method has been invoked, the receiver
263  * will answer <code>true</code> when sent the message
264  * <code>isDisposed()</code>.
265  *
266  * @see #release
267  * @see #destroy
268  * @see #checkDevice
269  */
dispose()270 public void dispose () {
271 	synchronized (Device.class) {
272 		if (isDisposed()) return;
273 		checkDevice ();
274 		release ();
275 		destroy ();
276 		deregister (this);
277 		xDisplay = 0;
278 		disposed = true;
279 		if (tracking) {
280 			tracking = false;
281 			stopTracking();
282 		}
283 	}
284 }
285 
dispose_Object(Object object)286 void dispose_Object (Object object) {
287 	synchronized (trackingLock) {
288 		for (int i=0; i<objects.length; i++) {
289 			if (objects [i] == object) {
290 				objects [i] = null;
291 				errors [i] = null;
292 				return;
293 			}
294 		}
295 	}
296 }
297 
findDevice(long xDisplay)298 static synchronized Device findDevice (long xDisplay) {
299 	for (int i=0; i<Devices.length; i++) {
300 		Device device = Devices [i];
301 		if (device != null && device.xDisplay == xDisplay) {
302 			return device;
303 		}
304 	}
305 	return null;
306 }
307 
deregister(Device device)308 synchronized static void deregister (Device device) {
309 	for (int i=0; i<Devices.length; i++) {
310 		if (device == Devices [i]) Devices [i] = null;
311 	}
312 }
313 
314 /**
315  * Destroys the device in the operating system and releases
316  * the device's handle.  If the device does not have a handle,
317  * this method may do nothing depending on the device.
318  * <p>
319  * This method is called after <code>release</code>.
320  * </p><p>
321  * Subclasses are supposed to reimplement this method and not
322  * call the <code>super</code> implementation.
323  * </p>
324  *
325  * @see #dispose
326  * @see #release
327  */
destroy()328 protected void destroy () {
329 }
330 
331 /**
332  * Returns a rectangle describing the receiver's size and location.
333  *
334  * @return the bounding rectangle
335  *
336  * @exception SWTException <ul>
337  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
338  * </ul>
339  */
getBounds()340 public Rectangle getBounds () {
341 	checkDevice ();
342 	return DPIUtil.autoScaleDown (getBoundsInPixels ());
343 }
344 
getBoundsInPixels()345 private Rectangle getBoundsInPixels () {
346 	return new Rectangle(0, 0, 0, 0);
347 }
348 
349 /**
350  * Returns a <code>DeviceData</code> based on the receiver.
351  * Modifications made to this <code>DeviceData</code> will not
352  * affect the receiver.
353  *
354  * @return a <code>DeviceData</code> containing the device's data and attributes
355  *
356  * @exception SWTException <ul>
357  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
358  * </ul>
359  *
360  * @see DeviceData
361  */
getDeviceData()362 public DeviceData getDeviceData () {
363 	checkDevice();
364 	DeviceData data = new DeviceData ();
365 	data.debug = debug;
366 	data.tracking = tracking;
367 	if (tracking) {
368 		synchronized (trackingLock) {
369 			int count = 0, length = objects.length;
370 			for (int i=0; i<length; i++) {
371 				if (objects [i] != null) count++;
372 			}
373 			int index = 0;
374 			data.objects = new Object [count];
375 			data.errors = new Error [count];
376 			for (int i=0; i<length; i++) {
377 				if (objects [i] != null) {
378 					data.objects [index] = objects [i];
379 					data.errors [index] = errors [i];
380 					index++;
381 				}
382 			}
383 		}
384 	} else {
385 		data.objects = new Object [0];
386 		data.errors = new Error [0];
387 	}
388 	return data;
389 }
390 
391 /**
392  * Returns a rectangle which describes the area of the
393  * receiver which is capable of displaying data.
394  *
395  * @return the client area
396  *
397  * @exception SWTException <ul>
398  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
399  * </ul>
400  *
401  * @see #getBounds
402  */
getClientArea()403 public Rectangle getClientArea () {
404 	return getBounds ();
405 }
406 
407 /**
408  * Returns the bit depth of the screen, which is the number of
409  * bits it takes to represent the number of unique colors that
410  * the screen is currently capable of displaying. This number
411  * will typically be one of 1, 8, 15, 16, 24 or 32.
412  *
413  * @return the depth of the screen
414  *
415  * @exception SWTException <ul>
416  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
417  * </ul>
418  */
getDepth()419 public int getDepth () {
420 	checkDevice ();
421 	return 0;
422 }
423 
424 /**
425  * Returns a point whose x coordinate is the logical horizontal
426  * dots per inch of the display, and whose y coordinate
427  * is the logical vertical dots per inch of the display.
428  *
429  * @return the horizontal and vertical DPI
430  *
431  * @exception SWTException <ul>
432  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
433  * </ul>
434  */
getDPI()435 public Point getDPI () {
436 	checkDevice ();
437 	return getScreenDPI();
438 }
439 
440 /**
441  * Returns <code>FontData</code> objects which describe
442  * the fonts that match the given arguments. If the
443  * <code>faceName</code> is null, all fonts will be returned.
444  *
445  * @param faceName the name of the font to look for, or null
446  * @param scalable if true only scalable fonts are returned, otherwise only non-scalable fonts are returned.
447  * @return the matching font data
448  *
449  * @exception SWTException <ul>
450  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
451  * </ul>
452  */
getFontList(String faceName, boolean scalable)453 public FontData[] getFontList (String faceName, boolean scalable) {
454 	checkDevice ();
455 	if (!scalable) return new FontData[0];
456 	long [] family = new long [1];
457 	long [] face = new long [1];
458 	long [] families = new long [1];
459 	int[] n_families = new int[1];
460 	long [] faces = new long [1];
461 	int[] n_faces = new int[1];
462 	long context;
463 	if (GTK.GTK4) {
464 		long fontMap = OS.pango_cairo_font_map_get_default ();
465 		context = OS.pango_font_map_create_context (fontMap);
466 	} else {
467 		context = GDK.gdk_pango_context_get();
468 	}
469 	OS.pango_context_list_families(context, families, n_families);
470 	int nFds = 0;
471 	FontData[] fds = new FontData[faceName != null ? 4 : n_families[0]];
472 	for (int i=0; i<n_families[0]; i++) {
473 		C.memmove(family, families[0] + i * C.PTR_SIZEOF, C.PTR_SIZEOF);
474 		boolean match = true;
475 		if (faceName != null) {
476 			long familyName = OS.pango_font_family_get_name(family[0]);
477 			int length = C.strlen(familyName);
478 			byte[] buffer = new byte[length];
479 			C.memmove(buffer, familyName, length);
480 			String name = new String(Converter.mbcsToWcs(buffer));
481 			match = faceName.equalsIgnoreCase(name);
482 		}
483 		if (match) {
484 			OS.pango_font_family_list_faces(family[0], faces, n_faces);
485 			for (int j=0; j<n_faces[0]; j++) {
486 				C.memmove(face, faces[0] + j * C.PTR_SIZEOF, C.PTR_SIZEOF);
487 				long fontDesc = OS.pango_font_face_describe(face[0]);
488 				Font font = Font.gtk_new(this, fontDesc);
489 				FontData data = font.getFontData()[0];
490 				if (nFds == fds.length) {
491 					FontData[] newFds = new FontData[fds.length + n_families[0]];
492 					System.arraycopy(fds, 0, newFds, 0, nFds);
493 					fds = newFds;
494 				}
495 				fds[nFds++] = data;
496 				OS.pango_font_description_free(fontDesc);
497 			}
498 			OS.g_free(faces[0]);
499 			if (faceName != null) break;
500 		}
501 	}
502 	OS.g_free(families[0]);
503 	OS.g_object_unref(context);
504 	if (nFds == fds.length) return fds;
505 	FontData[] result = new FontData[nFds];
506 	System.arraycopy(fds, 0, result, 0, nFds);
507 	return result;
508 }
509 
getScreenDPI()510 Point getScreenDPI () {
511 	Point ptDPI;
512 
513 	if (GTK.GTK4) {
514 		ptDPI = new Point (96, 96);
515 	} else {
516 		long screen = GDK.gdk_screen_get_default();
517 		int dpi = (int) GDK.gdk_screen_get_resolution(screen);
518 		ptDPI = dpi == -1 ? new Point (96, 96) : new Point (dpi, dpi);
519 	}
520 
521 	return ptDPI;
522 }
523 
524 /**
525  * Returns the matching standard color for the given
526  * constant, which should be one of the color constants
527  * specified in class <code>SWT</code>. Any value other
528  * than one of the SWT color constants which is passed
529  * in will result in the color black. This color should
530  * not be freed because it was allocated by the system,
531  * not the application.
532  *
533  * @param id the color constant
534  * @return the matching color
535  *
536  * @exception SWTException <ul>
537  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
538  * </ul>
539  *
540  * @see SWT
541  */
getSystemColor(int id)542 public Color getSystemColor (int id) {
543 	checkDevice ();
544 	switch (id) {
545 		case SWT.COLOR_TRANSPARENT:			return COLOR_TRANSPARENT;
546 		case SWT.COLOR_BLACK: 				return COLOR_BLACK;
547 		case SWT.COLOR_DARK_RED: 			return COLOR_DARK_RED;
548 		case SWT.COLOR_DARK_GREEN:	 		return COLOR_DARK_GREEN;
549 		case SWT.COLOR_DARK_YELLOW: 		return COLOR_DARK_YELLOW;
550 		case SWT.COLOR_DARK_BLUE: 			return COLOR_DARK_BLUE;
551 		case SWT.COLOR_DARK_MAGENTA: 		return COLOR_DARK_MAGENTA;
552 		case SWT.COLOR_DARK_CYAN: 			return COLOR_DARK_CYAN;
553 		case SWT.COLOR_GRAY: 				return COLOR_GRAY;
554 		case SWT.COLOR_DARK_GRAY: 			return COLOR_DARK_GRAY;
555 		case SWT.COLOR_RED: 				return COLOR_RED;
556 		case SWT.COLOR_GREEN: 				return COLOR_GREEN;
557 		case SWT.COLOR_YELLOW: 				return COLOR_YELLOW;
558 		case SWT.COLOR_BLUE: 				return COLOR_BLUE;
559 		case SWT.COLOR_MAGENTA: 			return COLOR_MAGENTA;
560 		case SWT.COLOR_CYAN: 				return COLOR_CYAN;
561 		case SWT.COLOR_WHITE: 				return COLOR_WHITE;
562 	}
563 	return COLOR_BLACK;
564 }
565 
566 /**
567  * Returns a reasonable font for applications to use.
568  * On some platforms, this will match the "default font"
569  * or "system font" if such can be found.  This font
570  * should not be freed because it was allocated by the
571  * system, not the application.
572  * <p>
573  * Typically, applications which want the default look
574  * should simply not set the font on the widgets they
575  * create. Widgets are always created with the correct
576  * default font for the class of user-interface component
577  * they represent.
578  * </p>
579  *
580  * @return a font
581  *
582  * @exception SWTException <ul>
583  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
584  * </ul>
585  */
getSystemFont()586 public Font getSystemFont () {
587 	checkDevice ();
588 	return systemFont;
589 }
590 
591 /**
592  * Returns <code>true</code> if the underlying window system prints out
593  * warning messages on the console, and <code>setWarnings</code>
594  * had previously been called with <code>true</code>.
595  *
596  * @return <code>true</code>if warnings are being handled, and <code>false</code> otherwise
597  *
598  * @exception SWTException <ul>
599  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
600  * </ul>
601  */
getWarnings()602 public boolean getWarnings () {
603 	checkDevice ();
604 	return warningLevel == 0;
605 }
606 
607 /**
608  * Initializes any internal resources needed by the
609  * device.
610  * <p>
611  * This method is called after <code>create</code>.
612  * </p><p>
613  * If subclasses reimplement this method, they must
614  * call the <code>super</code> implementation.
615  * </p>
616  *
617  * @see #create
618  */
init()619 protected void init () {
620 	if (debug) {
621 		if (xDisplay != 0) {
622 			/* Create the warning and error callbacks */
623 			Class<?> clazz = getClass ();
624 			synchronized (clazz) {
625 				int index = 0;
626 				while (index < Devices.length) {
627 					if (Devices [index] != null) break;
628 					index++;
629 				}
630 				if (index == Devices.length) {
631 					XErrorCallback = new Callback (clazz, "XErrorProc", 2);
632 					XNullErrorProc = XErrorCallback.getAddress ();
633 					XIOErrorCallback = new Callback (clazz, "XIOErrorProc", 1);
634 					XNullIOErrorProc = XIOErrorCallback.getAddress ();
635 					XErrorProc = OS.XSetErrorHandler (XNullErrorProc);
636 					XIOErrorProc = OS.XSetIOErrorHandler (XNullIOErrorProc);
637 				}
638 			}
639 			if (debug) OS.XSynchronize (xDisplay, true);
640 		}
641 	}
642 
643 	/* Create GTK warnings and error callbacks */
644 	if (xDisplay != 0) {
645 		logCallback = new Callback (this, "logProc", 4);
646 		logProc = logCallback.getAddress ();
647 
648 		/* Set GTK warning and error handlers */
649 		if (debug) {
650 			int flags = OS.G_LOG_LEVEL_MASK | OS.G_LOG_FLAG_FATAL | OS.G_LOG_FLAG_RECURSION;
651 			for (int i=0; i<log_domains.length; i++) {
652 				byte [] log_domain = Converter.wcsToMbcs (log_domains [i], true);
653 				handler_ids [i] = OS.g_log_set_handler (log_domain, flags, logProc, 0);
654 			}
655 		}
656 	}
657 
658 	/* Create the standard colors */
659 	COLOR_TRANSPARENT = new Color (0xFF, 0xFF,0xFF,0);
660 	COLOR_BLACK = new Color (0, 0,0);
661 	COLOR_DARK_RED = new Color (0x80, 0,0);
662 	COLOR_DARK_GREEN = new Color (0, 0x80,0);
663 	COLOR_DARK_YELLOW = new Color (0x80, 0x80,0);
664 	COLOR_DARK_BLUE = new Color (0, 0,0x80);
665 	COLOR_DARK_MAGENTA = new Color (0x80, 0,0x80);
666 	COLOR_DARK_CYAN = new Color (0, 0x80,0x80);
667 	COLOR_GRAY = new Color (0xC0, 0xC0,0xC0);
668 	COLOR_DARK_GRAY = new Color (0x80, 0x80,0x80);
669 	COLOR_RED = new Color (0xFF, 0,0);
670 	COLOR_GREEN = new Color (0, 0xFF,0);
671 	COLOR_YELLOW = new Color (0xFF, 0xFF,0);
672 	COLOR_BLUE = new Color (0, 0,0xFF);
673 	COLOR_MAGENTA = new Color (0xFF, 0,0xFF);
674 	COLOR_CYAN = new Color (0, 0xFF,0xFF);
675 	COLOR_WHITE = new Color (0xFF, 0xFF,0xFF);
676 
677 	emptyTab = OS.pango_tab_array_new(1, false);
678 	if (emptyTab == 0) SWT.error(SWT.ERROR_NO_HANDLES);
679 	OS.pango_tab_array_set_tab(emptyTab, 0, OS.PANGO_TAB_LEFT, 1);
680 
681 	if (GTK.GTK4) {
682 		shellHandle = GTK4.gtk_window_new();
683 	} else {
684 		shellHandle = GTK3.gtk_window_new (GTK.GTK_WINDOW_TOPLEVEL);
685 	}
686 	if (shellHandle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
687 	GTK.gtk_widget_realize(shellHandle);
688 
689 	this.dpi = getDPI();
690 	DPIUtil.setDeviceZoom (getDeviceZoom ());
691 
692 	if (GTK.GTK_VERSION >= OS.VERSION(3, 22, 0)) {
693 		double sx[] = new double[1];
694 		double sy[] = new double[1];
695 		long gdkResource;
696 		long surface;
697 		if (GTK.GTK4) {
698 			surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_RGB24, 10, 10);
699 		} else {
700 			gdkResource = GDK.gdk_get_default_root_window();
701 			surface = GDK.gdk_window_create_similar_surface(gdkResource, Cairo.CAIRO_CONTENT_COLOR, 10, 10);
702 		}
703 		Cairo.cairo_surface_get_device_scale(surface, sx, sy);
704 		DPIUtil.setUseCairoAutoScale((sx[0]*100) == DPIUtil.getDeviceZoom() || OS.isGNOME);
705 	}
706 
707 	/* Initialize the system font slot */
708 	long [] defaultFontArray = new long [1];
709 	long defaultFont = 0;
710 	long context = GTK.gtk_widget_get_style_context (shellHandle);
711 	if ("ppc64le".equals(System.getProperty("os.arch"))) {
712 		defaultFont = GTK.gtk_style_context_get_font (context, GTK.GTK_STATE_FLAG_NORMAL);
713 	} else {
714 		if (GTK.GTK4) {
715 			long[] fontPtr = new long[1];
716 			long settings = GTK.gtk_settings_get_default ();
717 			OS.g_object_get (settings, GTK.gtk_style_property_font, fontPtr, 0);
718 			if (fontPtr[0] != 0) {
719 				int length = C.strlen(fontPtr[0]);
720 				if (length != 0) {
721 					byte[] fontString = new byte [length + 1];
722 					C.memmove(fontString, fontPtr[0], length);
723 					OS.g_free(fontPtr[0]);
724 					defaultFont = OS.pango_font_description_from_string(fontString);
725 				}
726 			}
727 		} else {
728 			GTK.gtk_style_context_save(context);
729 			GTK.gtk_style_context_set_state(context, GTK.GTK_STATE_FLAG_NORMAL);
730 			GTK.gtk_style_context_get(context, GTK.GTK_STATE_FLAG_NORMAL, GTK.gtk_style_property_font, defaultFontArray, 0);
731 			GTK.gtk_style_context_restore(context);
732 			defaultFont = defaultFontArray [0];
733 		}
734 	}
735 	defaultFont = OS.pango_font_description_copy (defaultFont);
736 	Point dpi = getDPI(), screenDPI = getScreenDPI();
737 	if (dpi.y != screenDPI.y) {
738 		int size = OS.pango_font_description_get_size(defaultFont);
739 		OS.pango_font_description_set_size(defaultFont, size * dpi.y / screenDPI.y);
740 	}
741 	systemFont = Font.gtk_new (this, defaultFont);
742 
743 	//overrideThemeValues();
744 }
745 
746 /**
747  * For functionality & improved looks, we override some CSS theme values with custom values.
748  *
749  * Note about theme load mechanism:
750  * - This method is reached early at start of SWT initialization.
751  *   Later, platform.ui will call OS.setDarkThemePreferred(true), which tells Gtk to use dark theme.
752  *   This has the implication that the system theme can be 'Adwaita' (light), but later be 'darkened'
753  *   by platform.ui. This means that there should not be any color-specific overrides in Adwaita theming
754  *   because 'Adwaita' is used for both light and dark theme.
755  *
756  * Note about light/dark system theme:
757  * - If the System theme is Adwaita (light), eclipse can be forced to be dark with setDarkThemePreferred(true).
758  *   But if the System theme is Adwaita-dark, eclipse cannot be made 'light'.
759  *
760  * Note that much of eclipse 'dark theme' is done by platform.ui's CSS engine, not by SWT.
761  */
overrideThemeValues()762 private void overrideThemeValues () {
763 	long provider = GTK.gtk_css_provider_new();
764 
765 	BiFunction <String, Boolean, String> load = (path, isResource) -> {
766 		try  {
767 			BufferedReader buffer;
768 			if (isResource) {
769 				buffer = new BufferedReader(new InputStreamReader(Device.class.getResourceAsStream(path)));
770 			} else {
771 				buffer = new BufferedReader(new FileReader(new File(path)));
772 			}
773 			return buffer.lines().collect(Collectors.joining("\n"));
774 		} catch (IOException e) {
775 			System.err.println("SWT Warning: Failed to load " + (isResource ? "resource: " : "file: ") + path);
776 			return "";
777 		}
778 	};
779 
780 	StringBuilder combinedCSS = new StringBuilder();
781 
782 	if (!GTK.GTK4) {
783 		// Load functional CSS fixes. Such as keyboard functionality for some widgets.
784 		combinedCSS.append(load.apply("/org/eclipse/swt/internal/gtk/swt_functional_gtk_3_20.css", true));
785 	}
786 
787 	// By default, load CSS theme fixes to overcome things such as excessive padding that breaks SWT otherwise.
788 	// Initially designed for Adwaita light/dark theme, but after investigation other themes (like Ubuntu's Ambiance + dark) seem to benefit from this also.
789 	// 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.
790 	// To turn on this flag, add the following vm argument:  -Dorg.eclipse.swt.internal.gtk.noThemingFixes
791 	// Note:
792 	// - Display.create() may override the theme name. See Display.create() ... OS.getThemeName(..).
793 	// - These fixes should not contain any color information, otherwise it might break a light/dark variant of the theme.
794 	//   Color fixes should be put either into the theme itself or via swt user api.
795 	if (System.getProperty("org.eclipse.swt.internal.gtk.noThemingFixes") == null) {
796 		combinedCSS.append(load.apply("/org/eclipse/swt/internal/gtk/swt_theming_fixes_gtk_3_20.css", true));
797 		if (GTK.GTK_VERSION >= OS.VERSION(3, 24, 5)) {
798 			combinedCSS.append(load.apply("/org/eclipse/swt/internal/gtk/swt_theming_fixes_gtk_3_24_5.css", true));
799 		}
800 	}
801 
802 	// Load CSS from user-defined CSS file.
803 	String additionalCSSPath = System.getProperty("org.eclipse.swt.internal.gtk.cssFile");
804 	if (additionalCSSPath != null){
805 		// Warning:
806 		// - gtk css syntax changed in 3.20. If you load custom css, it could break things depending on gtk version on system.
807 		// - Also, a lot of custom css/themes are buggy and may result in additional console warnings.
808 		combinedCSS.append(load.apply(additionalCSSPath, false));
809 	}
810 
811 	if (GTK.GTK4) {
812 		long display = GDK.gdk_display_get_default();
813 		if (display == 0 || provider == 0) {
814 			System.err.println("SWT Warning: Override of theme values failed. Reason: could not acquire display or provider.");
815 			return;
816 		}
817 		GTK.gtk_style_context_add_provider_for_display (display, provider, GTK.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
818 	} else {
819 		long screen = GDK.gdk_screen_get_default();
820 		if (screen == 0 || provider == 0) {
821 			System.err.println("SWT Warning: Override of theme values failed. Reason: could not acquire screen or provider.");
822 			return;
823 		}
824 		GTK.gtk_style_context_add_provider_for_screen (screen, provider, GTK.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
825 	}
826 	if (GTK.GTK4) {
827 		GTK4.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (combinedCSS.toString(), true), -1);
828 	} else {
829 		GTK3.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (combinedCSS.toString(), true), -1, null);
830 	}
831 }
832 
833 /**
834  * Invokes platform specific functionality to allocate a new GC handle.
835  * <p>
836  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
837  * API for <code>Device</code>. It is marked public only so that it
838  * can be shared within the packages provided by SWT. It is not
839  * available on all platforms, and should never be called from
840  * application code.
841  * </p>
842  *
843  * @param data the platform specific GC data
844  * @return the platform specific GC handle
845  *
846  * @noreference This method is not intended to be referenced by clients.
847  */
848 @Override
internal_new_GC(GCData data)849 public abstract long internal_new_GC (GCData data);
850 
851 /**
852  * Invokes platform specific functionality to dispose a GC handle.
853  * <p>
854  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
855  * API for <code>Device</code>. It is marked public only so that it
856  * can be shared within the packages provided by SWT. It is not
857  * available on all platforms, and should never be called from
858  * application code.
859  * </p>
860  *
861  * @param hDC the platform specific GC handle
862  * @param data the platform specific GC data
863  *
864  * @noreference This method is not intended to be referenced by clients.
865  */
866 @Override
internal_dispose_GC(long hDC, GCData data)867 public abstract void internal_dispose_GC (long hDC, GCData data);
868 
869 /**
870  * Returns <code>true</code> if the device has been disposed,
871  * and <code>false</code> otherwise.
872  * <p>
873  * This method gets the dispose state for the device.
874  * When a device has been disposed, it is an error to
875  * invoke any other method using the device.
876  *
877  * @return <code>true</code> when the device is disposed and <code>false</code> otherwise
878  */
isDisposed()879 public boolean isDisposed () {
880 	synchronized (Device.class) {
881 		return disposed;
882 	}
883 }
884 
885 /**
886  * Loads the font specified by a file.  The font will be
887  * present in the list of fonts available to the application.
888  *
889  * @param path the font file path
890  * @return whether the font was successfully loaded
891  *
892  * @exception SWTException <ul>
893  *    <li>ERROR_NULL_ARGUMENT - if path is null</li>
894  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
895  * </ul>
896  *
897  * @see Font
898  *
899  * @since 3.3
900  */
loadFont(String path)901 public boolean loadFont (String path) {
902 	checkDevice();
903 	if (path == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
904 	byte [] buffer = Converter.wcsToMbcs (path, true);
905 	return OS.FcConfigAppFontAddFile (0, buffer);
906 }
907 
logProc(long log_domain, long log_level, long message, long user_data)908 long logProc (long log_domain, long log_level, long message, long user_data) {
909 	if (DEBUG) {
910 		new Error ().printStackTrace ();
911 	}
912 	if (warningLevel == 0) {
913 		if (DEBUG || debug) {
914 			new Error ().printStackTrace ();
915 		}
916 		OS.g_log_default_handler (log_domain, (int)log_level, message, 0);
917 	}
918 	return 0;
919 }
920 
new_Object(Object object)921 void new_Object (Object object) {
922 	synchronized (trackingLock) {
923 		for (int i=0; i<objects.length; i++) {
924 			if (objects [i] == null) {
925 				objects [i] = object;
926 				errors [i] = new Error ();
927 				return;
928 			}
929 		}
930 		Object [] newObjects = new Object [objects.length + 128];
931 		System.arraycopy (objects, 0, newObjects, 0, objects.length);
932 		newObjects [objects.length] = object;
933 		objects = newObjects;
934 		Error [] newErrors = new Error [errors.length + 128];
935 		System.arraycopy (errors, 0, newErrors, 0, errors.length);
936 		newErrors [errors.length] = new Error ();
937 		errors = newErrors;
938 	}
939 }
940 
register(Device device)941 static synchronized void register (Device device) {
942 	for (int i=0; i<Devices.length; i++) {
943 		if (Devices [i] == null) {
944 			Devices [i] = device;
945 			return;
946 		}
947 	}
948 	Device [] newDevices = new Device [Devices.length + 4];
949 	System.arraycopy (Devices, 0, newDevices, 0, Devices.length);
950 	newDevices [Devices.length] = device;
951 	Devices = newDevices;
952 }
953 
954 /**
955  * Releases any internal resources back to the operating
956  * system and clears all fields except the device handle.
957  * <p>
958  * When a device is destroyed, resources that were acquired
959  * on behalf of the programmer need to be returned to the
960  * operating system.  For example, if the device allocated a
961  * font to be used as the system font, this font would be
962  * freed in <code>release</code>.  Also,to assist the garbage
963  * collector and minimize the amount of memory that is not
964  * reclaimed when the programmer keeps a reference to a
965  * disposed device, all fields except the handle are zero'd.
966  * The handle is needed by <code>destroy</code>.
967  * </p>
968  * This method is called before <code>destroy</code>.
969  * <p>
970  * If subclasses reimplement this method, they must
971  * call the <code>super</code> implementation.
972  * </p>
973  *
974  * @see #dispose
975  * @see #destroy
976  */
release()977 protected void release () {
978 	if (shellHandle != 0) {
979 		if (GTK.GTK4) {
980 			GTK4.gtk_window_destroy(shellHandle);
981 		} else {
982 			GTK3.gtk_widget_destroy(shellHandle);
983 		}
984 	}
985 	shellHandle = 0;
986 
987 	/* Dispose the default font */
988 	if (systemFont != null) systemFont.dispose ();
989 	systemFont = null;
990 
991 	COLOR_BLACK = COLOR_DARK_RED = COLOR_DARK_GREEN = COLOR_DARK_YELLOW = COLOR_DARK_BLUE =
992 	COLOR_DARK_MAGENTA = COLOR_DARK_CYAN = COLOR_GRAY = COLOR_DARK_GRAY = COLOR_RED =
993 	COLOR_GREEN = COLOR_YELLOW = COLOR_BLUE = COLOR_MAGENTA = COLOR_CYAN = COLOR_WHITE = null;
994 
995 	if (emptyTab != 0) OS.pango_tab_array_free(emptyTab);
996 	emptyTab = 0;
997 
998 	/* Free the GTK error and warning handler */
999 	if (xDisplay != 0) {
1000 		for (int i=0; i<handler_ids.length; i++) {
1001 			if (handler_ids [i] != 0) {
1002 				byte [] log_domain = Converter.wcsToMbcs (log_domains [i], true);
1003 				OS.g_log_remove_handler (log_domain, handler_ids [i]);
1004 				handler_ids [i] = 0;
1005 			}
1006 		}
1007 		logCallback.dispose ();  logCallback = null;
1008 		handler_ids = null;  log_domains = null;
1009 		logProc = 0;
1010 	}
1011 	/* Dispose the settings callback */
1012 	if (gsettingsCallback != null) {
1013 		gsettingsCallback.dispose();
1014 		gsettingsCallback = null;
1015 	}
1016 	gsettingsProc = 0;
1017 
1018 
1019 }
1020 
1021 /**
1022  * If the underlying window system supports printing warning messages
1023  * to the console, setting warnings to <code>false</code> prevents these
1024  * messages from being printed. If the argument is <code>true</code> then
1025  * message printing is not blocked.
1026  *
1027  * @param warnings <code>true</code>if warnings should be printed, and <code>false</code> otherwise
1028  *
1029  * @exception SWTException <ul>
1030  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
1031  * </ul>
1032  */
setWarnings(boolean warnings)1033 public void setWarnings (boolean warnings) {
1034 	checkDevice ();
1035 	if (warnings) {
1036 		if (--warningLevel == 0) {
1037 			if (debug) return;
1038 			if (logProc != 0) {
1039 				for (int i=0; i<handler_ids.length; i++) {
1040 					if (handler_ids [i] != 0) {
1041 						byte [] log_domain = Converter.wcsToMbcs (log_domains [i], true);
1042 						OS.g_log_remove_handler (log_domain, handler_ids [i]);
1043 						handler_ids [i] = 0;
1044 					}
1045 				}
1046 			}
1047 		}
1048 	} else {
1049 		if (warningLevel++ == 0) {
1050 			if (debug) return;
1051 			if (logProc != 0) {
1052 				int flags = OS.G_LOG_LEVEL_MASK | OS.G_LOG_FLAG_FATAL | OS.G_LOG_FLAG_RECURSION;
1053 				for (int i=0; i<log_domains.length; i++) {
1054 					byte [] log_domain = Converter.wcsToMbcs (log_domains [i], true);
1055 					handler_ids [i] = OS.g_log_set_handler (log_domain, flags, logProc, 0);
1056 				}
1057 			}
1058 		}
1059 	}
1060 }
1061 
XErrorProc(long xDisplay, long xErrorEvent)1062 static long XErrorProc (long xDisplay, long xErrorEvent) {
1063 	Device device = findDevice (xDisplay);
1064 	if (device != null) {
1065 		if (device.warningLevel == 0) {
1066 			if (DEBUG || device.debug) {
1067 				new SWTError ().printStackTrace ();
1068 			}
1069 			OS.Call (XErrorProc, xDisplay, xErrorEvent);
1070 		}
1071 	} else {
1072 		if (DEBUG) new SWTError ().printStackTrace ();
1073 		OS.Call (XErrorProc, xDisplay, xErrorEvent);
1074 	}
1075 	return 0;
1076 }
1077 
XIOErrorProc(long xDisplay)1078 static long XIOErrorProc (long xDisplay) {
1079 	Device device = findDevice (xDisplay);
1080 	if (device != null) {
1081 		if (DEBUG || device.debug) {
1082 			new SWTError ().printStackTrace ();
1083 		}
1084 	} else {
1085 		if (DEBUG) new SWTError ().printStackTrace ();
1086 	}
1087 	OS.Call (XIOErrorProc, xDisplay, 0);
1088 	return 0;
1089 }
1090 
1091 /**
1092  * Gets the scaling factor from the device and calculates the zoom level.
1093  * @return zoom in percentage
1094  *
1095  * @noreference This method is not intended to be referenced by clients.
1096  * @nooverride This method is not intended to be re-implemented or extended by clients.
1097  * @since 3.105
1098  */
getDeviceZoom()1099 protected int getDeviceZoom() {
1100 	/*
1101 	 * We can hard-code 96 as gdk_screen_get_resolution will always return -1
1102 	 * if gdk_screen_set_resolution has not been called.
1103 	 */
1104 	int dpi = 96;
1105 	if (GTK.GTK_VERSION >= OS.VERSION(3, 22, 0)) {
1106 		long display = GDK.gdk_display_get_default();
1107 		long monitor;
1108 
1109 		if (GTK.GTK4) {
1110 			long surface = GTK4.gtk_native_get_surface(GTK4.gtk_widget_get_native(shellHandle));
1111 			monitor = GDK.gdk_display_get_monitor_at_surface(display, surface);
1112 		} else {
1113 			monitor = GDK.gdk_display_get_monitor_at_point(display, 0, 0);
1114 		}
1115 
1116 		int scale = GDK.gdk_monitor_get_scale_factor(monitor);
1117 		dpi = dpi * scale;
1118 	} else {
1119 		long screen = GDK.gdk_screen_get_default();
1120 		dpi = (int) GDK.gdk_screen_get_resolution (screen);
1121 		if (dpi <= 0) dpi = 96; // gdk_screen_get_resolution returns -1 in case of error
1122 		int monitor_num = GDK.gdk_screen_get_monitor_at_point (screen, 0, 0);
1123 		int scale = GDK.gdk_screen_get_monitor_scale_factor (screen, monitor_num);
1124 		dpi = dpi * scale;
1125 	}
1126 
1127 	return DPIUtil.mapDPIToZoom (dpi);
1128 }
1129 
1130 }
1131