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 }