1 module dlangui.platforms.x11.x11app;
2 
3 public import dlangui.core.config;
4 static if (BACKEND_X11):
5 
6 import dlangui.core.logger;
7 import dlangui.core.events;
8 import dlangui.core.files;
9 import dlangui.core.types : toUTF8;
10 import dlangui.graphics.drawbuf;
11 import dlangui.graphics.fonts;
12 import dlangui.graphics.ftfonts;
13 import dlangui.graphics.resources;
14 import dlangui.widgets.styles;
15 import dlangui.widgets.widget;
16 import dlangui.platforms.common.platform;
17 
18 import core.stdc.string;
19 import std.stdio;
20 import std.string;
21 import std.utf;
22 private import core.stdc.config : c_ulong, c_long;
23 
24 import x11.Xlib;
25 import x11.Xutil;
26 import x11.Xtos;
27 import x11.Xatom;
28 import x11.X;
29 
30 static if (ENABLE_OPENGL) {
31     import derelict.opengl3.gl3;
32     import derelict.opengl3.gl;
33     import dlangui.graphics.gldrawbuf;
34     import dlangui.graphics.glsupport;
35     import derelict.opengl3.glx;
36 
37     private __gshared derelict.util.xtypes.XVisualInfo * x11visual;
38     private __gshared Colormap x11cmap;
39     private __gshared bool _gl3Reloaded = false;
40 }
41 
42 //pragma(lib, "X11");
43 
44 alias XWindow = x11.Xlib.Window;
45 alias DWindow = dlangui.platforms.common.platform.Window;
46 
47 private __gshared
48 {
49     Display * x11display;
50     Display * x11display2;
51     int x11screen;
52     XIM xim;
53 
54     string localClipboardContent;
55     bool _enableOpengl = false;
56 
57     Cursor[CursorType.Hand + 1] x11cursors;
58 
59     Atom   atom_UTF8_STRING;
60     Atom   atom_CLIPBOARD;
61     Atom   atom_TARGETS;
62 
63     Atom   atom_WM_PROTOCOLS;
64     Atom   atom_WM_DELETE_WINDOW;
65 
66     Atom   atom_NET_WM_ICON;
67     Atom   atom_NET_WM_NAME;
68     Atom   atom_NET_WM_ICON_NAME;
69 
70     Atom   atom_NET_WM_STATE;
71     Atom   atom_NET_WM_STATE_MODAL;
72     Atom   atom_NET_WM_STATE_MAXIMIZED_VERT;
73     Atom   atom_NET_WM_STATE_MAXIMIZED_HORZ;
74     Atom   atom_NET_WM_STATE_HIDDEN;
75     Atom   atom_NET_WM_STATE_FULLSCREEN;
76 
77     Atom   atom_DLANGUI_TIMER_EVENT;
78     Atom   atom_DLANGUI_TASK_EVENT;
79     Atom   atom_DLANGUI_CLOSE_WINDOW_EVENT;
80     Atom   atom_DLANGUI_CLIPBOARD_BUFFER;
81 }
82 
83 static void setupX11Atoms()
84 {
85     assert(x11display !is null, "X Connection must be established before getting atoms");
86     //TODO: not sure which atoms should be taken with or without onlyIfExists flag
87     atom_UTF8_STRING = XInternAtom(x11display, "UTF8_STRING", True);
88     atom_CLIPBOARD   = XInternAtom(x11display, "CLIPBOARD", True);
89     atom_TARGETS     = XInternAtom(x11display, "TARGETS", True);
90     atom_WM_PROTOCOLS = XInternAtom(x11display, "WM_PROTOCOLS", False);
91     atom_WM_DELETE_WINDOW = XInternAtom(x11display, "WM_DELETE_WINDOW", False);
92     atom_NET_WM_ICON = XInternAtom(x11display, "_NET_WM_ICON", True);
93     atom_NET_WM_NAME = XInternAtom(x11display, "_NET_WM_NAME", True);
94     atom_NET_WM_ICON_NAME = XInternAtom(x11display, "_NET_WM_ICON_NAME", True);
95     atom_NET_WM_STATE = XInternAtom(x11display, "_NET_WM_STATE", True);
96     atom_NET_WM_STATE_MODAL = XInternAtom(x11display, "_NET_WM_STATE_MODAL", True);
97     atom_NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(x11display, "_NET_WM_STATE_MAXIMIZED_VERT", True);
98     atom_NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(x11display, "_NET_WM_STATE_MAXIMIZED_HORZ", True);
99     atom_NET_WM_STATE_HIDDEN = XInternAtom(x11display, "_NET_WM_STATE_HIDDEN", True);
100     atom_NET_WM_STATE_FULLSCREEN = XInternAtom(x11display, "_NET_WM_STATE_FULLSCREEN", True);
101 
102     atom_DLANGUI_TIMER_EVENT     = XInternAtom(x11display, "DLANGUI_TIMER_EVENT", False);
103     atom_DLANGUI_TASK_EVENT     = XInternAtom(x11display, "DLANGUI_TASK_EVENT", False);
104     atom_DLANGUI_CLOSE_WINDOW_EVENT = XInternAtom(x11display, "DLANGUI_CLOSE_WINDOW_EVENT", False);
105     atom_DLANGUI_CLIPBOARD_BUFFER = XInternAtom(x11display, "DLANGUI_CLIPBOARD_BUFFER", False);
106 }
107 
108 // Cursor font constants
109 enum {
110     XC_X_cursor=0,
111     XC_arrow=2,
112     XC_based_arrow_down=4,
113     XC_based_arrow_up=6,
114     XC_boat = 8,
115     XC_bogosity = 10,
116     XC_bottom_left_corner=12,
117     XC_bottom_right_corner=14,
118     XC_bottom_side=16,
119     XC_bottom_tee=18,
120     XC_box_spiral=20,
121     XC_center_ptr=22,
122     XC_circle=24,
123     XC_clock=26,
124     XC_coffee_mug=28,
125     XC_cross=30,
126     XC_cross_reverse=32,
127     XC_crosshair=34,
128     XC_diamond_cross=36,
129     XC_dot=38,
130     XC_dotbox=40,
131     XC_double_arrow=42,
132     XC_draft_large=44,
133     XC_draft_small=46,
134     XC_draped_box=48,
135     XC_exchange=50,
136     XC_fleur=52,
137     XC_gobbler=54,
138     XC_gumby=56,
139     XC_hand1=58,
140     XC_hand2=60,
141     XC_heart=62,
142     XC_icon=64,
143     XC_iron_cross=66,
144     XC_left_ptr=68,
145     XC_left_side=70,
146     XC_left_tee=72,
147     XC_leftbutton=74,
148     XC_ll_angle=76,
149     XC_lr_angle=78,
150     XC_man=80,
151     XC_middlebutton=82,
152     XC_mouse=84,
153     XC_pencil=86,
154     XC_pirate=88,
155     XC_plus=90,
156     XC_question_arrow=92,
157     XC_right_ptr=94,
158     XC_right_side=96,
159     XC_right_tee=98,
160     XC_rightbutton=100,
161     XC_rtl_logo=102,
162     XC_sailboat=104,
163     XC_sb_down_arrow=106,
164     XC_sb_h_double_arrow=108,
165     XC_sb_left_arrow=110,
166     XC_sb_right_arrow=112,
167     XC_sb_up_arrow=114,
168     XC_sb_v_double_arrow=116,
169     XC_shuttle=118,
170     XC_sizing=120,
171     XC_spider=122,
172     XC_spraycan=124,
173     XC_star=126,
174     XC_target=128,
175     XC_tcross=130,
176     XC_top_left_arrow=132,
177     XC_top_left_corner=134,
178     XC_top_right_corner=136,
179     XC_top_side=138,
180     XC_top_tee=140,
181     XC_trek=142,
182     XC_ul_angle=144,
183     XC_umbrella=146,
184     XC_ur_angle=148,
185     XC_watch=150,
186     XC_xterm=152,
187 }
188 
189 private GC createGC(Display* display, XWindow win)
190 {
191     GC gc;                /* handle of newly created GC.  */
192     uint valuemask = GCFunction | GCBackground | GCForeground | GCPlaneMask;        /* which values in 'values' to  */
193     /* check when creating the GC.  */
194     XGCValues values;            /* initial values for the GC.   */
195     values.plane_mask = AllPlanes;
196     int screen_num = DefaultScreen(display);
197     values.function_ = GXcopy;
198     values.background = WhitePixel(display, screen_num);
199     values.foreground = BlackPixel(display, screen_num);
200 
201     gc = XCreateGC(display, win, valuemask, &values);
202     if (!gc) {
203         Log.e("X11: Cannot create GC");
204         return null;
205         //fprintf(stderr, "XCreateGC: \n");
206     }
207 
208     uint line_width = 2;        /* line width for the GC.       */
209     int line_style = LineSolid;        /* style for lines drawing and  */
210     int cap_style = CapButt;        /* style of the line's edje and */
211     int join_style = JoinBevel;        /*  joined lines.        */
212 
213     /* define the style of lines that will be drawn using this GC. */
214     XSetLineAttributes(display, gc,
215         line_width, line_style, cap_style, join_style);
216 
217     /* define the fill style for the GC. to be 'solid filling'. */
218     XSetFillStyle(display, gc, FillSolid);
219 
220     return gc;
221 }
222 
223 class X11Window : DWindow {
224     protected X11Platform _platform;
225     protected dstring _caption;
226     protected XWindow _win;
227     protected GC _gc;
228     private __gshared XIC xic;
229 
230     X11Window[] _children;
231     X11Window _parent;
232 
233     bool _needRedraw;
234     int _cachedWidth, _cachedHeight;
235 
236     static if (ENABLE_OPENGL) {
237         GLXContext _glc;
238     }
239 
240     this(X11Platform platform, dstring caption, DWindow parent, uint flags, uint width = 0, uint height = 0) {
241         _platform = platform;
242         //backgroundColor = 0xFFFFFF;
243         debug Log.d("X11Window: Creating window");
244         if (width == 0)
245             width = 500;
246         if (height == 0)
247             height = 300;
248         _cachedWidth = _dx = width;
249         _cachedHeight = _dy = height;
250         _flags = flags;
251 
252         /* get the colors black and white (see section for details) */
253         c_ulong black, white;
254         black = BlackPixel(x11display, x11screen);    /* get color black */
255         white = WhitePixel(x11display, x11screen);  /* get color white */
256 
257         /* once the display is initialized, create the window.
258                This window will be have be 200 pixels across and 300 down.
259                It will have the foreground white and background black
260         */
261 
262         Log.d("Creating window of size ", _dx, "x", _dy);
263         static if (true) {
264             XSetWindowAttributes swa;
265             uint swamask = CWEventMask | CWBackPixel;
266             swa.background_pixel = white;
267             swa.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
268                 EnterWindowMask | LeaveWindowMask | PointerMotionMask | ButtonMotionMask | ExposureMask | VisibilityChangeMask |
269                     FocusChangeMask | KeymapStateMask | StructureNotifyMask | PropertyChangeMask;
270             Visual * visual = DefaultVisual(x11display, x11screen);
271             int depth = DefaultDepth(x11display, x11screen);
272             static if (ENABLE_OPENGL) {
273                 if (_enableOpengl) {
274                     swamask |= CWColormap;
275                     swa.colormap = x11cmap;
276                     visual = cast(Visual*)x11visual.visual;
277                     depth = x11visual.depth;
278                 }
279             }
280 
281             _win = XCreateWindow(x11display, DefaultRootWindow(x11display),
282                 0, 0,
283                 _dx, _dy, 0,
284                 depth,
285                 InputOutput,
286                 visual,
287                 swamask,
288                 &swa);
289 //            _win = XCreateSimpleWindow(x11display, DefaultRootWindow(x11display),
290 //                0, 0,
291 //                _dx, _dy, 5, black, white);
292         } else {
293             XSetWindowAttributes attr;
294             attr.do_not_propagate_mask = 0;
295             attr.override_redirect = True;
296             attr.cursor = Cursor();
297             attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask;
298             attr.background_pixel = white;
299 
300             _win = XCreateWindow(x11display, DefaultRootWindow(x11display),
301                 0, 0,
302                 _dx, _dy, 5,
303                 CopyFromParent, // depth
304                 CopyFromParent, // class
305                 cast(Visual*)CopyFromParent, // visual
306                 CWEventMask|CWBackPixel|CWCursor|CWDontPropagate,
307                 &attr
308                 );
309             if (!_win)
310                 return;
311 
312         }
313         windowCaption = caption;
314         XSetWMProtocols(x11display, _win, &atom_WM_DELETE_WINDOW, 1);
315         _windowState = WindowState.hidden;
316 
317         _children.reserve(20);
318         _parent = cast(X11Window) parent;
319         if (_parent)
320             _parent._children ~= this;
321 
322         if (!(flags & WindowFlag.Resizable)) {
323             XSizeHints sizeHints;
324             sizeHints.min_width = width;
325             sizeHints.min_height = height;
326             sizeHints.max_width = width;
327             sizeHints.max_height = height;
328             sizeHints.flags = PMaxSize | PMinSize;
329             XSetWMNormalHints(x11display, _win, &sizeHints);
330         }
331         if (flags & WindowFlag.Fullscreen) {
332             if (atom_NET_WM_STATE_FULLSCREEN != None) {
333                 changeWindowState(_NET_WM_STATE_ADD, atom_NET_WM_STATE_FULLSCREEN);
334             }
335             else
336                 Log.w("Missing _NET_WM_STATE_FULLSCREEN atom");
337         }
338         if (flags & WindowFlag.Modal) {
339             if (_parent) {
340                 XSetTransientForHint(x11display, _win, _parent._win);
341             } else {
342                 Log.w("Top-level modal window");
343             }
344             if (atom_NET_WM_STATE_MODAL != None) {
345                 changeWindowState(_NET_WM_STATE_ADD, atom_NET_WM_STATE_MODAL);
346             } else {
347                 Log.w("Missing _NET_WM_STATE_MODAL atom");
348             }
349         }
350         /* this routine determines which types of input are allowed in
351                the input.  see the appropriate section for details...
352         */
353 //        XSelectInput(x11display, _win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
354 //            EnterWindowMask | LeaveWindowMask | PointerMotionMask | ButtonMotionMask | ExposureMask | VisibilityChangeMask |
355 //            FocusChangeMask | KeymapStateMask | StructureNotifyMask);
356 
357         /* create the Graphics Context */
358         _gc = createGC(x11display, _win);
359         //_gc = XCreateGC(x11display, _win, 0, cast(XGCValues*)null);
360         //Log.d("X11Window: windowId=", _win, " gc=", _gc);
361 
362 
363 
364 
365         /* here is another routine to set the foreground and background
366                colors _currently_ in use in the window.
367         */
368         //XSetBackground(x11display, _gc, white);
369         //XSetForeground(x11display, _gc, black);
370 
371         /* clear the window and bring it on top of the other windows */
372         //XClearWindow(x11display, _win);
373         //XFlush(x11display);
374 
375         handleWindowStateChange(WindowState.unspecified, Rect(0, 0, _dx, _dy));
376 
377         if (platform.defaultWindowIcon.length != 0)
378             windowIcon = drawableCache.getImage(platform.defaultWindowIcon);
379     }
380 
381     ~this() {
382         debug Log.d("Destroying X11 window");
383         if (timer) {
384             timer.stop();
385         }
386         if (_parent) {
387             import std.algorithm : countUntil, remove;
388             ptrdiff_t index = countUntil(_parent._children,this);
389             if (index > -1 ) {
390                 _parent._children = _parent._children.remove(index);
391             }
392             _parent = null;
393         }
394         static if (ENABLE_OPENGL) {
395             if (_glc) {
396                 glXDestroyContext(x11display, _glc);
397                 _glc = null;
398             }
399         }
400         if (_drawbuf)
401             destroy(_drawbuf);
402         if (_gc) {
403             XFreeGC(x11display, _gc);
404             _gc = null;
405         }
406         if (_win) {
407             XDestroyWindow(x11display, _win);
408             _win = 0;
409         }
410     }
411 
412     /// show window
413     override void show() {
414         Log.d("X11Window.show");
415         XMapRaised(x11display, _win);
416         XFlush(x11display);
417         static if (ENABLE_OPENGL) {
418             if (_enableOpengl) {
419                 _glc = glXCreateContext(x11display, x11visual, null, GL_TRUE);
420                 if (!_glc) {
421                     _enableOpengl = false;
422                 } else {
423                     glXMakeCurrent(x11display, cast(uint)_win, _glc);
424                     _enableOpengl = initGLSupport(_platform.GLVersionMajor < 3);
425                     if (!_enableOpengl && _glc) {
426                         glXDestroyContext(x11display, _glc);
427                         _glc = null;
428                     }
429                 }
430             }
431             if (_enableOpengl) {
432                 Log.d("Open GL support is enabled");
433             } else {
434                 Log.d("Open GL support is disabled");
435             }
436         }
437         if (_mainWidget) {
438             _mainWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED);
439             if (flags & WindowFlag.MeasureSize)
440                 resizeWindow(Point(_mainWidget.measuredWidth, _mainWidget.measuredHeight));
441             else
442                 adjustWindowOrContentSize(_mainWidget.measuredWidth, _mainWidget.measuredHeight);
443 
444             adjustPositionDuringShow();
445 
446             _mainWidget.setFocus();
447         }
448     }
449 
450     override protected void handleWindowStateChange(WindowState newState, Rect newWindowRect = RECT_VALUE_IS_NOT_SET) {
451         super.handleWindowStateChange(newState, newWindowRect);
452     }
453 
454     protected final void changeWindowState(int action, Atom firstProperty, Atom secondProperty = None) nothrow
455     {
456         XEvent ev;
457         memset(&ev, 0, ev.sizeof);
458         ev.xany.type = ClientMessage;
459         ev.xclient.window = _win;
460         ev.xclient.message_type = atom_NET_WM_STATE;
461         ev.xclient.format = 32;
462         ev.xclient.data.l[0] = action;
463         ev.xclient.data.l[1] = firstProperty;
464         if (secondProperty != None)
465             ev.xclient.data.l[2] = secondProperty;
466         ev.xclient.data.l[3] = 0;
467         XSendEvent(x11display, RootWindow(x11display, x11screen), false, SubstructureNotifyMask|SubstructureRedirectMask, &ev);
468     }
469 
470     protected enum {
471         _NET_WM_STATE_REMOVE = 0,
472         _NET_WM_STATE_ADD,
473         _NET_WM_STATE_TOGGLE
474     }
475 
476     override bool setWindowState(WindowState newState, bool activate = false, Rect newWindowRect = RECT_VALUE_IS_NOT_SET) {
477         if (_win == None) {
478             return false;
479         }
480         bool result = false;
481         switch(newState) {
482             case WindowState.maximized:
483                 if (atom_NET_WM_STATE != None && atom_NET_WM_STATE_MAXIMIZED_HORZ != None && atom_NET_WM_STATE_MAXIMIZED_VERT != None) {
484                     changeWindowState(_NET_WM_STATE_ADD, atom_NET_WM_STATE_MAXIMIZED_HORZ, atom_NET_WM_STATE_MAXIMIZED_VERT);
485                     result = true;
486                 }
487                 break;
488             case WindowState.minimized:
489                 if (atom_NET_WM_STATE != None && atom_NET_WM_STATE_HIDDEN != None) {
490                     changeWindowState(_NET_WM_STATE_ADD, atom_NET_WM_STATE_HIDDEN);
491                     result = true;
492                 }
493                 break;
494             case WindowState.hidden:
495                 XUnmapWindow(x11display, _win);
496                 result = true;
497                 break;
498             case WindowState.normal:
499                 if (atom_NET_WM_STATE != None &&
500                     atom_NET_WM_STATE_MAXIMIZED_HORZ != None &&
501                     atom_NET_WM_STATE_MAXIMIZED_VERT != None &&
502                     atom_NET_WM_STATE_HIDDEN != None)
503                 {
504                     changeWindowState(_NET_WM_STATE_REMOVE, atom_NET_WM_STATE_MAXIMIZED_HORZ, atom_NET_WM_STATE_MAXIMIZED_VERT);
505                     changeWindowState(_NET_WM_STATE_REMOVE, atom_NET_WM_STATE_HIDDEN);
506                     changeWindowState(_NET_WM_STATE_REMOVE, atom_NET_WM_STATE_FULLSCREEN);
507                     result = true;
508                 }
509                 break;
510             case WindowState.fullscreen:
511                 if (atom_NET_WM_STATE != None && atom_NET_WM_STATE_FULLSCREEN != None) {
512                     changeWindowState(_NET_WM_STATE_ADD, atom_NET_WM_STATE_FULLSCREEN);
513                     result = true;
514                 }
515                 break;
516             default:
517                 break;
518         }
519 
520         // change size and/or position
521         bool rectChanged = false;
522         if (newWindowRect != RECT_VALUE_IS_NOT_SET && (newState == WindowState.normal || newState == WindowState.unspecified)) {
523             // change position
524             if (newWindowRect.top != int.min && newWindowRect.left != int.min) {
525                 XMoveWindow(x11display, _win, newWindowRect.left, newWindowRect.top);
526                 rectChanged = true;
527                 result = true;
528             }
529 
530             // change size
531             if (newWindowRect.bottom != int.min && newWindowRect.right != int.min) {
532                 if (!(flags & WindowFlag.Resizable)) {
533                     XSizeHints sizeHints;
534                     sizeHints.min_width = newWindowRect.right;
535                     sizeHints.min_height = newWindowRect.bottom;
536                     sizeHints.max_width = newWindowRect.right;
537                     sizeHints.max_height = newWindowRect.bottom;
538                     sizeHints.flags = PMaxSize | PMinSize;
539                     XSetWMNormalHints(x11display, _win, &sizeHints);
540                 }
541                 XResizeWindow(x11display, _win, newWindowRect.right, newWindowRect.bottom);
542                 rectChanged = true;
543                 result = true;
544             }
545         }
546 
547         if (activate) {
548             XMapRaised(x11display, _win);
549             result = true;
550         }
551         XFlush(x11display);
552 
553         //needed here to make _windowRect and _windowState valid
554         //example: change size by resizeWindow() and make some calculations using windowRect
555         if (rectChanged) {
556             handleWindowStateChange(newState, Rect(newWindowRect.left == int.min ? _windowRect.left : newWindowRect.left,
557                 newWindowRect.top == int.min ? _windowRect.top : newWindowRect.top, newWindowRect.right == int.min ? _windowRect.right : newWindowRect.right,
558                 newWindowRect.bottom == int.min ? _windowRect.bottom : newWindowRect.bottom));
559         }
560         else
561             handleWindowStateChange(newState, RECT_VALUE_IS_NOT_SET);
562 
563 
564         return result;
565     }
566 
567     override @property DWindow parentWindow() {
568         return _parent;
569     }
570 
571     private bool _isActive;
572     override protected void handleWindowActivityChange(bool isWindowActive) {
573         _isActive = isWindowActive;
574         super.handleWindowActivityChange(isWindowActive);
575     }
576 
577     override @property bool isActive() {
578         return _isActive;
579     }
580 
581     override @property dstring windowCaption() {
582         return _caption;
583     }
584 
585     override @property void windowCaption(dstring caption) {
586         _caption = caption;
587         auto captionc = _caption.toUTF8;
588         auto captionz = cast(ubyte*)captionc.toStringz;
589         XTextProperty nameProperty;
590         nameProperty.value = captionz;
591         nameProperty.encoding = atom_UTF8_STRING;
592         nameProperty.format = 8;
593         nameProperty.nitems = cast(uint)captionc.length;
594         XStoreName(x11display, _win, cast(char*)captionz); // this may not support unicode
595         XSetWMName(x11display, _win, &nameProperty);
596         XChangeProperty(x11display, _win, atom_NET_WM_NAME, atom_UTF8_STRING, 8, PropModeReplace, captionz, cast(int)captionc.length);
597         //XFlush(x11display); //TODO: not sure if XFlush is required
598     }
599 
600     /// sets window icon
601     override @property void windowIcon(DrawBufRef buf) {
602         ColorDrawBuf icon = cast(ColorDrawBuf)buf.get;
603         if (!icon) {
604             Log.e("Trying to set null icon for window");
605             return;
606         }
607         immutable int iconw = 32;
608         immutable int iconh = 32;
609         ColorDrawBuf iconDraw = new ColorDrawBuf(iconw, iconh);
610         scope(exit) destroy(iconDraw);
611         iconDraw.fill(0xFF000000);
612         iconDraw.drawRescaled(Rect(0, 0, iconw, iconh), icon, Rect(0, 0, icon.width, icon.height));
613         iconDraw.invertAndPreMultiplyAlpha();
614         c_long[] propData = new c_long[2 + iconw * iconh];
615         propData[0] = iconw;
616         propData[1] = iconh;
617         auto iconData = iconDraw.scanLine(0);
618         foreach(i; 0..iconw*iconh) {
619             propData[i+2] = iconData[i];
620         }
621         XChangeProperty(x11display, _win, atom_NET_WM_ICON, XA_CARDINAL, 32, PropModeReplace, cast(ubyte*)propData.ptr, cast(int)propData.length);
622     }
623 
624     /// request window redraw
625     override void invalidate() {
626         if (!_needRedraw) {
627             debug(x11) Log.d("Window.invalidate()");
628             _needRedraw = true;
629         }
630     }
631 
632     /// close window
633     override void close() {
634         Log.d("X11Window.close()");
635         _platform.closeWindow(this);
636     }
637 
638     ColorDrawBuf _drawbuf;
639     protected void drawUsingBitmap() {
640         if (_dx > 0 && _dy > 0) {
641             //Log.d("drawUsingBitmap()");
642             // prepare drawbuf
643             if (_drawbuf is null)
644                 _drawbuf = new ColorDrawBuf(_dx, _dy);
645             else
646                 _drawbuf.resize(_dx, _dy);
647             _drawbuf.resetClipping();
648             // draw widgets into buffer
649             _drawbuf.fill(backgroundColor);
650             onDraw(_drawbuf);
651             // draw buffer on X11 window
652             XImage img;
653             img.width = _drawbuf.width;
654             img.height = _drawbuf.height;
655             img.xoffset = 0;
656             img.format = ZPixmap;
657             img.data = cast(char*)_drawbuf.scanLine(0);
658             img.bitmap_unit = 32;
659             img.bitmap_pad = 32;
660             img.bitmap_bit_order = LSBFirst;
661             img.depth = 24;
662             img.chars_per_line = _drawbuf.width * 4;
663             img.bits_per_pixel = 32;
664             img.red_mask = 0xFF0000;
665             img.green_mask = 0x00FF00;
666             img.blue_mask = 0x0000FF;
667             XInitImage(&img);
668             //XSetClipOrigin(x11display, _gc, 0, 0);
669             XPutImage(x11display, _win,
670                 _gc, //DefaultGC(x11display, DefaultScreen(x11display)),
671                 &img,
672                 0, 0, 0, 0,
673                 _drawbuf.width,
674                 _drawbuf.height);
675             //XFlush(x11display); // no need to XFlush since it will be called in event loop
676         }
677     }
678 
679     protected void drawUsingOpengl() {
680         static if (ENABLE_OPENGL) {
681             //Log.d("drawUsingOpengl()");
682             glXMakeCurrent(x11display, cast(uint)_win, _glc);
683             glDisable(GL_DEPTH_TEST);
684             glViewport(0, 0, _dx, _dy);
685             float a = 1.0f;
686             float r = ((_backgroundColor >> 16) & 255) / 255.0f;
687             float g = ((_backgroundColor >> 8) & 255) / 255.0f;
688             float b = ((_backgroundColor >> 0) & 255) / 255.0f;
689             glClearColor(r, g, b, a);
690             glClear(GL_COLOR_BUFFER_BIT);
691             GLDrawBuf buf = new GLDrawBuf(_dx, _dy, false);
692             buf.beforeDrawing();
693             onDraw(buf);
694             buf.afterDrawing();
695             glXSwapBuffers(x11display, cast(uint)_win);
696             destroy(buf);
697         }
698     }
699 
700     void redraw() {
701         _needRedraw = false;
702         //Use values cached by ConfigureNotify to avoid XGetWindowAttributes call.
703         //XWindowAttributes window_attributes_return;
704         //XGetWindowAttributes(x11display, _win, &window_attributes_return);
705         //Log.d(format("XGetWindowAttributes reported size %d, %d", window_attributes_return.width, window_attributes_return.height));
706         immutable width = _cachedWidth;
707         immutable height = _cachedHeight;
708         if (width > 0 && height > 0)
709             onResize(width, height);
710         debug(x11) Log.d(format("redraw(%d, %d)", width, height));
711         if (_enableOpengl)
712             drawUsingOpengl();
713         else
714             drawUsingBitmap();
715     }
716 
717     protected ButtonDetails _lbutton;
718     protected ButtonDetails _mbutton;
719     protected ButtonDetails _rbutton;
720 
721     ushort convertMouseFlags(uint flags) {
722         ushort res = 0;
723         if (flags & Button1Mask)
724             res |= MouseFlag.LButton;
725         if (flags & Button2Mask)
726             res |= MouseFlag.RButton;
727         if (flags & Button3Mask)
728             res |= MouseFlag.MButton;
729         return res;
730     }
731 
732     MouseButton convertMouseButton(uint button) {
733         if (button == Button1)
734             return MouseButton.Left;
735         if (button == Button2)
736             return MouseButton.Right;
737         if (button == Button3)
738             return MouseButton.Middle;
739         return MouseButton.None;
740     }
741 
742     ushort lastFlags;
743     short lastx;
744     short lasty;
745     uint _keyFlags;
746     void processMouseEvent(MouseAction action, uint button, uint state, int x, int y) {
747         MouseEvent event = null;
748         if (action == MouseAction.Wheel) {
749             // handle wheel
750             short wheelDelta = cast(short)y;
751             if (_keyFlags & KeyFlag.Shift)
752                 lastFlags |= MouseFlag.Shift;
753             else
754                 lastFlags &= ~MouseFlag.Shift;
755             if (_keyFlags & KeyFlag.Control)
756                 lastFlags |= MouseFlag.Control;
757             else
758                 lastFlags &= ~MouseFlag.Control;
759             if (_keyFlags & KeyFlag.Alt)
760                 lastFlags |= MouseFlag.Alt;
761             else
762                 lastFlags &= ~MouseFlag.Alt;
763             if (wheelDelta)
764                 event = new MouseEvent(action, MouseButton.None, lastFlags, lastx, lasty, wheelDelta);
765         } else {
766             lastFlags = convertMouseFlags(state);
767             if (_keyFlags & KeyFlag.Shift)
768                 lastFlags |= MouseFlag.Shift;
769             if (_keyFlags & KeyFlag.Control)
770                 lastFlags |= MouseFlag.Control;
771             if (_keyFlags & KeyFlag.Alt)
772                 lastFlags |= MouseFlag.Alt;
773             lastx = cast(short)x;
774             lasty = cast(short)y;
775             MouseButton btn = convertMouseButton(button);
776             event = new MouseEvent(action, btn, lastFlags, lastx, lasty);
777         }
778         if (event) {
779             ButtonDetails * pbuttonDetails = null;
780             if (button == MouseButton.Left)
781                 pbuttonDetails = &_lbutton;
782             else if (button == MouseButton.Right)
783                 pbuttonDetails = &_rbutton;
784             else if (button == MouseButton.Middle)
785                 pbuttonDetails = &_mbutton;
786             if (pbuttonDetails) {
787                 if (action == MouseAction.ButtonDown) {
788                     pbuttonDetails.down(cast(short)x, cast(short)y, lastFlags);
789                 } else if (action == MouseAction.ButtonUp) {
790                     pbuttonDetails.up(cast(short)x, cast(short)y, lastFlags);
791                 }
792             }
793             event.lbutton = _lbutton;
794             event.rbutton = _rbutton;
795             event.mbutton = _mbutton;
796             bool res = dispatchMouseEvent(event);
797             if (res) {
798                 debug(mouse) Log.d("Calling update() after mouse event");
799                 update();
800                 //invalidate();
801             }
802         }
803     }
804 
805     uint convertKeyCode(uint keyCode) {
806         import x11.keysymdef;
807         alias KeyCode = dlangui.core.events.KeyCode;
808         switch(keyCode) {
809             case XK_0:
810                 return KeyCode.KEY_0;
811             case XK_1:
812                 return KeyCode.KEY_1;
813             case XK_2:
814                 return KeyCode.KEY_2;
815             case XK_3:
816                 return KeyCode.KEY_3;
817             case XK_4:
818                 return KeyCode.KEY_4;
819             case XK_5:
820                 return KeyCode.KEY_5;
821             case XK_6:
822                 return KeyCode.KEY_6;
823             case XK_7:
824                 return KeyCode.KEY_7;
825             case XK_8:
826                 return KeyCode.KEY_8;
827             case XK_9:
828                 return KeyCode.KEY_9;
829             case XK_A:
830             case XK_a:
831                 return KeyCode.KEY_A;
832             case XK_B:
833             case XK_b:
834                 return KeyCode.KEY_B;
835             case XK_C:
836             case XK_c:
837                 return KeyCode.KEY_C;
838             case XK_D:
839             case XK_d:
840                 return KeyCode.KEY_D;
841             case XK_E:
842             case XK_e:
843                 return KeyCode.KEY_E;
844             case XK_F:
845             case XK_f:
846                 return KeyCode.KEY_F;
847             case XK_G:
848             case XK_g:
849                 return KeyCode.KEY_G;
850             case XK_H:
851             case XK_h:
852                 return KeyCode.KEY_H;
853             case XK_I:
854             case XK_i:
855                 return KeyCode.KEY_I;
856             case XK_J:
857             case XK_j:
858                 return KeyCode.KEY_J;
859             case XK_K:
860             case XK_k:
861                 return KeyCode.KEY_K;
862             case XK_L:
863             case XK_l:
864                 return KeyCode.KEY_L;
865             case XK_M:
866             case XK_m:
867                 return KeyCode.KEY_M;
868             case XK_N:
869             case XK_n:
870                 return KeyCode.KEY_N;
871             case XK_O:
872             case XK_o:
873                 return KeyCode.KEY_O;
874             case XK_P:
875             case XK_p:
876                 return KeyCode.KEY_P;
877             case XK_Q:
878             case XK_q:
879                 return KeyCode.KEY_Q;
880             case XK_R:
881             case XK_r:
882                 return KeyCode.KEY_R;
883             case XK_S:
884             case XK_s:
885                 return KeyCode.KEY_S;
886             case XK_T:
887             case XK_t:
888                 return KeyCode.KEY_T;
889             case XK_U:
890             case XK_u:
891                 return KeyCode.KEY_U;
892             case XK_V:
893             case XK_v:
894                 return KeyCode.KEY_V;
895             case XK_W:
896             case XK_w:
897                 return KeyCode.KEY_W;
898             case XK_X:
899             case XK_x:
900                 return KeyCode.KEY_X;
901             case XK_Y:
902             case XK_y:
903                 return KeyCode.KEY_Y;
904             case XK_Z:
905             case XK_z:
906                 return KeyCode.KEY_Z;
907             case XK_F1:
908                 return KeyCode.F1;
909             case XK_F2:
910                 return KeyCode.F2;
911             case XK_F3:
912                 return KeyCode.F3;
913             case XK_F4:
914                 return KeyCode.F4;
915             case XK_F5:
916                 return KeyCode.F5;
917             case XK_F6:
918                 return KeyCode.F6;
919             case XK_F7:
920                 return KeyCode.F7;
921             case XK_F8:
922                 return KeyCode.F8;
923             case XK_F9:
924                 return KeyCode.F9;
925             case XK_F10:
926                 return KeyCode.F10;
927             case XK_F11:
928                 return KeyCode.F11;
929             case XK_F12:
930                 return KeyCode.F12;
931             case XK_F13:
932                 return KeyCode.F13;
933             case XK_F14:
934                 return KeyCode.F14;
935             case XK_F15:
936                 return KeyCode.F15;
937             case XK_F16:
938                 return KeyCode.F16;
939             case XK_F17:
940                 return KeyCode.F17;
941             case XK_F18:
942                 return KeyCode.F18;
943             case XK_F19:
944                 return KeyCode.F19;
945             case XK_F20:
946                 return KeyCode.F20;
947             case XK_F21:
948                 return KeyCode.F21;
949             case XK_F22:
950                 return KeyCode.F22;
951             case XK_F23:
952                 return KeyCode.F23;
953             case XK_F24:
954                 return KeyCode.F24;
955             case XK_BackSpace:
956                 return KeyCode.BACK;
957             case XK_space:
958                 return KeyCode.SPACE;
959             case XK_Tab:
960                 return KeyCode.TAB;
961             case XK_Return:
962             case XK_KP_Enter:
963                 return KeyCode.RETURN;
964             case XK_Escape:
965                 return KeyCode.ESCAPE;
966             case XK_KP_Delete:
967             case XK_Delete:
968             //case 0x40000063: // dirty hack for Linux - key on keypad
969                 return KeyCode.DEL;
970             case XK_Insert:
971             case XK_KP_Insert:
972                 //case 0x40000062: // dirty hack for Linux - key on keypad
973                 return KeyCode.INS;
974             case XK_KP_Home:
975             case XK_Home:
976             //case 0x4000005f: // dirty hack for Linux - key on keypad
977                 return KeyCode.HOME;
978             case XK_KP_Page_Up:
979             case XK_Page_Up:
980             //case 0x40000061: // dirty hack for Linux - key on keypad
981                 return KeyCode.PAGEUP;
982             case XK_KP_End:
983             case XK_End:
984             //case 0x40000059: // dirty hack for Linux - key on keypad
985                 return KeyCode.END;
986             case XK_KP_Page_Down:
987             case XK_Page_Down:
988             //case 0x4000005b: // dirty hack for Linux - key on keypad
989                 return KeyCode.PAGEDOWN;
990             case XK_KP_Left:
991             case XK_Left:
992             //case 0x4000005c: // dirty hack for Linux - key on keypad
993                 return KeyCode.LEFT;
994             case XK_KP_Right:
995             case XK_Right:
996             //case 0x4000005e: // dirty hack for Linux - key on keypad
997                 return KeyCode.RIGHT;
998             case XK_KP_Up:
999             case XK_Up:
1000             //case 0x40000060: // dirty hack for Linux - key on keypad
1001                 return KeyCode.UP;
1002             case XK_KP_Down:
1003             case XK_Down:
1004             //case 0x4000005a: // dirty hack for Linux - key on keypad
1005                 return KeyCode.DOWN;
1006             case XK_Control_L:
1007                 return KeyCode.LCONTROL;
1008             case XK_Shift_L:
1009                 return KeyCode.LSHIFT;
1010             case XK_Alt_L:
1011                 return KeyCode.LALT;
1012             case XK_Control_R:
1013                 return KeyCode.RCONTROL;
1014             case XK_Shift_R:
1015                 return KeyCode.RSHIFT;
1016             case XK_Alt_R:
1017                 return KeyCode.RALT;
1018             case XK_slash:
1019             case XK_KP_Divide:
1020                 return KeyCode.KEY_DIVIDE;
1021             default:
1022                 return 0x10000 | keyCode;
1023         }
1024     }
1025 
1026     uint convertKeyFlags(uint flags) {
1027         uint res;
1028         if (flags & ControlMask)
1029             res |= KeyFlag.Control;
1030         if (flags & ShiftMask)
1031             res |= KeyFlag.Shift;
1032         if (flags & LockMask)
1033             res |= KeyFlag.Alt;
1034 //        if (flags & KMOD_RCTRL)
1035 //            res |= KeyFlag.RControl | KeyFlag.Control;
1036 //        if (flags & KMOD_RSHIFT)
1037 //            res |= KeyFlag.RShift | KeyFlag.Shift;
1038 //        if (flags & KMOD_RALT)
1039 //            res |= KeyFlag.RAlt | KeyFlag.Alt;
1040 //        if (flags & KMOD_LCTRL)
1041 //            res |= KeyFlag.LControl | KeyFlag.Control;
1042 //        if (flags & KMOD_LSHIFT)
1043 //            res |= KeyFlag.LShift | KeyFlag.Shift;
1044 //        if (flags & KMOD_LALT)
1045 //            res |= KeyFlag.LAlt | KeyFlag.Alt;
1046         return res;
1047     }
1048 
1049 
1050     bool processKeyEvent(KeyAction action, uint keyCode, uint flags) {
1051         //debug(DebugSDL)
1052         Log.d("processKeyEvent ", action, " X11 key=0x", format("%08x", keyCode), " X11 flags=0x", format("%08x", flags));
1053         keyCode = convertKeyCode(keyCode);
1054         flags = convertKeyFlags(flags);
1055         Log.d("processKeyEvent ", action, " converted key=0x", format("%08x", keyCode), " flags=0x", format("%08x", flags));
1056 
1057         alias KeyCode = dlangui.core.events.KeyCode;
1058         if (action == KeyAction.KeyDown) {
1059             switch(keyCode) {
1060                 case KeyCode.ALT:
1061                     flags |= KeyFlag.Alt;
1062                     break;
1063                 case KeyCode.RALT:
1064                     flags |= KeyFlag.Alt | KeyFlag.RAlt;
1065                     break;
1066                 case KeyCode.LALT:
1067                     flags |= KeyFlag.Alt | KeyFlag.LAlt;
1068                     break;
1069                 case KeyCode.CONTROL:
1070                     flags |= KeyFlag.Control;
1071                     break;
1072                 case KeyCode.RCONTROL:
1073                     flags |= KeyFlag.Control | KeyFlag.RControl;
1074                     break;
1075                 case KeyCode.LCONTROL:
1076                     flags |= KeyFlag.Control | KeyFlag.LControl;
1077                     break;
1078                 case KeyCode.SHIFT:
1079                     flags |= KeyFlag.Shift;
1080                     break;
1081                 case KeyCode.RSHIFT:
1082                     flags |= KeyFlag.Shift | KeyFlag.RShift;
1083                     break;
1084                 case KeyCode.LSHIFT:
1085                     flags |= KeyFlag.Shift | KeyFlag.LShift;
1086                     break;
1087                 default:
1088                     break;
1089             }
1090         }
1091         _keyFlags = flags;
1092 
1093         debug(DebugSDL) Log.d("processKeyEvent ", action, " converted key=0x", format("%08x", keyCode), " converted flags=0x", format("%08x", flags));
1094         bool res = dispatchKeyEvent(new KeyEvent(action, keyCode, flags));
1095         //            if ((keyCode & 0x10000) && (keyCode & 0xF000) != 0xF000) {
1096         //                dchar[1] text;
1097         //                text[0] = keyCode & 0xFFFF;
1098         //                res = dispatchKeyEvent(new KeyEvent(KeyAction.Text, keyCode, flags, cast(dstring)text)) || res;
1099         //            }
1100         if (res) {
1101             debug(keys) Log.d("Calling update() after key event");
1102             //invalidate();
1103             update();
1104         }
1105         return res;
1106     }
1107 
1108     bool processTextInput(dstring ds, uint flags) {
1109         flags = convertKeyFlags(flags);
1110         bool res = dispatchKeyEvent(new KeyEvent(KeyAction.Text, 0, flags, ds));
1111         if (res) {
1112             debug(keys) Log.d("Calling update() after text event");
1113             invalidate();
1114         }
1115         return res;
1116     }
1117 
1118     /// after drawing, call to schedule redraw if animation is active
1119     override void scheduleAnimation() {
1120         invalidate();
1121     }
1122 
1123     TimerThread timer;
1124     private long _nextExpectedTimerTs;
1125 
1126     /// schedule timer for interval in milliseconds - call window.onTimer when finished
1127     override protected void scheduleSystemTimer(long intervalMillis) {
1128         if (!timer) {
1129             timer = new TimerThread(delegate() {
1130                 XEvent ev;
1131                 memset(&ev, 0, ev.sizeof);
1132                 //ev.xclient = XClientMessageEvent.init;
1133                 ev.xclient.type = ClientMessage;
1134                 ev.xclient.message_type = atom_DLANGUI_TIMER_EVENT;
1135                 ev.xclient.window = _win;
1136                 ev.xclient.display = x11display2;
1137                 ev.xclient.format = 32;
1138                 //Log.d("Sending timer event");
1139                 XLockDisplay(x11display2);
1140                 XSendEvent(x11display2, _win, false, StructureNotifyMask, &ev);
1141                 XFlush(x11display2);
1142                 XUnlockDisplay(x11display2);
1143             });
1144         }
1145         if (intervalMillis < 10)
1146             intervalMillis = 10;
1147         long nextts = currentTimeMillis + intervalMillis;
1148         if (_nextExpectedTimerTs == 0 || _nextExpectedTimerTs > nextts) {
1149             _nextExpectedTimerTs = nextts;
1150             timer.set(nextts);
1151         }
1152     }
1153 
1154     bool handleTimer() {
1155         if (!_nextExpectedTimerTs)
1156             return false;
1157         long ts = currentTimeMillis;
1158         if (ts >= _nextExpectedTimerTs) {
1159             _nextExpectedTimerTs = 0;
1160             onTimer();
1161             return true;
1162         }
1163         return false;
1164     }
1165 
1166     /// post event to handle in UI thread (this method can be used from background thread)
1167     override void postEvent(CustomEvent event) {
1168         super.postEvent(event);
1169         XEvent ev;
1170         memset(&ev, 0, ev.sizeof);
1171         ev.xclient.type = ClientMessage;
1172         ev.xclient.window = _win;
1173         ev.xclient.display = x11display2;
1174         ev.xclient.message_type = atom_DLANGUI_TASK_EVENT;
1175         ev.xclient.format = 32;
1176         ev.xclient.data.l[0] = event.uniqueId;
1177         XLockDisplay(x11display2);
1178         XSendEvent(x11display2, _win, false, StructureNotifyMask, &ev);
1179         XFlush(x11display2);
1180         XUnlockDisplay(x11display2);
1181         //        SDL_Event sdlevent;
1182 //        sdlevent.user.type = USER_EVENT_ID;
1183 //        sdlevent.user.code = cast(int)event.uniqueId;
1184 //        sdlevent.user.windowID = windowId;
1185 //        SDL_PushEvent(&sdlevent);
1186     }
1187 
1188     protected uint _lastCursorType = CursorType.None;
1189     /// sets cursor type for window
1190     override protected void setCursorType(uint cursorType) {
1191         if (_lastCursorType != cursorType) {
1192             Log.d("setCursorType(", cursorType, ")");
1193             _lastCursorType = cursorType;
1194             XDefineCursor(x11display, _win, x11cursors[cursorType]);
1195             XFlush(x11display);
1196         }
1197     }
1198 }
1199 
1200 private immutable int CUSTOM_EVENT = 32;
1201 private immutable int TIMER_EVENT = 8;
1202 
1203 class X11Platform : Platform {
1204 
1205     this() {
1206     }
1207 
1208     private X11Window[XWindow] _windowMap;
1209     private X11Window[] _windowList;
1210 
1211     /**
1212      * create window
1213      * Args:
1214      *         windowCaption = window caption text
1215      *         parent = parent Window, or null if no parent
1216      *         flags = WindowFlag bit set, combination of Resizable, Modal, Fullscreen
1217      *      width = window width
1218      *      height = window height
1219      *
1220      * Window w/o Resizable nor Fullscreen will be created with size based on measurement of its content widget
1221      */
1222     override DWindow createWindow(dstring windowCaption, DWindow parent, uint flags = WindowFlag.Resizable, uint width = 0, uint height = 0) {
1223         int newwidth = width;
1224         int newheight = height;
1225         X11Window window = new X11Window(this, windowCaption, parent, flags, newwidth, newheight);
1226         _windowMap[window._win] = window;
1227         _windowList ~= window;
1228         return window;
1229     }
1230 
1231     X11Window findWindow(XWindow windowId) {
1232         if (windowId in _windowMap)
1233             return _windowMap[windowId];
1234         return null;
1235     }
1236 
1237     /**
1238      * close window
1239      *
1240      * Closes window earlier created with createWindow()
1241      */
1242     override void closeWindow(DWindow w) {
1243         X11Window window = cast(X11Window)w;
1244         XEvent ev;
1245         memset(&ev, 0, ev.sizeof);
1246         ev.xclient.type = ClientMessage;
1247         ev.xclient.message_type = atom_DLANGUI_CLOSE_WINDOW_EVENT;
1248         ev.xclient.window = window._win;
1249         ev.xclient.display = x11display2;
1250         ev.xclient.format = 32;
1251         Log.d("Sending close window event");
1252         XLockDisplay(x11display2);
1253         XSendEvent(x11display2, window._win, false, StructureNotifyMask, &ev);
1254         XFlush(x11display2);
1255         XUnlockDisplay(x11display2);
1256 
1257         for (uint i = 0; i < _windowList.length; i++) {
1258             if (w is _windowList[i]) {
1259                 for (uint j = i; j + 1 < _windowList.length; j++)
1260                     _windowList[j] = _windowList[j + 1];
1261                 _windowList[$ - 1] = null;
1262                 _windowList.length--;
1263                 break;
1264             }
1265         }
1266 
1267     }
1268 
1269     bool handleTimers() {
1270         bool handled = false;
1271         foreach(w; _windowMap) {
1272             if (w.handleTimer()) {
1273                 handled = true;
1274                 break;
1275             }
1276         }
1277         return handled;
1278     }
1279 
1280     final bool allWindowsClosed() {
1281         return _windowMap.length == 0;
1282     }
1283 
1284     /**
1285      * Starts application message loop.
1286      *
1287      * When returned from this method, application is shutting down.
1288      */
1289     override int enterMessageLoop() {
1290         import core.thread;
1291         XEvent event;        /* the XEvent declaration !!! */
1292         KeySym key;        /* a dealie-bob to handle KeyPress Events */
1293         char[255] text;        /* a char buffer for KeyPress Events */
1294 
1295         Log.d("enterMessageLoop()");
1296         XComposeStatus compose;
1297 
1298         import core.sys.posix.sys.select;
1299         int x11displayFd = ConnectionNumber(x11display);
1300         fd_set fdSet;
1301         FD_ZERO(&fdSet);
1302         FD_SET(x11displayFd, &fdSet);
1303         scope(exit) FD_ZERO(&fdSet);
1304         while(!allWindowsClosed()) {
1305             // Note:  only events we set the mask for are detected!
1306             foreach(win; _windowMap) {
1307                 if (win._needRedraw) {
1308                     win.redraw();
1309                 }
1310             }
1311             XFlush(x11display);
1312             int eventsInQueue = XEventsQueued(x11display, QueuedAlready);
1313             if (!eventsInQueue) {
1314                 import core.stdc.errno;
1315                 int selectResult;
1316                 do {
1317                     timeval zeroTime;
1318                     selectResult = select(x11displayFd + 1, &fdSet, null, null, &zeroTime);
1319                 } while(selectResult == -1 && errno == EINTR);
1320                 if (selectResult < 0) {
1321                     Log.e("X11: display fd select error");
1322                 } else if (selectResult == 1) {
1323                     Log.d("X11: XPending");
1324                     eventsInQueue = XPending(x11display);
1325                 }
1326             }
1327             if (!eventsInQueue) {
1328                 debug(x11) Log.d("X11: Sleeping");
1329                 Thread.sleep(dur!("msecs")(10));
1330             }
1331             foreach(eventIndex; 0..eventsInQueue)
1332             {
1333                 if (allWindowsClosed())
1334                     break;
1335                 XNextEvent(x11display, &event);
1336                 switch (event.type) {
1337                     case ConfigureNotify:
1338                         X11Window w = findWindow(event.xconfigure.window);
1339                         if (w) {
1340                             w._cachedWidth = event.xconfigure.width;
1341                             w._cachedHeight = event.xconfigure.height;
1342                             w.handleWindowStateChange(WindowState.unspecified, Rect(event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height));
1343                         } else {
1344                             Log.e("ConfigureNotify: Window not found");
1345                         }
1346                         break;
1347                     case PropertyNotify:
1348                         if (event.xproperty.atom == atom_NET_WM_STATE && event.xproperty.state == PropertyNewValue) {
1349                             X11Window w = findWindow(event.xproperty.window);
1350                             if (w) {
1351                                 Atom type;
1352                                 int format;
1353                                 ubyte* properties;
1354                                 c_ulong dataLength, overflow;
1355                                 if (XGetWindowProperty(x11display, event.xproperty.window, atom_NET_WM_STATE,
1356                                     0, int.max/4, False, AnyPropertyType, &type, &format, &dataLength, &overflow, &properties) == 0) {
1357                                     scope(exit) XFree(properties);
1358                                     // check for minimized
1359                                     bool minimized = false;
1360                                     for (int i=0; i < dataLength ; i++) {
1361                                         if (((cast(c_ulong*)properties)[i]) == atom_NET_WM_STATE_HIDDEN) {
1362                                             w.handleWindowStateChange(WindowState.minimized);
1363                                             minimized = true;
1364                                         }
1365                                     }
1366                                     if (!minimized) {
1367                                         bool maximizedH = false;
1368                                         bool maximizedV = false;
1369                                         for (int i=0; i < dataLength ; i++) {
1370                                             if (((cast(c_ulong*)properties)[i]) == atom_NET_WM_STATE_MAXIMIZED_VERT)
1371                                                 maximizedV = true;
1372                                             if (((cast(c_ulong*)properties)[i]) == atom_NET_WM_STATE_MAXIMIZED_HORZ)
1373                                                 maximizedH = true;
1374                                         }
1375 
1376                                         if (maximizedV && maximizedH)
1377                                             w.handleWindowStateChange(WindowState.maximized);
1378                                         else
1379                                             w.handleWindowStateChange(WindowState.normal);
1380 
1381                                     }
1382                                 }
1383                             }
1384                         }
1385                         break;
1386                     case MapNotify:
1387                         X11Window w = findWindow(event.xmap.window);
1388                         if (w) {
1389                             w.handleWindowStateChange(WindowState.normal);
1390                         }
1391                         break;
1392                     case UnmapNotify:
1393                         X11Window w = findWindow(event.xunmap.window);
1394                         if (w) {
1395                             w.handleWindowStateChange(WindowState.hidden);
1396                         }
1397                         break;
1398                     case Expose:
1399                         if (event.xexpose.count == 0) {
1400                             X11Window w = findWindow(event.xexpose.window);
1401                             if (w) {
1402                                 w.invalidate();
1403                             } else {
1404                                 Log.e("Expose: Window not found");
1405                             }
1406                         } else {
1407                             Log.d("Expose: non-0 count");
1408                         }
1409                         break;
1410                     case KeyPress:
1411                         Log.d("X11: KeyPress event");
1412                         X11Window w = findWindow(event.xkey.window);
1413                         if (w) {
1414                             char[100] buf;
1415                             KeySym ks;
1416                             Status s;
1417                             if (!w.xic) {
1418                                 w.xic = XCreateIC(xim,
1419                                     XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
1420                                     XNClientWindow, w._win, 0);
1421                                 if (!w.xic) {
1422                                     Log.e("Cannot create input context");
1423                                 }
1424                             }
1425 
1426                             if (!w.xic)
1427                                 XLookupString(&event.xkey, buf.ptr, buf.length - 1, &ks, &compose);
1428                             else {
1429                                 Xutf8LookupString(w.xic, &event.xkey, buf.ptr, cast(int)buf.length - 1, &ks, &s);
1430                                 if (s != XLookupChars && s != XLookupBoth)
1431                                     XLookupString(&event.xkey, buf.ptr, buf.length - 1, &ks, &compose);
1432                             }
1433                             foreach(ref ch; buf) {
1434                                 if (ch == 255 || ch < 32 || ch == 127)
1435                                     ch = 0;
1436                             }
1437                             string txt = fromStringz(buf.ptr).dup;
1438                             import std.utf;
1439                             dstring dtext;
1440                             try {
1441                                 if (txt.length)
1442                                     dtext = toUTF32(txt);
1443                             } catch (UTFException e) {
1444                                 // ignore, invalid text
1445                             }
1446                             debug(x11) Log.d("X11: KeyPress event bytes=", txt.length, " text=", txt, " dtext=", dtext);
1447                             if (dtext.length) {
1448                                 w.processTextInput(dtext, event.xkey.state);
1449                             } else {
1450                                 w.processKeyEvent(KeyAction.KeyDown, cast(uint)ks,
1451                                     //event.xkey.keycode,
1452                                     event.xkey.state);
1453                             }
1454                         } else {
1455                             Log.e("Window not found");
1456                         }
1457                         break;
1458                     case KeyRelease:
1459                         Log.d("X11: KeyRelease event");
1460                         X11Window w = findWindow(event.xkey.window);
1461                         if (w) {
1462                             char[100] buf;
1463                             KeySym ks;
1464                             XLookupString(&event.xkey, buf.ptr, buf.length - 1, &ks, &compose);
1465                             w.processKeyEvent(KeyAction.KeyUp, cast(uint)ks,
1466                                 //event.xkey.keycode,
1467                                 event.xkey.state);
1468                         } else {
1469                             Log.e("Window not found");
1470                         }
1471                         break;
1472                     case ButtonPress:
1473                         Log.d("X11: ButtonPress event");
1474                         X11Window w = findWindow(event.xbutton.window);
1475                         if (w) {
1476                             w.processMouseEvent(MouseAction.ButtonDown, event.xbutton.button, event.xbutton.state, event.xbutton.x, event.xbutton.y);
1477                         } else {
1478                             Log.e("Window not found");
1479                         }
1480                         break;
1481                     case ButtonRelease:
1482                         Log.d("X11: ButtonRelease event");
1483                         X11Window w = findWindow(event.xbutton.window);
1484                         if (w) {
1485                             w.processMouseEvent(MouseAction.ButtonUp, event.xbutton.button, event.xbutton.state, event.xbutton.x, event.xbutton.y);
1486                         } else {
1487                             Log.e("Window not found");
1488                         }
1489                         break;
1490                     case MotionNotify:
1491                         debug(x11) Log.d("X11: MotionNotify event");
1492                         X11Window w = findWindow(event.xmotion.window);
1493                         if (w) {
1494                             w.processMouseEvent(MouseAction.Move, 0, event.xmotion.state, event.xmotion.x, event.xmotion.y);
1495                         } else {
1496                             Log.e("Window not found");
1497                         }
1498                         break;
1499                     case EnterNotify:
1500                         Log.d("X11: EnterNotify event");
1501                         X11Window w = findWindow(event.xcrossing.window);
1502                         if (!w) {
1503                             Log.e("Window not found");
1504                         }
1505                         break;
1506                     case LeaveNotify:
1507                         Log.d("X11: LeaveNotify event");
1508                         X11Window w = findWindow(event.xcrossing.window);
1509                         if (w) {
1510                             w.processMouseEvent(MouseAction.Leave, 0, event.xcrossing.state, event.xcrossing.x, event.xcrossing.y);
1511                         } else {
1512                             Log.e("Window not found");
1513                         }
1514                         break;
1515                     case CreateNotify:
1516                         Log.d("X11: CreateNotify event");
1517                         X11Window w = findWindow(event.xcreatewindow.window);
1518                         if (!w) {
1519                             Log.e("Window not found");
1520                         }
1521                         break;
1522                     case DestroyNotify:
1523                         Log.d("X11: DestroyNotify event");
1524                         break;
1525                     case ResizeRequest:
1526                         Log.d("X11: ResizeRequest event");
1527                         X11Window w = findWindow(event.xresizerequest.window);
1528                         if (!w) {
1529                             Log.e("Window not found");
1530                         }
1531                         break;
1532                     case FocusIn:
1533                         Log.d("X11: FocusIn event");
1534                         X11Window w = findWindow(event.xfocus.window);
1535                         if (w)
1536                             w.handleWindowActivityChange(true);
1537                         else
1538                             Log.e("Window not found");
1539                         break;
1540                     case FocusOut:
1541                         Log.d("X11: FocusOut event");
1542                         X11Window w = findWindow(event.xfocus.window);
1543                         if (w)
1544                             w.handleWindowActivityChange(false);
1545                         else
1546                             Log.e("Window not found");
1547                         break;
1548                     case KeymapNotify:
1549                         Log.d("X11: KeymapNotify event");
1550                         X11Window w = findWindow(event.xkeymap.window);
1551                         break;
1552                     case SelectionClear:
1553                         Log.d("X11: SelectionClear event");
1554                         break;
1555                     case SelectionRequest:
1556                         debug(x11) Log.d("X11: SelectionRequest event");
1557                         if (event.xselectionrequest.owner in _windowMap) {
1558                             XSelectionRequestEvent *selectionRequest = &event.xselectionrequest;
1559 
1560                             XEvent selectionEvent;
1561                             memset(&selectionEvent, 0, selectionEvent.sizeof);
1562                             selectionEvent.xany.type = SelectionNotify;
1563                             selectionEvent.xselection.selection = selectionRequest.selection;
1564                             selectionEvent.xselection.target = selectionRequest.target;
1565                             selectionEvent.xselection.property = None;
1566                             selectionEvent.xselection.requestor = selectionRequest.requestor;
1567                             selectionEvent.xselection.time = selectionRequest.time;
1568 
1569                             if (selectionRequest.target == XA_STRING || selectionRequest.target == atom_UTF8_STRING) {
1570                                 static if (false) {
1571                                     int currentSelectionFormat;
1572                                     Atom currentSelectionType;
1573                                     c_ulong selectionDataLength, overflow;
1574                                     ubyte* selectionDataPtr;
1575                                     if (XGetWindowProperty(x11display, DefaultRootWindow(x11display), atom_DLANGUI_CLIPBOARD_BUFFER,
1576                                         0, int.max/4, False, selectionRequest.target,
1577                                         &currentSelectionType, &currentSelectionFormat, &selectionDataLength,
1578                                         &overflow, &selectionDataPtr) == 0)
1579                                     {
1580                                         scope(exit) XFree(selectionDataPtr);
1581                                         XChangeProperty(x11display, selectionRequest.requestor, selectionRequest.property,
1582                                             selectionRequest.target, 8, PropModeReplace,
1583                                             selectionDataPtr, cast(int)selectionDataLength);
1584                                     }
1585                                 } else {
1586                                     XChangeProperty(x11display, selectionRequest.requestor, selectionRequest.property,
1587                                             selectionRequest.target, 8, PropModeReplace,
1588                                             cast(ubyte*)localClipboardContent, cast(int)localClipboardContent.length);
1589                                 }
1590                                 selectionEvent.xselection.property = selectionRequest.property;
1591                             } else if (selectionRequest.target == atom_TARGETS) {
1592                                 Atom[3] supportedFormats = [atom_UTF8_STRING, XA_STRING, atom_TARGETS];
1593                                 XChangeProperty(x11display, selectionRequest.requestor, selectionRequest.property,
1594                                     XA_ATOM, 32, PropModeReplace,
1595                                     cast(ubyte*)supportedFormats.ptr, cast(int)supportedFormats.length);
1596                                 selectionEvent.xselection.property = selectionRequest.property;
1597                             }
1598                             XSendEvent(x11display, selectionRequest.requestor, False, 0, &selectionEvent);
1599                         }
1600                         break;
1601                     case SelectionNotify:
1602                         Log.d("X11: SelectionNotify event");
1603                         X11Window w = findWindow(event.xselection.requestor);
1604                         break;
1605                     case ClientMessage:
1606                         debug(x11) Log.d("X11: ClientMessage event");
1607                         X11Window w = findWindow(event.xclient.window);
1608                         if (w) {
1609                             if (event.xclient.message_type == atom_DLANGUI_TASK_EVENT) {
1610                                 w.handlePostedEvent(cast(uint)event.xclient.data.l[0]);
1611                             } else if (event.xclient.message_type == atom_DLANGUI_TIMER_EVENT) {
1612                                 w.handleTimer();
1613                             } else if (event.xclient.message_type == atom_WM_PROTOCOLS) {
1614                                 Log.d("Handling WM_PROTOCOLS");
1615                                 if ((event.xclient.format == 32) && (event.xclient.data.l[0]) == atom_WM_DELETE_WINDOW) {
1616                                     Log.d("Handling WM_DELETE_WINDOW");
1617                                     _windowMap.remove(w._win);
1618                                     destroy(w);
1619                                 }
1620                             } else if (event.xclient.message_type == atom_DLANGUI_CLOSE_WINDOW_EVENT) {
1621                                 _windowMap.remove(w._win);
1622                                 destroy(w);
1623                             }
1624                         } else {
1625                             Log.e("Window not found");
1626                         }
1627                         break;
1628                     default:
1629                         break;
1630                 }
1631             }
1632         }
1633         return 0;
1634     }
1635 
1636     /// check has clipboard text
1637     override bool hasClipboardText(bool mouseBuffer = false) {
1638         return (localClipboardContent.length != 0);
1639     }
1640 
1641     /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux)
1642     override dstring getClipboardText(bool mouseBuffer = false) {
1643         return toUTF32(localClipboardContent);
1644     }
1645 
1646     /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux)
1647     override void setClipboardText(dstring text, bool mouseBuffer = false) {
1648         localClipboardContent = toUTF8(text);
1649         if (!mouseBuffer && atom_CLIPBOARD == None) {
1650             Log.e("No CLIPBOARD atom available");
1651             return;
1652         }
1653         XWindow xwindow = None;
1654         // Find any top-level window
1655         foreach(w; _windowMap) {
1656             if (w._parent is null && w._win != None) {
1657                 xwindow = w._win;
1658             }
1659         }
1660         if (xwindow == None) {
1661             Log.e("Could not find window to save clipboard text");
1662             return;
1663         }
1664         static if (false) {
1665             // This is example of how setting clipboard contents can be implemented without global variable
1666             auto textc = text.toUTF8;
1667             XChangeProperty(x11display, DefaultRootWindow(x11display), atom_DLANGUI_CLIPBOARD_BUFFER, XA_STRING, 8, PropModeReplace, cast(ubyte*)textc.ptr, cast(int)textc.length);
1668         }
1669 
1670         if (mouseBuffer && XGetSelectionOwner(x11display, XA_PRIMARY) != xwindow) {
1671             XSetSelectionOwner(x11display, XA_PRIMARY, xwindow, CurrentTime);
1672         } else if (XGetSelectionOwner(x11display, atom_CLIPBOARD != xwindow)) {
1673             XSetSelectionOwner(x11display, atom_CLIPBOARD, xwindow, CurrentTime);
1674         }
1675     }
1676 
1677     /// calls request layout for all windows
1678     override void requestLayout() {
1679         foreach(w; _windowMap) {
1680             w.requestLayout();
1681         }
1682     }
1683 
1684     /// returns true if there is some modal window opened above this window, and this window should not process mouse/key input and should not allow closing
1685     override bool hasModalWindowsAbove(DWindow w) {
1686         // override in platform specific class
1687         for (uint i = 0; i + 1 < _windowList.length; i++) {
1688             if (_windowList[i] is w) {
1689                 for (uint j = i + 1; j < _windowList.length; j++) {
1690                     if (_windowList[j].flags & WindowFlag.Modal && _windowList[j].windowState != WindowState.hidden)
1691                         return true;
1692                 }
1693                 return false;
1694             }
1695         }
1696         return false;
1697     }
1698 
1699     /// handle theme change: e.g. reload some themed resources
1700     override void onThemeChanged() {
1701         foreach(w; _windowMap)
1702             w.dispatchThemeChanged();
1703     }
1704 
1705 }
1706 
1707 import core.thread;
1708 import core.sync.mutex;
1709 import core.sync.condition;
1710 class TimerThread : Thread {
1711     Mutex mutex;
1712     Condition condition;
1713     bool stopped;
1714     long nextEventTs;
1715     void delegate() callback;
1716 
1717     this(void delegate() timerCallback) {
1718         callback = timerCallback;
1719         mutex = new Mutex();
1720         condition = new Condition(mutex);
1721         super(&run);
1722         start();
1723     }
1724 
1725     ~this() {
1726         stop();
1727         destroy(condition);
1728         destroy(mutex);
1729     }
1730 
1731     void set(long nextTs) {
1732         mutex.lock();
1733         if (nextEventTs == 0 || nextEventTs > nextTs) {
1734             nextEventTs = nextTs;
1735             condition.notify();
1736         }
1737         mutex.unlock();
1738     }
1739     void run() {
1740 
1741         while (!stopped) {
1742             bool expired = false;
1743 
1744             mutex.lock();
1745 
1746             long ts = currentTimeMillis;
1747             long timeToWait = nextEventTs == 0 ? 1000000 : nextEventTs - ts;
1748             if (timeToWait < 10)
1749                 timeToWait = 10;
1750 
1751             if (nextEventTs == 0)
1752                 condition.wait();
1753             else
1754                 condition.wait(dur!"msecs"(timeToWait));
1755 
1756             if (stopped) {
1757                 mutex.unlock();
1758                 break;
1759             }
1760             ts = currentTimeMillis;
1761             if (nextEventTs && nextEventTs < ts && !stopped) {
1762                 expired = true;
1763                 nextEventTs = 0;
1764             }
1765 
1766             mutex.unlock();
1767 
1768             if (expired)
1769                 callback();
1770         }
1771     }
1772     void stop() {
1773         if (stopped)
1774             return;
1775         stopped = true;
1776         mutex.lock();
1777         condition.notify();
1778         mutex.unlock();
1779         join();
1780     }
1781 }
1782 
1783 
1784 extern(C) int DLANGUImain(string[] args)
1785 {
1786     initLogs();
1787 
1788     if (!initFontManager()) {
1789         Log.e("******************************************************************");
1790         Log.e("No font files found!!!");
1791         Log.e("Currently, only hardcoded font paths implemented.");
1792         Log.e("Probably you can modify sdlapp.d to add some fonts for your system.");
1793         Log.e("TODO: use fontconfig");
1794         Log.e("******************************************************************");
1795         assert(false);
1796     }
1797     initResourceManagers();
1798 
1799     currentTheme = createDefaultTheme();
1800 
1801     XInitThreads();
1802 
1803     /* use the information from the environment variable DISPLAY
1804        to create the X connection:
1805     */
1806     x11display = XOpenDisplay(null);
1807     if (!x11display) {
1808         Log.e("Cannot open X11 display");
1809         return 1;
1810     }
1811     x11display2 = XOpenDisplay(null);
1812     if (!x11display2) {
1813         Log.e("Cannot open secondary connection for X11 display");
1814         return 1;
1815     }
1816 
1817     x11screen = DefaultScreen(x11display);
1818 
1819     static if (ENABLE_OPENGL) {
1820         try {
1821             DerelictGL3.missingSymbolCallback = &gl3MissingSymFunc;
1822             DerelictGL3.load();
1823             DerelictGL.missingSymbolCallback = &gl3MissingSymFunc;
1824             DerelictGL.load();
1825             Log.d("OpenGL library loaded ok");
1826             GLint[] att = [ GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None ];
1827             XWindow root;
1828             root = DefaultRootWindow(x11display);
1829             x11visual = glXChooseVisual(x11display, 0, cast(int*)att.ptr);
1830             if (x11visual) {
1831                 x11cmap = XCreateColormap(x11display, root, cast(Visual*)x11visual.visual, AllocNone);
1832                 _enableOpengl = true;
1833             } else {
1834                 Log.e("Cannot find suitable Visual for using of OpenGL");
1835             }
1836         } catch (Exception e) {
1837             Log.e("Cannot load OpenGL library", e);
1838         }
1839     }
1840 
1841 
1842     setupX11Atoms();
1843 
1844     x11cursors[CursorType.None] = XCreateFontCursor(x11display, XC_arrow);
1845     x11cursors[CursorType.Parent] = XCreateFontCursor(x11display, XC_arrow);
1846     x11cursors[CursorType.Arrow] = XCreateFontCursor(x11display, XC_left_ptr);
1847     x11cursors[CursorType.IBeam] = XCreateFontCursor(x11display, XC_xterm);
1848     x11cursors[CursorType.Wait] = XCreateFontCursor(x11display, XC_watch);
1849     x11cursors[CursorType.Crosshair] = XCreateFontCursor(x11display, XC_tcross);
1850     x11cursors[CursorType.WaitArrow] = XCreateFontCursor(x11display, XC_watch);
1851     x11cursors[CursorType.SizeNWSE] = XCreateFontCursor(x11display, XC_fleur);
1852     x11cursors[CursorType.SizeNESW] = XCreateFontCursor(x11display, XC_fleur);
1853     x11cursors[CursorType.SizeWE] = XCreateFontCursor(x11display, XC_sb_h_double_arrow);
1854     x11cursors[CursorType.SizeNS] = XCreateFontCursor(x11display, XC_sb_v_double_arrow);
1855     x11cursors[CursorType.SizeAll] = XCreateFontCursor(x11display, XC_fleur);
1856     x11cursors[CursorType.No] = XCreateFontCursor(x11display, XC_pirate);
1857     x11cursors[CursorType.Hand] = XCreateFontCursor(x11display, XC_hand2);
1858 
1859     xim = XOpenIM(x11display, null, null, null);
1860     if (!xim) {
1861         Log.e("Cannot open input method");
1862     }
1863 
1864     Log.d("X11 display=", x11display, " screen=", x11screen);
1865 
1866 
1867 
1868     X11Platform x11platform = new X11Platform();
1869 
1870     Platform.setInstance(x11platform);
1871 
1872     int res = 0;
1873 
1874     version (unittest) {
1875     } else {
1876         res = UIAppMain(args);
1877     }
1878 
1879     //Log.e("Widget instance count after UIAppMain: ", Widget.instanceCount());
1880 
1881     Log.d("Destroying X11 platform");
1882     Platform.setInstance(null);
1883 
1884     releaseResourcesOnAppExit();
1885 
1886 
1887     XCloseDisplay(x11display);
1888     XCloseDisplay(x11display2);
1889 
1890     Log.d("Exiting main width result=", res);
1891 
1892     return res;
1893 }