1 /*
2  *  BSD 3-Clause License
3  *  
4  *  Copyright (c) 2016, Mango-Engine Team
5  *  All rights reserved.
6  *  
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are met:
9  *  
10  *  * Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *  
13  *  * Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *  
17  *  * Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from
19  *    this software without specific prior written permission.
20  *  
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 module mango_engine.graphics.window;
33 
34 import mango_engine.util;
35 import mango_engine.game;
36 import mango_engine.event.graphics;
37 import mango_engine.graphics.renderer;
38 
39 import mango_stl.misc;
40 
41 /// Represents different screen sync types
42 enum SyncType {
43     /// No sync
44     SYNC_NONE,
45     /// Vertical Sync (double/triple buffering)
46     SYNC_VSYNC,
47     /// Adaptive Sync (G-Sync, FreeSync)
48     SYNC_ADAPTIVE
49 }
50 
51 /// Thrown when there is an error creating the Window context.
52 class WindowContextFailedException : Exception {
53     this(in string message) {
54         super(message);
55     }   
56 }
57 
58 // TODO: Seperate input handling into a seperate InputManager?
59 alias InputHook = void delegate() @system;
60 
61 /// Backend interface class: represents a window.
62 abstract class Window {
63     immutable SyncType syncType;
64 
65     private shared GameManager _game;
66     private shared Renderer _renderer;
67 
68     private shared string _title;
69     private shared uint _width;
70     private shared uint _height;
71     private shared bool _visible = false;
72 
73     @property protected GameManager game() @trusted nothrow { return cast(GameManager) _game; }
74     @property protected Renderer renderer() @trusted nothrow { return cast(Renderer) _renderer; }
75 
76     /// The title of the Window.
77     @property string title() @safe nothrow { return _title; }
78     /// The title of the Window.
79     @property void title(in string title) @trusted {
80         _title = title; // TODO: concurrency fixes!
81         game.eventManager.fireEvent(new WindowTitleChangeEvent(title, this));
82         setTitle_(title);
83     }
84 
85     /// The width of the window in pixels.
86     @property uint width() @safe nothrow { return _width; }
87     /// The height of the window in pixels.
88     @property uint height() @safe nothrow { return _height; }
89     /// If the window is currently being displayed.
90     @property bool visible() @safe nothrow { return _visible; }
91     /// Show or hide the window.
92     @property void visible(bool visible) @trusted {
93         _visible = visible;
94         game.eventManager.fireEvent(visible ? new WindowShowEvent(this) : new WindowShowEvent(this));
95         setVisible_(visible);
96     }
97 
98     protected this(Renderer renderer, in string title, in uint width, in uint height, SyncType syncType) @trusted nothrow {
99         this._renderer = cast(shared) renderer;
100         this.syncType = syncType;
101 
102         this._title = title;
103         this._width = width;
104         this._height = height;
105     }
106 
107     static Window build(Renderer renderer, in string title, in uint width, in uint height, SyncType syncType) {
108         mixin(InterfaceClassFactory!("window", "Window", "renderer, title, width, height, syncType"));
109     }
110 
111     /++
112         FOR INTERNAL USE!!!
113 
114         Because a Window instance is created before a 
115         GameManager instance exists, the GameManager needs
116         to notify the Window class that it has been created,
117         and pass an instance of itself to the window.
118     +/
119     void gamemanager_notify(GameManager game) @trusted 
120     in {
121         assert(_game is null, "Tried to set GameManager when it was already set.");
122     } body {
123         this._game = cast(shared) game;
124         onGamemanager_notify();
125     }
126 
127     protected abstract void onGamemanager_notify() @system;
128     protected abstract void setTitle_(in string title) @system;
129     protected abstract void setVisible_(in bool visible) @system;
130     protected abstract void resize_(in uint width, in uint height) @system;
131 }