1 module gfm.sdl2.window;
2 
3 import std.string;
4 
5 import derelict.sdl2.sdl;
6 
7 import std.logger;
8 
9 import gfm.math.vector,
10        gfm.math.box,
11        gfm.sdl2.sdl,
12        gfm.sdl2.surface;
13 
14 /// SDL Window wrapper.
15 /// There is two ways to receive events, either by polling a SDL2 object, 
16 /// or by overriding the event callbacks.
17 class SDL2Window
18 {
19     public
20     {
21         /// Initially invisible.
22         /// Accepts the same constants as the SDL2 function.
23         this(SDL2 sdl2, int x, int y, int width, int height, int flags)
24         {
25             _sdl2 = sdl2;
26             _logger = sdl2._logger;
27             _surface = null;
28             _glContext = null;
29             _surfaceMustBeRenewed = false;
30 
31             bool OpenGL = (flags & SDL_WINDOW_OPENGL) != 0;
32 
33             if (OpenGL)
34             {
35                 // put here your desired context profile and version
36 
37                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
38                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
39                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
40                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
41 
42                 // force debug OpenGL context creation in debug mode
43                 debug
44                 {
45                     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
46                 }
47             }
48 
49             _window = SDL_CreateWindow(toStringz(""), x, y, width, height, flags);
50             if (_window == null)
51                 throw new SDL2Exception("SDL_CreateWindow failed: " ~ _sdl2.getErrorString());
52 
53             _id = SDL_GetWindowID(_window);
54 
55             // register window for event dispatch
56             _sdl2.registerWindow(this);
57 
58             if (OpenGL)
59                 _glContext = new SDL2GLContext(this);
60         }
61 
62         /// Releases the SDL resource.
63         final void close()
64         {
65             if (_glContext !is null)
66             {
67                 _glContext.close();
68                 _glContext = null;
69             }
70 
71             if (_window !is null)
72             {
73                 _sdl2.unregisterWindow(this);
74                 SDL_DestroyWindow(_window);
75                 _window = null;
76             }
77         }
78 
79         ~this()
80         {
81             close();
82         }
83 
84         final void setFullscreen(bool activated)
85         {
86             SDL_SetWindowFullscreen(_window, activated ? SDL_WINDOW_FULLSCREEN : 0);
87         }
88 
89         final void setPosition(vec2i position)
90         {
91             SDL_SetWindowPosition(_window, position.x, position.y);
92         }
93 
94         final void setSize(vec2i size)
95         {
96             SDL_SetWindowSize(_window, size.x, size.y);
97         }
98 
99         final vec2i getSize()
100         {
101             int w, h;
102             SDL_GetWindowSize(_window, &w, &h);
103             return vec2i(w, h);
104         }
105 
106         final void setTitle(string title)
107         {
108             SDL_SetWindowTitle(_window, toStringz(title));
109         }
110 
111         final void show()
112         {
113             SDL_ShowWindow(_window);
114         }
115 
116         final void hide()
117         {
118             SDL_HideWindow(_window);
119         }
120 
121         final void minimize()
122         {
123             SDL_MinimizeWindow(_window);
124         }
125 
126         final void maximize()
127         {
128             SDL_MaximizeWindow(_window);
129         }
130 
131         final SDL2Surface surface()
132         {
133             if (!hasValidSurface())
134             {
135                 SDL_Surface* internalSurface = SDL_GetWindowSurface(_window);
136                 if (internalSurface is null)
137                     _sdl2.throwSDL2Exception("SDL_GetWindowSurface");
138 
139                 // renews surface as needed
140                 _surfaceMustBeRenewed = false;
141                 _surface = new SDL2Surface(_sdl2, internalSurface,  SDL2Surface.Owned.NO);
142             }
143             return _surface;
144         }
145 
146         final void updateSurface()
147         {
148             if (!hasValidSurface())
149                 surface();
150 
151             int res = SDL_UpdateWindowSurface(_window);
152             if (res != 0)
153                 _sdl2.throwSDL2Exception("SDL_UpdateWindowSurface");
154             
155         }
156 
157         final int id()
158         {
159             return _id;
160         }
161 
162         SDL_SysWMinfo getWindowInfo()
163         {
164             SDL_SysWMinfo info;
165             int res = SDL_GetWindowWMInfo(_window, &info);
166             if (res != SDL_TRUE)
167                 _sdl2.throwSDL2Exception("SDL_GetWindowWMInfo");
168             return info;
169         }
170 
171         // override these function, they are event callbacks
172 
173         void onShow()
174         {
175         }
176 
177         void onHide()
178         {
179         }
180 
181         void onExposed()
182         {
183             _surfaceMustBeRenewed = true;
184         }
185 
186         void onMove(int x, int y)
187         {        
188         }
189         
190         void onResized(int width, int height)
191         {
192             _surfaceMustBeRenewed = true;
193         }
194 
195         void onSizeChanged()
196         {
197             _surfaceMustBeRenewed = true;
198         }
199 
200         void onMinimized()
201         {
202             _surfaceMustBeRenewed = true;
203         }
204 
205         void onMaximized()
206         {
207             _surfaceMustBeRenewed = true;
208         }
209 
210         void onRestored()
211         {            
212         }
213 
214         void onEnter()
215         {
216         }
217         
218         void onLeave()
219         {
220         }
221         
222         void onFocusGained()
223         {
224         }
225 
226         void onFocusLost()
227         {
228         }
229         
230         void onClose()
231         {
232         }
233 
234         void swapBuffers()
235         {
236             if (_glContext is null)
237                 throw new SDL2Exception("swapBuffers failed: not an OpenGL window");
238             SDL_GL_SwapWindow(_window);
239         }
240     }
241 
242     package
243     {
244         SDL2 _sdl2;
245         SDL_Window* _window;
246     }
247 
248     private
249     {
250         Logger _logger;
251         SDL2Surface _surface;
252         SDL2GLContext _glContext;
253         uint _id;
254 
255         bool _surfaceMustBeRenewed;
256 
257         bool hasValidSurface()
258         {
259             return (!_surfaceMustBeRenewed) && (_surface !is null);
260         }
261     }
262 }
263 
264 /// SDL OpenGL context wrapper.
265 final class SDL2GLContext
266 {
267     public
268     {
269         this(SDL2Window window)
270         {
271             _window = window;
272             _context = SDL_GL_CreateContext(window._window);
273             _initialized = true;
274         }
275 
276         ~this()
277         {
278             close();
279         }
280 
281         void close()
282         {
283             if (_initialized)
284             {
285                 // work-around Issue #19
286                 // SDL complains with log message "wglMakeCurrent(): The handle is invalid."
287                 // in the SDL_DestroyWindow() call if we destroy the OpenGL context before-hand
288                 //
289                 // SDL_GL_DeleteContext(_context);
290                 _initialized = false;
291             }
292         }
293 
294         void makeCurrent()
295         {
296             if (0 != SDL_GL_MakeCurrent(_window._window, _context))
297                 _window._sdl2.throwSDL2Exception("SDL_GL_MakeCurrent");
298         }
299     }
300 
301     package
302     {
303         SDL_GLContext _context;
304         SDL2Window _window;
305     }
306 
307     private
308     {
309         bool _initialized;
310     }
311 }
312