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