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: 272 @property XVisualInfo* getVisualInfo() 273 { 274 return visual; 275 } 276 277 override: 278 void setAttributes(GraphicsAttributes attributes) 279 { 280 import std.exception : enforce; 281 import std.conv : to; 282 283 int[] glxAttributes = 284 [ 285 GLX_X_RENDERABLE , True, 286 GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 287 GLX_RENDER_TYPE , GLX_RGBA_BIT, 288 GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 289 GLX_RED_SIZE , attributes.redSize, 290 GLX_GREEN_SIZE , attributes.greenSize, 291 GLX_BLUE_SIZE , attributes.blueSize, 292 GLX_ALPHA_SIZE , attributes.alphaSize, 293 GLX_DEPTH_SIZE , attributes.depthSize, 294 GLX_STENCIL_SIZE , attributes.stencilSize, 295 GLX_DOUBLEBUFFER , attributes.doubleBuffer.to!int, 296 None 297 ]; 298 299 int fbcount = 0; 300 scope fbc = glXChooseFBConfig( runtime.display, runtime.displayID, 301 glxAttributes.ptr, &fbcount); 302 scope(success) XFree(fbc); 303 enforce!Exception(fbc, ConfFindError); 304 305 int bestFbc = -1, bestNum = -1; 306 foreach (int i; 0 .. fbcount) 307 { 308 int sampBuff, samples; 309 glXGetFBConfigAttrib( runtime.display, fbc[i], 310 GLX_SAMPLE_BUFFERS, &sampBuff); 311 glXGetFBConfigAttrib( runtime.display, fbc[i], 312 GLX_SAMPLES, &samples); 313 314 if (bestFbc < 0 || (sampBuff && samples > bestNum)) 315 { 316 bestFbc = i; 317 bestNum = samples; 318 } 319 } 320 321 this.bestFbcs = fbc[bestFbc]; 322 enforce!Exception(bestFbcs, ConfFindError); 323 324 this.visual = glXGetVisualFromFBConfig(runtime.display, bestFbcs); 325 enforce!Exception(visual, ConfFindError); 326 } 327 328 void create(IWindow window) 329 { 330 _context = glXCreateNewContext( runtime.display, this.bestFbcs, 331 GLX_RGBA_TYPE, null, true); 332 } 333 334 void destroy() 335 { 336 glXDestroyContext(runtime.display, _context); 337 if(visual) XFree(visual); 338 } 339 } 340 341 version (Posix) 342 class Window : IWindow 343 { 344 import tida.runtime, tida.image; 345 import x11.X, x11.Xlib, x11.Xutil; 346 import dglx.glx; 347 import std.utf : toUTFz; 348 349 private: 350 string _title; 351 bool _fullscreen; 352 bool _border; 353 bool _resizable; 354 bool _alwaysTop; 355 IContext _context; 356 uint _widthInit; 357 uint _heightInit; 358 359 public: 360 x11.X.Window handle; 361 Visual* visual; 362 int depth; 363 364 @trusted: 365 this(uint w, uint h, string caption) 366 { 367 this._widthInit = w; 368 this._heightInit = h; 369 } 370 371 void createFromXVisual(XVisualInfo* vinfo, int posX = 100, int posY = 100) 372 { 373 visual = vinfo.visual; 374 depth = vinfo.depth; 375 376 auto rootWindow = runtime.rootWindow; 377 378 XSetWindowAttributes windowAttribs; 379 windowAttribs.border_pixel = 0x000000; 380 windowAttribs.background_pixel = 0xFFFFFF; 381 windowAttribs.override_redirect = True; 382 windowAttribs.colormap = XCreateColormap(runtime.display, rootWindow, 383 visual, AllocNone); 384 385 windowAttribs.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | 386 KeyReleaseMask | ButtonReleaseMask | EnterWindowMask | 387 LeaveWindowMask | PointerMotionMask | StructureNotifyMask; 388 389 this.handle = XCreateWindow (runtime.display, rootWindow, posX, posY, _widthInit, _heightInit, 0, depth, 390 InputOutput, visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, 391 &windowAttribs); 392 393 title = _title; 394 395 Atom wmAtom = XInternAtom(runtime.display, "WM_DELETE_WINDOW", 0); 396 XSetWMProtocols(runtime.display, this.handle, &wmAtom, 1); 397 } 398 399 int getDepth() 400 { 401 return depth; 402 } 403 404 Visual* getVisual() 405 { 406 return visual; 407 } 408 409 ~this() 410 { 411 this.destroy(); 412 } 413 override: 414 @property int x() 415 { 416 XWindowAttributes winAttrib; 417 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 418 419 return winAttrib.x; 420 } 421 422 @property int y() 423 { 424 XWindowAttributes winAttrib; 425 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 426 427 return winAttrib.y; 428 } 429 430 @property uint width() 431 { 432 XWindowAttributes winAttrib; 433 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 434 435 return winAttrib.width; 436 } 437 438 @property uint height() 439 { 440 XWindowAttributes winAttrib; 441 XGetWindowAttributes(runtime.display, this.handle, &winAttrib); 442 443 return winAttrib.height; 444 } 445 446 @property void fullscreen(bool value) 447 { 448 XEvent event; 449 450 const wmState = XInternAtom(runtime.display, 451 "_NET_WM_STATE", 0); 452 const wmFullscreen = XInternAtom( runtime.display, 453 "_NET_WM_STATE_FULLSCREEN", 0); 454 455 event.xany.type = ClientMessage; 456 event.xclient.message_type = wmState; 457 event.xclient.format = 32; 458 event.xclient.window = this.handle; 459 event.xclient.data.l[1] = wmFullscreen; 460 event.xclient.data.l[3] = 0; 461 462 event.xclient.data.l[0] = value; 463 464 XSendEvent(runtime.display,runtime.rootWindow,0, 465 SubstructureNotifyMask | SubstructureRedirectMask, &event); 466 467 this._fullscreen = value; 468 } 469 470 @property bool fullscreen() 471 { 472 return this._fullscreen; 473 } 474 475 @property void resizable(bool value) 476 { 477 long flags; 478 479 scope XSizeHints* sh = XAllocSizeHints(); 480 scope(exit) XFree(sh); 481 482 XGetWMNormalHints(runtime.display, this.handle, sh, &flags); 483 484 if(!value) 485 { 486 sh.flags |= PMinSize | PMaxSize; 487 sh.min_width = this.width; 488 sh.max_width = this.width; 489 sh.min_height = this.height; 490 sh.max_height = this.height; 491 }else 492 { 493 sh.flags &= ~(PMinSize | PMaxSize); 494 } 495 496 this._resizable = value; 497 XSetWMNormalHints(runtime.display, this.handle, sh); 498 } 499 500 @property bool resizable() 501 { 502 return this._resizable; 503 } 504 505 @property void border(bool value) 506 { 507 import std.conv : to; 508 509 struct MWMHints { 510 ulong flags; 511 ulong functions; 512 ulong decorations; 513 long inputMode; 514 ulong status; 515 } 516 517 const hint = MWMHints(1 << 1, 0, value.to!ulong, 0, 0); 518 const wmHINTS = XInternAtom(runtime.display, "_MOTIF_WM_HINTS", 0); 519 520 XChangeProperty(runtime.display, this.handle, wmHINTS, wmHINTS, 32, 521 PropModeReplace, cast(ubyte*) &hint, MWMHints.sizeof / long.sizeof); 522 523 this._border = value; 524 } 525 526 @property bool border() 527 { 528 return this._border; 529 } 530 531 @property void title(string value) 532 { 533 XStoreName(runtime.display, this.handle, value.toUTFz!(char*)); 534 XSetIconName(runtime.display, this.handle, value.toUTFz!(char*)); 535 536 this._title = value; 537 } 538 539 @property string title() 540 { 541 return this._title; 542 } 543 544 @property void alwaysOnTop(bool value) 545 { 546 const wmState = XInternAtom(runtime.display, "_NET_WM_STATE", 0); 547 const wmAbove = XInternAtom(runtime.display, "_NET_WM_STATE_ABOVE", 0); 548 549 XEvent event; 550 event.xclient.type = ClientMessage; 551 event.xclient.serial = 0; 552 event.xclient.send_event = true; 553 event.xclient.display = runtime.display; 554 event.xclient.window = this.handle; 555 event.xclient.message_type = wmState; 556 event.xclient.format = 32; 557 event.xclient.data.l[0] = value; 558 event.xclient.data.l[1] = wmAbove; 559 event.xclient.data.l[2 .. 5] = 0; 560 561 XSendEvent( runtime.display, runtime.rootWindow, false, 562 SubstructureRedirectMask | SubstructureNotifyMask, &event); 563 XFlush(runtime.display); 564 565 this._alwaysTop = value; 566 } 567 568 @property bool alwaysOnTop() 569 { 570 return this._alwaysTop; 571 } 572 573 void icon(Image iconimage) 574 { 575 import tida.color; 576 577 ulong[] pixels = [ cast(ulong) iconimage.width, 578 cast(ulong) iconimage.height]; 579 580 foreach(pixel; iconimage.pixels) 581 pixels ~= pixel.to!(ulong, PixelFormat.ARGB); 582 583 const first = XInternAtom(runtime.display, "_NET_WM_ICON", 0); 584 const second = XInternAtom(runtime.display, "CARDINAL", 0); 585 586 XChangeProperty(runtime.display, this.handle, first, second, 32, 587 PropModeReplace, cast(ubyte*) pixels, 588 cast(int) pixels.length); 589 } 590 591 @property void context(IContext ctx) 592 { 593 this._context = ctx; 594 595 auto glxctx = (cast(Context) ctx)._context; 596 if (glxctx is null) 597 throw new Exception("Context is null!"); 598 599 glXMakeCurrent(runtime.display, this.handle, glxctx); 600 } 601 602 @property IContext context() 603 { 604 return this._context; 605 } 606 607 void resize(uint w, uint h) 608 { 609 XResizeWindow(runtime.display, this.handle, w, h); 610 } 611 612 void move(int xposition, int yposition) 613 { 614 XMoveWindow(runtime.display, this.handle, xposition, yposition); 615 } 616 617 void show() 618 { 619 XMapWindow(runtime.display, this.handle); 620 XClearWindow(runtime.display, this.handle); 621 } 622 623 void hide() 624 { 625 XUnmapWindow(runtime.display, this.handle); 626 } 627 628 void swapBuffers() 629 { 630 import dglx.glx : glXSwapBuffers; 631 632 glXSwapBuffers(runtime.display, this.handle); 633 } 634 635 void destroy() 636 { 637 XDestroyWindow(runtime.display, this.handle); 638 this.handle = 0; 639 } 640 } 641 642 version(Windows) 643 class Context : IContext 644 { 645 import tida.runtime; 646 import core.sys.windows.windows; 647 import std.exception : enforce; 648 649 private: 650 GraphicsAttributes attributes; 651 HDC deviceHandle; 652 653 PIXELFORMATDESCRIPTOR pfd; 654 655 alias FwglChoosePixelFormatARB = extern(C) bool function( HDC hdc, 656 int *piAttribIList, 657 float *pfAttribFList, 658 uint nMaxFormats, 659 int *piFormats, 660 uint *nNumFormats); 661 662 alias FwglGetExtensionsStringARB = extern(C) char* function(HDC hdc); 663 664 alias FwglCreateContextAttribsARB = extern(C) HGLRC function(HDC, HGLRC, int*); 665 666 FwglChoosePixelFormatARB wglChoosePixelFormatARB; 667 FwglGetExtensionsStringARB wglGetExtensionsStringARB; 668 FwglCreateContextAttribsARB wglCreateContextAttribsARB; 669 670 enum 671 { 672 WGL_DRAW_TO_WINDOW_ARB = 0x2001, 673 WGL_RED_BITS_ARB = 0x2015, 674 WGL_GREEN_BITS_ARB = 0x2017, 675 WGL_BLUE_BITS_ARB = 0x2019, 676 WGL_ALPHA_BITS_ARB = 0x201B, 677 WGL_DOUBLE_BUFFER_ARB = 0x2011, 678 WGL_DEPTH_BITS_ARB = 0x2022, 679 WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091, 680 WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092, 681 WGL_CONTEXT_FLAGS_ARB = 0x2094, 682 WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x0002, 683 WGL_SUPPORT_OPENGL_ARB = 0x2010, 684 WGL_COLOR_BITS_ARB = 0x2014, 685 WGL_STENCIL_BITS_ARB = 0x2023, 686 WGL_ACCELERATION_ARB = 0x2003, 687 WGL_FULL_ACCELERATION_ARB = 0x2027, 688 WGL_PIXEL_TYPE_ARB = 0x2013, 689 WGL_TYPE_RGBA_ARB = 0x202B, 690 WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126, 691 WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001 692 } 693 694 public: 695 HGLRC _context; 696 697 @trusted: 698 ~this() 699 { 700 destroy(); 701 } 702 override: 703 void setAttributes(GraphicsAttributes attributes) 704 { 705 this.attributes = attributes; 706 707 const flags = 708 (attributes.doubleBuffer ? 709 (PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL) : 710 (PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL)); 711 712 pfd.nSize = PIXELFORMATDESCRIPTOR.sizeof; 713 pfd.nVersion = 1; 714 pfd.dwFlags = flags; 715 pfd.iPixelType = PFD_TYPE_RGBA; 716 pfd.cRedBits = cast(ubyte) this.attributes.redSize; 717 pfd.cGreenBits = cast(ubyte) this.attributes.greenSize; 718 pfd.cBlueBits = cast(ubyte) this.attributes.blueSize; 719 pfd.cAlphaBits = cast(ubyte) this.attributes.alphaSize; 720 pfd.cDepthBits = cast(ubyte) this.attributes.depthSize; 721 pfd.cStencilBits = cast(ubyte) this.attributes.stencilSize; 722 pfd.iLayerType = PFD_MAIN_PLANE; 723 } 724 725 void create(IWindow window) 726 { 727 scope handle = (cast(Window) window).handle; 728 deviceHandle = (cast(Window) window).dc; 729 auto chsPixel = ChoosePixelFormat(deviceHandle, &pfd); 730 enforce!Exception(chsPixel != 0, ConfFindError); 731 732 SetPixelFormat(deviceHandle, chsPixel, &pfd); 733 734 scope ctx = wglCreateContext(deviceHandle); 735 wglMakeCurrent(deviceHandle, ctx); 736 737 void* data = wglGetProcAddress("wglGetExtensionsStringARB"); 738 enforce!Exception(data,"wglGetExtensionsStringARB pointer is null"); 739 wglGetExtensionsStringARB = cast(FwglGetExtensionsStringARB) data; 740 data = null; 741 742 data = wglGetProcAddress("wglChoosePixelFormatARB"); 743 enforce!Exception(data,"wglChoosePixelFormatARB pointer is null"); 744 wglChoosePixelFormatARB = cast(FwglChoosePixelFormatARB) data; 745 data = null; 746 747 data = wglGetProcAddress("wglCreateContextAttribsARB"); 748 enforce!Exception(data,"wglCreateContextAttribsARB pointer is null"); 749 wglCreateContextAttribsARB = cast(FwglCreateContextAttribsARB) data; 750 data = null; 751 752 int[] iattrib = 753 [ 754 WGL_SUPPORT_OPENGL_ARB, true, 755 WGL_DRAW_TO_WINDOW_ARB, true, 756 WGL_DOUBLE_BUFFER_ARB, attributes.doubleBuffer, 757 WGL_RED_BITS_ARB, attributes.redSize, 758 WGL_GREEN_BITS_ARB, attributes.greenSize, 759 WGL_BLUE_BITS_ARB, attributes.blueSize, 760 WGL_ALPHA_BITS_ARB, attributes.alphaSize, 761 WGL_DEPTH_BITS_ARB, attributes.depthSize, 762 WGL_COLOR_BITS_ARB, attributes.colorDepth, 763 WGL_STENCIL_BITS_ARB, attributes.stencilSize, 764 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, 765 0 766 ]; 767 768 uint nNumFormats; 769 int nPixelFormat; 770 wglChoosePixelFormatARB(deviceHandle, iattrib.ptr, 771 null, 772 1, &nPixelFormat, 773 &nNumFormats); 774 enforce(nPixelFormat, "nPixelFormats error!"); 775 776 DescribePixelFormat(deviceHandle, nPixelFormat, pfd.sizeof, &pfd); 777 SetPixelFormat(deviceHandle, nPixelFormat, &pfd); 778 779 int[] attrib = 780 [ 781 WGL_CONTEXT_MAJOR_VERSION_ARB, this.attributes.glmajor, 782 WGL_CONTEXT_MINOR_VERSION_ARB, this.attributes.glminor, 783 WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 784 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 785 0 786 ]; 787 this._context = wglCreateContextAttribsARB( deviceHandle, 788 null, 789 attrib.ptr); 790 enforce(this._context, "ContextARB is not a create!"); 791 792 wglMakeCurrent(null, null); 793 wglDeleteContext(ctx); 794 } 795 796 void destroy() 797 { 798 wglDeleteContext(_context); 799 } 800 } 801 802 __gshared Window _wndptr; 803 804 version(Windows) 805 class Window : IWindow 806 { 807 import tida.runtime; 808 import tida.image; 809 import tida.color; 810 811 import std.utf : toUTFz; 812 import std.exception : enforce; 813 import core.sys.windows.windows; 814 815 class WindowException : Exception 816 { 817 import std.conv : to; 818 819 this(ulong errorID) @trusted 820 { 821 LPSTR messageBuffer = null; 822 823 size_t size = FormatMessageA( 824 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 825 null, 826 cast(uint) errorID, 827 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 828 cast(LPSTR) &messageBuffer, 829 0, 830 null); 831 832 833 super("[WinAPI] " ~ messageBuffer.to!string); 834 } 835 } 836 837 private: 838 uint _widthInit; 839 uint _heightInit; 840 841 string _title; 842 843 bool _fullscreen = false; 844 bool _border = true; 845 bool _resizable = true; 846 bool _alwaysTop = false; 847 848 IContext _context; 849 850 LONG style; 851 LONG oldStyle; 852 WINDOWPLACEMENT wpc; 853 854 public: 855 HWND handle; 856 HDC dc; 857 bool isClose = false; 858 bool isResize = false; 859 860 this(uint w, uint h, string caption) 861 { 862 this._widthInit = w; 863 this._heightInit = h; 864 _title = caption; 865 } 866 867 @trusted: 868 void create(int posX, int posY) 869 { 870 import std.traits : Signed; 871 872 extern(Windows) auto _wndProc(HWND hWnd, uint message, WPARAM wParam, LPARAM lParam) 873 { 874 switch (message) 875 { 876 case WM_CLOSE: 877 if (_wndptr is null || _wndptr.__vptr is null) return 0; 878 879 _wndptr.sendCloseEvent(); 880 return 0; 881 882 case WM_SIZE: 883 if (_wndptr is null || _wndptr.__vptr is null) return 0; 884 885 _wndptr.isResize = true; 886 return 0; 887 888 default: 889 return DefWindowProc(hWnd, message, wParam, lParam); 890 } 891 } 892 893 alias WinFun = extern (Windows) Signed!size_t function(void*, uint, size_t, Signed!size_t) nothrow @system; 894 895 WNDCLASSEX wc; 896 897 wc.cbSize = wc.sizeof; 898 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 899 wc.lpfnWndProc = cast(WinFun) &_wndProc; 900 wc.hInstance = runtime.instance; 901 wc.hCursor = LoadCursor(null, IDC_ARROW); 902 wc.lpszClassName = _title.toUTFz!(wchar*); 903 904 RegisterClassEx(&wc); 905 906 this.handle = CreateWindow( _title.toUTFz!(wchar*), 907 _title.toUTFz!(wchar*), 908 WS_CAPTION | WS_SYSMENU | WS_CLIPSIBLINGS | 909 WS_CLIPCHILDREN | WS_THICKFRAME, 910 posX, posY, this._widthInit, 911 this._heightInit, null, null, 912 runtime.instance, null); 913 914 if (this.handle is null) 915 throw new WindowException(GetLastError()); 916 917 dc = GetDC(this.handle); 918 919 resize(this._widthInit, this._heightInit); 920 921 _wndptr = this; 922 } 923 924 void sendCloseEvent() 925 { 926 this.isClose = true; 927 } 928 929 @property auto windowBorderSize() @trusted 930 { 931 if (_fullscreen || !_border) 932 { 933 return 0; 934 } else 935 { 936 RECT rcClient, rcWind; 937 GetClientRect(this.handle, &rcClient); 938 GetWindowRect(this.handle, &rcWind); 939 940 return ((rcWind.bottom - rcWind.top) - rcClient.bottom) / 2; 941 } 942 } 943 944 override: 945 @property int x() 946 { 947 RECT rect; 948 GetWindowRect(this.handle, &rect); 949 950 return rect.left; 951 } 952 953 @property int y() 954 { 955 RECT rect; 956 GetWindowRect(this.handle, &rect); 957 958 return rect.top; 959 } 960 961 @property uint width() 962 { 963 RECT rect; 964 GetWindowRect(this.handle, &rect); 965 966 return rect.right - rect.left; 967 } 968 969 @property uint height() 970 { 971 RECT rect; 972 GetWindowRect(this.handle, &rect); 973 974 return rect.bottom - rect.top - windowBorderSize; 975 } 976 977 @property void fullscreen(bool value) 978 { 979 if (value) 980 { 981 GetWindowPlacement(this.handle, &wpc); 982 983 if(style == 0) 984 style = GetWindowLong(this.handle, GWL_STYLE); 985 if(oldStyle == 0) 986 oldStyle = GetWindowLong(this.handle, GWL_EXSTYLE); 987 988 auto NewHWNDStyle = style; 989 NewHWNDStyle &= ~WS_BORDER; 990 NewHWNDStyle &= ~WS_DLGFRAME; 991 NewHWNDStyle &= ~WS_THICKFRAME; 992 993 auto NewHWNDStyleEx = oldStyle; 994 NewHWNDStyleEx &= ~WS_EX_WINDOWEDGE; 995 996 SetWindowLong( this.handle, GWL_STYLE, 997 NewHWNDStyle | WS_POPUP ); 998 SetWindowLong( this.handle, GWL_EXSTYLE, 999 NewHWNDStyleEx | WS_EX_TOPMOST); 1000 1001 ShowWindow(this.handle, SHOW_FULLSCREEN); 1002 } else 1003 { 1004 SetWindowLong(this.handle, GWL_STYLE, style); 1005 SetWindowLong(this.handle, GWL_EXSTYLE, oldStyle); 1006 ShowWindow(this.handle, SW_SHOWNORMAL); 1007 SetWindowPlacement(this.handle, &wpc); 1008 1009 style = 0; 1010 oldStyle = 0; 1011 } 1012 1013 this._fullscreen = value; 1014 } 1015 1016 @property bool fullscreen() 1017 { 1018 return this._fullscreen; 1019 } 1020 1021 @property void resizable(bool value) 1022 { 1023 auto lStyle = GetWindowLong(this.handle, GWL_STYLE); 1024 1025 if (value) 1026 lStyle |= WS_THICKFRAME; 1027 else 1028 lStyle &= ~(WS_THICKFRAME); 1029 1030 SetWindowLong(this.handle, GWL_STYLE, lStyle); 1031 1032 this._resizable = value; 1033 } 1034 1035 @property bool resizable() 1036 { 1037 return this._resizable; 1038 } 1039 1040 @property void border(bool value) 1041 { 1042 int bs; 1043 1044 if (border) 1045 bs = windowBorderSize; 1046 1047 auto style = GetWindowLong(this.handle, GWL_STYLE); 1048 1049 if (!value) 1050 style &= ~( 1051 WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | 1052 WS_MAXIMIZEBOX | WS_SYSMENU 1053 ); 1054 else 1055 style |= ( 1056 WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | 1057 WS_MAXIMIZEBOX | WS_SYSMENU 1058 ); 1059 1060 SetWindowLong(this.handle, GWL_STYLE, style); 1061 1062 SetWindowPos( this.handle, null, 0, 0, 0, 0, 1063 SWP_FRAMECHANGED | SWP_NOMOVE | 1064 SWP_NOSIZE | SWP_NOZORDER | 1065 SWP_NOOWNERZORDER); 1066 1067 this._border = value; 1068 } 1069 1070 @property bool border() 1071 { 1072 return this._border; 1073 } 1074 1075 @property void title(string value) 1076 { 1077 SetWindowTextA(this.handle, title.toUTFz!(char*)); 1078 1079 this._title = value; 1080 } 1081 1082 @property string title() 1083 { 1084 return this._title; 1085 } 1086 1087 @property void alwaysOnTop(bool value) 1088 { 1089 SetWindowPos( this.handle, 1090 value ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, 1091 SWP_NOMOVE | SWP_NOSIZE); 1092 1093 this._alwaysTop = value; 1094 } 1095 1096 @property bool alwaysOnTop() 1097 { 1098 return this._alwaysTop; 1099 } 1100 1101 @property void icon(Image iconimage) 1102 { 1103 HICON icon; 1104 1105 ubyte[] pixels = iconimage.bytes!(PixelFormat.BGRA); 1106 1107 ICONINFO icInfo; 1108 1109 auto bitmap = CreateBitmap( iconimage.width, 1110 iconimage.height, 1111 1,32,cast(PCVOID) pixels); 1112 1113 icInfo.hbmColor = bitmap; 1114 icInfo.hbmMask = CreateBitmap(iconimage.width,iconimage.height,1,1,null); 1115 1116 icon = CreateIconIndirect(&icInfo); 1117 1118 SendMessage(handle, WM_SETICON, ICON_SMALL, cast(LPARAM) icon); 1119 SendMessage(handle, WM_SETICON, ICON_BIG, cast(LPARAM) icon); 1120 } 1121 1122 @property void context(IContext ctx) 1123 { 1124 wglMakeCurrent(GetDC(this.handle),(cast(Context) ctx)._context); 1125 1126 this._context = ctx; 1127 } 1128 1129 @property IContext context() 1130 { 1131 return this._context; 1132 } 1133 1134 void resize(uint w, uint h) 1135 { 1136 RECT rcClient, rcWind; 1137 1138 GetClientRect(this.handle, &rcClient); 1139 GetWindowRect(this.handle, &rcWind); 1140 immutable offsetY = (rcWind.bottom - rcWind.top) - rcClient.bottom; 1141 1142 SetWindowPos(this.handle, null, x, y, w, h + offsetY - 1, 0); 1143 } 1144 1145 void move(int xposition, int yposition) 1146 { 1147 SetWindowPos(this.handle, null, xposition, yposition, width, height, 0); 1148 } 1149 1150 void show() 1151 { 1152 ShowWindow(this.handle, 1); 1153 } 1154 1155 void hide() 1156 { 1157 ShowWindow(this.handle, SW_HIDE); 1158 } 1159 1160 void swapBuffers() 1161 { 1162 SwapBuffers(dc); 1163 } 1164 1165 void destroy() 1166 { 1167 DestroyWindow(this.handle); 1168 this.handle = null; 1169 } 1170 } 1171 1172 /++ 1173 Creating a window in the window manager. When setting a parameter in a template, 1174 it can create both its regular version and with hardware graphics acceleration. 1175 1176 Params: 1177 type = Method of creation. 1178 `WithoutContext` - Only the window is created. 1179 The context is created after. 1180 `WithContext` - Creates both a window and a graphics context for 1181 using hardware graphics acceleration. 1182 window = Window pointer. 1183 posX = Position in the plane of the desktop along the x-axis. 1184 posY = Position in the plane of the desktop along the y-axis. 1185 1186 Throws: 1187 `Exception` If a window has not been created in the process 1188 (and this also applies to the creation of a graphical context). 1189 1190 Examples: 1191 --- 1192 windowInitialize!WithoutContext(window, 100, 100); /// Without context 1193 ... 1194 windowInitialize!WithContext(window, 100, 100); /// With context 1195 --- 1196 +/ 1197 void windowInitialize(int type = WithoutContext)( Window window, 1198 int posX, 1199 int posY) @trusted 1200 { 1201 version(Posix) 1202 { 1203 import tida.runtime; 1204 import x11.X, x11.Xlib, x11.Xutil; 1205 1206 static if (type == WithoutContext) 1207 { 1208 scope XVisualInfo* vinfo = new XVisualInfo(); 1209 vinfo.visual = XDefaultVisual(runtime.display, runtime.displayID); 1210 vinfo.depth = XDefaultDepth(runtime.display, runtime.displayID); 1211 1212 window.createFromXVisual(vinfo); 1213 1214 destroy(vinfo); 1215 }else 1216 { 1217 GraphicsAttributes attribs = AttribBySizeOfTheColor!8; 1218 attribs.glmajor = 4; 1219 attribs.glminor = 5; 1220 1221 Context context; 1222 1223 context = new Context(); 1224 context.setAttributes(attribs); 1225 context.create(null); 1226 1227 window.createFromXVisual(context.getVisualInfo(), 100, 100); 1228 window.context = context; 1229 } 1230 1231 window.show(); 1232 } 1233 else 1234 version(Windows) 1235 { 1236 window.create(posX, posY); 1237 1238 static if(type == WithContext) 1239 { 1240 GraphicsAttributes attribs = AttribBySizeOfTheColor!8; 1241 attribs.glmajor = 4; 1242 attribs.glminor = 5; 1243 1244 Context context; 1245 1246 void ctxCreate() 1247 { 1248 context = new Context(); 1249 context.setAttributes(attribs); 1250 context.create(window); 1251 } 1252 1253 try 1254 { 1255 ctxCreate(); 1256 } catch(Exception e) 1257 { 1258 attribs.glmajor = 3; 1259 attribs.glminor = 3; 1260 1261 try 1262 { 1263 ctxCreate(); 1264 } catch(Exception e) 1265 { 1266 attribs.glmajor = 3; 1267 attribs.glminor = 0; 1268 1269 ctxCreate(); 1270 } 1271 } 1272 1273 window.context = context; 1274 } 1275 1276 window.show(); 1277 } 1278 }