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