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