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 }