1 /++ 2 Implementation of cross-platform creation and management of a window. 3 4 Also, at the same time, it is possible to create a graphical context for the 5 window to be able to use hardware acceleration using a common open API - OpenGL. 6 7 Creating_a_window: 8 First of all, creating a window begins with allocating memory and setting 9 the input parameters: 10 --- 11 Window window = new Window(640, 480, "Example title"); 12 --- 13 14 Only input parameters are set in the constructor, this does not mean that the 15 window is ready for use. Here only the initial width, height and title of the 16 window are set. Other properties do not affect creation, everything is done 17 after the window is created. 18 19 The window is created by the $(LREF windowInitialize) function: 20 --- 21 windowInitialize(window, 100, 100); 22 ... 23 window.windowInitialize(100, 100); // UFCS 24 --- 25 26 Now, you can interact with the window or create a graphics context to access 27 hardware acceleration. To do this, again, allocate the mod memory for the 28 context collector and create a structure with a description of the 29 context properties: 30 --- 31 Context context = new Context(); 32 33 // We just set the parameters by color. Each color will weigh 8 bits. 34 GraphicsAttributes attributes = AttribBySizeOfTheColor!8; 35 context.setAttributes(attributes); 36 context.create(window); 37 38 // We set the current context to the window. 39 window.context = context; 40 --- 41 Now you will have access to hardware acceleration according to the attributes 42 you specified, what you can do next is load open graphics libraries and start 43 drawing primitives. To display primitives on the screen, 44 use the $(HREF window/IWindow.swapBuffers.html, IWindow.swapBuffers) function. 45 46 OS_specific_actions: 47 It may be that the built-in tools are not enough and you need, say, to change 48 some properties that other platforms cannot do. For this, each object has an 49 open $(B `handle`) field. Getting it is easy, however, be careful with what you do 50 with it. You can do such things that the interaction interface after your 51 manipulations will not be able to control it. To do this, it is enough not to 52 change the properties that can be controlled by the interaction interface. 53 54 Macros: 55 LREF = <a href="#$1">$1</a> 56 HREF = <a href="$1">$2</a> 57 PHOBREF = <a href="https://dlang.org/phobos/$1.html#$2">$2</a> 58 59 Authors: $(HREF https://github.com/TodNaz,TodNaz) 60 Copyright: Copyright (c) 2020 - 2021, TodNaz. 61 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 62 +/ 63 module tida.window; 64 65 enum ConfFindError = 66 "The required configuration for the specified graphic attributes was not found!"; 67 68 enum WithoutContext = 0; /// Without creating a graphical context. 69 enum WithContext = 1; /// With the creation of a graphical context. 70 71 /++ 72 Graphics attributes for creating a special graphics pipeline 73 (default parameters are indicated in the structure). 74 +/ 75 struct GraphicsAttributes 76 { 77 int redSize = 8; /// Red size 78 int greenSize = 8; /// Green size 79 int blueSize = 8; /// Blue size 80 int alphaSize = 8; /// Alpha channel size 81 int depthSize = 24; /// Depth size 82 int stencilSize = 8; /// Stencil size 83 int colorDepth = 32; /// Color depth 84 bool doubleBuffer = true; /// Double buffering 85 int glmajor = 3; /// GL major recomendet version. 86 int glminor = 0; /// GL minor recomendet version. 87 } 88 89 /++ 90 Automatic determination of the structure of graphic attributes by the total 91 size of the color unit. 92 93 Params: 94 colorSize = Color unit size. 95 +/ 96 template AttribBySizeOfTheColor(int colorSize) 97 { 98 enum AttribBySizeOfTheColor = GraphicsAttributes( colorSize, 99 colorSize, 100 colorSize, 101 colorSize); 102 } 103 104 /++ 105 Graphics context creation interface for hardware graphics acceleration. 106 107 With this technology, the context can be easily created with the given 108 attributes, and it is obligatory $(U after) the window is created. 109 110 Example: 111 --- 112 // Window creation code . . . 113 Context contex = new Context(); 114 context.setAttribute(attributes...); 115 context.create(window); 116 // The graphics context has been created! 117 --- 118 +/ 119 interface IContext 120 { 121 @safe: 122 /++ 123 Sets the input attributes for creating the graphics context. 124 At the same time, he must simultaneously give these attributes 125 unsparingly to the very object of the pixel description, 126 if such is possible. As a rule, this method is followed by the creation 127 of the context, but the function itself should not do this. 128 129 Params: 130 attributes = graphic context description attributes. 131 132 Throws: 133 $(PHOBREF object,Exception) if the graphics attributes did not fit the creation 134 of the context (see what parameters you could set, maybe inconsistent with 135 each other). 136 +/ 137 void setAttributes(GraphicsAttributes attributes); 138 139 /++ 140 Creates directly, a graphics context object for a specific platform, 141 based on the previously specified graphics context attributes. 142 143 Params: 144 window = Pointer to the window to create the graphics context. 145 146 Throws: 147 $(PHOBREF object,Exception) if the creation of the graphics context was 148 not successful. The attributes were probably not initialized. 149 +/ 150 void create(IWindow window); 151 152 /++ 153 Destroys the context. 154 +/ 155 void destroy(); 156 } 157 158 /++ 159 Window interaction interface. It does not provide its creation, it is created by 160 a separate function within the interface implementation, in particular 161 the `initialize` function. 162 +/ 163 interface IWindow 164 { 165 import tida.image; 166 167 @safe: 168 /// The position of the window in the plane of the desktop. 169 @property int x(); 170 171 /// The position of the window in the plane of the desktop. 172 @property int y(); 173 174 /// Window width 175 @property uint width(); 176 177 /// Window height 178 @property uint height(); 179 180 /// Window mode, namely whether windowed or full screen 181 @property void fullscreen(bool value); 182 183 /// Window mode, namely whether windowed or full screen 184 @property bool fullscreen(); 185 186 /// Whether the window can be resized by the user. 187 @property void resizable(bool value); 188 189 /// Whether the window can be resized by the user. 190 @property bool resizable(); 191 192 /// Frames around the window. 193 @property void border(bool value); 194 195 /// Frames around the window. 196 @property bool border(); 197 198 /// Window title. 199 @property void title(string value); 200 201 /// Window title. 202 @property string title(); 203 204 /// Whether the window is always on top of the others. 205 @property void alwaysOnTop(bool value); 206 207 /// Whether the window is always on top of the others. 208 @property bool alwaysOnTop(); 209 210 /// Dynamic window icon. 211 @property void icon(Image iconimage); 212 213 /// Grahphics context 214 @property void context(IContext ctx); 215 216 /// Grahphics context 217 @property IContext context(); 218 219 /++ 220 Window resizing function. 221 222 Params: 223 w = Window width. 224 h = Window height. 225 +/ 226 void resize(uint w, uint h); 227 228 /++ 229 Changes the position of the window in the plane of the desktop. 230 231 Params: 232 xposition = Position x-axis. 233 yposition = Position y-axis. 234 +/ 235 void move(int xposition, int yposition); 236 237 /++ 238 Shows a window in the plane of the desktop. 239 +/ 240 void show(); 241 242 /++ 243 Hide a window in the plane of the desktop. 244 (Can be tracked in the task manager.) 245 +/ 246 void hide(); 247 248 /++ 249 Swap two buffers. 250 +/ 251 void swapBuffers(); 252 253 /++ 254 Destroys the window and its associated data (not the structure itself, all values are reset to zero). 255 +/ 256 void destroy(); 257 } 258 259 version (Posix) 260 class Context : IContext 261 { 262 import tida.runtime; 263 import x11.X, x11.Xlib, x11.Xutil; 264 import dglx.glx; 265 266 private: 267 GLXContext _context; 268 XVisualInfo* visual; 269 GLXFBConfig bestFbcs; 270 271 public @trusted override: 272 void setAttributes(GraphicsAttributes attributes) 273 { 274 import std.exception : enforce; 275 import std.conv : to; 276 277 int[] glxAttributes = 278 [ 279 GLX_X_RENDERABLE , True, 280 GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 281 GLX_RENDER_TYPE , GLX_RGBA_BIT, 282 GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 283 GLX_RED_SIZE , attributes.redSize, 284 GLX_GREEN_SIZE , attributes.greenSize, 285 GLX_BLUE_SIZE , attributes.blueSize, 286 GLX_ALPHA_SIZE , attributes.alphaSize, 287 GLX_DEPTH_SIZE , attributes.depthSize, 288 GLX_STENCIL_SIZE , attributes.stencilSize, 289 GLX_DOUBLEBUFFER , attributes.doubleBuffer.to!int, 290 None 291 ]; 292 293 int fbcount = 0; 294 scope fbc = glXChooseFBConfig( runtime.display, runtime.displayID, 295 glxAttributes.ptr, &fbcount); 296 scope(success) XFree(fbc); 297 enforce!Exception(fbc, ConfFindError); 298 299 int bestFbc = -1, bestNum = -1; 300 foreach (int i; 0 .. fbcount) 301 { 302 int sampBuff, samples; 303 glXGetFBConfigAttrib( runtime.display, fbc[i], 304 GLX_SAMPLE_BUFFERS, &sampBuff); 305 glXGetFBConfigAttrib( runtime.display, fbc[i], 306 GLX_SAMPLES, &samples); 307 308 if (bestFbc < 0 || (sampBuff && samples > bestNum)) 309 { 310 bestFbc = i; 311 bestNum = samples; 312 } 313 } 314 315 this.bestFbcs = fbc[bestFbc]; 316 enforce!Exception(bestFbcs, ConfFindError); 317 318 this.visual = glXGetVisualFromFBConfig(runtime.display, bestFbcs); 319 enforce!Exception(visual, ConfFindError); 320 } 321 322 void create(IWindow window) 323 { 324 _context = glXCreateNewContext( runtime.display, this.bestFbcs, 325 GLX_RGBA_TYPE, null, true); 326 } 327 328 void destroy() 329 { 330 glXDestroyContext(runtime.display, _context); 331 if(visual) XFree(visual); 332 } 333 } 334 335 version (Posix) 336 class Window : IWindow 337 { 338 import tida.runtime, tida.image; 339 import x11.X, x11.Xlib, x11.Xutil; 340 import dglx.glx; 341 import std.utf : toUTFz; 342 343 private: 344 string _title; 345 bool _fullscreen; 346 bool _border; 347 bool _resizable; 348 bool _alwaysTop; 349 IContext _context; 350 uint _widthInit; 351 uint _heightInit; 352 353 public: 354 x11.X.Window handle; 355 Visual* visual; 356 int depth; 357 358 @trusted: 359 this(uint w, uint h, string caption) 360 { 361 this._widthInit = w; 362 this._heightInit = h; 363 } 364 365 void createFromXVisual(XVisualInfo* vinfo, int posX = 100, int posY = 100) 366 { 367 visual = vinfo.visual; 368 depth = vinfo.depth; 369 370 auto rootWindow = runtime.rootWindow; 371 372 XSetWindowAttributes windowAttribs; 373 windowAttribs.border_pixel = 0x000000; 374 windowAttribs.background_pixel = 0xFFFFFF; 375 windowAttribs.override_redirect = True; 376 windowAttribs.colormap = XCreateColormap(runtime.display, rootWindow, 377 visual, AllocNone); 378 379 windowAttribs.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | 380 KeyReleaseMask | ButtonReleaseMask | EnterWindowMask | 381 LeaveWindowMask | PointerMotionMask; 382 383 this.handle = XCreateWindow (runtime.display, rootWindow, posX, posY, _widthInit, _heightInit, 0, depth, 384 InputOutput, visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, 385 &windowAttribs); 386 387 title = _title; 388 389 Atom wmAtom = XInternAtom(runtime.display, "WM_DELETE_WINDOW", 0); 390 XSetWMProtocols(runtime.display, this.handle, &wmAtom, 1); 391 } 392 393 int getDepth() 394 { 395 return depth; 396 } 397 398 Visual* getVisual() 399 { 400 return visual; 401 } 402 403 ~this() 404 { 405 this.destroy(); 406 } 407 override: 408 @property int x() 409 { 410 XWindowAttributes winAttrib; 411 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 412 413 return winAttrib.x; 414 } 415 416 @property int y() 417 { 418 XWindowAttributes winAttrib; 419 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 420 421 return winAttrib.y; 422 } 423 424 @property uint width() 425 { 426 XWindowAttributes winAttrib; 427 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 428 429 return winAttrib.width; 430 } 431 432 @property uint height() 433 { 434 XWindowAttributes winAttrib; 435 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 436 437 return winAttrib.height; 438 } 439 440 @property void fullscreen(bool value) 441 { 442 XEvent event; 443 444 const wmState = XInternAtom(runtime.display, 445 "_NET_WM_STATE", 0); 446 const wmFullscreen = XInternAtom( runtime.display, 447 "_NET_WM_STATE_FULLSCREEN", 0); 448 449 event.xany.type = ClientMessage; 450 event.xclient.message_type = wmState; 451 event.xclient.format = 32; 452 event.xclient.window = this.handle; 453 event.xclient.data.l[1] = wmFullscreen; 454 event.xclient.data.l[3] = 0; 455 456 event.xclient.data.l[0] = value; 457 458 XSendEvent(runtime.display,runtime.rootWindow,0, 459 SubstructureNotifyMask | SubstructureRedirectMask, &event); 460 461 this._fullscreen = value; 462 } 463 464 @property bool fullscreen() 465 { 466 return this._fullscreen; 467 } 468 469 @property void resizable(bool value) 470 { 471 long flags; 472 473 scope XSizeHints* sh = XAllocSizeHints(); 474 scope(exit) XFree(sh); 475 476 XGetWMNormalHints(runtime.display, this.handle, sh, &flags); 477 478 if(!value) 479 { 480 sh.flags |= PMinSize | PMaxSize; 481 sh.min_width = this.width; 482 sh.max_width = this.width; 483 sh.min_height = this.height; 484 sh.max_height = this.height; 485 }else 486 { 487 sh.flags &= ~(PMinSize | PMaxSize); 488 } 489 490 this._resizable = value; 491 XSetWMNormalHints(runtime.display, this.handle, sh); 492 } 493 494 @property bool resizable() 495 { 496 return this._resizable; 497 } 498 499 @property void border(bool value) 500 { 501 import std.conv : to; 502 503 struct MWMHints { 504 ulong flags; 505 ulong functions; 506 ulong decorations; 507 long inputMode; 508 ulong status; 509 } 510 511 const hint = MWMHints(1 << 1, 0, value.to!ulong, 0, 0); 512 const wmHINTS = XInternAtom(runtime.display, "_MOTIF_WM_HINTS", 0); 513 514 XChangeProperty(runtime.display, this.handle, wmHINTS, wmHINTS, 32, 515 PropModeReplace, cast(ubyte*) &hint, MWMHints.sizeof / long.sizeof); 516 517 this._border = value; 518 } 519 520 @property bool border() 521 { 522 return this._border; 523 } 524 525 @property void title(string value) 526 { 527 XStoreName(runtime.display, this.handle, value.toUTFz!(char*)); 528 XSetIconName(runtime.display, this.handle, value.toUTFz!(char*)); 529 530 this._title = value; 531 } 532 533 @property string title() 534 { 535 return this._title; 536 } 537 538 @property void alwaysOnTop(bool value) 539 { 540 const wmState = XInternAtom(runtime.display, "_NET_WM_STATE", 0); 541 const wmAbove = XInternAtom(runtime.display, "_NET_WM_STATE_ABOVE", 0); 542 543 XEvent event; 544 event.xclient.type = ClientMessage; 545 event.xclient.serial = 0; 546 event.xclient.send_event = true; 547 event.xclient.display = runtime.display; 548 event.xclient.window = this.handle; 549 event.xclient.message_type = wmState; 550 event.xclient.format = 32; 551 event.xclient.data.l[0] = value; 552 event.xclient.data.l[1] = wmAbove; 553 event.xclient.data.l[2 .. 5] = 0; 554 555 XSendEvent( runtime.display, runtime.rootWindow, false, 556 SubstructureRedirectMask | SubstructureNotifyMask, &event); 557 XFlush(runtime.display); 558 559 this._alwaysTop = value; 560 } 561 562 @property bool alwaysOnTop() 563 { 564 return this._alwaysTop; 565 } 566 567 void icon(Image iconimage) 568 { 569 import tida.color; 570 571 ulong[] pixels = [ cast(ulong) iconimage.width, 572 cast(ulong) iconimage.height]; 573 574 foreach(pixel; iconimage.pixels) 575 pixels ~= pixel.to!(ulong, PixelFormat.ARGB); 576 577 const first = XInternAtom(runtime.display, "_NET_WM_ICON", 0); 578 const second = XInternAtom(runtime.display, "CARDINAL", 0); 579 580 XChangeProperty(runtime.display, this.handle, first, second, 32, 581 PropModeReplace, cast(ubyte*) pixels, 582 cast(int) pixels.length); 583 } 584 585 @property void context(IContext ctx) 586 { 587 this._context = ctx; 588 glXMakeCurrent(runtime.display, this.handle, (cast(Context) ctx)._context); 589 } 590 591 @property IContext context() 592 { 593 return this._context; 594 } 595 596 void resize(uint w, uint h) 597 { 598 XResizeWindow(runtime.display, this.handle, w, h); 599 } 600 601 void move(int xposition, int yposition) 602 { 603 XMoveWindow(runtime.display, this.handle, xposition, yposition); 604 } 605 606 void show() 607 { 608 XMapWindow(runtime.display, this.handle); 609 XClearWindow(runtime.display, this.handle); 610 } 611 612 void hide() 613 { 614 XUnmapWindow(runtime.display, this.handle); 615 } 616 617 void swapBuffers() 618 { 619 import dglx.glx : glXSwapBuffers; 620 621 glXSwapBuffers(runtime.display, this.handle); 622 } 623 624 void destroy() 625 { 626 XDestroyWindow(runtime.display, this.handle); 627 this.handle = 0; 628 } 629 } 630 631 version(Windows) 632 class Context : IContext 633 { 634 import tida.runtime; 635 import core.sys.windows.windows; 636 import std.exception : enforce; 637 638 private: 639 GraphicsAttributes attributes; 640 HDC deviceHandle; 641 642 PIXELFORMATDESCRIPTOR pfd; 643 644 alias FwglChoosePixelFormatARB = extern(C) bool function( HDC hdc, 645 int *piAttribIList, 646 float *pfAttribFList, 647 uint nMaxFormats, 648 int *piFormats, 649 uint *nNumFormats); 650 651 alias FwglGetExtensionsStringARB = extern(C) char* function(HDC hdc); 652 653 alias FwglCreateContextAttribsARB = extern(C) HGLRC function(HDC, HGLRC, int*); 654 655 FwglChoosePixelFormatARB wglChoosePixelFormatARB; 656 FwglGetExtensionsStringARB wglGetExtensionsStringARB; 657 FwglCreateContextAttribsARB wglCreateContextAttribsARB; 658 659 enum 660 { 661 WGL_DRAW_TO_WINDOW_ARB = 0x2001, 662 WGL_RED_BITS_ARB = 0x2015, 663 WGL_GREEN_BITS_ARB = 0x2017, 664 WGL_BLUE_BITS_ARB = 0x2019, 665 WGL_ALPHA_BITS_ARB = 0x201B, 666 WGL_DOUBLE_BUFFER_ARB = 0x2011, 667 WGL_DEPTH_BITS_ARB = 0x2022, 668 WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091, 669 WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092, 670 WGL_CONTEXT_FLAGS_ARB = 0x2094, 671 WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x0002, 672 WGL_SUPPORT_OPENGL_ARB = 0x2010, 673 WGL_COLOR_BITS_ARB = 0x2014, 674 WGL_STENCIL_BITS_ARB = 0x2023, 675 WGL_ACCELERATION_ARB = 0x2003, 676 WGL_FULL_ACCELERATION_ARB = 0x2027, 677 WGL_PIXEL_TYPE_ARB = 0x2013, 678 WGL_TYPE_RGBA_ARB = 0x202B, 679 WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126, 680 WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001 681 } 682 683 public: 684 HGLRC _context; 685 686 @trusted: 687 ~this() 688 { 689 destroy(); 690 } 691 override: 692 void setAttributes(GraphicsAttributes attributes) 693 { 694 this.attributes = attributes; 695 696 const flags = 697 (attributes.doubleBuffer ? 698 (PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL) : 699 (PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL)); 700 701 pfd.nSize = PIXELFORMATDESCRIPTOR.sizeof; 702 pfd.nVersion = 1; 703 pfd.dwFlags = flags; 704 pfd.iPixelType = PFD_TYPE_RGBA; 705 pfd.cRedBits = cast(ubyte) this.attributes.redSize; 706 pfd.cGreenBits = cast(ubyte) this.attributes.greenSize; 707 pfd.cBlueBits = cast(ubyte) this.attributes.blueSize; 708 pfd.cAlphaBits = cast(ubyte) this.attributes.alphaSize; 709 pfd.cDepthBits = cast(ubyte) this.attributes.depthSize; 710 pfd.cStencilBits = cast(ubyte) this.attributes.stencilSize; 711 pfd.iLayerType = PFD_MAIN_PLANE; 712 } 713 714 void create(IWindow window) 715 { 716 scope handle = (cast(Window) window).handle; 717 deviceHandle = (cast(Window) window).dc; 718 auto chsPixel = ChoosePixelFormat(deviceHandle, &pfd); 719 enforce!Exception(chsPixel != 0, ConfFindError); 720 721 SetPixelFormat(deviceHandle, chsPixel, &pfd); 722 723 scope ctx = wglCreateContext(deviceHandle); 724 wglMakeCurrent(deviceHandle, ctx); 725 726 void* data = wglGetProcAddress("wglGetExtensionsStringARB"); 727 enforce!Exception(data,"wglGetExtensionsStringARB pointer is null"); 728 wglGetExtensionsStringARB = cast(FwglGetExtensionsStringARB) data; 729 data = null; 730 731 data = wglGetProcAddress("wglChoosePixelFormatARB"); 732 enforce!Exception(data,"wglChoosePixelFormatARB pointer is null"); 733 wglChoosePixelFormatARB = cast(FwglChoosePixelFormatARB) data; 734 data = null; 735 736 data = wglGetProcAddress("wglCreateContextAttribsARB"); 737 enforce!Exception(data,"wglCreateContextAttribsARB pointer is null"); 738 wglCreateContextAttribsARB = cast(FwglCreateContextAttribsARB) data; 739 data = null; 740 741 int[] iattrib = 742 [ 743 WGL_SUPPORT_OPENGL_ARB, true, 744 WGL_DRAW_TO_WINDOW_ARB, true, 745 WGL_DOUBLE_BUFFER_ARB, attributes.doubleBuffer, 746 WGL_RED_BITS_ARB, attributes.redSize, 747 WGL_GREEN_BITS_ARB, attributes.greenSize, 748 WGL_BLUE_BITS_ARB, attributes.blueSize, 749 WGL_ALPHA_BITS_ARB, attributes.alphaSize, 750 WGL_DEPTH_BITS_ARB, attributes.depthSize, 751 WGL_COLOR_BITS_ARB, attributes.colorDepth, 752 WGL_STENCIL_BITS_ARB, attributes.stencilSize, 753 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, 754 0 755 ]; 756 757 uint nNumFormats; 758 int nPixelFormat; 759 wglChoosePixelFormatARB(deviceHandle, iattrib.ptr, 760 null, 761 1, &nPixelFormat, 762 &nNumFormats); 763 enforce(nPixelFormat, "nPixelFormats error!"); 764 765 DescribePixelFormat(deviceHandle, nPixelFormat, pfd.sizeof, &pfd); 766 SetPixelFormat(deviceHandle, nPixelFormat, &pfd); 767 768 int[] attrib = 769 [ 770 WGL_CONTEXT_MAJOR_VERSION_ARB, this.attributes.glmajor, 771 WGL_CONTEXT_MINOR_VERSION_ARB, this.attributes.glminor, 772 WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 773 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 774 0 775 ]; 776 this._context = wglCreateContextAttribsARB( deviceHandle, 777 null, 778 attrib.ptr); 779 enforce(this._context, "ContextARB is not a create!"); 780 781 wglMakeCurrent(null, null); 782 wglDeleteContext(ctx); 783 } 784 785 void destroy() 786 { 787 wglDeleteContext(_context); 788 } 789 } 790 791 __gshared Window _wndptr; 792 793 version(Windows) 794 class Window : IWindow 795 { 796 import tida.runtime; 797 import tida.image; 798 import tida.color; 799 800 import std.utf : toUTFz; 801 import std.exception : enforce; 802 import core.sys.windows.windows; 803 804 pragma(lib, "opengl32.lib"); 805 806 private: 807 uint _widthInit; 808 uint _heightInit; 809 810 string _title; 811 812 bool _fullscreen = false; 813 bool _border = true; 814 bool _resizable = true; 815 bool _alwaysTop = false; 816 817 IContext _context; 818 819 LONG style; 820 LONG oldStyle; 821 WINDOWPLACEMENT wpc; 822 823 public: 824 HWND handle; 825 HDC dc; 826 bool isClose = false; 827 828 this(uint w, uint h, string caption) 829 { 830 this._widthInit = w; 831 this._heightInit = h; 832 _title = caption; 833 } 834 835 @trusted: 836 void create(int posX, int posY) 837 { 838 import std.traits : Signed; 839 840 extern(Windows) auto _wndProc(HWND hWnd, uint message, WPARAM wParam, LPARAM lParam) 841 { 842 switch (message) 843 { 844 case WM_CLOSE: 845 if (_wndptr is null || _wndptr.__vptr is null) return 0; 846 847 _wndptr.sendCloseEvent(); 848 return 0; 849 850 default: 851 return DefWindowProc(hWnd, message, wParam, lParam); 852 } 853 } 854 855 alias WinFun = extern (Windows) Signed!size_t function(void*, uint, size_t, Signed!size_t) nothrow @system; 856 857 WNDCLASSEX wc; 858 859 wc.cbSize = wc.sizeof; 860 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 861 wc.lpfnWndProc = cast(WinFun) &_wndProc; 862 wc.hInstance = runtime.instance; 863 wc.hCursor = LoadCursor(null, IDC_ARROW); 864 wc.lpszClassName = _title.toUTFz!(wchar*); 865 866 RegisterClassEx(&wc); 867 868 this.handle = CreateWindow( _title.toUTFz!(wchar*), 869 _title.toUTFz!(wchar*), 870 WS_CAPTION | WS_SYSMENU | WS_CLIPSIBLINGS | 871 WS_CLIPCHILDREN | WS_THICKFRAME, 872 posX, posY, this._widthInit, 873 this._heightInit, null, null, 874 runtime.instance, null); 875 876 enforce!Exception(this.handle, "Window is not create!"); 877 878 dc = GetDC(this.handle); 879 880 _wndptr = this; 881 } 882 883 void sendCloseEvent() 884 { 885 this.isClose = true; 886 } 887 888 override: 889 @property int x() 890 { 891 RECT rect; 892 GetWindowRect(this.handle, &rect); 893 894 return rect.left; 895 } 896 897 @property int y() 898 { 899 RECT rect; 900 GetWindowRect(this.handle, &rect); 901 902 return rect.top; 903 } 904 905 @property uint width() 906 { 907 RECT rect; 908 GetWindowRect(this.handle, &rect); 909 910 return rect.right - rect.left; 911 } 912 913 @property uint height() 914 { 915 RECT rect; 916 GetWindowRect(this.handle, &rect); 917 918 return rect.bottom - rect.top; 919 } 920 921 @property void fullscreen(bool value) 922 { 923 if (value) 924 { 925 GetWindowPlacement(this.handle, &wpc); 926 927 if(style == 0) 928 style = GetWindowLong(this.handle, GWL_STYLE); 929 if(oldStyle == 0) 930 oldStyle = GetWindowLong(this.handle, GWL_EXSTYLE); 931 932 auto NewHWNDStyle = style; 933 NewHWNDStyle &= ~WS_BORDER; 934 NewHWNDStyle &= ~WS_DLGFRAME; 935 NewHWNDStyle &= ~WS_THICKFRAME; 936 937 auto NewHWNDStyleEx = oldStyle; 938 NewHWNDStyleEx &= ~WS_EX_WINDOWEDGE; 939 940 SetWindowLong( this.handle, GWL_STYLE, 941 NewHWNDStyle | WS_POPUP ); 942 SetWindowLong( this.handle, GWL_EXSTYLE, 943 NewHWNDStyleEx | WS_EX_TOPMOST); 944 945 ShowWindow(this.handle, SHOW_FULLSCREEN); 946 } else 947 { 948 SetWindowLong(this.handle, GWL_STYLE, style); 949 SetWindowLong(this.handle, GWL_EXSTYLE, oldStyle); 950 ShowWindow(this.handle, SW_SHOWNORMAL); 951 SetWindowPlacement(this.handle, &wpc); 952 953 style = 0; 954 oldStyle = 0; 955 } 956 957 this._fullscreen = value; 958 } 959 960 @property bool fullscreen() 961 { 962 return this._fullscreen; 963 } 964 965 @property void resizable(bool value) 966 { 967 auto lStyle = GetWindowLong(this.handle, GWL_STYLE); 968 969 if (value) 970 lStyle |= WS_THICKFRAME; 971 else 972 lStyle &= ~(WS_THICKFRAME); 973 974 SetWindowLong(this.handle, GWL_STYLE, lStyle); 975 976 this._resizable = value; 977 } 978 979 @property bool resizable() 980 { 981 return this._resizable; 982 } 983 984 @property void border(bool value) 985 { 986 auto style = GetWindowLong(this.handle, GWL_STYLE); 987 988 if (!value) 989 style &= ~( 990 WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | 991 WS_MAXIMIZEBOX | WS_SYSMENU 992 ); 993 else 994 style |= ( 995 WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | 996 WS_MAXIMIZEBOX | WS_SYSMENU 997 ); 998 999 SetWindowLong(this.handle, GWL_STYLE, style); 1000 1001 SetWindowPos( this.handle, null, 0, 0, 0, 0, 1002 SWP_FRAMECHANGED | SWP_NOMOVE | 1003 SWP_NOSIZE | SWP_NOZORDER | 1004 SWP_NOOWNERZORDER); 1005 1006 this._border = value; 1007 } 1008 1009 @property bool border() 1010 { 1011 return this._border; 1012 } 1013 1014 @property void title(string value) 1015 { 1016 SetWindowTextA(this.handle, title.toUTFz!(char*)); 1017 1018 this._title = value; 1019 } 1020 1021 @property string title() 1022 { 1023 return this._title; 1024 } 1025 1026 @property void alwaysOnTop(bool value) 1027 { 1028 SetWindowPos( this.handle, 1029 value ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, 1030 SWP_NOMOVE | SWP_NOSIZE); 1031 1032 this._alwaysTop = value; 1033 } 1034 1035 @property bool alwaysOnTop() 1036 { 1037 return this._alwaysTop; 1038 } 1039 1040 @property void icon(Image iconimage) 1041 { 1042 HICON icon; 1043 1044 ubyte[] pixels = iconimage.bytes!(PixelFormat.BGRA); 1045 1046 ICONINFO icInfo; 1047 1048 auto bitmap = CreateBitmap( iconimage.width, 1049 iconimage.height, 1050 1,32,cast(PCVOID) pixels); 1051 1052 icInfo.hbmColor = bitmap; 1053 icInfo.hbmMask = CreateBitmap(iconimage.width,iconimage.height,1,1,null); 1054 1055 icon = CreateIconIndirect(&icInfo); 1056 1057 SendMessage(handle, WM_SETICON, ICON_SMALL, cast(LPARAM) icon); 1058 SendMessage(handle, WM_SETICON, ICON_BIG, cast(LPARAM) icon); 1059 } 1060 1061 @property void context(IContext ctx) 1062 { 1063 wglMakeCurrent(GetDC(this.handle),(cast(Context) ctx)._context); 1064 1065 this._context = ctx; 1066 } 1067 1068 @property IContext context() 1069 { 1070 return this._context; 1071 } 1072 1073 void resize(uint w, uint h) 1074 { 1075 SetWindowPos(this.handle, null, x, y ,w, h, 0); 1076 } 1077 1078 void move(int xposition, int yposition) 1079 { 1080 SetWindowPos(this.handle, null, xposition, yposition, width, height, 0); 1081 } 1082 1083 void show() 1084 { 1085 ShowWindow(this.handle, 1); 1086 } 1087 1088 void hide() 1089 { 1090 ShowWindow(this.handle, SW_HIDE); 1091 } 1092 1093 void swapBuffers() 1094 { 1095 SwapBuffers(dc); 1096 } 1097 1098 void destroy() 1099 { 1100 DestroyWindow(this.handle); 1101 this.handle = null; 1102 } 1103 } 1104 1105 /++ 1106 Creating a window in the window manager. When setting a parameter in a template, 1107 it can create both its regular version and with hardware graphics acceleration. 1108 1109 Params: 1110 type = Method of creation. 1111 `WithoutContext` - Only the window is created. 1112 The context is created after. 1113 `WithContext` - Creates both a window and a graphics context for 1114 using hardware graphics acceleration. 1115 window = Window pointer. 1116 posX = Position in the plane of the desktop along the x-axis. 1117 posY = Position in the plane of the desktop along the y-axis. 1118 1119 Throws: 1120 `Exception` If a window has not been created in the process 1121 (and this also applies to the creation of a graphical context). 1122 1123 Examples: 1124 --- 1125 windowInitialize!WithoutContext(window, 100, 100); /// Without context 1126 ... 1127 windowInitialize!WithContext(window, 100, 100); /// With context 1128 --- 1129 +/ 1130 void windowInitialize(int type = WithoutContext)( Window window, 1131 int posX, 1132 int posY) @trusted 1133 { 1134 version(Posix) { 1135 import tida.runtime; 1136 import x11.X, x11.Xlib, x11.Xutil; 1137 1138 scope XVisualInfo* vinfo = new XVisualInfo(); 1139 vinfo.visual = XDefaultVisual(runtime.display, runtime.displayID); 1140 vinfo.depth = XDefaultDepth(runtime.display, runtime.displayID); 1141 1142 window.createFromXVisual(vinfo); 1143 1144 destroy(vinfo); 1145 } 1146 else 1147 window.create(posX, posY); 1148 1149 static if(type == WithContext) 1150 { 1151 GraphicsAttributes attribs = AttribBySizeOfTheColor!8; 1152 attribs.glmajor = 4; 1153 attribs.glminor = 5; 1154 1155 Context context; 1156 1157 void ctxCreate() { 1158 context = new Context(); 1159 context.setAttributes(attribs); 1160 context.create(window); 1161 } 1162 1163 try 1164 { 1165 ctxCreate(); 1166 } catch(Exception e) 1167 { 1168 attribs.glmajor = 3; 1169 attribs.glminor = 3; 1170 1171 try 1172 { 1173 ctxCreate(); 1174 } catch(Exception e) 1175 { 1176 attribs.glmajor = 3; 1177 attribs.glminor = 0; 1178 1179 ctxCreate(); 1180 } 1181 } 1182 1183 window.context = context; 1184 } 1185 1186 window.show(); 1187 }