1 module src.dlangui.platforms.x11.x11app;
2 
3 version(linux) {
4 
5 	import std.string;
6 	import std.c.linux.X11.xcb.xcb;
7 	import std.c.linux.X11.xcb.shm;
8 	import std.c.linux.X11.xcb.xproto;
9 	import std.c.linux.X11.xcb.image;
10 	import std.c.linux.X11.keysymdef;
11 	import std.c.linux.linux;
12 	import std.c.stdlib;
13 	import std.conv;
14 
15 	import dlangui.core.logger;
16 	import dlangui.core.events;
17 	import dlangui.graphics.drawbuf;
18 	import dlangui.graphics.fonts;
19 	import dlangui.graphics.ftfonts;
20 	import dlangui.graphics.resources;
21 	import dlangui.widgets.styles;
22 	import dlangui.platforms.common.platform;
23 	
24 	version (USE_OPENGL) {
25 	    import dlangui.graphics.glsupport;
26 	}
27 	
28 	import derelict.opengl3.gl3;
29 	import derelict.opengl3.glx;
30 
31 //	pragma(lib, "xcb");
32 //	pragma(lib, "xcb-shm");
33 //	pragma(lib, "xcb-image");
34 //	pragma(lib, "X11-xcb");
35 //	pragma(lib, "X11");
36 //	pragma(lib, "dl");	
37 		
38 	extern (System)
39 	xcb_connection_t *XGetXCBConnection(std.c.linux.X11.Xlib.Display *dpy); 
40 	enum XEventQueueOwner { XlibOwnsEventQueue = 0, XCBOwnsEventQueue };
41 	extern (System)
42 	void XSetEventQueueOwner(std.c.linux.X11.Xlib.Display *dpy, XEventQueueOwner owner);
43 	
44 	class XCBWindow : Window {
45 		xcb_window_t         _w;
46 		xcb_gcontext_t       _g;
47 		xcb_image_t * 		_image;
48 		xcb_shm_segment_info_t shminfo;
49         /* Create GLX Window */
50         GLXDrawable _drawable;
51 		GLXWindow _glxwindow;
52 		
53 		private GLXContext _context;
54 		private int _visualID = 0;
55 		private xcb_colormap_t _colormap;		
56 		private GLXFBConfig _fb_config;
57 		
58 		@property xcb_window_t windowId() { return _w; }
59 		this(string caption, Window parent) {
60 			_caption = caption;
61 			Log.d("Creating XCB window");
62 			create();
63 		}
64 		~this() {
65 			Log.d("Destroying window");
66 		}
67 			
68 		bool create() {
69 			import std.c.linux.X11.Xlib;
70 			
71 			uint mask;
72 			uint values[3];
73 
74 			// disable opengl for testing
75 			_enableOpengl = false;
76 		    /* create black graphics context */
77 			if (true || !_enableOpengl) {
78 				_g = xcb_generate_id(_xcbconnection);
79 				_w = _xcbscreen.root;
80 				mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
81 				values[0] = _xcbscreen.black_pixel;
82 				values[1] = 0;
83 				xcb_create_gc(_xcbconnection, _g, _w, mask, &values[0]);
84 			}
85 
86 			ubyte depth = _xcbscreen.root_depth;
87 		    /* create window */
88 			_w = xcb_generate_id(_xcbconnection);
89 			
90 			Log.d("window=", _w, " gc=", _g);
91 			
92 			if (_enableOpengl) {
93 				int visual_attribs[] = [
94 					        GLX_RENDER_TYPE, GLX_RGBA_BIT,
95 					        GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
96 					        GLX_DOUBLEBUFFER, 1,
97 					        GLX_RED_SIZE, 8,
98 					        GLX_GREEN_SIZE, 8,
99 					        GLX_BLUE_SIZE, 8,
100 							std.c.linux.X11.Xlib.None];
101 				
102 			    Log.d("Getting framebuffer config");
103 			    int fbcount;
104 			    GLXFBConfig *fbc = glXChooseFBConfig(_display, DefaultScreen(_display), visual_attribs.ptr, &fbcount);
105 			    if (!fbc)
106 			    {
107 			        Log.d("Failed to retrieve a framebuffer config");
108 			        //return 1;
109 			    }
110 			 
111 			    Log.d("Getting XVisualInfo");
112 				_fb_config = fbc[0];
113 			    auto vi = glXGetVisualFromFBConfig(_display, _fb_config);			
114 				
115 				//auto vi = glXChooseVisual(_display, std.c.linux.X11.Xlib.DefaultScreen(_display), attributeList.ptr);
116 				_visualID = vi.visualid;		
117 				//swa.colormap = std.c.linux.X11.Xlib.XCreateColormap(_display, std.c.linux.X11.Xlib.RootWindow(_display, vi.screen), vi.visual, 0); // AllocNone			
118 
119 				Log.d("Creating color map");
120 				_colormap = xcb_generate_id(_xcbconnection);
121 		        /* Create colormap */
122 		        xcb_create_colormap(
123 		            _xcbconnection,
124 		            XCB_COLORMAP_ALLOC_NONE,
125 		            _colormap,
126 		            _xcbscreen.root,
127 		            _visualID
128 		        );				
129 				depth = cast(ubyte)vi.depth;
130 			}
131 			//mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
132 			//values[0] = _xcbscreen.white_pixel;
133 
134 			int visualId;
135 			uint eventmask = 
136 				  XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE 
137 				| XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_MOTION 
138 				| XCB_EVENT_MASK_ENTER_WINDOW   | XCB_EVENT_MASK_LEAVE_WINDOW
139 				| XCB_EVENT_MASK_KEY_PRESS      | XCB_EVENT_MASK_KEY_RELEASE
140 				| XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_VISIBILITY_CHANGE;
141 			if (_enableOpengl) {
142 				mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
143 				values[0] = _xcbscreen.white_pixel;
144 				values[1] = eventmask;
145 				values[2] = _colormap;
146 				//visualId = _xcbscreen.root_visual;
147 				visualId = _visualID;
148 			} else {
149 				mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
150 				values[0] = _xcbscreen.white_pixel;
151 				values[1] = eventmask;
152 				visualId = _xcbscreen.root_visual;
153 			}
154 			Log.d("xcb_create_window - window=", _w, " VisualID=", _visualID);
155 			auto res = xcb_create_window(_xcbconnection, 
156 				depth, //_xcbscreen.root_depth, 
157 				//XCB_COPY_FROM_PARENT,//_xcbscreen.root_depth, 
158 				_w, 
159 			    _xcbscreen.root,
160 		        50, 50, 500, 400, 
161 				1,
162 		        XCB_WINDOW_CLASS_INPUT_OUTPUT, 
163 				visualId,
164 		        mask, 
165 				&values[0]);
166 		  	xcb_flush(_xcbconnection);
167 			windowCaption = _caption;
168 			return true;
169 		}
170 		
171 		private int _imageDx;
172 		private int _imageDy;
173 		void createImage() {
174 			if (_image) {
175 				if (_imageDx == _imageDx && _imageDy == _dy)
176 					return; // already have image of proper size
177 	        	Log.i("CRXCBScreen::createImage - destroying existing image");
178 	            xcb_image_destroy(_image);
179 				_image = null;
180 			}
181 			_imageDx = _dx;
182 			_imageDy = _dy;
183 	        Log.i("CRXCBScreen::createImage ", _dx, "x", _dy);
184 	        xcb_shm_query_version_reply_t * rep_shm;
185 	        rep_shm = xcb_shm_query_version_reply (_xcbconnection,
186 	                xcb_shm_query_version(_xcbconnection),
187 	                null);
188 	        if(rep_shm) {
189 	            xcb_image_format_t format;
190 	            int shmctl_status;
191 
192 	            if (rep_shm.shared_pixmaps &&
193 	                    (rep_shm.major_version > 1 || rep_shm.minor_version > 0))
194 	                format = cast(xcb_image_format_t)rep_shm.pixmap_format;
195 	            else
196 	                format = XCB_IMAGE_FORMAT_Z_PIXMAP;
197 
198 	            _image = xcb_image_create_native (_xcbconnection, cast(short)_dx, cast(short)_dy,
199 	                 format, _xcbscreendepth, null, ~0, null);
200 	            //format, depth, NULL, ~0, NULL);
201 	            //format, depth, NULL, ~0, NULL);
202 	            assert(_image);
203 
204 	            shminfo.shmid = shmget (IPC_PRIVATE,
205 	                    _image.stride*_image.height,
206 	                    IPC_CREAT | octal!777);
207 	            assert(shminfo.shmid != cast(uint)-1);
208 	            shminfo.shmaddr = cast(ubyte*)shmat (shminfo.shmid, null, 0);
209 	            assert(shminfo.shmaddr);
210 	            _image.data = shminfo.shmaddr;
211 	            Log.d("Created image depth=", _image.depth, " bpp=", _image.bpp, " stride=", _image.stride );
212 
213 	            shminfo.shmseg = xcb_generate_id (_xcbconnection);
214 	            xcb_shm_attach (_xcbconnection, shminfo.shmseg,
215 	                    shminfo.shmid, 0);
216 	            shmctl_status = shmctl(shminfo.shmid, IPC_RMID, null);
217 	            assert(shmctl_status != -1);
218 	            free(rep_shm);
219 	        } else {
220 	            Log.e("Can't get shms");
221 	        }
222 		}
223 		
224 		void draw(ColorDrawBuf buf) {
225 	        int i;
226 	        i = xcb_image_shm_get(_xcbconnection, _w,
227 	                _image, shminfo,
228 	                0, 0,
229 	                XCB_ALL_PLANES);
230 	        if (!i) {
231 	            Log.e("cannot get shm image");
232 	            return;
233 	        }
234 			Rect rc;
235 			rc.right = buf.width;
236 			rc.bottom = buf.height;
237 			switch ( _image.bpp ) {
238 	            case 32:
239 	                {
240 	                    for (int y = rc.top; y<rc.bottom; y++) {
241 	                        uint * src = buf.scanLine(y);
242 	                        uint * dst = cast(uint *)(_image.data + _image.stride * y);
243 	                        for ( int x = 0; x < rc.right; x++ ) {
244 	                            uint data = src[x];
245 	                            dst[x] = data;
246 	                        }
247 	                    }
248 	                }
249 	                break;
250 				default:
251 					Log.d("image bit depth not supported: ", _image.bpp);
252 					break;
253 			}
254 	        xcb_image_shm_put(_xcbconnection, _w, _g,
255 	                _image, shminfo,
256 	                cast(short)rc.left, cast(short)rc.top, cast(short)rc.left, cast(short)rc.top, cast(ushort)rc.width(), cast(ushort)rc.height(), 0);
257 	        xcb_flush(_xcbconnection);
258 		}
259 		
260 		bool _derelictgl3Reloaded;
261 		override void show() {
262 			Log.d("XCBWindow.show()");
263 		    /* map (show) the window */
264 		  	xcb_map_window(_xcbconnection, _w);
265 		  	xcb_flush(_xcbconnection);
266 			//_enableOpengl = false; // test
267 			if (_enableOpengl && !_glxwindow) {
268 				Log.d("Calling glXCreateWindow display=", _display, " fbconfig=", _fb_config, " window=", _w);
269 	        	_glxwindow = glXCreateWindow(
270 			                _display,
271 			                _fb_config,
272 			                _w,
273 			                null);			
274 				if (!_glxwindow) {
275 					Log.e("Failed to create GLX window: disabling OpenGL");
276 					_enableOpengl = false;
277 				} else {
278 					import derelict.opengl3.glxext;
279 					import std.c.linux.X11.Xlib;
280 					
281 					_drawable = _glxwindow;
282 					
283 					if (!_derelictgl3Reloaded) {
284 						
285 						int count;
286                 		glGetIntegerv( GL_NUM_EXTENSIONS, &count );
287 						Log.d("Number of extensions: ", count);
288                 		for( int i=0; i<count; ++i ) {
289 							auto e = glGetStringi( GL_EXTENSIONS, i );
290 							Log.d("Extension: ", fromStringz(e));
291                 		}
292 
293 						
294 						Log.e("Reloading DerelictGL3");
295 						_derelictgl3Reloaded = true;
296 			        	_context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
297 						if (_context is null) {
298 							Log.e("Cannot create temporary context");
299 						}
300 						//glXMakeContextCurrent(_display, _drawable, _drawable, _context);
301 						glXMakeContextCurrent(_display, _glxwindow, _glxwindow, _context);
302 						//glXMakeCurrent(_display, _w, _context);
303 						DerelictGL3.reload();
304 						Log.e("Reloaded DerelictGL3 - removing temporary context");
305 						glXMakeCurrent(_display, 0, null);
306 						Log.e("Destroying context");
307 						glXDestroyContext(_display, _context);
308 						Log.e("DerelictGL3 initialized");
309 						_context = null;
310 					}
311 					
312 	 
313 					// Get the default screen's GLX extension list
314   					const char *glxExts = glXQueryExtensionsString( _display,
315                                                   DefaultScreen( _display ) );
316 					Log.d("GLX Extensions: ", fromStringz(glxExts));
317 					const char * exts = glGetString( GL_EXTENSIONS );
318 					Log.d("Extensions: ", fromStringz(exts));
319 					
320 					
321 					Log.d("GLX_ARB_get_proc_address=", GLX_ARB_get_proc_address);
322 					Log.d("GLX_ARB_create_context=", GLX_ARB_create_context);
323 						
324 					//da_glXCreateContextAttribsARB glXCreateContextAttribsARB;
325 					//Log.d("getting address of glXCreateContextAttribsARB");
326   					//glXCreateContextAttribsARB = cast(da_glXCreateContextAttribsARB)
327            			//		glXGetProcAddressARB( cast(const GLubyte *)("glXCreateContextAttribsARB".toStringz));
328 					
329 					//Log.d("glXCreateContextAttribsARB = ", &glXCreateContextAttribsARB);
330 					
331     				Log.d("Creating context");
332 			        //_context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
333 					if (!GLX_ARB_create_context) {
334 						Log.e("glXCreateContextAttribsARB function is not found");
335 			        	_context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
336 					} else {
337 						int context_attribs[] =
338 					    [
339 					        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
340 					        GLX_CONTEXT_MINOR_VERSION_ARB, 0,
341 					        None
342 					    ];
343 						Log.e("calling glXCreateContextAttribsARB");
344 			    		_context = glXCreateContextAttribsARB(_display, _fb_config, null, true, context_attribs.ptr);
345 					}
346     				Log.d("Created context: ", _context);
347 					
348 			        /* Create OpenGL context */
349 			        //auto context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
350 			        if (!_context) {
351 						_enableOpengl = false;
352 						Log.e("Failed to create OpenGL context");
353 					} else {
354 
355 					}
356 					
357 			        /* make OpenGL context current */
358 			        if(!glXMakeContextCurrent(_display, _drawable, _drawable, _context) || !initShaders()) {
359 						Log.e("Failed to make GL context current");
360 						_enableOpengl = false;
361 			            glXDestroyContext(_display, _context);
362 						_context = null;
363 					} else {
364 						
365 					}
366 				}
367 			}
368 		}
369 		string _caption;
370 		override @property string windowCaption() {
371 			return _caption;
372 		}
373 		override @property void windowCaption(string caption) {
374 			_caption = caption;
375 			const char * title = _caption.toStringz;
376 	        xcb_change_property (_xcbconnection,
377 	                             XCB_PROP_MODE_REPLACE,
378 	                             _w,
379 	                             XCB_ATOM_WM_NAME,
380 	                             XCB_ATOM_STRING,
381 	                             8,
382 	                             cast(uint)_caption.length,
383 	                             cast(void*)title);
384 		}
385 
386 		void redraw() {
387 			
388 			if (_enableOpengl) {
389 				import std.c.linux.X11.Xlib;
390 				Log.d("Drawing using opengl ", _dx, "x", _dy, " context=", _context);
391 				//glXMakeContextCurrent(_display, _drawable, _drawable, _context);
392 				glXMakeContextCurrent(_display, _glxwindow, _glxwindow, _context);
393 		        glEnable(GL_BLEND);
394 		        glDisable(GL_CULL_FACE);
395 		        glDisable(GL_DEPTH_TEST);
396 		        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
397 			    glViewport(0, 0, _dx, _dy);
398 				Log.d("glClearColor");
399 				glClearColor(0.2f, 0.4f, 0.9f, 1.0f);
400 				Log.d("glClear");
401         		glClear(GL_COLOR_BUFFER_BIT);
402 				
403 				import dlangui.graphics.gldrawbuf;
404 				GLDrawBuf buf = new GLDrawBuf(_dx, _dy);
405 				buf.beforeDrawing();
406 				onDraw(buf);
407 				buf.fillRect(Rect(0, 0, 100, 100), 0x805010);
408 				buf.afterDrawing();
409 				destroy(buf);
410 				Log.d("Calling glx swap buffers for drawable ", _drawable);
411 				glXSwapBuffers(_display, _drawable);
412 				//glXMakeContextCurrent(_display, _drawable, _drawable, null);
413 				xcb_flush(_xcbconnection);
414 				glXMakeContextCurrent (_display, None, None, null);
415 				glXWaitGL();
416 			} else {
417 				if (!_drawbuf)
418 					_drawbuf = new ColorDrawBuf(_dx, _dy);
419 				_drawbuf.resize(_dx, _dy);
420 				_drawbuf.fill(_backgroundColor);
421 				//Log.d("calling createImage");
422 				createImage();
423 				//Log.d("done createImage");
424 				onDraw(_drawbuf);
425 				draw(_drawbuf);
426 					/*
427 				xcb_rectangle_t r = { 20, 20, 60, 60 };
428 				xcb_poly_fill_rectangle(_xcbconnection, _w, _g,  1, &r);
429 				r = xcb_rectangle_t(cast(short)(_dx - 20 - 60), cast(short)(_dy - 20 - 60), 60, 60);
430 				xcb_poly_fill_rectangle(_xcbconnection, _w, _g,  1, &r);
431 					 */
432 				xcb_flush(_xcbconnection);
433 			}
434 		}
435 		
436 		ColorDrawBuf _drawbuf;
437 		bool _exposeSent;
438 		void processExpose(xcb_expose_event_t * event) {
439 			redraw();
440 			_exposeSent = false;
441 		}
442 
443 		/// request window redraw
444 		override void invalidate() {
445 			if (_exposeSent)
446 				return;
447 			_exposeSent = true;
448 			xcb_expose_event_t * event = cast(xcb_expose_event_t*)std.c.stdlib.malloc(xcb_expose_event_t.sizeof);
449 		    event.response_type = XCB_EXPOSE; /* The type of the event, here it is XCB_EXPOSE */
450 		    event.sequence = 0;
451 		    event.window = _w;        /* The Id of the window that receives the event (in case */
452 		                                    /* our application registered for events on several windows */
453 		    event.x = 0;             /* The x coordinate of the top-left part of the window that needs to be redrawn */
454 		    event.y = 0;             /* The y coordinate of the top-left part of the window that needs to be redrawn */
455 		    event.width = cast(ushort)_dx;         /* The width of the part of the window that needs to be redrawn */
456 		    event.height = cast(ushort)_dy;        /* The height of the part of the window that needs to be redrawn */
457 		    event.count = 1;
458 
459 			xcb_void_cookie_t res = xcb_send_event(_xcbconnection, false, _w, XCB_EVENT_MASK_EXPOSURE, cast(char *)event);
460 		  	xcb_flush(_xcbconnection);
461 		}
462 				
463 		protected ButtonDetails _lbutton;
464 		protected ButtonDetails _mbutton;
465 		protected ButtonDetails _rbutton;
466 		void processMouseEvent(MouseAction action, ubyte detail, ushort state, short x, short y) {
467 			MouseButton button = MouseButton.None;
468 			short wheelDelta = 0;
469         	ButtonDetails * pbuttonDetails = null;
470 			ushort flags = 0;
471 			if (state & XCB_BUTTON_MASK_1)
472 				flags |= MouseFlag.LButton;
473 			if (state & XCB_BUTTON_MASK_2)
474 				flags |= MouseFlag.MButton;
475 			if (state & XCB_BUTTON_MASK_3)
476 				flags |= MouseFlag.RButton;
477 			if (state & XCB_MOD_MASK_SHIFT)
478 				flags |= MouseFlag.Shift;
479 			if (state & XCB_MOD_MASK_CONTROL)
480 				flags |= MouseFlag.Control;
481 			if (state & XCB_MOD_MASK_LOCK)
482 				flags |= MouseFlag.Alt;
483 			if (action == MouseAction.ButtonDown || action == MouseAction.ButtonUp) {
484 				switch (detail) {
485 					case 1:
486 						button = MouseButton.Left;
487 		                pbuttonDetails = &_lbutton;
488 						if (action == MouseAction.ButtonDown)
489 							flags |= MouseFlag.LButton;
490 						else if (action == MouseAction.ButtonUp)
491 							flags &= ~MouseFlag.LButton;
492 						break;
493 					case 2:
494 						button = MouseButton.Middle;
495 		                pbuttonDetails = &_mbutton;
496 						if (action == MouseAction.ButtonDown)
497 							flags |= MouseFlag.MButton;
498 						else if (action == MouseAction.ButtonUp)
499 							flags &= ~MouseFlag.MButton;
500 						break;
501 					case 3:
502 						button = MouseButton.Right;
503 		                pbuttonDetails = &_rbutton;
504 						if (action == MouseAction.ButtonDown)
505 							flags |= MouseFlag.RButton;
506 						else if (action == MouseAction.ButtonUp)
507 							flags &= ~MouseFlag.RButton;
508 						break;
509 					case 4:
510 						if (action == MouseAction.ButtonUp)
511 							return;
512 						wheelDelta = -1;
513 						action = MouseAction.Wheel;
514 						break;
515 					case 5:
516 						if (action == MouseAction.ButtonUp)
517 							return;
518 						wheelDelta = 1;
519 						action = MouseAction.Wheel;
520 						break;
521 					default:
522 						// unknown button
523 						return;
524 				}
525 			}
526 			//Log.d("processMouseEvent ", action, " detail=", detail, " state=", state, " at coords ", x, ", ", y);
527 	        if (action == MouseAction.ButtonDown) {
528 	            pbuttonDetails.down(x, y, cast(ushort)flags);
529 	        } else if (action == MouseAction.ButtonUp) {
530 	            pbuttonDetails.up(x, y, cast(ushort)flags);
531 	        }
532 	        MouseEvent event = new MouseEvent(action, button, cast(ushort)flags, x, y, wheelDelta);
533 	        event.lbutton = _lbutton;
534 	        event.rbutton = _rbutton;
535 	        event.mbutton = _mbutton;
536 			bool res = dispatchMouseEvent(event);
537 	        if (res) {
538 	            Log.d("Calling update() after mouse event");
539 	            invalidate();
540 	        }
541 		}
542 		
543 	}
544 
545 	private __gshared xcb_connection_t * _xcbconnection;
546 	private __gshared xcb_screen_t     * _xcbscreen;
547 	private __gshared ubyte _xcbscreendepth;
548 	private __gshared bool _enableOpengl;
549 	private __gshared std.c.linux.X11.Xlib.Display * _display;
550 
551 	class XCBPlatform : Platform {
552 		this() {
553 			
554 		}
555 		~this() {
556 			foreach(ref XCBWindow wnd; _windowMap) {
557 				destroy(wnd);
558 				wnd = null;
559 			}
560 			_windowMap.clear();
561 			disconnect();
562 		}
563 		void disconnect() {
564 			/* Cleanup */
565 			if (_display) {
566 				Log.d("Closing X display");
567         		std.c.linux.X11.Xlib.XCloseDisplay(_display);
568 				_display = null;
569 			} else if (_xcbconnection) {
570 				Log.d("Closing XCB connection");
571 			    /* close connection to server */
572 	  			xcb_disconnect(_xcbconnection);
573 				_xcbconnection = null;
574 			}
575 		}
576 		bool connect() {
577 			
578 			try {
579 				DerelictGL3.load();
580 				_enableOpengl = true;
581 			} catch (Exception e) {
582 				Log.e("Cannot load opengl library", e);
583 			}
584 			//_enableOpengl = false; // test
585 			// X
586 			import std.c.linux.X11.Xlib;
587         	int default_screen;
588 			
589 			if (_enableOpengl) {
590 				Log.d("Opening display via XLib");
591 	        	/* Open Xlib Display */ 
592 	        	_display = XOpenDisplay(null);
593 				if (!_display)
594 	        	{
595 		            Log.e("Failed to open display using Xlib");
596 					_enableOpengl = false;
597 				} else {
598 					// display is opened
599 					default_screen = DefaultScreen(_display);
600 					Log.d("Opened display =");
601 					/* Get the XCB connection from the display */
602 			        _xcbconnection = XGetXCBConnection(_display);
603 			        if (!_xcbconnection)
604 			        {
605 			            XCloseDisplay(_display);
606 						_display = null;
607 			            Log.e("Failed to get XCB connection from Xlib display");
608 						_enableOpengl = false;
609 					} else {
610 					   /* Acquire event queue ownership */
611 				        XSetEventQueueOwner(_display, XEventQueueOwner.XCBOwnsEventQueue);
612 					}
613 				}
614 			}
615 
616 			if (_xcbconnection is null) {
617 				Log.d("Opening XCB connection");
618 			    /* open connection with the server */
619 			    _xcbconnection = xcb_connect(null,null);
620 			}
621 		    if (xcb_connection_has_error(_xcbconnection)) {
622 		        Log.e("Cannot open display");
623 				_xcbconnection = null;
624 		        return false;
625 		    }
626 			//XSetEventQueueOwner(display, XCBOwnsEventQueue);
627 			Log.d("Getting first screen");
628 		    /* get the first screen */
629 			
630 			if (_enableOpengl) {
631 		        /* Find XCB screen */
632 		        //xcb_screen_t *screen = null;
633 		        xcb_screen_iterator_t screen_iter = 
634 		            xcb_setup_roots_iterator(xcb_get_setup(_xcbconnection));
635 		        for(int screen_num = default_screen;
636 			            screen_iter.rem && screen_num > 0;
637 						--screen_num, xcb_screen_next(&screen_iter)) {
638 				}
639 		        _xcbscreen = screen_iter.data;			
640 			} else {
641 			  	_xcbscreen = xcb_setup_roots_iterator( xcb_get_setup(_xcbconnection) ).data;
642 			}
643 		    _xcbscreendepth = xcb_aux_get_depth(_xcbconnection, _xcbscreen);
644 
645 			if (_enableOpengl) {
646 				
647 				int versionMajor;
648 				int versionMinor;
649 				if (!glXQueryVersion(_display,
650                      &versionMajor,
651 					&versionMinor)) {
652 					Log.e("Cannot get GLX version");
653 				} else {
654 					Log.e("GLX version: ", versionMajor, ".", versionMinor);
655 				}
656 				
657 			}
658 			
659 			
660 			return true;
661 		}
662 		XCBWindow getWindow(xcb_window_t w) {
663 			if (w in _windowMap)
664 				return _windowMap[w];
665 			return null;
666 		}
667 		override Window createWindow(string windowCaption, Window parent) {
668 			XCBWindow res = new XCBWindow(windowCaption, parent);
669 			_windowMap[res.windowId] = res;
670 			return res;
671 		}
672 		override int enterMessageLoop() {
673 			Log.i("entering message loop");
674 			int done = 0;
675 			xcb_generic_event_t *e;
676 		    /* event loop */
677 		  	do {
678 		  		e = xcb_wait_for_event(_xcbconnection);
679 				if (e is null) {
680 					Log.w("NULL event received. Exiting message loop");
681 					break;
682 				}
683 		    	switch (e.response_type & ~0x80) {
684 					case XCB_CREATE_NOTIFY: {
685 							xcb_create_notify_event_t *event = cast(xcb_create_notify_event_t *)e;
686 							Log.i("XCB_CREATE_NOTIFY");
687 							XCBWindow window = getWindow(event.window);
688 							if (window !is null) {
689 								//
690 							} else {
691 								Log.w("Received message for unknown window", event.window);
692 							}
693 							break;
694 						}
695 					case XCB_DESTROY_NOTIFY: {
696 							xcb_destroy_notify_event_t *event = cast(xcb_destroy_notify_event_t *)e;
697 							Log.i("XCB_DESTROY_NOTIFY");
698 							XCBWindow window = getWindow(event.window);
699 							if (window !is null) {
700 								//
701 							} else {
702 								Log.w("Received message for unknown window", event.window);
703 							}
704 							break;
705 						}
706 					case XCB_MAP_NOTIFY: {
707 							xcb_map_notify_event_t *event = cast(xcb_map_notify_event_t *)e;
708 							Log.i("XCB_MAP_NOTIFY");
709 							XCBWindow window = getWindow(event.window);
710 							if (window !is null) {
711 								//
712 							} else {
713 								Log.w("Received message for unknown window", event.window);
714 							}
715 							break;
716 						}
717 					case XCB_UNMAP_NOTIFY: {
718 							xcb_unmap_notify_event_t *event = cast(xcb_unmap_notify_event_t *)e;
719 							Log.i("XCB_UNMAP_NOTIFY");
720 							XCBWindow window = getWindow(event.window);
721 							if (window !is null) {
722 								//
723 							} else {
724 								Log.w("Received message for unknown window", event.window);
725 							}
726 							break;
727 						}
728 					case XCB_VISIBILITY_NOTIFY: {
729 							xcb_visibility_notify_event_t *event = cast(xcb_visibility_notify_event_t *)e;
730 							Log.i("XCB_VISIBILITY_NOTIFY ", event.state);
731 							XCBWindow window = getWindow(event.window);
732 							if (window !is null) {
733 								//
734 							} else {
735 								Log.w("Received message for unknown window", event.window);
736 							}
737 							break;
738 						}
739 					case XCB_REPARENT_NOTIFY: {
740 							xcb_reparent_notify_event_t *event = cast(xcb_reparent_notify_event_t *)e;
741 							Log.i("XCB_REPARENT_NOTIFY");
742 							break;
743 						}
744 					case XCB_CONFIGURE_NOTIFY: {
745 							xcb_configure_notify_event_t *event = cast(xcb_configure_notify_event_t *)e;
746 							Log.i("XCB_CONFIGURE_NOTIFY ", event.width, "x", event.height);
747 							XCBWindow window = getWindow(event.window);
748 							if (window !is null) {
749 								//
750 								window.onResize(event.width, event.height);
751 							} else {
752 								Log.w("Received message for unknown window", event.window);
753 							}
754 							break;
755 						}
756 					case XCB_EXPOSE: {   /* draw or redraw the window */
757 							xcb_expose_event_t *expose = cast(xcb_expose_event_t *)e;
758 							Log.i("XCB_EXPOSE");
759 							XCBWindow window = getWindow(expose.window);
760 							if (window !is null) {
761 								window.processExpose(expose);
762 							} else {
763 								Log.w("Received message for unknown window", expose.window);
764 							}
765 				      		break;
766 						}
767 					case XCB_BUTTON_PRESS: {
768 							xcb_button_press_event_t *bp = cast(xcb_button_press_event_t *)e;
769 							Log.d("XCB_BUTTON_PRESS");
770 							XCBWindow window = getWindow(bp.event);
771 							if (window !is null) {
772 								//
773 								window.processMouseEvent(MouseAction.ButtonDown, bp.detail, bp.state, bp.event_x, bp.event_y);
774 							} else {
775 								Log.w("Received message for unknown window", bp.event);
776 							}
777 							break;
778 						}
779 					case XCB_BUTTON_RELEASE: {
780 							Log.d("XCB_BUTTON_RELEASE");
781 							xcb_button_release_event_t *br = cast(xcb_button_release_event_t *)e;
782 							XCBWindow window = getWindow(br.event);
783 							if (window !is null) {
784 								//
785 								window.processMouseEvent(MouseAction.ButtonUp, br.detail, br.state, br.event_x, br.event_y);
786 							} else {
787 								Log.w("Received message for unknown window", br.event);
788 							}
789 							break;
790 						}
791 		            case XCB_MOTION_NOTIFY: {
792 			                xcb_motion_notify_event_t *motion = cast(xcb_motion_notify_event_t *)e;
793 			                //Log.d("XCB_MOTION_NOTIFY ", motion.event, " at coords ", motion.event_x, ", ", motion.event_y);
794 							XCBWindow window = getWindow(motion.event);
795 							if (window !is null) {
796 								//
797 								window.processMouseEvent(MouseAction.Move, 0, motion.state, motion.event_x, motion.event_y);
798 							} else {
799 								Log.w("Received message for unknown window", motion.event);
800 							}
801 			                break;
802 			            }
803 		            case XCB_ENTER_NOTIFY: {
804 			                xcb_enter_notify_event_t *enter = cast(xcb_enter_notify_event_t *)e;
805 			                Log.d("XCB_ENTER_NOTIFY ", enter.event, " at coords ", enter.event_x, ", ", enter.event_y);
806 			                break;
807 			            }
808 		            case XCB_LEAVE_NOTIFY: {
809 		                xcb_leave_notify_event_t *leave = cast(xcb_leave_notify_event_t *)e;
810 		                Log.d("XCB_LEAVE_NOTIFY ", leave.event, " at coords ", leave.event_x, ", ", leave.event_y);
811 						XCBWindow window = getWindow(leave.event);
812 						if (window !is null) {
813 							//
814 							window.processMouseEvent(MouseAction.Leave, 0, leave.state, leave.event_x, leave.event_y);
815 						} else {
816 							Log.w("Received message for unknown window", leave.event);
817 						}
818 		                break;
819 		            }
820 		            case XCB_KEY_PRESS: {
821 			                xcb_key_press_event_t *kp = cast(xcb_key_press_event_t *)e;
822 			                //print_modifiers(kp.state);
823 			                Log.d("XCB_KEY_PRESS ", kp.event, " key=", kp.detail);
824 							if (kp.detail == XK_space) // exist by space
825 		      					done = 1;
826 			                break;
827 			            }
828 		            case XCB_KEY_RELEASE: {
829 			                xcb_key_release_event_t *kr = cast(xcb_key_release_event_t *)e;
830 			                //print_modifiers(kr.state);
831 				            Log.d("XCB_KEY_RELEASE ", kr.event, " key=", kr.detail);
832 			                break;
833 			            }			
834 					default:
835 						Log.v("unknown event: ", e.response_type & ~0x80);	
836 						break;
837 		      	}
838 		    	free(e);
839 		  	} while(!done);
840 			Log.i("exiting message loop");
841 			return 0;
842 		}
843 		XCBWindow[xcb_window_t] _windowMap;
844 	}
845 
846 	// entry point
847 	extern(C) int UIAppMain(string[] args);
848 		
849 	int main(string[] args)
850 	{
851 		
852 		setStderrLogger();
853 		setLogLevel(LogLevel.Trace);
854 
855 		FreeTypeFontManager ft = new FreeTypeFontManager();
856 		ft.registerFont("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FontFamily.SansSerif, "DejaVu", false, FontWeight.Normal);
857 		FontManager.instance = ft;
858 
859 		currentTheme = createDefaultTheme();
860 				
861 		XCBPlatform xcb = new XCBPlatform();
862 		if (!xcb.connect()) {
863 			return 1;
864 		}
865 		Platform.setInstance(xcb);
866 
867 		int res = 0;
868 			
869 			static if (true) {
870 				res = UIAppMain(args);
871 			} else {
872 				Window window = xcb.createWindow("Window Caption", null);
873 				window.show();
874 					
875 				res = xcb.enterMessageLoop();
876 			}
877 		
878 		Platform.setInstance(null);
879 		Log.d("Destroying XCB platform");
880 		destroy(xcb);
881 		
882 		currentTheme = null;
883 		drawableCache = null;
884 		imageCache = null;
885 		FontManager.instance = null;
886 		
887 		Log.d("Exiting main");
888 
889 	  	return res;
890 	}
891 
892 }