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