1 module framed;
2 import framed.win32;
3 import core.stdc.stdlib;
4 
5 version (Windows) {
6 	enum FramedWindowSupport = true;
7 }
8 else {
9 	enum FramedWindowSupport = false;
10 }
11 
12 nothrow:
13 
14 package enum FramebufferType {
15 	Null,
16 	Window,
17 }
18 
19 package struct WindowData {
20 nothrow:
21 
22 	void* udata;
23 	int function(void*) getWidth;
24 	int function(void*) getHeight;
25 	void function(void*) close;
26 	void function(void*, uint[]) update;
27 	void function(void*) yield;
28 	bool function(void*) evqEmpty;
29 	Event function(void*) evqFront;
30 	void function(void*) evqPopFront;
31 }
32 
33 enum EventType {
34 	CloseRequest,
35 	Resize,
36 	KeyDown,
37 	KeyRepeat,
38 	KeyUp,
39 	MouseMove,
40 	MouseDown,
41 	MouseUp,
42 	MouseEnter,
43 	MouseLeave,
44 }
45 
46 enum MouseButton {
47 	Left,
48 	Middle,
49 	Right,
50 }
51 
52 // dfmt off
53 enum KeyCode {
54 	Space,
55 	Quote,
56 	Comma,
57 	Minus,
58 	Period,
59 	Slash,
60 	D0, D1, D2, D3, D4, D5, D6, D7, D8, D9,
61 	Semicolon,
62 	Equal,
63 	A, B, C, D, E, F, G, H, I, J, K, L, M,
64 	N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
65 	LeftBracket,
66 	Backslash,
67 	RightBracket,
68 	Backtick,
69 	Escape,
70 	Enter,
71 	Tab,
72 	Backspace,
73 	Insert,
74 	Delete,
75 	Right,
76 	Left,
77 	Down,
78 	Up,
79 	PageUp,
80 	PageDown,
81 	Home,
82 	End,
83 	CapsLock,
84 	ScrollLock,
85 	NumLock,
86 	PrintScreen,
87 	Pause,
88 	F1, F2, F3, F4, F5, F6, F7, F8, F9, F10,
89 	F11, F12, F13, F14, F15, F16, F17, F18,
90 	F19, F20, F21, F22, F23, F24, F25,
91 	Numpad0, Numpad1, Numpad2,
92 	Numpad3, Numpad4, Numpad5,
93 	Numpad6, Numpad7, Numpad8,
94 	Numpad9, NumpadPeriod, NumpadSlash, NumpadMultiply,
95 	NumpadMinus, NumpadPlus, NumpadEnter, NumpadEqual,
96 	LeftShift, LeftCtrl, LeftAlt, LeftSuper,
97 	RightShift, RightCtrl, RightAlt, RightSuper,
98 	Menu,
99 }
100 // dfmt on
101 
102 struct Event {
103 nothrow:
104 	EventType type;
105 	long a;
106 	long b;
107 
108 	int width() const @property {
109 		return cast(int)(a & 0xFFFFFFFF);
110 	}
111 
112 	void width(int value) @property {
113 		a = (cast(long) height << 32L) | cast(long) value;
114 	}
115 
116 	int height() const @property {
117 		return cast(int)(a >> 32);
118 	}
119 
120 	void height(int value) @property {
121 		a = (cast(long) value << 32L) | cast(long) width;
122 	}
123 
124 	int x() const @property {
125 		return width;
126 	}
127 
128 	void x(int value) @property {
129 		width = value;
130 	}
131 
132 	int y() const @property {
133 		return height;
134 	}
135 
136 	void y(int value) @property {
137 		height = value;
138 	}
139 
140 	MouseButton button() const @property {
141 		return cast(MouseButton) b;
142 	}
143 
144 	void button(MouseButton value) @property {
145 		b = cast(long) value;
146 	}
147 
148 	KeyCode key() const @property {
149 		return cast(KeyCode) b;
150 	}
151 
152 	void key(KeyCode value) @property {
153 		b = cast(long) value;
154 	}
155 }
156 
157 package struct EventRange {
158 nothrow:
159 
160 	Framebuffer buffer;
161 
162 	bool empty() {
163 		final switch (buffer.type) {
164 		case FramebufferType.Null:
165 			return true;
166 		case FramebufferType.Window:
167 			return buffer.window.evqEmpty(buffer.window.udata);
168 		}
169 	}
170 
171 	Event front() {
172 		switch (buffer.type) {
173 		case FramebufferType.Window:
174 			return buffer.window.evqFront(buffer.window.udata);
175 		default:
176 			assert(0);
177 		}
178 	}
179 
180 	void popFront() {
181 		final switch (buffer.type) {
182 		case FramebufferType.Null:
183 			break;
184 		case FramebufferType.Window:
185 			buffer.window.evqPopFront(buffer.window.udata);
186 			break;
187 		}
188 	}
189 
190 }
191 
192 struct Framebuffer {
193 nothrow:
194 
195 	private FramebufferType type = FramebufferType.Null;
196 	private void* data;
197 	private int* count;
198 
199 	package this(FramebufferType type, void* data) {
200 		this.type = type;
201 		this.data = data;
202 		count = cast(int*) malloc(int.sizeof);
203 		*count = 1;
204 	}
205 
206 	this(ref return scope inout(Framebuffer) rhs) inout {
207 		type = rhs.type;
208 		data = rhs.data;
209 		count = rhs.count;
210 		*cast(int*) count += 1;
211 	}
212 
213 	~this() {
214 		if (count == null)
215 			return;
216 		*count -= 1;
217 		if (*count == 0) {
218 			final switch (type) {
219 			case FramebufferType.Null:
220 				break;
221 			case FramebufferType.Window:
222 				window.close(window.udata);
223 			}
224 			free(data);
225 			free(count);
226 		}
227 	}
228 
229 	private WindowData* window() {
230 		return cast(WindowData*) data;
231 	}
232 
233 	int width() {
234 		final switch (type) {
235 		case FramebufferType.Null:
236 			return 0;
237 		case FramebufferType.Window:
238 			return window.getWidth(window.udata);
239 		}
240 	}
241 
242 	int height() {
243 		final switch (type) {
244 		case FramebufferType.Null:
245 			return 0;
246 		case FramebufferType.Window:
247 			return window.getHeight(window.udata);
248 		}
249 	}
250 
251 	void update(uint[] buffer) {
252 		assert(buffer.length == width * cast(size_t) height, "Wrong buffer size");
253 		final switch (type) {
254 		case FramebufferType.Null:
255 			break;
256 		case FramebufferType.Window:
257 			window.update(window.udata, buffer);
258 			break;
259 		}
260 	}
261 
262 	void yield() {
263 		final switch (type) {
264 		case FramebufferType.Null:
265 			break;
266 		case FramebufferType.Window:
267 			window.yield(window.udata);
268 			break;
269 		}
270 	}
271 
272 	EventRange eventQueue() {
273 		return EventRange(this);
274 	}
275 
276 }
277 
278 struct WindowOptions {
279 nothrow:
280 
281 	int initialWidth = 640;
282 	int initialHeight = 480;
283 	bool resizable = true;
284 	string title = "Untitled window";
285 
286 	this(string title) {
287 		this.title = title;
288 	}
289 
290 	this(int initialWidth, int initialHeight) {
291 		this.initialWidth = initialWidth;
292 		this.initialHeight = initialHeight;
293 	}
294 
295 	this(string title, int initialWidth, int initialHeight) {
296 		this.title = title;
297 		this.initialWidth = initialWidth;
298 		this.initialHeight = initialHeight;
299 	}
300 }
301 
302 static if (FramedWindowSupport) {
303 	Framebuffer openWindow(WindowOptions options) {
304 		version (Windows) {
305 			return win32OpenWindow(options);
306 		}
307 		else {
308 			static assert(0);
309 		}
310 	}
311 
312 	Framebuffer openWindow(Args...)(Args args) {
313 		return openWindow(WindowOptions(args));
314 	}
315 }