1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.graphics.Device;
14 
15 import org.eclipse.swt.graphics.Color;
16 import org.eclipse.swt.graphics.Font;
17 import org.eclipse.swt.graphics.Drawable;
18 import org.eclipse.swt.graphics.DeviceData;
19 import org.eclipse.swt.graphics.Rectangle;
20 import org.eclipse.swt.graphics.FontData;
21 import org.eclipse.swt.graphics.Font;
22 import org.eclipse.swt.graphics.GCData;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.SWTError;
25 import org.eclipse.swt.internal.Compatibility;
26 import org.eclipse.swt.internal.gtk.OS;
27 import java.lang.all;
28 
29 
30 /**
31  * This class is the abstract superclass of all device objects,
32  * such as the Display device and the Printer device. Devices
33  * can have a graphics context (GC) created for them, and they
34  * can be drawn on by sending messages to the associated GC.
35  *
36  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
37  */
38 public abstract class Device : Drawable {
39     /**
40      * the handle to the X Display
41      * (Warning: This field is platform dependent)
42      * <p>
43      * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
44      * public API. It is marked protected only so that it can be shared
45      * within the packages provided by SWT. It is not available on all
46      * platforms and should never be accessed from application code.
47      * </p>
48      */
49     protected void* xDisplay;
50     GtkWidget* shellHandle;
51 
52     /* Debugging */
53     public static bool DEBUG = true;
54     bool debugging;
55     bool tracking;
56     Exception [] errors;
57     Object [] objects;
58     Object trackingLock;
59 
60     /* Colormap and reference count */
61     GdkColor *[] gdkColors;
62     int [] colorRefCount;
63 
64     /* Disposed flag */
65     bool disposed;
66 
67     /* Warning and Error Handlers */
68     //ptrdiff_t logProcFld;
69     //GLogFunc logCallback;
70     //NOT DONE - get list of valid names
71     String [] log_domains = ["GLib-GObject"[], "GLib", "GObject", "Pango", "ATK", "GdkPixbuf", "Gdk", "Gtk", "GnomeVFS"];
72     int [] handler_ids;// = new int [log_domains.length];
73     int warningLevel;
74 
75     /* X Warning and Error Handlers */
76     static extern(C) int function(void *) mXIOErrorHandler;
77     static extern(C) int function(void *, XErrorEvent *) mXErrorHandler;
78     //static int mXErrorCallback, mXIOErrorCallback;
79 
80     static ptrdiff_t XErrorProc, XIOErrorProc, XNullErrorProc, XNullIOErrorProc;
81     static Device[] Devices;
82 
83     /*
84     * The following colors are listed in the Windows
85     * Programmer's Reference as the colors in the default
86     * palette.
87     */
88     Color COLOR_BLACK, COLOR_DARK_RED, COLOR_DARK_GREEN, COLOR_DARK_YELLOW, COLOR_DARK_BLUE;
89     Color COLOR_DARK_MAGENTA, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY, COLOR_RED;
90     Color COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE;
91 
92     /* System Font */
93     Font systemFont;
94 
95     PangoTabArray* emptyTab;
96 
97     bool useXRender;
98 
99     static bool CAIRO_LOADED;
100 
101     /*
102     * TEMPORARY CODE. When a graphics object is
103     * created and the device parameter is null,
104     * the current Display is used. This presents
105     * a problem because SWT graphics does not
106     * reference classes in SWT widgets. The correct
107     * fix is to remove this feature. Unfortunately,
108     * too many application programs rely on this
109     * feature.
110     *
111     * This code will be removed in the future.
112     */
113     protected static Device CurrentDevice;
114     protected static Runnable DeviceFinder;
115 
116 //synchronized static void static_this(){
117 //    CREATE_LOCK = new Object();
118 //    Devices = new Device[4];
119 //}
120 /*
121 * TEMPORARY CODE.
122 */
123 static Device getDevice () {
124     synchronized {
125         if (DeviceFinder !is null) DeviceFinder.run();
126         Device device = CurrentDevice;
127         CurrentDevice = null;
128         return device;
129     }
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  */
143 public this() {
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  */
159 public this(DeviceData data) {
160     handler_ids = new int [log_domains.length];
161     debugging = DEBUG;
162     tracking = DEBUG;
163 
164     synchronized ( this.classinfo ) {
165         if (data !is null) {
166             debugging = data.debugging;
167             tracking = data.tracking;
168         }
169         if (tracking) {
170             errors = new Exception [128];
171             objects = new Object [128];
172             trackingLock = new Object ();
173         }
174         create (data);
175         init_ ();
176         register (this);
177     }
178 }
179 
180 void checkCairo() {
181 }
182 
183 /**
184  * Throws an <code>SWTException</code> if the receiver can not
185  * be accessed by the caller. This may include both checks on
186  * the state of the receiver and more generally on the entire
187  * execution context. This method <em>should</em> be called by
188  * device implementors to enforce the standard SWT invariants.
189  * <p>
190  * Currently, it is an error to invoke any method (other than
191  * <code>isDisposed()</code> and <code>dispose()</code>) on a
192  * device that has had its <code>dispose()</code> method called.
193  * </p><p>
194  * In future releases of SWT, there may be more or fewer error
195  * checks and exceptions may be thrown for different reasons.
196  * <p>
197  *
198  * @exception SWTException <ul>
199  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
200  * </ul>
201  */
202 protected void checkDevice () {
203     if (disposed) SWT.error(SWT.ERROR_DEVICE_DISPOSED);
204 }
205 
206 /**
207  * Creates the device in the operating system.  If the device
208  * does not have a handle, this method may do nothing depending
209  * on the device.
210  * <p>
211  * This method is called before <code>init</code>.
212  * </p><p>
213  * Subclasses are supposed to reimplement this method and not
214  * call the <code>super</code> implementation.
215  * </p>
216  *
217  * @param data the DeviceData which describes the receiver
218  *
219  * @see #init
220  */
221 protected void create (DeviceData data) {
222 }
223 
224 /**
225  * Disposes of the operating system resources associated with
226  * the receiver. After this method has been invoked, the receiver
227  * will answer <code>true</code> when sent the message
228  * <code>isDisposed()</code>.
229  *
230  * @see #release
231  * @see #destroy
232  * @see #checkDevice
233  */
234 public void dispose () {
235     synchronized (Device.classinfo) {
236         if (isDisposed()) return;
237         checkDevice ();
238         release ();
239         destroy ();
240         deregister (this);
241         xDisplay = null;
242         disposed = true;
243         if (tracking) {
244             synchronized (trackingLock) {
245                 objects = null;
246                 errors = null;
247                 trackingLock = null;
248             }
249         }
250     }
251 }
252 
253 void dispose_Object (Object object) {
254     synchronized (trackingLock) {
255         for (int i=0; i<objects.length; i++) {
256             if (objects [i] is object) {
257                 objects [i] = null;
258                 errors [i] = null;
259                 return;
260             }
261         }
262     }
263 }
264 
265 static Device findDevice (void* xDisplay) {
266     synchronized {
267         for (int i=0; i<Devices.length; i++) {
268             Device device = Devices [i];
269             if (device !is null && device.xDisplay is xDisplay) {
270                 return device;
271             }
272         }
273         return null;
274     }
275 }
276 
277 static void deregister (Device device) {
278     synchronized {   
279         for (int i=0; i<Devices.length; i++) {
280             if (device is Devices [i]) Devices [i] = null;
281         }
282     }
283 }
284 
285 /**
286  * Destroys the device in the operating system and releases
287  * the device's handle.  If the device does not have a handle,
288  * this method may do nothing depending on the device.
289  * <p>
290  * This method is called after <code>release</code>.
291  * </p><p>
292  * Subclasses are supposed to reimplement this method and not
293  * call the <code>super</code> implementation.
294  * </p>
295  *
296  * @see #dispose
297  * @see #release
298  */
299 protected void destroy () {
300 }
301 
302 /**
303  * Returns a rectangle describing the receiver's size and location.
304  *
305  * @return the bounding rectangle
306  *
307  * @exception SWTException <ul>
308  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
309  * </ul>
310  */
311 public Rectangle getBounds () {
312     checkDevice ();
313     return new Rectangle(0, 0, 0, 0);
314 }
315 
316 /**
317  * Returns a <code>DeviceData</code> based on the receiver.
318  * Modifications made to this <code>DeviceData</code> will not
319  * affect the receiver.
320  *
321  * @return a <code>DeviceData</code> containing the device's data and attributes
322  *
323  * @exception SWTException <ul>
324  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
325  * </ul>
326  *
327  * @see DeviceData
328  */
329 public DeviceData getDeviceData () {
330     checkDevice();
331     DeviceData data = new DeviceData ();
332     data.debugging = debugging;
333     data.tracking = tracking;
334     if (tracking) {
335         synchronized (trackingLock) {
336             auto count = 0, length = objects.length;
337             for (ptrdiff_t i=0; i<length; i++) {
338                 if (objects [i] !is null) count++;
339             }
340             int index = 0;
341             data.objects = new Object [count];
342             data.errors = new Exception [count];
343             for (int i=0; i<length; i++) {
344                 if (objects [i] !is null) {
345                     data.objects [index] = objects [i];
346                     data.errors [index] = errors [i];
347                     index++;
348                 }
349             }
350         }
351     } else {
352         data.objects = null;
353         data.errors = null;
354     }
355     return data;
356 }
357 
358 /**
359  * Returns a rectangle which describes the area of the
360  * receiver which is capable of displaying data.
361  *
362  * @return the client area
363  *
364  * @exception SWTException <ul>
365  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
366  * </ul>
367  *
368  * @see #getBounds
369  */
370 public Rectangle getClientArea () {
371     checkDevice ();
372     return getBounds ();
373 }
374 
375 /**
376  * Returns the bit depth of the screen, which is the number of
377  * bits it takes to represent the number of unique colors that
378  * the screen is currently capable of displaying. This number
379  * will typically be one of 1, 8, 15, 16, 24 or 32.
380  *
381  * @return the depth of the screen
382  *
383  * @exception SWTException <ul>
384  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
385  * </ul>
386  */
387 public int getDepth () {
388     checkDevice ();
389     return 0;
390 }
391 
392 /**
393  * Returns a point whose x coordinate is the horizontal
394  * dots per inch of the display, and whose y coordinate
395  * is the vertical dots per inch of the display.
396  *
397  * @return the horizontal and vertical DPI
398  *
399  * @exception SWTException <ul>
400  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
401  * </ul>
402  */
403 public Point getDPI () {
404     checkDevice ();
405     return new Point (72, 72);
406 }
407 
408 /**
409  * Returns <code>FontData</code> objects which describe
410  * the fonts that match the given arguments. If the
411  * <code>faceName</code> is null, all fonts will be returned.
412  *
413  * @param faceName the name of the font to look for, or null
414  * @param scalable if true only scalable fonts are returned, otherwise only non-scalable fonts are returned.
415  * @return the matching font data
416  *
417  * @exception SWTException <ul>
418  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
419  * </ul>
420  */
421 public FontData[] getFontList (String faceName, bool scalable) {
422     checkDevice ();
423     if (!scalable) return new FontData[0];
424     PangoFontFamily* family;
425     PangoFontFace * face;
426     PangoFontFamily** families;
427     int n_families;
428     PangoFontFace ** faces;
429     int n_faces;
430     auto context = OS.gdk_pango_context_get();
431     OS.pango_context_list_families(context, &families, &n_families);
432     int nFds = 0;
433     FontData[] fds = new FontData[faceName !is null ? 4 : n_families];
434     for (int i=0; i<n_families; i++) {
435         family = families[i];
436         bool match = true;
437         if (faceName !is null) {
438             auto familyName = OS.pango_font_family_get_name(family);
439             match = Compatibility.equalsIgnoreCase(faceName, fromStringz( familyName ));
440         }
441         if (match) {
442             OS.pango_font_family_list_faces(family, &faces, &n_faces);
443             for (int j=0; j<n_faces; j++) {
444                 face = faces[j];
445                 auto fontDesc = OS.pango_font_face_describe(face);
446                 Font font = Font.gtk_new(this, fontDesc);
447                 FontData data = font.getFontData()[0];
448                 if (nFds is fds.length) {
449                     FontData[] newFds = new FontData[fds.length + n_families];
450                     System.arraycopy(fds, 0, newFds, 0, nFds);
451                     fds = newFds;
452                 }
453                 fds[nFds++] = data;
454                 OS.pango_font_description_free(fontDesc);
455             }
456             OS.g_free(faces);
457             if (faceName !is null) break;
458         }
459     }
460     OS.g_free(families);
461     OS.g_object_unref(context);
462     if (nFds is fds.length) return fds;
463     FontData[] result = new FontData[nFds];
464     System.arraycopy(fds, 0, result, 0, nFds);
465     return result;
466 }
467 
468 /**
469  * Returns the matching standard color for the given
470  * constant, which should be one of the color constants
471  * specified in class <code>SWT</code>. Any value other
472  * than one of the SWT color constants which is passed
473  * in will result in the color black. This color should
474  * not be freed because it was allocated by the system,
475  * not the application.
476  *
477  * @param id the color constant
478  * @return the matching color
479  *
480  * @exception SWTException <ul>
481  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
482  * </ul>
483  *
484  * @see SWT
485  */
486 public Color getSystemColor (int id) {
487     checkDevice ();
488     switch (id) {
489         case SWT.COLOR_BLACK:               return COLOR_BLACK;
490         case SWT.COLOR_DARK_RED:            return COLOR_DARK_RED;
491         case SWT.COLOR_DARK_GREEN:          return COLOR_DARK_GREEN;
492         case SWT.COLOR_DARK_YELLOW:         return COLOR_DARK_YELLOW;
493         case SWT.COLOR_DARK_BLUE:           return COLOR_DARK_BLUE;
494         case SWT.COLOR_DARK_MAGENTA:        return COLOR_DARK_MAGENTA;
495         case SWT.COLOR_DARK_CYAN:           return COLOR_DARK_CYAN;
496         case SWT.COLOR_GRAY:                return COLOR_GRAY;
497         case SWT.COLOR_DARK_GRAY:           return COLOR_DARK_GRAY;
498         case SWT.COLOR_RED:                 return COLOR_RED;
499         case SWT.COLOR_GREEN:               return COLOR_GREEN;
500         case SWT.COLOR_YELLOW:              return COLOR_YELLOW;
501         case SWT.COLOR_BLUE:                return COLOR_BLUE;
502         case SWT.COLOR_MAGENTA:             return COLOR_MAGENTA;
503         case SWT.COLOR_CYAN:                return COLOR_CYAN;
504         case SWT.COLOR_WHITE:               return COLOR_WHITE;
505         default:
506     }
507     return COLOR_BLACK;
508 }
509 
510 /**
511  * Returns a reasonable font for applications to use.
512  * On some platforms, this will match the "default font"
513  * or "system font" if such can be found.  This font
514  * should not be freed because it was allocated by the
515  * system, not the application.
516  * <p>
517  * Typically, applications which want the default look
518  * should simply not set the font on the widgets they
519  * create. Widgets are always created with the correct
520  * default font for the class of user-interface component
521  * they represent.
522  * </p>
523  *
524  * @return a font
525  *
526  * @exception SWTException <ul>
527  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
528  * </ul>
529  */
530 public Font getSystemFont () {
531     checkDevice ();
532     return systemFont;
533 }
534 
535 /**
536  * Returns <code>true</code> if the underlying window system prints out
537  * warning messages on the console, and <code>setWarnings</code>
538  * had previously been called with <code>true</code>.
539  *
540  * @return <code>true</code>if warnings are being handled, and <code>false</code> otherwise
541  *
542  * @exception SWTException <ul>
543  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
544  * </ul>
545  */
546 public bool getWarnings () {
547     checkDevice ();
548     return warningLevel is 0;
549 }
550 
551 /**
552  * Initializes any internal resources needed by the
553  * device.
554  * <p>
555  * This method is called after <code>create</code>.
556  * </p><p>
557  * If subclasses reimplement this method, they must
558  * call the <code>super</code> implementation.
559  * </p>
560  *
561  * @see #create
562  */
563 protected void init_ () {
564     if (xDisplay !is null) {
565         int event_basep, error_basep;
566         if (OS.XRenderQueryExtension (xDisplay, &event_basep, &error_basep)) {
567             int major_version, minor_version;
568             OS.XRenderQueryVersion (xDisplay, &major_version, &minor_version);
569             useXRender = major_version > 0 || (major_version is 0 && minor_version >= 8);
570         }
571     }
572 
573     if (debugging) {
574         if (xDisplay !is null) {
575             /* Create the warning and error callbacks */
576             synchronized (this.classinfo) {
577                 int index = 0;
578                 while (index < Devices.length) {
579                     if (Devices [index] !is null) break;
580                     index++;
581                 }
582                 if (index is Devices.length) {
583                     OS.XSetErrorHandler ( & XErrorProcFunc );
584                     OS.XSetIOErrorHandler ( & XIOErrorProcFunc );
585                 }
586             }
587             OS.XSynchronize (xDisplay, true);
588         }
589     }
590 
591     /* Create GTK warnings and error callbacks */
592     if (xDisplay !is null) {
593         /* Set GTK warning and error handlers */
594         if (debugging) {
595             int flags = OS.G_LOG_LEVEL_MASK | OS.G_LOG_FLAG_FATAL | OS.G_LOG_FLAG_RECURSION;
596             for (int i=0; i<log_domains.length; i++) {
597                 handler_ids [i] = OS.g_log_set_handler (log_domains [i].toStringzValidPtr(), flags, & logFunction, cast(void*)this);
598             }
599         }
600     }
601 
602     /* Create the standard colors */
603     COLOR_BLACK = new Color (this, 0,0,0);
604     COLOR_DARK_RED = new Color (this, 0x80,0,0);
605     COLOR_DARK_GREEN = new Color (this, 0,0x80,0);
606     COLOR_DARK_YELLOW = new Color (this, 0x80,0x80,0);
607     COLOR_DARK_BLUE = new Color (this, 0,0,0x80);
608     COLOR_DARK_MAGENTA = new Color (this, 0x80,0,0x80);
609     COLOR_DARK_CYAN = new Color (this, 0,0x80,0x80);
610     COLOR_GRAY = new Color (this, 0xC0,0xC0,0xC0);
611     COLOR_DARK_GRAY = new Color (this, 0x80,0x80,0x80);
612     COLOR_RED = new Color (this, 0xFF,0,0);
613     COLOR_GREEN = new Color (this, 0,0xFF,0);
614     COLOR_YELLOW = new Color (this, 0xFF,0xFF,0);
615     COLOR_BLUE = new Color (this, 0,0,0xFF);
616     COLOR_MAGENTA = new Color (this, 0xFF,0,0xFF);
617     COLOR_CYAN = new Color (this, 0,0xFF,0xFF);
618     COLOR_WHITE = new Color (this, 0xFF,0xFF,0xFF);
619 
620     emptyTab = OS.pango_tab_array_new(1, false);
621     if (emptyTab is null) SWT.error(SWT.ERROR_NO_HANDLES);
622     OS.pango_tab_array_set_tab(emptyTab, 0, OS.PANGO_TAB_LEFT, 1);
623 
624     shellHandle = OS.gtk_window_new(OS.GTK_WINDOW_TOPLEVEL);
625     if (shellHandle is null) SWT.error(SWT.ERROR_NO_HANDLES);
626     OS.gtk_widget_realize(shellHandle);
627 
628     /* Initialize the system font slot */
629     systemFont = getSystemFont ();
630 }
631 
632 /**
633  * Invokes platform specific functionality to allocate a new GC handle.
634  * <p>
635  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
636  * API for <code>Device</code>. It is marked public only so that it
637  * can be shared within the packages provided by SWT. It is not
638  * available on all platforms, and should never be called from
639  * application code.
640  * </p>
641  *
642  * @param data the platform specific GC data
643  * @return the platform specific GC handle
644  */
645 public abstract GdkGC* internal_new_GC (GCData data);
646 
647 /**
648  * Invokes platform specific functionality to dispose a GC handle.
649  * <p>
650  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
651  * API for <code>Device</code>. It is marked public only so that it
652  * can be shared within the packages provided by SWT. It is not
653  * available on all platforms, and should never be called from
654  * application code.
655  * </p>
656  *
657  * @param hDC the platform specific GC handle
658  * @param data the platform specific GC data
659  */
660 public abstract void internal_dispose_GC (GdkGC* handle, GCData data);
661 
662 /**
663  * Returns <code>true</code> if the device has been disposed,
664  * and <code>false</code> otherwise.
665  * <p>
666  * This method gets the dispose state for the device.
667  * When a device has been disposed, it is an error to
668  * invoke any other method using the device.
669  *
670  * @return <code>true</code> when the device is disposed and <code>false</code> otherwise
671  */
672 public bool isDisposed () {
673     synchronized (Device.classinfo) {
674         return disposed;
675     }
676 }
677 
678 /**
679  * Loads the font specified by a file.  The font will be
680  * present in the list of fonts available to the application.
681  *
682  * @param path the font file path
683  * @return whether the font was successfully loaded
684  *
685  * @exception SWTException <ul>
686  *    <li>ERROR_NULL_ARGUMENT - if path is null</li>
687  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
688  * </ul>
689  *
690  * @see Font
691  *
692  * @since 3.3
693  */
694 public bool loadFont (String path) {
695     checkDevice();
696     if (path is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
697     return cast(bool) OS.FcConfigAppFontAddFile (null, path.toStringzValidPtr());
698 }
699 
700 private static extern(C) void logFunction (char* log_domain, int log_level, char* message, void* user_data) {
701     Device dev = cast(Device)user_data;
702     if (dev.warningLevel is 0) {
703         if (DEBUG || dev.debugging) {
704             ExceptionPrintStackTrace(new Exception (""));
705         }
706         OS.g_log_default_handler (log_domain, log_level, message, user_data);
707     }
708     return;
709 }
710 
711 void new_Object (Object object) {
712     synchronized (trackingLock) {
713         for (int i=0; i<objects.length; i++) {
714             if (objects [i] is null) {
715                 objects [i] = object;
716                 errors [i] = new Exception ("");
717                 return;
718             }
719         }
720         Object [] newObjects = new Object [objects.length + 128];
721         System.arraycopy (objects, 0, newObjects, 0, objects.length);
722         newObjects [objects.length] = object;
723         objects = newObjects;
724         Exception [] newErrors = new Exception [errors.length + 128];
725         System.arraycopy (errors, 0, newErrors, 0, errors.length);
726         newErrors [errors.length] = new Exception ("");
727         errors = newErrors;
728     }
729 }
730 
731 static void register (Device device) {
732     synchronized {
733         for (int i=0; i<Devices.length; i++) {
734             if (Devices [i] is null) {
735                 Devices [i] = device;
736                 return;
737             }
738         }
739         Device [] newDevices = new Device [Devices.length + 4];
740         System.arraycopy (Devices, 0, newDevices, 0, Devices.length);
741         newDevices [Devices.length] = device;
742         Devices = newDevices;
743     }
744 }
745 
746 /**
747  * Releases any internal resources back to the operating
748  * system and clears all fields except the device handle.
749  * <p>
750  * When a device is destroyed, resources that were acquired
751  * on behalf of the programmer need to be returned to the
752  * operating system.  For example, if the device allocated a
753  * font to be used as the system font, this font would be
754  * freed in <code>release</code>.  Also,to assist the garbage
755  * collector and minimize the amount of memory that is not
756  * reclaimed when the programmer keeps a reference to a
757  * disposed device, all fields except the handle are zero'd.
758  * The handle is needed by <code>destroy</code>.
759  * </p>
760  * This method is called before <code>destroy</code>.
761  * </p><p>
762  * If subclasses reimplement this method, they must
763  * call the <code>super</code> implementation.
764  * </p>
765  *
766  * @see #dispose
767  * @see #destroy
768  */
769 protected void release () {
770     if (shellHandle !is null) OS.gtk_widget_destroy(shellHandle);
771     shellHandle = null;
772 
773     if (gdkColors !is null) {
774         auto colormap = OS.gdk_colormap_get_system();
775         for (int i = 0; i < gdkColors.length; i++) {
776             GdkColor* color = gdkColors [i];
777             if (color !is null) {
778                 while (colorRefCount [i] > 0) {
779                     OS.gdk_colormap_free_colors(colormap, color, 1);
780                     --colorRefCount [i];
781                 }
782             }
783         }
784     }
785     gdkColors = null;
786     colorRefCount = null;
787     COLOR_BLACK = COLOR_DARK_RED = COLOR_DARK_GREEN = COLOR_DARK_YELLOW = COLOR_DARK_BLUE =
788     COLOR_DARK_MAGENTA = COLOR_DARK_CYAN = COLOR_GRAY = COLOR_DARK_GRAY = COLOR_RED =
789     COLOR_GREEN = COLOR_YELLOW = COLOR_BLUE = COLOR_MAGENTA = COLOR_CYAN = COLOR_WHITE = null;
790 
791     if (emptyTab !is null ) OS.pango_tab_array_free(emptyTab);
792     emptyTab = null;
793 
794     /* Free the GTK error and warning handler */
795     if (xDisplay !is null) {
796         for (int i=0; i<handler_ids.length; i++) {
797             if (handler_ids [i] !is 0 ) {
798                 OS.g_log_remove_handler (log_domains [i].toStringzValidPtr(), handler_ids [i]);
799                 handler_ids [i] = 0;
800             }
801         }
802         //logCallback.dispose ();  logCallback = null;
803         handler_ids = null;  log_domains = null;
804         //logProcFld = 0;
805     }
806 }
807 
808 /**
809  * If the underlying window system supports printing warning messages
810  * to the console, setting warnings to <code>false</code> prevents these
811  * messages from being printed. If the argument is <code>true</code> then
812  * message printing is not blocked.
813  *
814  * @param warnings <code>true</code>if warnings should be printed, and <code>false</code> otherwise
815  *
816  * @exception SWTException <ul>
817  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
818  * </ul>
819  */
820 public void setWarnings (bool warnings) {
821     checkDevice ();
822     if (warnings) {
823         if (--warningLevel is 0) {
824             if (debugging) return;
825             for (int i=0; i<handler_ids.length; i++) {
826                 if (handler_ids [i] !is 0) {
827                     OS.g_log_remove_handler (log_domains [i].toStringzValidPtr(), handler_ids [i]);
828                     handler_ids [i] = 0;
829                 }
830             }
831         }
832     } else {
833         if (warningLevel++ is 0) {
834             if (debugging) return;
835             int flags = OS.G_LOG_LEVEL_MASK | OS.G_LOG_FLAG_FATAL | OS.G_LOG_FLAG_RECURSION;
836             for (int i=0; i<log_domains.length; i++) {
837                 handler_ids [i] = OS.g_log_set_handler (log_domains [i].toStringzValidPtr(), flags, & logFunction, cast(void*)this );
838             }
839         }
840     }
841 }
842 
843 private static extern(C) int XErrorProcFunc (void* xDisplay, org.eclipse.swt.internal.gtk.OS.XErrorEvent* xErrorEvent) {
844     Device device = findDevice (xDisplay);
845     if (device !is null) {
846         if (device.warningLevel is 0) {
847             if (DEBUG || device.debugging) {
848                 foreach( msg; (new Exception ("")).info ){
849                     getDwtLogger().error( __FILE__, __LINE__,  "{}", msg );
850                 }
851             }
852             //PORTING_FIXME ??
853             //OS.Call (XErrorProc, xDisplay, xErrorEvent);
854         }
855     } else {
856         if (DEBUG) (new SWTError ()).printStackTrace ();
857         //PORTING_FIXME ??
858         //OS.Call (XErrorProc, xDisplay, xErrorEvent);
859     }
860     return 0;
861 }
862 
863 private static extern(C)  int XIOErrorProcFunc (void* xDisplay) {
864     Device device = findDevice (xDisplay);
865     if (device !is null) {
866         if (DEBUG || device.debugging) {
867             foreach( msg; (new Exception ("")).info ){
868                 getDwtLogger().error( __FILE__, __LINE__,  "trc {}", msg );
869             }
870         }
871     } else {
872         if (DEBUG) {
873             foreach( msg; (new Exception ("")).info ){
874                 getDwtLogger().error( __FILE__, __LINE__,  "{}", msg );
875             }
876         }
877     }
878     //PORTING_FIXME ??
879     //OS.Call (XIOErrorProc, xDisplay, 0);
880     return 0;
881 }
882 
883 }