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