1 module gfm.sdl2.window;
2 
3 import std.string;
4 
5 import derelict.sdl2.sdl;
6 
7 import std.experimental.logger;
8 
9 import gfm.sdl2.sdl,
10        gfm.sdl2.surface,
11        gfm.sdl2.mouse,
12        gfm.sdl2.keyboard;
13 
14 
15 /// SDL Window wrapper.
16 /// There is two ways to receive events, either by polling a SDL2 object,
17 /// or by overriding the event callbacks.
18 final class SDL2Window
19 {
20     public
21     {
22         /// Creates a SDL window which targets a window.
23         /// See_also: $(LINK http://wiki.libsdl.org/SDL_CreateWindow)
24         /// Throws: $(D SDL2Exception) on error.
25         this(SDL2 sdl2, int x, int y, int width, int height, int flags)
26         {
27             _sdl2 = sdl2;
28             _logger = sdl2._logger;
29             _surface = null;
30             _glContext = null;
31             _surfaceMustBeRenewed = false;
32 
33             bool OpenGL = (flags & SDL_WINDOW_OPENGL) != 0;
34             _window = SDL_CreateWindow(toStringz(""), x, y, width, height, flags);
35             if (_window == null)
36 			{
37 				string message = "SDL_CreateWindow failed: " ~ _sdl2.getErrorString().idup;
38                 throw new SDL2Exception(message);
39 			}
40 
41             _id = SDL_GetWindowID(_window);
42 
43             if (OpenGL)
44                 _glContext = new SDL2GLContext(this);
45         }
46 
47         /// Creates a SDL window from anexisting handle.
48         /// See_also: $(LINK http://wiki.libsdl.org/SDL_CreateWindowFrom)
49         /// Throws: $(D SDL2Exception) on error.
50         this(SDL2 sdl2, void* windowData)
51         {
52             _sdl2 = sdl2;
53             _logger = sdl2._logger;
54             _surface = null;
55             _glContext = null;
56             _surfaceMustBeRenewed = false;
57              _window = SDL_CreateWindowFrom(windowData);
58              if (_window == null)
59              {
60                  string message = "SDL_CreateWindowFrom failed: " ~ _sdl2.getErrorString().idup;
61                  throw new SDL2Exception(message);
62              }
63 
64             _id = SDL_GetWindowID(_window);
65         }
66 
67 
68         /// Releases the SDL resource.
69         /// See_also: $(LINK http://wiki.libsdl.org/SDL_DestroyWindow)
70         ~this()
71         {
72             if (_glContext !is null)
73             {
74                 debug ensureNotInGC("SDL2Window");
75                 _glContext.destroy();
76                 _glContext = null;
77             }
78 
79             if (_window !is null)
80             {
81                 debug ensureNotInGC("SDL2Window");
82                 SDL_DestroyWindow(_window);
83                 _window = null;
84             }
85         }
86 
87         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowFullscreen)
88         /// Throws: $(D SDL2Exception) on error.
89         final void setFullscreenSetting(uint flags)
90         {
91             if (SDL_SetWindowFullscreen(_window, flags) != 0)
92                 _sdl2.throwSDL2Exception("SDL_SetWindowFullscreen");
93         }
94 
95         /// Returns: X window coordinate.
96         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
97         final int getX()
98         {
99             return getPosition().x;
100         }
101 
102         /// Returns: Y window coordinate.
103         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
104         final int getY()
105         {
106             return getPosition().y;
107         }
108 
109         /// Returns: Window coordinates.
110         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
111         final SDL_Point getPosition()
112         {
113             int x, y;
114             SDL_GetWindowPosition(_window, &x, &y);
115             return SDL_Point(x, y);
116         }
117 
118         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowPosition)
119         final void setPosition(int positionX, int positionY)
120         {
121             SDL_SetWindowPosition(_window, positionX, positionY);
122         }
123 
124         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowSize)
125         final void setSize(int width, int height)
126         {
127             SDL_SetWindowSize(_window, width, height);
128         }
129 
130         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
131         /// Returns: Window size in pixels.
132         final SDL_Point getSize()
133         {
134             int w, h;
135             SDL_GetWindowSize(_window, &w, &h);
136             return SDL_Point(w, h);
137         }
138 
139         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowIcon)
140         final void setIcon(SDL2Surface icon)
141         {
142             SDL_SetWindowIcon(_window, icon.handle());
143         }
144 
145         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowBordered)
146         final void setBordered(bool bordered)
147         {
148             SDL_SetWindowBordered(_window, bordered ? SDL_TRUE : SDL_FALSE);
149         }
150 
151         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
152         /// Returns: Window width in pixels.
153         final int getWidth()
154         {
155             int w, h;
156             SDL_GetWindowSize(_window, &w, &h);
157             return w;
158         }
159 
160         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
161         /// Returns: Window height in pixels.
162         final int getHeight()
163         {
164             int w, h;
165             SDL_GetWindowSize(_window, &w, &h);
166             return h;
167         }
168 
169         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowTitle)
170         final void setTitle(string title)
171         {
172             SDL_SetWindowTitle(_window, toStringz(title));
173         }
174 
175         /// See_also: $(LINK http://wiki.libsdl.org/SDL_ShowWindow)
176         final void show()
177         {
178             SDL_ShowWindow(_window);
179         }
180 
181         /// See_also: $(LINK http://wiki.libsdl.org/SDL_HideWindow)
182         final void hide()
183         {
184             SDL_HideWindow(_window);
185         }
186 
187         /// See_also: $(LINK http://wiki.libsdl.org/SDL_MinimizeWindow)
188         final void minimize()
189         {
190             SDL_MinimizeWindow(_window);
191         }
192 
193         /// See_also: $(LINK http://wiki.libsdl.org/SDL_MaximizeWindow)
194         final void maximize()
195         {
196             SDL_MaximizeWindow(_window);
197         }
198 
199         /// Returns: Window surface.
200         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSurface)
201         /// Throws: $(D SDL2Exception) on error.
202         final SDL2Surface surface()
203         {
204             if (!hasValidSurface())
205             {
206                 SDL_Surface* internalSurface = SDL_GetWindowSurface(_window);
207                 if (internalSurface is null)
208                     _sdl2.throwSDL2Exception("SDL_GetWindowSurface");
209 
210                 // renews surface as needed
211                 _surfaceMustBeRenewed = false;
212                 _surface = new SDL2Surface(_sdl2, internalSurface,  SDL2Surface.Owned.NO);
213             }
214             return _surface;
215         }
216 
217         /// Submit changes to the window surface.
218         /// See_also: $(LINK http://wiki.libsdl.org/SDL_UpdateWindowSurface)
219         /// Throws: $(D SDL2Exception) on error.
220         final void updateSurface()
221         {
222             if (!hasValidSurface())
223                 surface();
224 
225             int res = SDL_UpdateWindowSurface(_window);
226             if (res != 0)
227                 _sdl2.throwSDL2Exception("SDL_UpdateWindowSurface");
228 
229         }
230 
231         /// Returns: Window ID.
232         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowID)
233         final int id()
234         {
235             return _id;
236         }
237 
238         /// Returns: System-specific window information, useful to use a third-party rendering library.
239         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowWMInfo)
240         /// Throws: $(D SDL2Exception) on error.
241         SDL_SysWMinfo getWindowInfo()
242         {
243             SDL_SysWMinfo info;
244             SDL_VERSION(&info.version_);
245             int res = SDL_GetWindowWMInfo(_window, &info);
246             if (res != SDL_TRUE)
247                 _sdl2.throwSDL2Exception("SDL_GetWindowWMInfo");
248             return info;
249         }
250 
251         /// Swap OpenGL buffers.
252         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GL_SwapWindow)
253         /// Throws: $(D SDL2Exception) on error.
254         void swapBuffers()
255         {
256             if (_glContext is null)
257                 throw new SDL2Exception("swapBuffers failed: not an OpenGL window");
258             SDL_GL_SwapWindow(_window);
259         }
260     }
261 
262     package
263     {
264         SDL2 _sdl2;
265         SDL_Window* _window;
266     }
267 
268     private
269     {
270         Logger _logger;
271         SDL2Surface _surface;
272         SDL2GLContext _glContext;
273         uint _id;
274 
275         bool _surfaceMustBeRenewed;
276 
277         bool hasValidSurface()
278         {
279             return (!_surfaceMustBeRenewed) && (_surface !is null);
280         }
281     }
282 }
283 
284 /// SDL OpenGL context wrapper. You probably don't need to use it directly.
285 final class SDL2GLContext
286 {
287     public
288     {
289         /// Creates an OpenGL context for a given SDL window.
290         this(SDL2Window window)
291         {
292             _window = window;
293             _context = SDL_GL_CreateContext(window._window);
294             _initialized = true;
295         }
296 
297         ~this()
298         {
299             close();
300         }
301 
302         /// Release the associated SDL ressource.
303         void close()
304         {
305             if (_initialized)
306             {
307                 // work-around Issue #19
308                 // SDL complains with log message "wglMakeCurrent(): The handle is invalid."
309                 // in the SDL_DestroyWindow() call if we destroy the OpenGL context before-hand
310                 //
311                 // SDL_GL_DeleteContext(_context);
312                 _initialized = false;
313             }
314         }
315 
316         /// Makes this OpenGL context current.
317         /// Throws: $(D SDL2Exception) on error.
318         void makeCurrent()
319         {
320             if (0 != SDL_GL_MakeCurrent(_window._window, _context))
321                 _window._sdl2.throwSDL2Exception("SDL_GL_MakeCurrent");
322         }
323     }
324 
325     package
326     {
327         SDL_GLContext _context;
328         SDL2Window _window;
329     }
330 
331     private
332     {
333         bool _initialized;
334     }
335 }
336