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 short lastx; 821 short lasty; 822 uint _keyFlags; 823 824 void processMouseEvent(MouseAction action, uint button, uint state, int x, int y) { 825 MouseEvent event = null; 826 ushort lastFlags; 827 828 if (action == MouseAction.Wheel) { 829 // handle wheel 830 short wheelDelta = cast(short)y; 831 if (_keyFlags & KeyFlag.Shift) 832 lastFlags |= MouseFlag.Shift; 833 else 834 lastFlags &= ~MouseFlag.Shift; 835 if (_keyFlags & KeyFlag.Control) 836 lastFlags |= MouseFlag.Control; 837 else 838 lastFlags &= ~MouseFlag.Control; 839 if (_keyFlags & KeyFlag.Alt) 840 lastFlags |= MouseFlag.Alt; 841 else 842 lastFlags &= ~MouseFlag.Alt; 843 if (wheelDelta) 844 event = new MouseEvent(action, MouseButton.None, lastFlags, lastx, lasty, wheelDelta); 845 } else { 846 MouseButton btn = convertMouseButton(button); 847 lastFlags = convertMouseFlags(state, btn, action == MouseAction.ButtonDown); 848 849 if (_keyFlags & KeyFlag.Shift) 850 lastFlags |= MouseFlag.Shift; 851 if (_keyFlags & KeyFlag.Control) 852 lastFlags |= MouseFlag.Control; 853 if (_keyFlags & KeyFlag.Alt) 854 lastFlags |= MouseFlag.Alt; 855 lastx = cast(short)x; 856 lasty = cast(short)y; 857 event = new MouseEvent(action, btn, lastFlags, lastx, lasty); 858 } 859 if (event) { 860 ButtonDetails * pbuttonDetails = null; 861 if (button == MouseButton.Left) 862 pbuttonDetails = &_lbutton; 863 else if (button == MouseButton.Right) 864 pbuttonDetails = &_rbutton; 865 else if (button == MouseButton.Middle) 866 pbuttonDetails = &_mbutton; 867 if (pbuttonDetails) { 868 if (action == MouseAction.ButtonDown) { 869 pbuttonDetails.down(cast(short)x, cast(short)y, lastFlags); 870 } else if (action == MouseAction.ButtonUp) { 871 pbuttonDetails.up(cast(short)x, cast(short)y, lastFlags); 872 } 873 } 874 event.lbutton = _lbutton; 875 event.rbutton = _rbutton; 876 event.mbutton = _mbutton; 877 878 bool res = dispatchMouseEvent(event); 879 if (res) { 880 debug(mouse) Log.d("Calling update() after mouse event"); 881 update(); 882 //invalidate(); 883 } 884 } 885 } 886 887 888 uint convertKeyCode(uint keyCode) { 889 import x11.keysymdef; 890 alias KeyCode = dlangui.core.events.KeyCode; 891 switch(keyCode) { 892 case XK_0: 893 return KeyCode.KEY_0; 894 case XK_1: 895 return KeyCode.KEY_1; 896 case XK_2: 897 return KeyCode.KEY_2; 898 case XK_3: 899 return KeyCode.KEY_3; 900 case XK_4: 901 return KeyCode.KEY_4; 902 case XK_5: 903 return KeyCode.KEY_5; 904 case XK_6: 905 return KeyCode.KEY_6; 906 case XK_7: 907 return KeyCode.KEY_7; 908 case XK_8: 909 return KeyCode.KEY_8; 910 case XK_9: 911 return KeyCode.KEY_9; 912 case XK_A: 913 case XK_a: 914 return KeyCode.KEY_A; 915 case XK_B: 916 case XK_b: 917 return KeyCode.KEY_B; 918 case XK_C: 919 case XK_c: 920 return KeyCode.KEY_C; 921 case XK_D: 922 case XK_d: 923 return KeyCode.KEY_D; 924 case XK_E: 925 case XK_e: 926 return KeyCode.KEY_E; 927 case XK_F: 928 case XK_f: 929 return KeyCode.KEY_F; 930 case XK_G: 931 case XK_g: 932 return KeyCode.KEY_G; 933 case XK_H: 934 case XK_h: 935 return KeyCode.KEY_H; 936 case XK_I: 937 case XK_i: 938 return KeyCode.KEY_I; 939 case XK_J: 940 case XK_j: 941 return KeyCode.KEY_J; 942 case XK_K: 943 case XK_k: 944 return KeyCode.KEY_K; 945 case XK_L: 946 case XK_l: 947 return KeyCode.KEY_L; 948 case XK_M: 949 case XK_m: 950 return KeyCode.KEY_M; 951 case XK_N: 952 case XK_n: 953 return KeyCode.KEY_N; 954 case XK_O: 955 case XK_o: 956 return KeyCode.KEY_O; 957 case XK_P: 958 case XK_p: 959 return KeyCode.KEY_P; 960 case XK_Q: 961 case XK_q: 962 return KeyCode.KEY_Q; 963 case XK_R: 964 case XK_r: 965 return KeyCode.KEY_R; 966 case XK_S: 967 case XK_s: 968 return KeyCode.KEY_S; 969 case XK_T: 970 case XK_t: 971 return KeyCode.KEY_T; 972 case XK_U: 973 case XK_u: 974 return KeyCode.KEY_U; 975 case XK_V: 976 case XK_v: 977 return KeyCode.KEY_V; 978 case XK_W: 979 case XK_w: 980 return KeyCode.KEY_W; 981 case XK_X: 982 case XK_x: 983 return KeyCode.KEY_X; 984 case XK_Y: 985 case XK_y: 986 return KeyCode.KEY_Y; 987 case XK_Z: 988 case XK_z: 989 return KeyCode.KEY_Z; 990 case XK_F1: 991 return KeyCode.F1; 992 case XK_F2: 993 return KeyCode.F2; 994 case XK_F3: 995 return KeyCode.F3; 996 case XK_F4: 997 return KeyCode.F4; 998 case XK_F5: 999 return KeyCode.F5; 1000 case XK_F6: 1001 return KeyCode.F6; 1002 case XK_F7: 1003 return KeyCode.F7; 1004 case XK_F8: 1005 return KeyCode.F8; 1006 case XK_F9: 1007 return KeyCode.F9; 1008 case XK_F10: 1009 return KeyCode.F10; 1010 case XK_F11: 1011 return KeyCode.F11; 1012 case XK_F12: 1013 return KeyCode.F12; 1014 case XK_F13: 1015 return KeyCode.F13; 1016 case XK_F14: 1017 return KeyCode.F14; 1018 case XK_F15: 1019 return KeyCode.F15; 1020 case XK_F16: 1021 return KeyCode.F16; 1022 case XK_F17: 1023 return KeyCode.F17; 1024 case XK_F18: 1025 return KeyCode.F18; 1026 case XK_F19: 1027 return KeyCode.F19; 1028 case XK_F20: 1029 return KeyCode.F20; 1030 case XK_F21: 1031 return KeyCode.F21; 1032 case XK_F22: 1033 return KeyCode.F22; 1034 case XK_F23: 1035 return KeyCode.F23; 1036 case XK_F24: 1037 return KeyCode.F24; 1038 case XK_BackSpace: 1039 return KeyCode.BACK; 1040 case XK_space: 1041 return KeyCode.SPACE; 1042 case XK_Tab: 1043 return KeyCode.TAB; 1044 case XK_Return: 1045 case XK_KP_Enter: 1046 return KeyCode.RETURN; 1047 case XK_Escape: 1048 return KeyCode.ESCAPE; 1049 case XK_KP_Delete: 1050 case XK_Delete: 1051 //case 0x40000063: // dirty hack for Linux - key on keypad 1052 return KeyCode.DEL; 1053 case XK_Insert: 1054 case XK_KP_Insert: 1055 //case 0x40000062: // dirty hack for Linux - key on keypad 1056 return KeyCode.INS; 1057 case XK_KP_Home: 1058 case XK_Home: 1059 //case 0x4000005f: // dirty hack for Linux - key on keypad 1060 return KeyCode.HOME; 1061 case XK_KP_Page_Up: 1062 case XK_Page_Up: 1063 //case 0x40000061: // dirty hack for Linux - key on keypad 1064 return KeyCode.PAGEUP; 1065 case XK_KP_End: 1066 case XK_End: 1067 //case 0x40000059: // dirty hack for Linux - key on keypad 1068 return KeyCode.END; 1069 case XK_KP_Page_Down: 1070 case XK_Page_Down: 1071 //case 0x4000005b: // dirty hack for Linux - key on keypad 1072 return KeyCode.PAGEDOWN; 1073 case XK_KP_Left: 1074 case XK_Left: 1075 //case 0x4000005c: // dirty hack for Linux - key on keypad 1076 return KeyCode.LEFT; 1077 case XK_KP_Right: 1078 case XK_Right: 1079 //case 0x4000005e: // dirty hack for Linux - key on keypad 1080 return KeyCode.RIGHT; 1081 case XK_KP_Up: 1082 case XK_Up: 1083 //case 0x40000060: // dirty hack for Linux - key on keypad 1084 return KeyCode.UP; 1085 case XK_KP_Down: 1086 case XK_Down: 1087 //case 0x4000005a: // dirty hack for Linux - key on keypad 1088 return KeyCode.DOWN; 1089 case XK_Control_L: 1090 return KeyCode.LCONTROL; 1091 case XK_Shift_L: 1092 return KeyCode.LSHIFT; 1093 case XK_Alt_L: 1094 return KeyCode.LALT; 1095 case XK_Control_R: 1096 return KeyCode.RCONTROL; 1097 case XK_Shift_R: 1098 return KeyCode.RSHIFT; 1099 case XK_Alt_R: 1100 return KeyCode.RALT; 1101 case XK_slash: 1102 case XK_KP_Divide: 1103 return KeyCode.KEY_DIVIDE; 1104 default: 1105 return 0x10000 | keyCode; 1106 } 1107 } 1108 1109 uint convertKeyFlags(uint flags) { 1110 uint res; 1111 if (flags & ControlMask) 1112 res |= KeyFlag.Control; 1113 if (flags & ShiftMask) 1114 res |= KeyFlag.Shift; 1115 if (flags & LockMask) 1116 res |= KeyFlag.Alt; 1117 // if (flags & KMOD_RCTRL) 1118 // res |= KeyFlag.RControl | KeyFlag.Control; 1119 // if (flags & KMOD_RSHIFT) 1120 // res |= KeyFlag.RShift | KeyFlag.Shift; 1121 // if (flags & KMOD_RALT) 1122 // res |= KeyFlag.RAlt | KeyFlag.Alt; 1123 // if (flags & KMOD_LCTRL) 1124 // res |= KeyFlag.LControl | KeyFlag.Control; 1125 // if (flags & KMOD_LSHIFT) 1126 // res |= KeyFlag.LShift | KeyFlag.Shift; 1127 // if (flags & KMOD_LALT) 1128 // res |= KeyFlag.LAlt | KeyFlag.Alt; 1129 return res; 1130 } 1131 1132 1133 bool processKeyEvent(KeyAction action, uint keyCode, uint flagsX11) { 1134 //debug(DebugSDL) 1135 Log.d("processKeyEvent ", action, " X11 key=0x", format("%08x", keyCode), " X11 flags=0x", format("%08x", flagsX11)); 1136 keyCode = convertKeyCode(keyCode); 1137 uint flagsConverted = convertKeyFlags(flagsX11); 1138 1139 alias KeyCode = dlangui.core.events.KeyCode; 1140 1141 { 1142 uint flags = 0; 1143 switch(keyCode) { 1144 case KeyCode.ALT: 1145 flags |= KeyFlag.Alt; 1146 break; 1147 case KeyCode.RALT: 1148 flags |= KeyFlag.Alt | KeyFlag.RAlt; 1149 break; 1150 case KeyCode.LALT: 1151 flags |= KeyFlag.Alt | KeyFlag.LAlt; 1152 break; 1153 case KeyCode.CONTROL: 1154 flags |= KeyFlag.Control; 1155 break; 1156 case KeyCode.RCONTROL: 1157 flags |= KeyFlag.Control | KeyFlag.RControl; 1158 break; 1159 case KeyCode.LCONTROL: 1160 flags |= KeyFlag.Control | KeyFlag.LControl; 1161 break; 1162 case KeyCode.SHIFT: 1163 flags |= KeyFlag.Shift; 1164 break; 1165 case KeyCode.RSHIFT: 1166 flags |= KeyFlag.Shift | KeyFlag.RShift; 1167 break; 1168 case KeyCode.LSHIFT: 1169 flags |= KeyFlag.Shift | KeyFlag.LShift; 1170 break; 1171 default: 1172 break; 1173 } 1174 if (action == KeyAction.KeyDown) 1175 flagsConverted |= flags; 1176 else 1177 flagsConverted &= ~flags; 1178 } 1179 Log.d("processKeyEvent ", action, " converted key=0x", format("%08x", keyCode), " flags=0x", format("%08x", flagsConverted)); 1180 _keyFlags = flagsConverted; 1181 1182 debug(DebugSDL) Log.d("processKeyEvent ", action, " converted key=0x", format("%08x", keyCode), " converted flags=0x", format("%08x", flagsConverted)); 1183 bool res = dispatchKeyEvent(new KeyEvent(action, keyCode, flagsConverted)); 1184 // if ((keyCode & 0x10000) && (keyCode & 0xF000) != 0xF000) { 1185 // dchar[1] text; 1186 // text[0] = keyCode & 0xFFFF; 1187 // res = dispatchKeyEvent(new KeyEvent(KeyAction.Text, keyCode, flags, cast(dstring)text)) || res; 1188 // } 1189 if (res) { 1190 debug(keys) Log.d("Calling update() after key event"); 1191 //invalidate(); 1192 update(); 1193 } 1194 return res; 1195 } 1196 1197 bool processTextInput(dstring ds, uint flags) { 1198 flags = convertKeyFlags(flags); 1199 bool res = dispatchKeyEvent(new KeyEvent(KeyAction.Text, 0, flags, ds)); 1200 if (res) { 1201 debug(keys) Log.d("Calling update() after text event"); 1202 invalidate(); 1203 } 1204 return res; 1205 } 1206 1207 /// after drawing, call to schedule redraw if animation is active 1208 override void scheduleAnimation() { 1209 invalidate(); 1210 } 1211 1212 TimerThread timer; 1213 private long _nextExpectedTimerTs; 1214 1215 /// schedule timer for interval in milliseconds - call window.onTimer when finished 1216 override protected void scheduleSystemTimer(long intervalMillis) { 1217 if (!timer) { 1218 timer = new TimerThread(delegate() { 1219 XEvent ev; 1220 memset(&ev, 0, ev.sizeof); 1221 //ev.xclient = XClientMessageEvent.init; 1222 ev.xclient.type = ClientMessage; 1223 ev.xclient.message_type = atom_DLANGUI_TIMER_EVENT; 1224 ev.xclient.window = _win; 1225 ev.xclient.display = x11display2; 1226 ev.xclient.format = 32; 1227 //Log.d("Sending timer event"); 1228 XLockDisplay(x11display2); 1229 XSendEvent(x11display2, _win, false, StructureNotifyMask, &ev); 1230 XFlush(x11display2); 1231 XUnlockDisplay(x11display2); 1232 }); 1233 } 1234 if (intervalMillis < 10) 1235 intervalMillis = 10; 1236 long nextts = currentTimeMillis + intervalMillis; 1237 if (_nextExpectedTimerTs == 0 || _nextExpectedTimerTs > nextts) { 1238 _nextExpectedTimerTs = nextts; 1239 timer.set(nextts); 1240 } 1241 } 1242 1243 bool handleTimer() { 1244 if (!_nextExpectedTimerTs) 1245 return false; 1246 long ts = currentTimeMillis; 1247 if (ts >= _nextExpectedTimerTs) { 1248 _nextExpectedTimerTs = 0; 1249 onTimer(); 1250 return true; 1251 } 1252 return false; 1253 } 1254 1255 /// post event to handle in UI thread (this method can be used from background thread) 1256 override void postEvent(CustomEvent event) { 1257 super.postEvent(event); 1258 XEvent ev; 1259 memset(&ev, 0, ev.sizeof); 1260 ev.xclient.type = ClientMessage; 1261 ev.xclient.window = _win; 1262 ev.xclient.display = x11display2; 1263 ev.xclient.message_type = atom_DLANGUI_TASK_EVENT; 1264 ev.xclient.format = 32; 1265 ev.xclient.data.l[0] = event.uniqueId; 1266 XLockDisplay(x11display2); 1267 XSendEvent(x11display2, _win, false, StructureNotifyMask, &ev); 1268 XFlush(x11display2); 1269 XUnlockDisplay(x11display2); 1270 // SDL_Event sdlevent; 1271 // sdlevent.user.type = USER_EVENT_ID; 1272 // sdlevent.user.code = cast(int)event.uniqueId; 1273 // sdlevent.user.windowID = windowId; 1274 // SDL_PushEvent(&sdlevent); 1275 } 1276 1277 protected uint _lastCursorType = CursorType.None; 1278 /// sets cursor type for window 1279 override protected void setCursorType(uint cursorType) { 1280 if (_lastCursorType != cursorType) { 1281 Log.d("setCursorType(", cursorType, ")"); 1282 _lastCursorType = cursorType; 1283 XDefineCursor(x11display, _win, x11cursors[cursorType]); 1284 XFlush(x11display); 1285 } 1286 } 1287 } 1288 1289 private immutable int CUSTOM_EVENT = 32; 1290 private immutable int TIMER_EVENT = 8; 1291 1292 class X11Platform : Platform { 1293 1294 this() { 1295 import std.file : thisExePath; 1296 import std.path : baseName; 1297 _classname = (baseName(thisExePath()) ~ "\0").dup.ptr; 1298 } 1299 1300 private X11Window[XWindow] _windowMap; 1301 private char* _classname; 1302 1303 /** 1304 * create window 1305 * Args: 1306 * windowCaption = window caption text 1307 * parent = parent Window, or null if no parent 1308 * flags = WindowFlag bit set, combination of Resizable, Modal, Fullscreen 1309 * width = window width 1310 * height = window height 1311 * 1312 * Window w/o Resizable nor Fullscreen will be created with size based on measurement of its content widget 1313 */ 1314 override DWindow createWindow(dstring windowCaption, DWindow parent, uint flags = WindowFlag.Resizable, uint width = 0, uint height = 0) { 1315 int newwidth = width; 1316 int newheight = height; 1317 X11Window window = new X11Window(this, windowCaption, parent, flags, newwidth, newheight); 1318 _windowMap[window._win] = window; 1319 return window; 1320 } 1321 1322 X11Window findWindow(XWindow windowId) { 1323 if (windowId in _windowMap) 1324 return _windowMap[windowId]; 1325 return null; 1326 } 1327 1328 /** 1329 * close window 1330 * 1331 * Closes window earlier created with createWindow() 1332 */ 1333 override void closeWindow(DWindow w) { 1334 X11Window window = cast(X11Window)w; 1335 XEvent ev; 1336 memset(&ev, 0, ev.sizeof); 1337 ev.xclient.type = ClientMessage; 1338 ev.xclient.message_type = atom_DLANGUI_CLOSE_WINDOW_EVENT; 1339 ev.xclient.window = window._win; 1340 ev.xclient.display = x11display2; 1341 ev.xclient.format = 32; 1342 Log.d("Sending close window event"); 1343 XLockDisplay(x11display2); 1344 XSendEvent(x11display2, window._win, false, StructureNotifyMask, &ev); 1345 XFlush(x11display2); 1346 XUnlockDisplay(x11display2); 1347 } 1348 1349 bool handleTimers() { 1350 bool handled = false; 1351 foreach(w; _windowMap) { 1352 if (w.handleTimer()) { 1353 handled = true; 1354 break; 1355 } 1356 } 1357 return handled; 1358 } 1359 1360 final bool allWindowsClosed() { 1361 return _windowMap.length == 0; 1362 } 1363 1364 protected int numberOfPendingEvents(int msecs = 10) 1365 { 1366 import core.sys.posix.sys.select; 1367 int x11displayFd = ConnectionNumber(x11display); 1368 fd_set fdSet; 1369 FD_ZERO(&fdSet); 1370 FD_SET(x11displayFd, &fdSet); 1371 scope(exit) FD_ZERO(&fdSet); 1372 1373 int eventsInQueue = XEventsQueued(x11display, QueuedAlready); 1374 if (!eventsInQueue) { 1375 import core.stdc.errno; 1376 int selectResult; 1377 do { 1378 timeval timeout; 1379 timeout.tv_usec = msecs; 1380 selectResult = select(x11displayFd + 1, &fdSet, null, null, &timeout); 1381 } while(selectResult == -1 && errno == EINTR); 1382 if (selectResult < 0) { 1383 Log.e("X11: display fd select error"); 1384 } else if (selectResult == 1) { 1385 //Log.d("X11: XPending"); 1386 eventsInQueue = XPending(x11display); 1387 } 1388 } 1389 return eventsInQueue; 1390 } 1391 1392 protected void processXEvent(ref XEvent event) 1393 { 1394 XComposeStatus compose; 1395 switch (event.type) { 1396 case ConfigureNotify: 1397 X11Window w = findWindow(event.xconfigure.window); 1398 if (w) { 1399 w._cachedWidth = event.xconfigure.width; 1400 w._cachedHeight = event.xconfigure.height; 1401 w.handleWindowStateChange(WindowState.unspecified, Rect(event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height)); 1402 } else { 1403 Log.e("ConfigureNotify: Window not found"); 1404 } 1405 break; 1406 case PropertyNotify: 1407 if (event.xproperty.atom == atom_NET_WM_STATE && event.xproperty.state == PropertyNewValue) { 1408 X11Window w = findWindow(event.xproperty.window); 1409 if (w) { 1410 Atom type; 1411 int format; 1412 ubyte* properties; 1413 c_ulong dataLength, overflow; 1414 if (XGetWindowProperty(x11display, event.xproperty.window, atom_NET_WM_STATE, 1415 0, int.max/4, False, AnyPropertyType, &type, &format, &dataLength, &overflow, &properties) == 0) { 1416 scope(exit) XFree(properties); 1417 // check for minimized 1418 bool minimized = false; 1419 for (int i=0; i < dataLength ; i++) { 1420 if (((cast(c_ulong*)properties)[i]) == atom_NET_WM_STATE_HIDDEN) { 1421 w.handleWindowStateChange(WindowState.minimized); 1422 minimized = true; 1423 } 1424 } 1425 if (!minimized) { 1426 bool maximizedH = false; 1427 bool maximizedV = false; 1428 for (int i=0; i < dataLength ; i++) { 1429 if (((cast(c_ulong*)properties)[i]) == atom_NET_WM_STATE_MAXIMIZED_VERT) 1430 maximizedV = true; 1431 if (((cast(c_ulong*)properties)[i]) == atom_NET_WM_STATE_MAXIMIZED_HORZ) 1432 maximizedH = true; 1433 } 1434 1435 if (maximizedV && maximizedH) 1436 w.handleWindowStateChange(WindowState.maximized); 1437 else 1438 w.handleWindowStateChange(WindowState.normal); 1439 1440 } 1441 } 1442 } 1443 } 1444 break; 1445 case MapNotify: 1446 X11Window w = findWindow(event.xmap.window); 1447 if (w) { 1448 w.handleWindowStateChange(WindowState.normal); 1449 } 1450 break; 1451 case UnmapNotify: 1452 X11Window w = findWindow(event.xunmap.window); 1453 if (w) { 1454 w.handleWindowStateChange(WindowState.hidden); 1455 } 1456 break; 1457 case Expose: 1458 if (event.xexpose.count == 0) { 1459 X11Window w = findWindow(event.xexpose.window); 1460 if (w) { 1461 w.invalidate(); 1462 } else { 1463 Log.e("Expose: Window not found"); 1464 } 1465 } else { 1466 Log.d("Expose: non-0 count"); 1467 } 1468 break; 1469 case KeyPress: 1470 Log.d("X11: KeyPress event"); 1471 X11Window w = findWindow(event.xkey.window); 1472 if (w) { 1473 char[100] buf; 1474 KeySym ks; 1475 Status s; 1476 if (!w.xic) { 1477 w.xic = XCreateIC(xim, 1478 XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 1479 XNClientWindow, w._win, 0); 1480 if (!w.xic) { 1481 Log.e("Cannot create input context"); 1482 } 1483 } 1484 1485 if (!w.xic) 1486 XLookupString(&event.xkey, buf.ptr, buf.length - 1, &ks, &compose); 1487 else { 1488 Xutf8LookupString(w.xic, &event.xkey, buf.ptr, cast(int)buf.length - 1, &ks, &s); 1489 if (s != XLookupChars && s != XLookupBoth) 1490 XLookupString(&event.xkey, buf.ptr, buf.length - 1, &ks, &compose); 1491 } 1492 foreach(ref ch; buf) { 1493 if (ch == 255 || ch < 32 || ch == 127) 1494 ch = 0; 1495 } 1496 string txt = fromStringz(buf.ptr).dup; 1497 import std.utf; 1498 dstring dtext; 1499 try { 1500 if (txt.length) 1501 dtext = toUTF32(txt); 1502 } catch (UTFException e) { 1503 // ignore, invalid text 1504 } 1505 debug(x11) Log.d("X11: KeyPress event bytes=", txt.length, " text=", txt, " dtext=", dtext); 1506 if (dtext.length) { 1507 w.processTextInput(dtext, event.xkey.state); 1508 } else { 1509 w.processKeyEvent(KeyAction.KeyDown, cast(uint)ks, 1510 //event.xkey.keycode, 1511 event.xkey.state); 1512 } 1513 } else { 1514 Log.e("Window not found"); 1515 } 1516 break; 1517 case KeyRelease: 1518 Log.d("X11: KeyRelease event"); 1519 X11Window w = findWindow(event.xkey.window); 1520 if (w) { 1521 char[100] buf; 1522 KeySym ks; 1523 XLookupString(&event.xkey, buf.ptr, buf.length - 1, &ks, &compose); 1524 w.processKeyEvent(KeyAction.KeyUp, cast(uint)ks, 1525 //event.xkey.keycode, 1526 event.xkey.state); 1527 } else { 1528 Log.e("Window not found"); 1529 } 1530 break; 1531 case ButtonPress: 1532 Log.d("X11: ButtonPress event"); 1533 X11Window w = findWindow(event.xbutton.window); 1534 if (w) { 1535 if (event.xbutton.button == 4 || event.xbutton.button == 5) { 1536 w.processMouseEvent(MouseAction.Wheel, 0, 0, 0, event.xbutton.button == 4 ? 1 : -1); 1537 } else { 1538 w.processMouseEvent(MouseAction.ButtonDown, event.xbutton.button, event.xbutton.state, event.xbutton.x, event.xbutton.y); 1539 } 1540 } else { 1541 Log.e("Window not found"); 1542 } 1543 break; 1544 case ButtonRelease: 1545 Log.d("X11: ButtonRelease event"); 1546 X11Window w = findWindow(event.xbutton.window); 1547 if (w) { 1548 w.processMouseEvent(MouseAction.ButtonUp, event.xbutton.button, event.xbutton.state, event.xbutton.x, event.xbutton.y); 1549 } else { 1550 Log.e("Window not found"); 1551 } 1552 break; 1553 case MotionNotify: 1554 debug(x11) Log.d("X11: MotionNotify event"); 1555 X11Window w = findWindow(event.xmotion.window); 1556 if (w) { 1557 w.processMouseEvent(MouseAction.Move, 0, event.xmotion.state, event.xmotion.x, event.xmotion.y); 1558 } else { 1559 Log.e("Window not found"); 1560 } 1561 break; 1562 case EnterNotify: 1563 Log.d("X11: EnterNotify event"); 1564 X11Window w = findWindow(event.xcrossing.window); 1565 if (w) { 1566 w.processMouseEvent(MouseAction.Move, 0, event.xmotion.state, event.xcrossing.x, event.xcrossing.y); 1567 } else { 1568 Log.e("Window not found"); 1569 } 1570 break; 1571 case LeaveNotify: 1572 Log.d("X11: LeaveNotify event"); 1573 X11Window w = findWindow(event.xcrossing.window); 1574 if (w) { 1575 w.processMouseEvent(MouseAction.Leave, 0, event.xcrossing.state, event.xcrossing.x, event.xcrossing.y); 1576 } else { 1577 Log.e("Window not found"); 1578 } 1579 break; 1580 case CreateNotify: 1581 Log.d("X11: CreateNotify event"); 1582 X11Window w = findWindow(event.xcreatewindow.window); 1583 if (!w) { 1584 Log.e("Window not found"); 1585 } 1586 break; 1587 case DestroyNotify: 1588 Log.d("X11: DestroyNotify event"); 1589 break; 1590 case ResizeRequest: 1591 Log.d("X11: ResizeRequest event"); 1592 X11Window w = findWindow(event.xresizerequest.window); 1593 if (!w) { 1594 Log.e("Window not found"); 1595 } 1596 break; 1597 case FocusIn: 1598 Log.d("X11: FocusIn event"); 1599 X11Window w = findWindow(event.xfocus.window); 1600 if (w) 1601 w.handleWindowActivityChange(true); 1602 else 1603 Log.e("Window not found"); 1604 break; 1605 case FocusOut: 1606 Log.d("X11: FocusOut event"); 1607 X11Window w = findWindow(event.xfocus.window); 1608 if (w) 1609 w.handleWindowActivityChange(false); 1610 else 1611 Log.e("Window not found"); 1612 break; 1613 case KeymapNotify: 1614 Log.d("X11: KeymapNotify event"); 1615 X11Window w = findWindow(event.xkeymap.window); 1616 break; 1617 case SelectionClear: 1618 Log.d("X11: SelectionClear event"); 1619 break; 1620 case SelectionRequest: 1621 debug(x11) Log.d("X11: SelectionRequest event"); 1622 if (event.xselectionrequest.owner in _windowMap) { 1623 XSelectionRequestEvent *selectionRequest = &event.xselectionrequest; 1624 1625 XEvent selectionEvent; 1626 memset(&selectionEvent, 0, selectionEvent.sizeof); 1627 selectionEvent.xany.type = SelectionNotify; 1628 selectionEvent.xselection.selection = selectionRequest.selection; 1629 selectionEvent.xselection.target = selectionRequest.target; 1630 selectionEvent.xselection.property = None; 1631 selectionEvent.xselection.requestor = selectionRequest.requestor; 1632 selectionEvent.xselection.time = selectionRequest.time; 1633 1634 if (selectionRequest.target == XA_STRING || selectionRequest.target == atom_UTF8_STRING) { 1635 int currentSelectionFormat; 1636 Atom currentSelectionType; 1637 c_ulong selectionDataLength, overflow; 1638 ubyte* selectionDataPtr; 1639 if (XGetWindowProperty(x11display, DefaultRootWindow(x11display), atom_DLANGUI_CLIPBOARD_BUFFER, 1640 0, int.max/4, False, selectionRequest.target, 1641 ¤tSelectionType, ¤tSelectionFormat, &selectionDataLength, 1642 &overflow, &selectionDataPtr) == 0) 1643 { 1644 scope(exit) XFree(selectionDataPtr); 1645 XChangeProperty(x11display, selectionRequest.requestor, selectionRequest.property, 1646 selectionRequest.target, 8, PropModeReplace, 1647 selectionDataPtr, cast(int)selectionDataLength); 1648 } 1649 selectionEvent.xselection.property = selectionRequest.property; 1650 } else if (selectionRequest.target == atom_TARGETS) { 1651 Atom[3] supportedFormats = [atom_UTF8_STRING, XA_STRING, atom_TARGETS]; 1652 XChangeProperty(x11display, selectionRequest.requestor, selectionRequest.property, 1653 XA_ATOM, 32, PropModeReplace, 1654 cast(ubyte*)supportedFormats.ptr, cast(int)supportedFormats.length); 1655 selectionEvent.xselection.property = selectionRequest.property; 1656 } 1657 XSendEvent(x11display, selectionRequest.requestor, False, 0, &selectionEvent); 1658 } 1659 break; 1660 case SelectionNotify: 1661 debug(x11) Log.d("X11: SelectionNotify event"); 1662 X11Window w = findWindow(event.xselection.requestor); 1663 if (w) { 1664 waitingForSelection = false; 1665 } 1666 break; 1667 case ClientMessage: 1668 debug(x11) Log.d("X11: ClientMessage event"); 1669 X11Window w = findWindow(event.xclient.window); 1670 if (w) { 1671 if (event.xclient.message_type == atom_DLANGUI_TASK_EVENT) { 1672 w.handlePostedEvent(cast(uint)event.xclient.data.l[0]); 1673 } else if (event.xclient.message_type == atom_DLANGUI_TIMER_EVENT) { 1674 w.handleTimer(); 1675 } else if (event.xclient.message_type == atom_DLANGUI_REDRAW_EVENT) { 1676 if (event.xclient.data.l[0] == w._lastRedrawEventCode) 1677 w.redraw(); 1678 } else if (event.xclient.message_type == atom_WM_PROTOCOLS) { 1679 Log.d("Handling WM_PROTOCOLS"); 1680 if ((event.xclient.format == 32) && (event.xclient.data.l[0]) == atom_WM_DELETE_WINDOW) { 1681 Log.d("Handling WM_DELETE_WINDOW"); 1682 _windowMap.remove(w._win); 1683 destroy(w); 1684 } 1685 } else if (event.xclient.message_type == atom_DLANGUI_CLOSE_WINDOW_EVENT) { 1686 _windowMap.remove(w._win); 1687 destroy(w); 1688 } 1689 } else { 1690 Log.e("Window not found"); 1691 } 1692 break; 1693 default: 1694 break; 1695 } 1696 } 1697 1698 protected void pumpEvents() 1699 { 1700 XFlush(x11display); 1701 // Note: only events we set the mask for are detected! 1702 while(numberOfPendingEvents()) 1703 { 1704 if (allWindowsClosed()) 1705 break; 1706 XEvent event; /* the XEvent declaration !!! */ 1707 XNextEvent(x11display, &event); 1708 processXEvent(event); 1709 } 1710 } 1711 1712 /** 1713 * Starts application message loop. 1714 * 1715 * When returned from this method, application is shutting down. 1716 */ 1717 override int enterMessageLoop() { 1718 Log.d("enterMessageLoop()"); 1719 1720 while(!allWindowsClosed()) { 1721 pumpEvents(); 1722 } 1723 return 0; 1724 } 1725 1726 /// check has clipboard text 1727 override bool hasClipboardText(bool mouseBuffer = false) { 1728 const selectionType = mouseBuffer ? XA_PRIMARY : atom_CLIPBOARD; 1729 return XGetSelectionOwner(x11display, selectionType) != None; 1730 } 1731 1732 protected bool waitingForSelection; 1733 /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 1734 override dstring getClipboardText(bool mouseBuffer = false) { 1735 const selectionType = mouseBuffer ? XA_PRIMARY : atom_CLIPBOARD; 1736 auto owner = XGetSelectionOwner(x11display, selectionType); 1737 if (owner == None) { 1738 Log.d("Selection owner is none"); 1739 return ""d; 1740 } else { 1741 // Find any top-level window 1742 XWindow xwindow; 1743 foreach(w; _windowMap) { 1744 if (w._parent is null && w._win != None) { 1745 xwindow = w._win; 1746 break; 1747 } 1748 } 1749 if (xwindow != None) { 1750 import std.datetime; 1751 waitingForSelection = true; 1752 XConvertSelection(x11display, selectionType, atom_UTF8_STRING, atom_DLANGUI_CLIPBOARD_BUFFER, xwindow, CurrentTime); 1753 auto stopWaiting = Clock.currTime() + dur!"msecs"(500); 1754 while(waitingForSelection) { 1755 if (stopWaiting <= Clock.currTime()) { 1756 waitingForSelection = false; 1757 setClipboardText(""d); 1758 Log.e("Waiting for clipboard contents timeout"); 1759 return ""d; 1760 } 1761 pumpEvents(); 1762 } 1763 Atom selectionTarget; 1764 int selectionFormat; 1765 c_ulong selectionDataLength, overflow; 1766 ubyte* selectionDataPtr; 1767 if (XGetWindowProperty(x11display, xwindow, atom_DLANGUI_CLIPBOARD_BUFFER, 0, int.max/4, False, 0, &selectionTarget, &selectionFormat, &selectionDataLength, &overflow, &selectionDataPtr) == 0) 1768 { 1769 scope(exit) XFree(selectionDataPtr); 1770 if (selectionTarget == XA_STRING || selectionTarget == atom_UTF8_STRING) { 1771 char[] selectionText = cast(char[])selectionDataPtr[0..selectionDataLength]; 1772 return toUTF32(selectionText); 1773 } else { 1774 Log.d("Selection type is not a string!"); 1775 } 1776 } 1777 } else { 1778 Log.d("Could not find any window to get selection"); 1779 } 1780 } 1781 return ""d; 1782 } 1783 1784 /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 1785 override void setClipboardText(dstring text, bool mouseBuffer = false) { 1786 if (!mouseBuffer && atom_CLIPBOARD == None) { 1787 Log.e("No CLIPBOARD atom available"); 1788 return; 1789 } 1790 auto selection = mouseBuffer ? XA_PRIMARY : atom_CLIPBOARD; 1791 XWindow xwindow = None; 1792 // Find any top-level window 1793 foreach(w; _windowMap) { 1794 if (w._parent is null && w._win != None) { 1795 xwindow = w._win; 1796 } 1797 } 1798 if (xwindow == None) { 1799 Log.e("Could not find window to save clipboard text"); 1800 return; 1801 } 1802 1803 auto textc = text.toUTF8; 1804 XChangeProperty(x11display, DefaultRootWindow(x11display), atom_DLANGUI_CLIPBOARD_BUFFER, atom_UTF8_STRING, 8, PropModeReplace, cast(ubyte*)textc.ptr, cast(int)textc.length); 1805 1806 if (XGetSelectionOwner(x11display, selection) != xwindow) { 1807 XSetSelectionOwner(x11display, selection, xwindow, CurrentTime); 1808 } 1809 } 1810 1811 /// calls request layout for all windows 1812 override void requestLayout() { 1813 foreach(w; _windowMap) { 1814 w.requestLayout(); 1815 } 1816 } 1817 1818 /// 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 1819 override bool hasModalWindowsAbove(DWindow w) { 1820 X11Window x11Win = cast (X11Window) w; 1821 if (x11Win) { 1822 return x11Win.hasVisibleModalChild(); 1823 } 1824 return false; 1825 } 1826 1827 /// handle theme change: e.g. reload some themed resources 1828 override void onThemeChanged() { 1829 super.onThemeChanged(); 1830 foreach(w; _windowMap) 1831 w.dispatchThemeChanged(); 1832 } 1833 1834 } 1835 1836 import core.thread; 1837 import core.sync.mutex; 1838 import core.sync.condition; 1839 class TimerThread : Thread { 1840 Mutex mutex; 1841 Condition condition; 1842 bool stopped; 1843 long nextEventTs; 1844 void delegate() callback; 1845 1846 this(void delegate() timerCallback) { 1847 callback = timerCallback; 1848 mutex = new Mutex(); 1849 condition = new Condition(mutex); 1850 super(&run); 1851 start(); 1852 } 1853 1854 ~this() { 1855 stop(); 1856 destroy(condition); 1857 destroy(mutex); 1858 } 1859 1860 void set(long nextTs) { 1861 mutex.lock(); 1862 if (nextEventTs == 0 || nextEventTs > nextTs) { 1863 nextEventTs = nextTs; 1864 condition.notify(); 1865 } 1866 mutex.unlock(); 1867 } 1868 void run() { 1869 1870 while (!stopped) { 1871 bool expired = false; 1872 1873 mutex.lock(); 1874 1875 long ts = currentTimeMillis; 1876 long timeToWait = nextEventTs == 0 ? 1000000 : nextEventTs - ts; 1877 if (timeToWait < 10) 1878 timeToWait = 10; 1879 1880 if (nextEventTs == 0) 1881 condition.wait(); 1882 else 1883 condition.wait(dur!"msecs"(timeToWait)); 1884 1885 if (stopped) { 1886 mutex.unlock(); 1887 break; 1888 } 1889 ts = currentTimeMillis; 1890 if (nextEventTs && nextEventTs < ts && !stopped) { 1891 expired = true; 1892 nextEventTs = 0; 1893 } 1894 1895 mutex.unlock(); 1896 1897 if (expired) 1898 callback(); 1899 } 1900 } 1901 void stop() { 1902 if (stopped) 1903 return; 1904 stopped = true; 1905 mutex.lock(); 1906 condition.notify(); 1907 mutex.unlock(); 1908 join(); 1909 } 1910 } 1911 1912 1913 extern(C) int DLANGUImain(string[] args) 1914 { 1915 initLogs(); 1916 1917 if (!initFontManager()) { 1918 Log.e("******************************************************************"); 1919 Log.e("No font files found!!!"); 1920 Log.e("Currently, only hardcoded font paths implemented."); 1921 Log.e("Probably you can modify sdlapp.d to add some fonts for your system."); 1922 Log.e("TODO: use fontconfig"); 1923 Log.e("******************************************************************"); 1924 assert(false); 1925 } 1926 initResourceManagers(); 1927 1928 currentTheme = createDefaultTheme(); 1929 1930 XInitThreads(); 1931 1932 /* use the information from the environment variable DISPLAY 1933 to create the X connection: 1934 */ 1935 x11display = XOpenDisplay(null); 1936 if (!x11display) { 1937 Log.e("Cannot open X11 display"); 1938 return 1; 1939 } 1940 x11display2 = XOpenDisplay(null); 1941 if (!x11display2) { 1942 Log.e("Cannot open secondary connection for X11 display"); 1943 return 1; 1944 } 1945 1946 x11screen = DefaultScreen(x11display); 1947 1948 static if (ENABLE_OPENGL) { 1949 try { 1950 DerelictGL3.missingSymbolCallback = &gl3MissingSymFunc; 1951 DerelictGL3.load(); 1952 DerelictGL.missingSymbolCallback = &gl3MissingSymFunc; 1953 DerelictGL.load(); 1954 Log.d("OpenGL library loaded ok"); 1955 GLint[] att = [ GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None ]; 1956 XWindow root; 1957 root = DefaultRootWindow(x11display); 1958 x11visual = glXChooseVisual(x11display, 0, cast(int*)att.ptr); 1959 if (x11visual) { 1960 x11cmap = XCreateColormap(x11display, root, cast(Visual*)x11visual.visual, AllocNone); 1961 _enableOpengl = true; 1962 } else { 1963 Log.e("Cannot find suitable Visual for using of OpenGL"); 1964 } 1965 } catch (Exception e) { 1966 Log.e("Cannot load OpenGL library", e); 1967 } 1968 } 1969 1970 1971 setupX11Atoms(); 1972 1973 x11cursors[CursorType.None] = XCreateFontCursor(x11display, XC_arrow); 1974 x11cursors[CursorType.NotSet] = XCreateFontCursor(x11display, XC_arrow); 1975 x11cursors[CursorType.Arrow] = XCreateFontCursor(x11display, XC_left_ptr); 1976 x11cursors[CursorType.IBeam] = XCreateFontCursor(x11display, XC_xterm); 1977 x11cursors[CursorType.Wait] = XCreateFontCursor(x11display, XC_watch); 1978 x11cursors[CursorType.Crosshair] = XCreateFontCursor(x11display, XC_tcross); 1979 x11cursors[CursorType.WaitArrow] = XCreateFontCursor(x11display, XC_watch); 1980 x11cursors[CursorType.SizeNWSE] = XCreateFontCursor(x11display, XC_fleur); 1981 x11cursors[CursorType.SizeNESW] = XCreateFontCursor(x11display, XC_fleur); 1982 x11cursors[CursorType.SizeWE] = XCreateFontCursor(x11display, XC_sb_h_double_arrow); 1983 x11cursors[CursorType.SizeNS] = XCreateFontCursor(x11display, XC_sb_v_double_arrow); 1984 x11cursors[CursorType.SizeAll] = XCreateFontCursor(x11display, XC_fleur); 1985 x11cursors[CursorType.No] = XCreateFontCursor(x11display, XC_pirate); 1986 x11cursors[CursorType.Hand] = XCreateFontCursor(x11display, XC_hand2); 1987 1988 xim = XOpenIM(x11display, null, null, null); 1989 if (!xim) { 1990 Log.e("Cannot open input method"); 1991 } 1992 1993 Log.d("X11 display=", x11display, " screen=", x11screen); 1994 1995 1996 1997 X11Platform x11platform = new X11Platform(); 1998 1999 Platform.setInstance(x11platform); 2000 2001 int res = 0; 2002 2003 version (unittest) { 2004 } else { 2005 res = UIAppMain(args); 2006 } 2007 2008 //Log.e("Widget instance count after UIAppMain: ", Widget.instanceCount()); 2009 2010 Log.d("Destroying X11 platform"); 2011 Platform.setInstance(null); 2012 2013 releaseResourcesOnAppExit(); 2014 2015 2016 XCloseDisplay(x11display); 2017 XCloseDisplay(x11display2); 2018 2019 Log.d("Exiting main width result=", res); 2020 2021 return res; 2022 }