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