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.sdl2.sdl,
10        gfm.sdl2.surface;
11 
12 /// SDL Window wrapper.
13 /// There is two ways to receive events, either by polling a SDL2 object, 
14 /// or by overriding the event callbacks.
15 class SDL2Window
16 {
17     public
18     {
19         /// Creates a SDL window which targets a window.
20         /// See_also: $(LINK http://wiki.libsdl.org/SDL_CreateWindow)
21         /// Throws: $(D SDL2Exception) on error.
22         this(SDL2 sdl2, int x, int y, int width, int height, int flags)
23         {
24             _sdl2 = sdl2;
25             _logger = sdl2._logger;
26             _surface = null;
27             _glContext = null;
28             _surfaceMustBeRenewed = false;
29 
30             bool OpenGL = (flags & SDL_WINDOW_OPENGL) != 0;
31 
32             if (OpenGL)
33             {
34                 // put here your desired context profile and version
35 
36                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
37                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
38                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
39                 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
40 
41                 // force debug OpenGL context creation in debug mode
42                 debug
43                 {
44                     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
45                 }
46             }
47 
48             _window = SDL_CreateWindow(toStringz(""), x, y, width, height, flags);
49             if (_window == null)
50                 throw new SDL2Exception("SDL_CreateWindow failed: " ~ _sdl2.getErrorString());
51 
52             _id = SDL_GetWindowID(_window);
53 
54             // register window for event dispatch
55             _sdl2.registerWindow(this);
56 
57             if (OpenGL)
58                 _glContext = new SDL2GLContext(this);
59         }
60 
61         /// Releases the SDL resource.
62         /// See_also: $(LINK http://wiki.libsdl.org/SDL_DestroyWindow)
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         ///
80         ~this()
81         {
82             close();
83         }
84 
85         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowFullscreen)
86         /// Throws: $(D SDL2Exception) on error.
87         deprecated("Use setFullscreenSetting instead") final void setFullscreen(bool fullscreen)
88         {
89             SDL_SetWindowFullscreen(_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
90         }
91 
92         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowFullscreen)
93         /// Throws: $(D SDL2Exception) on error.
94         final void setFullscreenSetting(uint flags)
95         {
96             if (SDL_SetWindowFullscreen(_window, flags) != 0)
97                 _sdl2.throwSDL2Exception("SDL_SetWindowFullscreen");
98         }
99 
100         /// Returns: X window coordinate.
101         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
102         final int getX()
103         {
104             return getPosition().x;
105         }
106 
107         /// Returns: Y window coordinate.
108         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
109         final int getY()
110         {
111             return getPosition().y;
112         }
113 
114         /// Returns: Window coordinates.
115         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
116         final SDL_Point getPosition()
117         {
118             int x, y;
119             SDL_GetWindowPosition(_window, &x, &y);
120             return SDL_Point(x, y);
121         }
122 
123         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowPosition)
124         final void setPosition(int positionX, int positionY)
125         {
126             SDL_SetWindowPosition(_window, positionX, positionY);
127         }
128 
129         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowSize)
130         final void setSize(int width, int height)
131         {
132             SDL_SetWindowSize(_window, width, height);
133         }
134 
135         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
136         /// Returns: Window size in pixels.
137         final SDL_Point getSize()
138         {
139             int w, h;
140             SDL_GetWindowSize(_window, &w, &h);
141             return SDL_Point(w, h);
142         }
143 
144         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
145         /// Returns: Window width in pixels.
146         final int getWidth()
147         {
148             int w, h;
149             SDL_GetWindowSize(_window, &w, &h);
150             return w;
151         }
152 
153         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
154         /// Returns: Window height in pixels.
155         final int getHeight()
156         {
157             int w, h;
158             SDL_GetWindowSize(_window, &w, &h);
159             return h;
160         }
161 
162         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowTitle)
163         final void setTitle(string title)
164         {
165             SDL_SetWindowTitle(_window, toStringz(title));
166         }
167 
168         /// See_also: $(LINK http://wiki.libsdl.org/SDL_ShowWindow)
169         final void show()
170         {
171             SDL_ShowWindow(_window);
172         }
173 
174         /// See_also: $(LINK http://wiki.libsdl.org/SDL_HideWindow)
175         final void hide()
176         {
177             SDL_HideWindow(_window);
178         }
179 
180         /// See_also: $(LINK http://wiki.libsdl.org/SDL_MinimizeWindow)
181         final void minimize()
182         {
183             SDL_MinimizeWindow(_window);
184         }
185 
186         /// See_also: $(LINK http://wiki.libsdl.org/SDL_MaximizeWindow)
187         final void maximize()
188         {
189             SDL_MaximizeWindow(_window);
190         }
191 
192         /// Returns: Window surface.
193         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSurface)
194         /// Throws: $(D SDL2Exception) on error.
195         final SDL2Surface surface()
196         {
197             if (!hasValidSurface())
198             {
199                 SDL_Surface* internalSurface = SDL_GetWindowSurface(_window);
200                 if (internalSurface is null)
201                     _sdl2.throwSDL2Exception("SDL_GetWindowSurface");
202 
203                 // renews surface as needed
204                 _surfaceMustBeRenewed = false;
205                 _surface = new SDL2Surface(_sdl2, internalSurface,  SDL2Surface.Owned.NO);
206             }
207             return _surface;
208         }
209 
210         /// Submit changes to the window surface.
211         /// See_also: $(LINK http://wiki.libsdl.org/SDL_UpdateWindowSurface)
212         /// Throws: $(D SDL2Exception) on error.
213         final void updateSurface()
214         {
215             if (!hasValidSurface())
216                 surface();
217 
218             int res = SDL_UpdateWindowSurface(_window);
219             if (res != 0)
220                 _sdl2.throwSDL2Exception("SDL_UpdateWindowSurface");
221             
222         }
223 
224         /// Returns: Window ID.
225         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowID)
226         final int id()
227         {
228             return _id;
229         }
230 
231         /// Returns: System-specific window information, useful to use a third-party rendering library.
232         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowWMInfo)
233         /// Throws: $(D SDL2Exception) on error.
234         SDL_SysWMinfo getWindowInfo()
235         {
236             SDL_SysWMinfo info;
237             SDL_VERSION(&info.version_);
238             int res = SDL_GetWindowWMInfo(_window, &info);
239             if (res != SDL_TRUE)
240                 _sdl2.throwSDL2Exception("SDL_GetWindowWMInfo");
241             return info;
242         }
243 
244         // override these function, they are event callbacks
245 
246         ///
247         void onShow()
248         {
249         }
250 
251         ///
252         void onHide()
253         {
254         }
255 
256         ///
257         void onExposed()
258         {
259             _surfaceMustBeRenewed = true;
260         }
261 
262         ///
263         void onMove(int x, int y)
264         {        
265         }
266         
267         ///
268         void onResized(int width, int height)
269         {
270             _surfaceMustBeRenewed = true;
271         }
272 
273         ///
274         void onSizeChanged()
275         {
276             _surfaceMustBeRenewed = true;
277         }
278 
279         ///
280         void onMinimized()
281         {
282             _surfaceMustBeRenewed = true;
283         }
284 
285         ///
286         void onMaximized()
287         {
288             _surfaceMustBeRenewed = true;
289         }
290 
291         ///
292         void onRestored()
293         {            
294         }
295 
296         ///
297         void onEnter()
298         {
299         }
300         
301         ///
302         void onLeave()
303         {
304         }
305 
306         ///
307         void onFocusGained()
308         {
309         }
310 
311         ///
312         void onFocusLost()
313         {
314         }
315         
316         ///
317         void onClose()
318         {
319         }
320 
321         /// Swap OpenGL buffers.
322         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GL_SwapWindow)
323         /// Throws: $(D SDL2Exception) on error.
324         void swapBuffers()
325         {
326             if (_glContext is null)
327                 throw new SDL2Exception("swapBuffers failed: not an OpenGL window");
328             SDL_GL_SwapWindow(_window);
329         }
330     }
331 
332     package
333     {
334         SDL2 _sdl2;
335         SDL_Window* _window;
336     }
337 
338     private
339     {
340         Logger _logger;
341         SDL2Surface _surface;
342         SDL2GLContext _glContext;
343         uint _id;
344 
345         bool _surfaceMustBeRenewed;
346 
347         bool hasValidSurface()
348         {
349             return (!_surfaceMustBeRenewed) && (_surface !is null);
350         }
351     }
352 }
353 
354 /// SDL OpenGL context wrapper. You probably don't need to use it directly.
355 final class SDL2GLContext
356 {
357     public
358     {
359         /// Creates for a given SDL window. 
360         this(SDL2Window window)
361         {
362             _window = window;
363             _context = SDL_GL_CreateContext(window._window);
364             _initialized = true;
365         }
366 
367         ~this()
368         {
369             close();
370         }
371 
372         /// Release the associated SDL ressource.
373         void close()
374         {
375             if (_initialized)
376             {
377                 // work-around Issue #19
378                 // SDL complains with log message "wglMakeCurrent(): The handle is invalid."
379                 // in the SDL_DestroyWindow() call if we destroy the OpenGL context before-hand
380                 //
381                 // SDL_GL_DeleteContext(_context);
382                 _initialized = false;
383             }
384         }
385 
386         /// Makes this OpenGL context current.
387         /// Throws: $(D SDL2Exception) on error.
388         void makeCurrent()
389         {
390             if (0 != SDL_GL_MakeCurrent(_window._window, _context))
391                 _window._sdl2.throwSDL2Exception("SDL_GL_MakeCurrent");
392         }
393     }
394 
395     package
396     {
397         SDL_GLContext _context;
398         SDL2Window _window;
399     }
400 
401     private
402     {
403         bool _initialized;
404     }
405 }
406