1 // Written in the D programming language.
2
3 /**
4
5 This module contains implementation of Win32 platform support
6
7 Provides XCB platform and window classes.
8
9 Usually you don't need to use this module directly.
10
11 NOTE: Current implementation of XCB backend does not work well. Consider using of SDL2 backend instead.
12
13 Synopsis:
14
15 ----
16 import dlangui.platforms.x11.x11app;
17 ----
18
19 Copyright: Vadim Lopatin, 2014
20 License: Boost License 1.0
21 Authors: Vadim Lopatin, coolreader.org@gmail.com
22 */
23 module src.dlangui.platforms.x11.x11app;
24
25 version(USE_XCB):
26
27 import std.string;
28 import std.c.linux.X11.xcb.xcb;
29 import std.c.linux.X11.xcb.shm;
30 import std.c.linux.X11.xcb.xproto;
31 import std.c.linux.X11.xcb.image;
32 import std.c.linux.X11.keysymdef;
33 import std.c.linux.linux;
34 import std.c.stdlib;
35 import std.conv;
36
37 import dlangui.core.logger;
38 import dlangui.core.events;
39 import dlangui.core.files;
40 import dlangui.graphics.drawbuf;
41 import dlangui.graphics.fonts;
42 import dlangui.graphics.ftfonts;
43 import dlangui.graphics.resources;
44 import dlangui.widgets.styles;
45 import dlangui.platforms.common.platform;
46
47 version (USE_OPENGL) {
48 import dlangui.graphics.glsupport;
49 }
50
51 import derelict.opengl3.gl3;
52 import derelict.opengl3.glx;
53
54 // pragma(lib, "xcb");
55 // pragma(lib, "xcb-shm");
56 // pragma(lib, "xcb-image");
57 // pragma(lib, "X11-xcb");
58 // pragma(lib, "X11");
59 // pragma(lib, "dl");
60
61 extern (System)
62 xcb_connection_t *XGetXCBConnection(std.c.linux.X11.Xlib.Display *dpy);
63 enum XEventQueueOwner { XlibOwnsEventQueue = 0, XCBOwnsEventQueue };
64 extern (System)
65 void XSetEventQueueOwner(std.c.linux.X11.Xlib.Display *dpy, XEventQueueOwner owner);
66
67 struct xcb_key_symbols_t {};
68 /* enumeration for col parameter? */
69 enum xcb_lookup_t {
70 xcb_lookup_none_t = 1,
71 xcb_lookup_chars_t = 2,
72 xcb_lookup_key_sym_t = 3,
73 xcb_lookup_both_t = 4
74 };
75
76 extern(C) xcb_key_symbols_t *xcb_key_symbols_alloc (xcb_connection_t *c);
77
78 extern(C) void xcb_key_symbols_free (xcb_key_symbols_t *syms);
79
80 extern(C) xcb_keysym_t xcb_key_symbols_get_keysym (xcb_key_symbols_t *syms,
81 xcb_keycode_t keycode,
82 int col);
83
84 extern(C) xcb_keysym_t xcb_key_press_lookup_keysym (xcb_key_symbols_t *syms,
85 xcb_key_press_event_t *event,
86 int col);
87
88 extern(C) xcb_keysym_t xcb_key_release_lookup_keysym (xcb_key_symbols_t *syms,
89 xcb_key_release_event_t *event,
90 int col);
91 class XCBWindow : Window {
92 xcb_window_t _w;
93 xcb_gcontext_t _g;
94 xcb_image_t * _image;
95 xcb_shm_segment_info_t shminfo;
96 version(USE_OPENGL) {
97 /* Create GLX Window */
98 private GLXDrawable _drawable;
99 private GLXWindow _glxwindow;
100 private GLXContext _context;
101 private GLXFBConfig _fb_config;
102 }
103 private int _visualID = 0;
104 private xcb_colormap_t _colormap;
105
106
107 @property xcb_window_t windowId() { return _w; }
108 this(string caption, Window parent) {
109 _caption = caption;
110 Log.d("Creating XCB window");
111 create();
112 }
113 ~this() {
114 Log.d("Destroying window");
115 }
116
117 bool create() {
118 import std.c.linux.X11.Xlib;
119
120 uint mask;
121 uint[3] values;
122
123 // disable opengl for testing
124 _enableOpengl = false;
125 /* create black graphics context */
126 if (true || !_enableOpengl) {
127 _g = xcb_generate_id(_xcbconnection);
128 _w = _xcbscreen.root;
129 mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
130 values[0] = _xcbscreen.black_pixel;
131 values[1] = 0;
132 xcb_create_gc(_xcbconnection, _g, _w, mask, &values[0]);
133 }
134
135 ubyte depth = _xcbscreen.root_depth;
136 /* create window */
137 _w = xcb_generate_id(_xcbconnection);
138
139 Log.d("window=", _w, " gc=", _g);
140 version (USE_OPENGL) {
141 if (_enableOpengl) {
142 int[] visual_attribs = [
143 GLX_RENDER_TYPE, GLX_RGBA_BIT,
144 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
145 GLX_DOUBLEBUFFER, 1,
146 GLX_RED_SIZE, 8,
147 GLX_GREEN_SIZE, 8,
148 GLX_BLUE_SIZE, 8,
149 std.c.linux.X11.Xlib.None];
150
151 Log.d("Getting framebuffer config");
152 int fbcount;
153 GLXFBConfig *fbc = glXChooseFBConfig(_display, DefaultScreen(_display), visual_attribs.ptr, &fbcount);
154 if (!fbc)
155 {
156 Log.d("Failed to retrieve a framebuffer config");
157 //return 1;
158 }
159
160 Log.d("Getting XVisualInfo");
161 _fb_config = fbc[0];
162 auto vi = glXGetVisualFromFBConfig(_display, _fb_config);
163
164 //auto vi = glXChooseVisual(_display, std.c.linux.X11.Xlib.DefaultScreen(_display), attributeList.ptr);
165 _visualID = vi.visualid;
166 //swa.colormap = std.c.linux.X11.Xlib.XCreateColormap(_display, std.c.linux.X11.Xlib.RootWindow(_display, vi.screen), vi.visual, 0); // AllocNone
167
168 Log.d("Creating color map");
169 _colormap = xcb_generate_id(_xcbconnection);
170 /* Create colormap */
171 xcb_create_colormap(
172 _xcbconnection,
173 XCB_COLORMAP_ALLOC_NONE,
174 _colormap,
175 _xcbscreen.root,
176 _visualID
177 );
178 depth = cast(ubyte)vi.depth;
179 }
180 }
181 //mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
182 //values[0] = _xcbscreen.white_pixel;
183
184 int visualId;
185 uint eventmask =
186 XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
187 | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_MOTION
188 | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW
189 | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE
190 | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_VISIBILITY_CHANGE;
191 if (_enableOpengl) {
192 mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
193 values[0] = _xcbscreen.white_pixel;
194 values[1] = eventmask;
195 values[2] = _colormap;
196 //visualId = _xcbscreen.root_visual;
197 visualId = _visualID;
198 } else {
199 mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
200 values[0] = _xcbscreen.white_pixel;
201 values[1] = eventmask;
202 visualId = _xcbscreen.root_visual;
203 }
204 Log.d("xcb_create_window - window=", _w, " VisualID=", _visualID);
205 auto res = xcb_create_window(_xcbconnection,
206 depth, //_xcbscreen.root_depth,
207 //XCB_COPY_FROM_PARENT,//_xcbscreen.root_depth,
208 _w,
209 _xcbscreen.root,
210 30, 30, 800, 650,
211 1,
212 XCB_WINDOW_CLASS_INPUT_OUTPUT,
213 visualId,
214 mask,
215 &values[0]);
216 xcb_flush(_xcbconnection);
217 windowCaption = _caption;
218 return true;
219 }
220
221 private int _imageDx;
222 private int _imageDy;
223 void createImage() {
224 if (_image) {
225 if (_imageDx == _imageDx && _imageDy == _dy)
226 return; // already have image of proper size
227 Log.i("CRXCBScreen::createImage - destroying existing image");
228 xcb_image_destroy(_image);
229 _image = null;
230 }
231 _imageDx = _dx;
232 _imageDy = _dy;
233 Log.i("CRXCBScreen::createImage ", _dx, "x", _dy);
234 xcb_shm_query_version_reply_t * rep_shm;
235 rep_shm = xcb_shm_query_version_reply (_xcbconnection,
236 xcb_shm_query_version(_xcbconnection),
237 null);
238 if(rep_shm) {
239 xcb_image_format_t format;
240 int shmctl_status;
241
242 if (rep_shm.shared_pixmaps &&
243 (rep_shm.major_version > 1 || rep_shm.minor_version > 0))
244 format = cast(xcb_image_format_t)rep_shm.pixmap_format;
245 else
246 format = XCB_IMAGE_FORMAT_Z_PIXMAP;
247
248 _image = xcb_image_create_native (_xcbconnection, cast(short)_dx, cast(short)_dy,
249 format, _xcbscreendepth, null, ~0, null);
250 //format, depth, NULL, ~0, NULL);
251 //format, depth, NULL, ~0, NULL);
252 assert(_image);
253
254 shminfo.shmid = shmget (IPC_PRIVATE,
255 _image.stride*_image.height,
256 IPC_CREAT | octal!777);
257 assert(shminfo.shmid != cast(uint)-1);
258 shminfo.shmaddr = cast(ubyte*)shmat (shminfo.shmid, null, 0);
259 assert(shminfo.shmaddr);
260 _image.data = shminfo.shmaddr;
261 Log.d("Created image depth=", _image.depth, " bpp=", _image.bpp, " stride=", _image.stride );
262
263 shminfo.shmseg = xcb_generate_id (_xcbconnection);
264 xcb_shm_attach (_xcbconnection, shminfo.shmseg,
265 shminfo.shmid, 0);
266 shmctl_status = shmctl(shminfo.shmid, IPC_RMID, null);
267 assert(shmctl_status != -1);
268 free(rep_shm);
269 } else {
270 Log.e("Can't get shms");
271 }
272 }
273
274 void draw(ColorDrawBuf buf) {
275 int i;
276 i = xcb_image_shm_get(_xcbconnection, _w,
277 _image, shminfo,
278 0, 0,
279 XCB_ALL_PLANES);
280 if (!i) {
281 Log.e("cannot get shm image");
282 return;
283 }
284 Rect rc;
285 rc.right = buf.width;
286 rc.bottom = buf.height;
287 switch ( _image.bpp ) {
288 case 32:
289 {
290 for (int y = rc.top; y<rc.bottom; y++) {
291 uint * src = buf.scanLine(y);
292 uint * dst = cast(uint *)(_image.data + _image.stride * y);
293 for ( int x = 0; x < rc.right; x++ ) {
294 uint data = src[x];
295 dst[x] = data;
296 }
297 }
298 }
299 break;
300 default:
301 Log.d("image bit depth not supported: ", _image.bpp);
302 break;
303 }
304 xcb_image_shm_put(_xcbconnection, _w, _g,
305 _image, shminfo,
306 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);
307 xcb_flush(_xcbconnection);
308 }
309
310 bool _derelictgl3Reloaded;
311 override void show() {
312 Log.d("XCBWindow.show()");
313 /* map (show) the window */
314 xcb_map_window(_xcbconnection, _w);
315 xcb_flush(_xcbconnection);
316 //_enableOpengl = false; // test
317 version (USE_OPENGL) {
318 if (_enableOpengl && !_glxwindow) {
319 Log.d("Calling glXCreateWindow display=", _display, " fbconfig=", _fb_config, " window=", _w);
320 _glxwindow = glXCreateWindow(
321 _display,
322 _fb_config,
323 _w,
324 null);
325 if (!_glxwindow) {
326 Log.e("Failed to create GLX window: disabling OpenGL");
327 _enableOpengl = false;
328 } else {
329 import derelict.opengl3.glxext;
330 import std.c.linux.X11.Xlib;
331
332 _drawable = _glxwindow;
333
334 if (!_derelictgl3Reloaded) {
335
336 int count;
337 glGetIntegerv( GL_NUM_EXTENSIONS, &count );
338 Log.d("Number of extensions: ", count);
339 for( int i=0; i<count; ++i ) {
340 auto e = glGetStringi( GL_EXTENSIONS, i );
341 Log.d("Extension: ", fromStringz(e));
342 }
343
344
345 Log.e("Reloading DerelictGL3");
346 _derelictgl3Reloaded = true;
347 _context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
348 if (_context is null) {
349 Log.e("Cannot create temporary context");
350 }
351 //glXMakeContextCurrent(_display, _drawable, _drawable, _context);
352 glXMakeContextCurrent(_display, _glxwindow, _glxwindow, _context);
353 //glXMakeCurrent(_display, _w, _context);
354 DerelictGL3.reload();
355 Log.e("Reloaded DerelictGL3 - removing temporary context");
356 glXMakeCurrent(_display, 0, null);
357 Log.e("Destroying context");
358 glXDestroyContext(_display, _context);
359 Log.e("DerelictGL3 initialized");
360 _context = null;
361 }
362
363
364 // Get the default screen's GLX extension list
365 const char *glxExts = glXQueryExtensionsString( _display,
366 DefaultScreen( _display ) );
367 Log.d("GLX Extensions: ", fromStringz(glxExts));
368 const char * exts = glGetString( GL_EXTENSIONS );
369 Log.d("Extensions: ", fromStringz(exts));
370
371
372 Log.d("GLX_ARB_get_proc_address=", GLX_ARB_get_proc_address);
373 Log.d("GLX_ARB_create_context=", GLX_ARB_create_context);
374
375 //da_glXCreateContextAttribsARB glXCreateContextAttribsARB;
376 //Log.d("getting address of glXCreateContextAttribsARB");
377 //glXCreateContextAttribsARB = cast(da_glXCreateContextAttribsARB)
378 // glXGetProcAddressARB( cast(const GLubyte *)("glXCreateContextAttribsARB".toStringz));
379
380 //Log.d("glXCreateContextAttribsARB = ", &glXCreateContextAttribsARB);
381
382 Log.d("Creating context");
383 //_context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
384 if (!GLX_ARB_create_context) {
385 Log.e("glXCreateContextAttribsARB function is not found");
386 _context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
387 } else {
388 int[] context_attribs =
389 [
390 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
391 GLX_CONTEXT_MINOR_VERSION_ARB, 0,
392 None
393 ];
394 Log.e("calling glXCreateContextAttribsARB");
395 _context = glXCreateContextAttribsARB(_display, _fb_config, null, true, context_attribs.ptr);
396 }
397 Log.d("Created context: ", _context);
398
399 /* Create OpenGL context */
400 //auto context = glXCreateNewContext(_display, _fb_config, GLX_RGBA_TYPE, null, true);
401 if (!_context) {
402 _enableOpengl = false;
403 Log.e("Failed to create OpenGL context");
404 } else {
405
406 }
407
408 /* make OpenGL context current */
409 if(!glXMakeContextCurrent(_display, _drawable, _drawable, _context) || !initShaders()) {
410 Log.e("Failed to make GL context current");
411 _enableOpengl = false;
412 glXDestroyContext(_display, _context);
413 _context = null;
414 } else {
415
416 }
417 }
418 }
419 }
420 }
421
422 protected string _caption;
423
424 override @property string windowCaption() {
425 return _caption;
426 }
427
428 override @property void windowCaption(string caption) {
429 _caption = caption;
430 const char * title = _caption.toStringz;
431 xcb_change_property (_xcbconnection,
432 XCB_PROP_MODE_REPLACE,
433 _w,
434 XCB_ATOM_WM_NAME,
435 XCB_ATOM_STRING,
436 8,
437 cast(uint)_caption.length,
438 cast(void*)title);
439 }
440
441 void redraw() {
442
443 if (_enableOpengl) {
444 version(USE_OPENGL) {
445 import std.c.linux.X11.Xlib;
446 Log.d("Drawing using opengl ", _dx, "x", _dy, " context=", _context);
447 //glXMakeContextCurrent(_display, _drawable, _drawable, _context);
448 glXMakeContextCurrent(_display, _glxwindow, _glxwindow, _context);
449 glEnable(GL_BLEND);
450 glDisable(GL_CULL_FACE);
451 glDisable(GL_DEPTH_TEST);
452 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
453 glViewport(0, 0, _dx, _dy);
454 Log.d("glClearColor");
455 glClearColor(0.2f, 0.4f, 0.9f, 1.0f);
456 Log.d("glClear");
457 glClear(GL_COLOR_BUFFER_BIT);
458
459 import dlangui.graphics.gldrawbuf;
460 GLDrawBuf buf = new GLDrawBuf(_dx, _dy);
461 buf.beforeDrawing();
462 onDraw(buf);
463 buf.fillRect(Rect(0, 0, 100, 100), 0x805010);
464 buf.afterDrawing();
465 destroy(buf);
466 Log.d("Calling glx swap buffers for drawable ", _drawable);
467 glXSwapBuffers(_display, _drawable);
468 //glXMakeContextCurrent(_display, _drawable, _drawable, null);
469 xcb_flush(_xcbconnection);
470 glXMakeContextCurrent (_display, None, None, null);
471 glXWaitGL();
472 }
473 } else {
474 if (!_drawbuf)
475 _drawbuf = new ColorDrawBuf(_dx, _dy);
476 _drawbuf.resize(_dx, _dy);
477 _drawbuf.fill(_backgroundColor);
478 //Log.d("calling createImage");
479 createImage();
480 //Log.d("done createImage");
481 onDraw(_drawbuf);
482 draw(_drawbuf);
483 /*
484 xcb_rectangle_t r = { 20, 20, 60, 60 };
485 xcb_poly_fill_rectangle(_xcbconnection, _w, _g, 1, &r);
486 r = xcb_rectangle_t(cast(short)(_dx - 20 - 60), cast(short)(_dy - 20 - 60), 60, 60);
487 xcb_poly_fill_rectangle(_xcbconnection, _w, _g, 1, &r);
488 */
489 xcb_flush(_xcbconnection);
490 }
491 }
492
493 ColorDrawBuf _drawbuf;
494 bool _exposeSent;
495 void processExpose(xcb_expose_event_t * event) {
496 redraw();
497 _exposeSent = false;
498 }
499
500 /// request window redraw
501 override void invalidate() {
502 if (_exposeSent)
503 return;
504 _exposeSent = true;
505 xcb_expose_event_t * event = cast(xcb_expose_event_t*)std.c.stdlib.malloc(xcb_expose_event_t.sizeof);
506 event.response_type = XCB_EXPOSE; /* The type of the event, here it is XCB_EXPOSE */
507 event.sequence = 0;
508 event.window = _w; /* The Id of the window that receives the event (in case */
509 /* our application registered for events on several windows */
510 event.x = 0; /* The x coordinate of the top-left part of the window that needs to be redrawn */
511 event.y = 0; /* The y coordinate of the top-left part of the window that needs to be redrawn */
512 event.width = cast(ushort)_dx; /* The width of the part of the window that needs to be redrawn */
513 event.height = cast(ushort)_dy; /* The height of the part of the window that needs to be redrawn */
514 event.count = 1;
515
516 xcb_void_cookie_t res = xcb_send_event(_xcbconnection, false, _w, XCB_EVENT_MASK_EXPOSURE, cast(char *)event);
517 xcb_flush(_xcbconnection);
518 }
519
520 protected ButtonDetails _lbutton;
521 protected ButtonDetails _mbutton;
522 protected ButtonDetails _rbutton;
523 void processMouseEvent(MouseAction action, ubyte detail, ushort state, short x, short y) {
524 MouseButton button = MouseButton.None;
525 short wheelDelta = 0;
526 ButtonDetails * pbuttonDetails = null;
527 ushort flags = 0;
528 if (state & XCB_BUTTON_MASK_1)
529 flags |= MouseFlag.LButton;
530 if (state & XCB_BUTTON_MASK_2)
531 flags |= MouseFlag.MButton;
532 if (state & XCB_BUTTON_MASK_3)
533 flags |= MouseFlag.RButton;
534 if (state & XCB_MOD_MASK_SHIFT)
535 flags |= MouseFlag.Shift;
536 if (state & XCB_MOD_MASK_CONTROL)
537 flags |= MouseFlag.Control;
538 if (state & XCB_MOD_MASK_LOCK)
539 flags |= MouseFlag.Alt;
540 if (action == MouseAction.ButtonDown || action == MouseAction.ButtonUp) {
541 switch (detail) {
542 case 1:
543 button = MouseButton.Left;
544 pbuttonDetails = &_lbutton;
545 if (action == MouseAction.ButtonDown)
546 flags |= MouseFlag.LButton;
547 else if (action == MouseAction.ButtonUp)
548 flags &= ~MouseFlag.LButton;
549 break;
550 case 2:
551 button = MouseButton.Middle;
552 pbuttonDetails = &_mbutton;
553 if (action == MouseAction.ButtonDown)
554 flags |= MouseFlag.MButton;
555 else if (action == MouseAction.ButtonUp)
556 flags &= ~MouseFlag.MButton;
557 break;
558 case 3:
559 button = MouseButton.Right;
560 pbuttonDetails = &_rbutton;
561 if (action == MouseAction.ButtonDown)
562 flags |= MouseFlag.RButton;
563 else if (action == MouseAction.ButtonUp)
564 flags &= ~MouseFlag.RButton;
565 break;
566 case 4:
567 if (action == MouseAction.ButtonUp)
568 return;
569 wheelDelta = -1;
570 action = MouseAction.Wheel;
571 break;
572 case 5:
573 if (action == MouseAction.ButtonUp)
574 return;
575 wheelDelta = 1;
576 action = MouseAction.Wheel;
577 break;
578 default:
579 // unknown button
580 return;
581 }
582 }
583 //Log.d("processMouseEvent ", action, " detail=", detail, " state=", state, " at coords ", x, ", ", y);
584 if (action == MouseAction.ButtonDown) {
585 pbuttonDetails.down(x, y, cast(ushort)flags);
586 } else if (action == MouseAction.ButtonUp) {
587 pbuttonDetails.up(x, y, cast(ushort)flags);
588 }
589 MouseEvent event = new MouseEvent(action, button, cast(ushort)flags, x, y, wheelDelta);
590 event.lbutton = _lbutton;
591 event.rbutton = _rbutton;
592 event.mbutton = _mbutton;
593 bool res = dispatchMouseEvent(event);
594 if (res) {
595 Log.d("Calling update() after mouse event");
596 invalidate();
597 }
598 }
599
600 uint convertKeyCode(uint keyCode) {
601 switch (keyCode) {
602 case XK_BackSpace:
603 return KeyCode.BACK;
604 case XK_Tab:
605 return KeyCode.TAB;
606 case XK_Linefeed:
607 case XK_Return:
608 return KeyCode.RETURN;
609 case XK_Shift_L:
610 return KeyCode.LSHIFT;
611 case XK_Shift_R:
612 return KeyCode.RSHIFT;
613 case XK_Control_L:
614 return KeyCode.LCONTROL;
615 case XK_Control_R:
616 return KeyCode.RCONTROL;
617 case XK_Alt_L:
618 return KeyCode.LALT;
619 case XK_Alt_R:
620 return KeyCode.RALT;
621 case XK_Pause:
622 return KeyCode.PAUSE;
623 case XK_Caps_Lock:
624 return KeyCode.CAPS;
625 case XK_Escape:
626 return KeyCode.ESCAPE;
627 case XK_KP_Space:
628 return KeyCode.SPACE;
629 case XK_KP_Page_Up:
630 case XK_Page_Up:
631 return KeyCode.PAGEUP;
632 case XK_KP_Page_Down:
633 case XK_Page_Down:
634 return KeyCode.PAGEDOWN;
635 case XK_End:
636 case XK_KP_End:
637 return KeyCode.END;
638 case XK_Home:
639 case XK_KP_Home:
640 return KeyCode.HOME;
641 case XK_KP_Left:
642 case XK_Left:
643 return KeyCode.LEFT;
644 case XK_KP_Up:
645 case XK_Up:
646 return KeyCode.UP;
647 case XK_KP_Right:
648 case XK_Right:
649 return KeyCode.RIGHT;
650 case XK_KP_Down:
651 case XK_Down:
652 return KeyCode.DOWN;
653 case XK_Insert:
654 case XK_KP_Insert:
655 return KeyCode.INS;
656 case XK_KP_Delete:
657 case XK_Delete:
658 return KeyCode.DEL;
659 case XK_0:
660 return KeyCode.KEY_0;
661 case XK_1:
662 return KeyCode.KEY_1;
663 case XK_2:
664 return KeyCode.KEY_2;
665 case XK_3:
666 return KeyCode.KEY_3;
667 case XK_4:
668 return KeyCode.KEY_4;
669 case XK_5:
670 return KeyCode.KEY_5;
671 case XK_6:
672 return KeyCode.KEY_6;
673 case XK_7:
674 return KeyCode.KEY_7;
675 case XK_8:
676 return KeyCode.KEY_8;
677 case XK_9:
678 return KeyCode.KEY_9;
679 case XK_A:
680 return KeyCode.KEY_A;
681 case XK_B:
682 return KeyCode.KEY_B;
683 case XK_C:
684 return KeyCode.KEY_C;
685 case XK_D:
686 return KeyCode.KEY_D;
687 case XK_E:
688 return KeyCode.KEY_E;
689 case XK_F:
690 return KeyCode.KEY_F;
691 case XK_G:
692 return KeyCode.KEY_G;
693 case XK_H:
694 return KeyCode.KEY_H;
695 case XK_I:
696 return KeyCode.KEY_I;
697 case XK_J:
698 return KeyCode.KEY_J;
699 case XK_K:
700 return KeyCode.KEY_K;
701 case XK_L:
702 return KeyCode.KEY_L;
703 case XK_M:
704 return KeyCode.KEY_M;
705 case XK_N:
706 return KeyCode.KEY_N;
707 case XK_O:
708 return KeyCode.KEY_O;
709 case XK_P:
710 return KeyCode.KEY_P;
711 case XK_Q:
712 return KeyCode.KEY_Q;
713 case XK_R:
714 return KeyCode.KEY_R;
715 case XK_S:
716 return KeyCode.KEY_S;
717 case XK_T:
718 return KeyCode.KEY_T;
719 case XK_U:
720 return KeyCode.KEY_U;
721 case XK_V:
722 return KeyCode.KEY_V;
723 case XK_W:
724 return KeyCode.KEY_W;
725 case XK_X:
726 return KeyCode.KEY_X;
727 case XK_Y:
728 return KeyCode.KEY_Y;
729 case XK_Z:
730 return KeyCode.KEY_Z;
731 //return KeyCode.LWIN = 0x5b,
732 //return KeyCode.RWIN = 0x5c,
733 case XK_KP_0:
734 return KeyCode.NUM_0;
735 case XK_KP_1:
736 return KeyCode.NUM_1;
737 case XK_KP_2:
738 return KeyCode.NUM_2;
739 case XK_KP_3:
740 return KeyCode.NUM_3;
741 case XK_KP_4:
742 return KeyCode.NUM_4;
743 case XK_KP_5:
744 return KeyCode.NUM_5;
745 case XK_KP_6:
746 return KeyCode.NUM_6;
747 case XK_KP_7:
748 return KeyCode.NUM_7;
749 case XK_KP_8:
750 return KeyCode.NUM_8;
751 case XK_KP_9:
752 return KeyCode.NUM_9;
753 case XK_asterisk:
754 return KeyCode.MUL;
755 case XK_plus:
756 return KeyCode.ADD;
757 case XK_slash:
758 return KeyCode.DIV;
759 case XK_minus:
760 return KeyCode.SUB;
761 case XK_period:
762 return KeyCode.DECIMAL;
763 case XK_F1:
764 return KeyCode.F1;
765 case XK_F2:
766 return KeyCode.F2;
767 case XK_F3:
768 return KeyCode.F3;
769 case XK_F4:
770 return KeyCode.F4;
771 case XK_F5:
772 return KeyCode.F5;
773 case XK_F6:
774 return KeyCode.F6;
775 case XK_F7:
776 return KeyCode.F7;
777 case XK_F8:
778 return KeyCode.F8;
779 case XK_F9:
780 return KeyCode.F9;
781 case XK_F10:
782 return KeyCode.F10;
783 case XK_F11:
784 return KeyCode.F11;
785 case XK_F12:
786 return KeyCode.F12;
787 case XK_Num_Lock:
788 return KeyCode.NUMLOCK;
789 case XK_Scroll_Lock:
790 return KeyCode.SCROLL;
791 default:
792 return 0x10000 | keyCode;
793 }
794 }
795
796 uint convertKeyFlags(uint flags) {
797 return 0;
798 }
799
800 bool processKeyEvent(KeyAction action, uint keyCode, uint flags) {
801 Log.d("processKeyEvent ", action, " x11 key=", keyCode, " x11 flags=", flags);
802 keyCode = convertKeyCode(keyCode);
803 flags = convertKeyFlags(flags);
804 Log.d("processKeyEvent ", action, " converted key=", keyCode, " converted flags=", flags);
805 bool res = dispatchKeyEvent(new KeyEvent(action, keyCode, flags));
806 if (keyCode & 0x10000 && (keyCode & 0xF000) != 0xF000) {
807 dchar[1] text;
808 text[0] = keyCode & 0xFFFF;
809 res = dispatchKeyEvent(new KeyEvent(KeyAction.Text, keyCode, flags, cast(dstring)text)) || res;
810 }
811 if (res) {
812 Log.d("Calling update() after key event");
813 invalidate();
814 }
815 return res;
816 }
817 }
818
819 private __gshared xcb_connection_t * _xcbconnection;
820 private __gshared xcb_screen_t * _xcbscreen;
821 private __gshared ubyte _xcbscreendepth;
822 private __gshared bool _enableOpengl;
823 private __gshared std.c.linux.X11.Xlib.Display * _display;
824
825 class XCBPlatform : Platform {
826 this() {
827
828 }
829 ~this() {
830 foreach(ref XCBWindow wnd; _windowMap) {
831 destroy(wnd);
832 wnd = null;
833 }
834 _windowMap.clear();
835 disconnect();
836 }
837 void disconnect() {
838 /* Cleanup */
839 if (_display) {
840 Log.d("Closing X display");
841 std.c.linux.X11.Xlib.XCloseDisplay(_display);
842 _display = null;
843 } else if (_xcbconnection) {
844 Log.d("Closing XCB connection");
845 /* close connection to server */
846 xcb_disconnect(_xcbconnection);
847 _xcbconnection = null;
848 }
849 }
850 bool connect() {
851
852 try {
853 DerelictGL3.load();
854 _enableOpengl = true;
855 } catch (Exception e) {
856 Log.e("Cannot load opengl library", e);
857 }
858 //_enableOpengl = false; // test
859 // X
860 import std.c.linux.X11.Xlib;
861 int default_screen;
862
863 if (_enableOpengl) {
864 Log.d("Opening display via XLib");
865 /* Open Xlib Display */
866 _display = XOpenDisplay(null);
867 if (!_display)
868 {
869 Log.e("Failed to open display using Xlib");
870 _enableOpengl = false;
871 } else {
872 // display is opened
873 default_screen = DefaultScreen(_display);
874 Log.d("Opened display =");
875 /* Get the XCB connection from the display */
876 _xcbconnection = XGetXCBConnection(_display);
877 if (!_xcbconnection)
878 {
879 XCloseDisplay(_display);
880 _display = null;
881 Log.e("Failed to get XCB connection from Xlib display");
882 _enableOpengl = false;
883 } else {
884 /* Acquire event queue ownership */
885 XSetEventQueueOwner(_display, XEventQueueOwner.XCBOwnsEventQueue);
886 }
887 }
888 }
889
890 if (_xcbconnection is null) {
891 Log.d("Opening XCB connection");
892 /* open connection with the server */
893 _xcbconnection = xcb_connect(null,null);
894 }
895 if (xcb_connection_has_error(_xcbconnection)) {
896 Log.e("Cannot open display");
897 _xcbconnection = null;
898 return false;
899 }
900 //XSetEventQueueOwner(display, XCBOwnsEventQueue);
901 Log.d("Getting first screen");
902 /* get the first screen */
903
904 if (_enableOpengl) {
905 /* Find XCB screen */
906 //xcb_screen_t *screen = null;
907 xcb_screen_iterator_t screen_iter =
908 xcb_setup_roots_iterator(xcb_get_setup(_xcbconnection));
909 for(int screen_num = default_screen;
910 screen_iter.rem && screen_num > 0;
911 --screen_num, xcb_screen_next(&screen_iter)) {
912 }
913 _xcbscreen = screen_iter.data;
914 } else {
915 _xcbscreen = xcb_setup_roots_iterator( xcb_get_setup(_xcbconnection) ).data;
916 }
917 _xcbscreendepth = xcb_aux_get_depth(_xcbconnection, _xcbscreen);
918
919 if (_enableOpengl) {
920
921 int versionMajor;
922 int versionMinor;
923 if (!glXQueryVersion(_display,
924 &versionMajor,
925 &versionMinor)) {
926 Log.e("Cannot get GLX version");
927 } else {
928 Log.e("GLX version: ", versionMajor, ".", versionMinor);
929 }
930
931 }
932
933
934 return true;
935 }
936 XCBWindow getWindow(xcb_window_t w) {
937 if (w in _windowMap)
938 return _windowMap[w];
939 return null;
940 }
941 override Window createWindow(string windowCaption, Window parent) {
942 XCBWindow res = new XCBWindow(windowCaption, parent);
943 _windowMap[res.windowId] = res;
944 return res;
945 }
946 override int enterMessageLoop() {
947 Log.i("entering message loop");
948 int done = 0;
949 xcb_generic_event_t *e;
950 xcb_key_symbols_t * keysyms;// = xcb_key_symbols_alloc(_xcbconnection);
951 keysyms = xcb_key_symbols_alloc(_xcbconnection);
952 if (!keysyms)
953 Log.e("xcb_key_symbols_alloc returned null");
954 /* event loop */
955 do {
956 e = xcb_wait_for_event(_xcbconnection);
957 if (e is null) {
958 Log.w("NULL event received. Exiting message loop");
959 break;
960 }
961 switch (e.response_type & ~0x80) {
962 case XCB_CREATE_NOTIFY: {
963 xcb_create_notify_event_t *event = cast(xcb_create_notify_event_t *)e;
964 Log.i("XCB_CREATE_NOTIFY");
965 XCBWindow window = getWindow(event.window);
966 if (window !is null) {
967 //
968 } else {
969 Log.w("Received message for unknown window", event.window);
970 }
971 break;
972 }
973 case XCB_DESTROY_NOTIFY: {
974 xcb_destroy_notify_event_t *event = cast(xcb_destroy_notify_event_t *)e;
975 Log.i("XCB_DESTROY_NOTIFY");
976 XCBWindow window = getWindow(event.window);
977 if (window !is null) {
978 //
979 } else {
980 Log.w("Received message for unknown window", event.window);
981 }
982 break;
983 }
984 case XCB_MAP_NOTIFY: {
985 xcb_map_notify_event_t *event = cast(xcb_map_notify_event_t *)e;
986 Log.i("XCB_MAP_NOTIFY");
987 XCBWindow window = getWindow(event.window);
988 if (window !is null) {
989 //
990 } else {
991 Log.w("Received message for unknown window", event.window);
992 }
993 break;
994 }
995 case XCB_UNMAP_NOTIFY: {
996 xcb_unmap_notify_event_t *event = cast(xcb_unmap_notify_event_t *)e;
997 Log.i("XCB_UNMAP_NOTIFY");
998 XCBWindow window = getWindow(event.window);
999 if (window !is null) {
1000 //
1001 } else {
1002 Log.w("Received message for unknown window", event.window);
1003 }
1004 break;
1005 }
1006 case XCB_VISIBILITY_NOTIFY: {
1007 xcb_visibility_notify_event_t *event = cast(xcb_visibility_notify_event_t *)e;
1008 Log.i("XCB_VISIBILITY_NOTIFY ", event.state);
1009 XCBWindow window = getWindow(event.window);
1010 if (window !is null) {
1011 //
1012 } else {
1013 Log.w("Received message for unknown window", event.window);
1014 }
1015 break;
1016 }
1017 case XCB_REPARENT_NOTIFY: {
1018 xcb_reparent_notify_event_t *event = cast(xcb_reparent_notify_event_t *)e;
1019 Log.i("XCB_REPARENT_NOTIFY");
1020 break;
1021 }
1022 case XCB_CONFIGURE_NOTIFY: {
1023 xcb_configure_notify_event_t *event = cast(xcb_configure_notify_event_t *)e;
1024 Log.i("XCB_CONFIGURE_NOTIFY ", event.width, "x", event.height);
1025 XCBWindow window = getWindow(event.window);
1026 if (window !is null) {
1027 //
1028 window.onResize(event.width, event.height);
1029 } else {
1030 Log.w("Received message for unknown window", event.window);
1031 }
1032 break;
1033 }
1034 case XCB_EXPOSE: { /* draw or redraw the window */
1035 xcb_expose_event_t *expose = cast(xcb_expose_event_t *)e;
1036 Log.i("XCB_EXPOSE");
1037 XCBWindow window = getWindow(expose.window);
1038 if (window !is null) {
1039 window.processExpose(expose);
1040 } else {
1041 Log.w("Received message for unknown window", expose.window);
1042 }
1043 break;
1044 }
1045 case XCB_BUTTON_PRESS: {
1046 xcb_button_press_event_t *bp = cast(xcb_button_press_event_t *)e;
1047 Log.d("XCB_BUTTON_PRESS");
1048 XCBWindow window = getWindow(bp.event);
1049 if (window !is null) {
1050 //
1051 window.processMouseEvent(MouseAction.ButtonDown, bp.detail, bp.state, bp.event_x, bp.event_y);
1052 } else {
1053 Log.w("Received message for unknown window", bp.event);
1054 }
1055 break;
1056 }
1057 case XCB_BUTTON_RELEASE: {
1058 Log.d("XCB_BUTTON_RELEASE");
1059 xcb_button_release_event_t *br = cast(xcb_button_release_event_t *)e;
1060 XCBWindow window = getWindow(br.event);
1061 if (window !is null) {
1062 //
1063 window.processMouseEvent(MouseAction.ButtonUp, br.detail, br.state, br.event_x, br.event_y);
1064 } else {
1065 Log.w("Received message for unknown window", br.event);
1066 }
1067 break;
1068 }
1069 case XCB_MOTION_NOTIFY: {
1070 xcb_motion_notify_event_t *motion = cast(xcb_motion_notify_event_t *)e;
1071 //Log.d("XCB_MOTION_NOTIFY ", motion.event, " at coords ", motion.event_x, ", ", motion.event_y);
1072 XCBWindow window = getWindow(motion.event);
1073 if (window !is null) {
1074 //
1075 window.processMouseEvent(MouseAction.Move, 0, motion.state, motion.event_x, motion.event_y);
1076 } else {
1077 Log.w("Received message for unknown window", motion.event);
1078 }
1079 break;
1080 }
1081 case XCB_ENTER_NOTIFY: {
1082 xcb_enter_notify_event_t *enter = cast(xcb_enter_notify_event_t *)e;
1083 Log.d("XCB_ENTER_NOTIFY ", enter.event, " at coords ", enter.event_x, ", ", enter.event_y);
1084 break;
1085 }
1086 case XCB_LEAVE_NOTIFY: {
1087 xcb_leave_notify_event_t *leave = cast(xcb_leave_notify_event_t *)e;
1088 Log.d("XCB_LEAVE_NOTIFY ", leave.event, " at coords ", leave.event_x, ", ", leave.event_y);
1089 XCBWindow window = getWindow(leave.event);
1090 if (window !is null) {
1091 //
1092 window.processMouseEvent(MouseAction.Leave, 0, leave.state, leave.event_x, leave.event_y);
1093 } else {
1094 Log.w("Received message for unknown window", leave.event);
1095 }
1096 break;
1097 }
1098 case XCB_KEY_PRESS: {
1099 xcb_key_press_event_t *kp = cast(xcb_key_press_event_t *)e;
1100 //print_modifiers(kp.state);
1101 Log.d("XCB_KEY_PRESS ", kp.event, " key=", kp.detail);
1102 XCBWindow window = getWindow(kp.event);
1103 if (window !is null) {
1104 //xcb_keysym_t keysym = xcb_key_press_lookup_keysym(keysyms, kp, xcb_lookup_t.xcb_lookup_key_sym_t);
1105 xcb_keysym_t keysym = xcb_key_press_lookup_keysym(keysyms, kp, 2);
1106 Log.d("xcb_key_press_lookup_keysym - key=", kp.detail, " keysym=", keysym);
1107 //xcb_keysym_t keysym = xcb_key_symbols_get_keysym(keysyms, kp.detail, xcb_lookup_t.xcb_lookup_key_sym_t);
1108 window.processKeyEvent(KeyAction.KeyDown, keysym, kp.state);
1109 }
1110 //if (kp.detail == XK_space) // exist by space
1111 // done = 1;
1112 break;
1113 }
1114 case XCB_KEY_RELEASE: {
1115 xcb_key_release_event_t *kr = cast(xcb_key_release_event_t *)e;
1116 //print_modifiers(kr.state);
1117 Log.d("XCB_KEY_RELEASE ", kr.event, " key=", kr.detail);
1118 XCBWindow window = getWindow(kr.event);
1119 if (window !is null) {
1120 //xcb_keysym_t keysym = xcb_key_symbols_get_keysym(keysyms, kr.detail, xcb_lookup_t.xcb_lookup_key_sym_t);
1121 xcb_keysym_t keysym = xcb_key_release_lookup_keysym(keysyms, kr, 2);
1122 window.processKeyEvent(KeyAction.KeyUp, keysym, kr.state);
1123 }
1124 break;
1125 }
1126 default:
1127 Log.v("unknown event: ", e.response_type & ~0x80);
1128 break;
1129 }
1130 free(e);
1131 } while(!done);
1132 if (keysyms)
1133 xcb_key_symbols_free(keysyms);
1134 Log.i("exiting message loop");
1135 return 0;
1136 }
1137
1138 /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux)
1139 override dstring getClipboardText(bool mouseBuffer = false) {
1140 return ""d;
1141 }
1142
1143 /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux)
1144 override void setClipboardText(dstring text, bool mouseBuffer = false) {
1145 }
1146
1147 protected XCBWindow[xcb_window_t] _windowMap;
1148 }
1149
1150 // entry point
1151 extern(C) int UIAppMain(string[] args);
1152
1153 int main(string[] args)
1154 {
1155
1156 setStderrLogger();
1157 Log.setLogLevel(LogLevel.Trace);
1158
1159 FreeTypeFontManager ft = new FreeTypeFontManager();
1160 ft.registerFont("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FontFamily.SansSerif, "DejaVu", false, FontWeight.Normal);
1161 FontManager.instance = ft;
1162
1163 currentTheme = createDefaultTheme();
1164
1165 XCBPlatform xcb = new XCBPlatform();
1166 if (!xcb.connect()) {
1167 return 1;
1168 }
1169 Platform.setInstance(xcb);
1170
1171 int res = 0;
1172
1173 static if (true) {
1174 res = UIAppMain(args);
1175 } else {
1176 Window window = xcb.createWindow("Window Caption", null);
1177 window.show();
1178
1179 res = xcb.enterMessageLoop();
1180 }
1181
1182 Platform.setInstance(null);
1183 Log.d("Destroying XCB platform");
1184 destroy(xcb);
1185
1186 currentTheme = null;
1187 drawableCache = null;
1188 imageCache = null;
1189 FontManager.instance = null;
1190
1191 Log.d("Exiting main");
1192
1193 return res;
1194 }