1 module ws.gl.context;
2 
3 
4 import
5     std.conv,
6     std.algorithm,
7     std.string,
8     std.traits,
9     ws.wm,
10     ws.exception,
11     ws.gl.gl;
12 
13 
14 version(Windows){
15 
16 
17     string getLastError(){
18         DWORD errcode = GetLastError();
19         if(!errcode)
20             return "No error";
21         LPCSTR msgBuf;
22         DWORD i = FormatMessageA(
23             cast(uint)(
24             FORMAT_MESSAGE_ALLOCATE_BUFFER |
25             FORMAT_MESSAGE_FROM_SYSTEM |
26             FORMAT_MESSAGE_IGNORE_INSERTS),
27             null,
28             errcode,
29             cast(uint)MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
30             cast(LPSTR)&msgBuf,
31             0,
32             null
33         );
34         string text = to!string(msgBuf);
35         LocalFree(cast(HLOCAL)msgBuf);
36         return text;
37     }
38 
39 
40     import ws.wm.win32.api;
41 
42     private static GraphicsContext current;
43 
44     auto chooseBestPixelFormat(){
45         
46     }
47 
48     __gshared class GlContext {
49 
50         GraphicsContext handle;
51         __gshared GraphicsContext sharedHandle;
52         WindowHandle window;
53         HDC deviceContext;
54 
55         private GraphicsContext spawn(){
56             auto c = wm.wglCreateContextAttribsARB(GetDC(window), sharedHandle, null);
57             if(!c)
58                 throw new Exception("Failed to create shared context");
59             return c;
60         }
61 
62         this(WindowHandle window, GraphicsContext context){
63             this.window = window;
64             this.handle = context;
65             this.sharedHandle = context;
66         }
67 
68         this(WindowHandle window){
69             enum antiAliasing = 0;
70             this.window = window;
71             deviceContext = GetDC(window);
72             try {
73                 if(!deviceContext)
74                     throw new Exception("window.Show failed: GetDC");
75                 uint formatCount = 0;
76                 int pixelFormat;
77                 int[] iAttribList = [
78                     0x2001, true,
79                     0x2010, true,
80                     0x2011, true,
81                     0x2003, 0x2027,
82                     0x2014, 0x202B,
83                     0x2014, 24,
84                     0x201B, 8,
85                     0x2022, 16,
86                     0x2023, 8,
87                     0x2011, true,
88                     0x2041, antiAliasing > 1 ? true : false,
89                     0x2042, antiAliasing,
90                     0
91                 ];
92                 wm.wglChoosePixelFormatARB(deviceContext, iAttribList.ptr, null, 1, &pixelFormat, &formatCount);
93                 if(!formatCount)
94                     throw new Exception("wglChoosePixelFormatARB failed: %s".format(getLastError()));
95                 SetPixelFormat(deviceContext, pixelFormat, null);
96             }catch(Exception e){
97                 ubyte depth = 32;
98                 ubyte color = 24;
99                 ubyte stencil = 8;
100                 PIXELFORMATDESCRIPTOR descriptor;
101                 descriptor.nSize        = descriptor.sizeof;
102                 descriptor.nVersion     = 1;
103                 descriptor.iLayerType   = PFD_MAIN_PLANE;
104                 descriptor.dwFlags      = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
105                 descriptor.iPixelType   = PFD_TYPE_RGBA;
106                 descriptor.cColorBits   = depth;
107                 descriptor.cDepthBits   = color;
108                 descriptor.cStencilBits = stencil;
109                 descriptor.cAlphaBits   = depth == 32 ? 8 : 0;
110                 auto pixelFormat = ChoosePixelFormat(deviceContext, &descriptor);
111                 SetPixelFormat(deviceContext, pixelFormat, &descriptor);
112             }
113 
114             try {
115                 enum WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091;
116                 enum WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092;
117                 enum WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002;
118                 int[] attribs = [
119                     WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
120                     WGL_CONTEXT_MINOR_VERSION_ARB, 3,
121                     //WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 1,
122                     0, 0
123                 ];
124                 handle = wm.wglCreateContextAttribsARB(deviceContext, null, attribs.ptr);
125                 if(!handle)
126                     throw new Exception("wglCreateContextAttribsARB() failed: %s".format(getLastError()));
127             }catch(Exception e){
128                 import std.stdio; writeln(e);
129                 handle = core.sys.windows.wingdi.wglCreateContext(deviceContext);
130             }
131             this.sharedHandle = handle;
132             wglMakeCurrent(deviceContext, handle);
133             DerelictGL3.reload();
134         }
135 
136         void swapBuffers(){
137             assert(handle == sharedHandle, "Can only swap from main");
138             SwapBuffers(deviceContext);
139         }
140 
141         template opDispatch(string s){
142             auto opDispatch(Args...)(Args args){
143                 if(!handle && sharedHandle){
144                     synchronized(this){
145                         handle = spawn;
146                     }
147                 }
148                 if(current != handle){
149                     wglMakeCurrent(window, handle);
150                     current = handle;
151                 }
152                 assert(wglGetCurrentContext() == handle);
153                 debug { gl.check("?"); }
154                 enum s = s[0..1].capitalize ~ s[1..$];
155                 mixin("alias returns = ReturnType!(gl" ~ s ~ ");");
156                 static if(!is(returns == void)){
157                     mixin("auto result = gl" ~ s ~ "(args);");
158                     debug { gl.check("gl" ~ s); }
159                     return result;
160                 }else{
161                     mixin("gl" ~ s ~ "(args);");
162                     debug { gl.check("gl" ~ s); }
163                 }
164             }
165         }
166 
167     }
168 
169 
170 }
171 
172 
173 version(Posix){
174 
175     import ws.wm.x11.api;
176     import derelictX = derelict.util.xtypes;
177 
178     import derelict.opengl3.glx;
179 
180     private static GraphicsContext current;
181 
182     T_glXCreateContextAttribsARB glXCreateContextAttribsARB;
183 
184     class GlContext {
185 
186         GraphicsContext handle;
187         WindowHandle window;
188         Display* display;
189 
190         this(WindowHandle window){
191             this(wm.displayHandle, window);
192         }
193 
194         this(Display* display, WindowHandle window){
195             this.window = window;
196             this.display = display;
197             XVisualInfo* graphicsInfo;
198             try {
199 
200         		wm.glCore = true;
201         		glXCreateContextAttribsARB = cast(T_glXCreateContextAttribsARB)
202                                              glXGetProcAddress("glXCreateContextAttribsARB");
203         		if(!glXCreateContextAttribsARB)
204         			wm.glCore = false;
205 
206                 if(!wm.glCore)
207                     throw new Exception("disabled");
208                 
209                 XWindowAttributes wa;
210                 assert(XGetWindowAttributes(display, window, &wa));
211 
212                 XVisualInfo visual;
213                 visual.screen   = DefaultScreen(display);
214                 visual.visualid = XVisualIDFromVisual(wa.visual);
215                 int visualCount = 0;
216                 graphicsInfo = XGetVisualInfo(display, VisualIDMask | VisualScreenMask, &visual, &visualCount);
217                 
218                 size_t config = size_t.max;
219 
220                 int nbConfigs = 0;
221                 GLXFBConfig* configs = glXChooseFBConfig(display, DefaultScreen(display), null, &nbConfigs);
222 
223                 foreach(i, fbc; configs[0..nbConfigs]){
224                     auto vis = cast(XVisualInfo*)glXGetVisualFromFBConfig(display, fbc);
225                     if(!vis)
226                         continue;
227                     scope(exit)
228                         XFree(vis);
229                     if(vis.visualid == visual.visualid){
230                         config = i;
231                         break;
232                     }
233                 }
234 
235                 import std.stdio;
236                 writeln(*graphicsInfo);
237 
238                 if(config == size_t.max)
239                     throw new Exception("Failed to get FB config");
240 
241                 int[] attribs = [
242                     GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
243                     GLX_CONTEXT_MINOR_VERSION_ARB, 3,
244                     0
245                 ];
246 
247                 handle = glXCreateContextAttribsARB(
248                         display, configs[config], null, cast(int)True, attribs.ptr
249                 );
250                 if(!handle)
251                     throw new Exception("glXCreateContextAttribsARB failed");
252 
253                 debug(glContextInfo){
254                     import std.stdio;
255                     writeln(graphicsInfo.depth);
256                 }
257 
258             }catch(Exception e){
259                 import std.stdio;
260                 writeln(e);
261                 GLint[] att = [GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_ALPHA_SIZE, 8, GLX_DOUBLEBUFFER, 0];
262                 graphicsInfo = cast(XVisualInfo*)glXChooseVisual(display, 0, att.ptr);
263                 handle = glXCreateContext(display, cast(derelictX.XVisualInfo*)graphicsInfo, null, True);
264                 if(!handle)
265                     throw new Exception("glXCreateContext failed");
266             }
267             glXMakeCurrent(display, cast(uint)window, cast(__GLXcontextRec*)handle);
268             current = handle;
269             DerelictGL3.reload();
270 
271         }
272 
273         void swapBuffers(){
274             glXSwapBuffers(display, cast(uint)window);
275         }
276 
277         template opDispatch(string s){
278             auto opDispatch(Args...)(Args args){
279                 if(current != handle){
280                     debug(glContextSwitch){
281                         import std.stdio;
282                         writeln("CONTEXT SWITCH %s -> %s".format(current, handle));
283                     }
284                     glXMakeCurrent(display, cast(uint)window, cast(__GLXcontextRec*)handle);
285                     current = handle;
286                 }
287                 assert(glXGetCurrentContext() == cast(__GLXcontextRec*)handle);
288                 debug { gl.check("?"); }
289                 enum s = s[0..1].capitalize ~ s[1..$];
290                 mixin("alias returns = ReturnType!(gl" ~ s ~ ");");
291                 static if(!is(returns == void)){
292                     mixin("auto result = gl" ~ s ~ "(args);");
293                     debug { gl.check("gl" ~ s); }
294                     return result;
295                 }else{
296                     mixin("gl" ~ s ~ "(args);");
297                     debug { gl.check("gl" ~ s); }
298                 }
299             }
300         }
301 
302     }
303 
304 }