1 // FIXME: the audio thread needs to trigger an event in the event of its death too
2 
3 // i could add a "time" uniform for the shaders automatically. unity does a float4 i think with ticks in it
4 // register cheat code? or even a fighting game combo..
5 /++
6 	An add-on for simpledisplay.d, joystick.d, and simpleaudio.d
7 	that includes helper functions for writing simple games (and perhaps
8 	other multimedia programs). Whereas simpledisplay works with
9 	an event-driven framework, arsd.game always uses a consistent
10 	timer for updates.
11 
12 	$(PITFALL
13 		I AM NO LONGER HAPPY WITH THIS INTERFACE AND IT WILL CHANGE.
14 
15 		At least, I am going to change the delta time over to drawFrame
16 		for fractional interpolation while keeping the time step fixed.
17 
18 		If you depend on it the way it is, you'll want to fork.
19 	)
20 
21 
22 	The general idea is you provide a game class which implements a minimum of
23 	three functions: `update`, `drawFrame`, and `getWindow`. Your main function
24 	calls `runGame!YourClass();`.
25 
26 	`getWindow` is called first. It is responsible for creating the window and
27 	initializing your setup. Then the game loop is started, which will call `update`,
28 	to update your game state, and `drawFrame`, which draws the current state.
29 
30 	`update` is called on a consistent timer. It should always do exactly one delta-time
31 	step of your game work and the library will ensure it is called often enough to keep
32 	game time where it should be with real time. `drawFrame` will be called when an opportunity
33 	arises, possibly more or less often than `update` is called. `drawFrame` gets an argument
34 	telling you how close it is to the next `update` that you can use for interpolation.
35 
36 	How, exactly, you decide to draw and update is up to you, but I strongly recommend that you
37 	keep your game state inside the game class, or at least accessible from it. In other words,
38 	avoid using global and static variables.
39 
40 	It might be easier to understand by example. Behold:
41 
42 	---
43 	import arsd.game;
44 
45 	final class MyGame : GameHelperBase {
46 		/// Called when it is time to redraw the frame. The interpolate member
47 		/// tells you the fraction of an update has passed since the last update
48 		/// call; you can use this to make smoother animations if you like.
49 		override void drawFrame(float interpolate) {
50 			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);
51 
52 			glLoadIdentity();
53 
54 			glColor3f(1.0, 1.0, 1.0);
55 			glTranslatef(x, y, 0);
56 			glBegin(GL_QUADS);
57 
58 			glVertex2i(0, 0);
59 			glVertex2i(16, 0);
60 			glVertex2i(16, 16);
61 			glVertex2i(0, 16);
62 
63 			glEnd();
64 		}
65 
66 		int x, y;
67 		override bool update() {
68 			x += 1;
69 			y += 1;
70 			return true;
71 		}
72 
73 		override SimpleWindow getWindow() {
74 			// if you want to use OpenGL 3 or nanovega or whatever, you can set it up in here too.
75 			auto window = create2dWindow("My game");
76 			// load textures and such here
77 			return window;
78 		}
79 	}
80 
81 	void main() {
82 		runGame!MyGame(20 /*targetUpdateRate - shoot for 20 updates per second of game state*/);
83 		// please note that it can draw faster than this; updates should be less than drawn frames per second.
84 	}
85 	---
86 
87 	Of course, this isn't much of a game, since there's no input. The [GameHelperBase] provides a few ways for your
88 	`update` function to check for user input: you can check the current state of and transition since last update
89 	of a SNES-style [VirtualController] through [GameHelperBase.snes], or the computer keyboard and mouse through
90 	[GameHelperBase.keyboardState] and (FIXME: expose mouse). Touch events are not implemented at this time and I have
91 	no timetable for when they will be, but I do want to add them at some point.
92 
93 	The SNES controller is great if your game can work with it because it will automatically map to various gamepads
94 	as well as to the standard computer keyboard. This gives the user a lot of flexibility in how they control the game.
95 	If it doesn't though, you can try the other models. However, I don't recommend you try to mix them in the same game mode,
96 	since you wouldn't want a user to accidentally trigger the controller while trying to type their name, for example.
97 
98 	If you just do the basics here, you'll have a working basic game. You can also get additional
99 	features by implementing more functions, like `override bool wantAudio() { return true; } ` will
100 	enable audio, for example. You can then trigger sounds and music to play in your `update` function.
101 
102 	Let's expand the example to show this:
103 
104 	// FIXME: paste in game2.d contents here
105 
106 	A game usually isn't just one thing, and it might help to separate these out. I call these [GameScreen]s.
107 	The name might not be perfect, but the idea is that even a basic game might still have, for example, a
108 	title screen and a gameplay screen. These are likely to have different controls, different drawing, and some
109 	different state.
110 
111 
112 	The MyGame handler is actually a template, so you don't have virtual
113 	function indirection and not all functions are required. The interfaces
114 	are just to help you get the signatures right, they don't force virtual
115 	dispatch at runtime.
116 
117 	$(H2 Input)
118 
119 	In the overview, I mentioned that there's input available through a few means. Among the functions are:
120 
121 	Gamepads, mouse buttons, and keyboards:
122 		wasPressed - returns true if the button was not pressed but became pressed over the update period.
123 		wasReleased - returns true if the button was pressed, but was released over the update period
124 		wasClicked - returns true if the button was released but became pressed and released again since you last asked without much other movement in between
125 		isHeld - returns true if the button is currently held down
126 	Gamepad specific (remember the keyboard emulates a basic gamepad):
127 		startRecordingButtons - starts recording buttons
128 		getRecordedButtons - gets the sequence of button presses with associated times
129 		stopRecordingButtons - stops recording buttons
130 
131 		You might use this to check for things like cheat codes and fighting game style special moves.
132 	Keyboard-specific:
133 		startRecordingCharacters - starts recording keyboard character input
134 		getRecordedCharacters - returns the characters typed since you started recording characters
135 		stopRecordingCharacters - stops recording characters and clears the recording
136 
137 		You might use this for taking input for chat or character name selection.
138 	Mouse and joystick:
139 		startRecordingPath - starts recording paths, each point coming off the operating system is noted with a timestamp relative to when the recording started
140 		getRecordedPath - gets the current recorded path
141 		stopRecordingPath - stops recording the path and clears the recording.
142 
143 		You might use this for things like finding circles in Mario Party.
144 	Mouse-specific:
145 		// actually instead of capture/release i might make it a property of the screen. we'll see.
146 		captureCursor - captures the cursor inside the window
147 		releaseCursor - releases any existing capture
148 		currentPosition - returns the current position over the window, in pixels, with (0,0) being the upper left.
149 		changeInPosition - returns the change in position since last time you asked
150 		wheelMotion - change in wheel ticks since last time you asked
151 	Joystick-specific (be aware that the mouse will act as an emulated joystick):
152 		currentPosition - returns the current position of the stick, 0,0 being centered and -1, 1 being the upper left corner and 1,-1 being the lower right position. Note that there is a dead zone in the middle of joysticks that does not count so minute wiggles are filtered out.
153 		changeInPosition - returns the change in position since last time you asked
154 
155 		There may also be raw input data available, since this uses arsd.joystick.
156 
157 	$(H2 Window control)
158 
159 	FIXME: no public functions for this yet.
160 
161 	You can check for resizes and if the user wants to close to give you a chance to save the game before closing. You can also call `window.close();`. The library normally takes care of this for you.
162 
163 	Minimized windows will put the game on hold automatically. Maximize and full screen is handled automatically. You can request full screen when creating the window, or use the simpledisplay functions in runInGuiThreadAsync (but don't if you don't need to).
164 
165 	Showing and hiding cursor can be done in sdpy too.
166 
167 	Text drawing prolly shouldn't bitmap scale when the window is blown up, e.g. hidpi. Other things can just auto scale tho. The library should take care of this automatically.
168 
169 	You can set window title and icon when creating it too.
170 
171 	$(H2 Drawing)
172 
173 	I try not to force any one drawing model upon you. I offer four options out of the box and any opengl library has a good chance of working with appropriate setup.
174 
175 	The out-of-the-box choices are:
176 
177 	$(LIST
178 		* Old-style OpenGL, 2d or 3d, with glBegin, glEnd, glRotate, etc. For text, you can use [arsd.ttf.OpenGlLimitedFont]
179 
180 		* New-style OpenGL, 2d or 3d, with shaders and your own math libraries. For text, you can use [arsd.ttf.OpenGlLimitedFont] with new style flag enabled.
181 
182 		* [Nanovega|arsd.nanovega] 2d vector graphics. Nanovega supports its own text drawing functions.
183 
184 		* The `BasicDrawing` functions provided by `arsd.game`. To some extent, you'll be able to mix and match these with other drawing models. It is just bare minimum functionality you might find useful made in a more concise form than even old-style opengl.
185 	)
186 
187 	Please note that the simpledisplay ScreenPainter will NOT work in a game `drawFrame` function.
188 
189 	You can switch between 2d and 3d modes when drawing either with opengl functions or with my helper functions like go2d (FIXME: not in the right module yet).
190 
191 	$(H3 Images)
192 
193 	use arsd.image and the OpenGlTexture object.
194 
195 	$(H3 Text)
196 
197 	use [OpenGlLimitedFont] and maybe [OperatingSystemFont]
198 
199 	$(H3 3d models)
200 
201 	FIXME add something
202 
203 	$(H2 Audio)
204 
205 	done through arsd.simpleaudio
206 
207 	$(H2 Collision detection)
208 
209 	Nanovega actually offers this but generally you're on your own. arsd's Rectangle functions offer some too.
210 
211 	$(H2 Labeling variables)
212 
213 	You can label and categorize variables in your game to help get and set them automatically. For example, marking them as `@Saved` and `@ResetOnNewDungeon` which you use to do batch updates. FIXME: implement this.
214 
215 	$(H2 Random numbers)
216 
217 	std.random works but might want another thing so the seed is saved with the game.
218 
219 	$(H2 Screenshots)
220 
221 	simpledisplay has a function for it. FIXME give a one-stop function here.
222 
223 	$(H2 Stuff missing from raylib that might be useful)
224 
225 	the screen space functions. the 3d model stuff.
226 
227 	$(H2 Online play)
228 
229 	FIXME: not implemented
230 
231 	If you make your games input strictly use the virtual controller functions, it supports multiple players. Locally, they can be multiple gamepads plugged in to the computer. Over the network, you can have multiple players connect to someone acting as a server and it sends input from each player's computers to everyone else which is exposed to the game as other virtual controllers.
232 
233 	The way this works is before your game actually starts running, if the game was run with the network flag (which can come from command line or through the `runGame` parameter), one player will act as the server and others will connect to them
234 
235 	There is also a chat function built in.
236 
237 		getUserChat(recipients, prompt) - tells the input system that you want to accept a user chat message.
238 		drawUserChat(Point, Color, Font) - returns null if not getting user chat, otherwise returns the current string (what about the carat?)
239 		cancelGetChat - cancels a getUserChat.
240 
241 		sendBotChat(recipients, sender, message) - sends a chat from your program to the other users (will be marked as a bot message)
242 
243 		getChatHistory
244 		getLatestChat - returns the latest chat not yet returned, or null if none have come in recently
245 
246 		Chat messages take an argument defining the recipients, which you might want to limit if there are teams.
247 
248 	In your Game object, there is a `filterUserChat` method you can optionally implement. This is given the message they typed. If you return the message, it will send it to other players. Or you can return null to cancel sending it on the network. You might then use the chat function to implement cheat codes like the old Warcraft and Starcraft games. If the player is not connected on the network, nothing happens even if you do return a message, since there is nobody to send it to.
249 
250 	You can also implement a `chatHistoryLength` which tells how many messages to keep in memory.
251 
252 	Finally, you can send custom network messages with `sendNetworkUpdate` and `getNetworkUpdate`, which work with your own arbitrary structs that represent data packets. Each one can be sent to recipients like chat messages but this is strictly for the program to read  These take an argument to decide if it should be the tcp or udp connections.
253 
254 	$(H2 Split screen)
255 
256 	When playing locally, you might want to split your window for multiple players to see. The library might offer functions to help you in future versions. Your code should realize when it is split screen and adjust the ui accordingly regardless.
257 
258 	$(H2 Library internals)
259 
260 	To better understand why things work the way they do, here's an overview of the internal architecture of the library. Much of the information here may be changed in future versions of the library, so try to think more about the concepts than the specifics as you read.
261 
262 	$(H3 The game clock)
263 
264 	$(H3 Thread layout)
265 
266 	It runs four threads: a UI thread, a graphics thread, an audio thread, and a game thread.
267 
268 	The UI thread runs your `getWindow` function, but otherwise is managed by the library. It handles input messages, window resizes, and other things. Being built on [arsd.simpledisplay], it is possible for you to do work in it with the `runInGuiThread` and `runInGuiThreadAsync` functions, which might be useful if, for example, you wanted to open other windows. But you should generally avoid it.
269 
270 	The graphics thread runs your `load` and `drawFrame` functions. It gets the OpenGL context bound to it after the window is created, and expects to always have it. Since OpenGL contexts cannot be simultaneously shared across two threads, this means your other functions shouldn't try to access any of these objects. (It is possible to release the context from one thread, then attach it in another - indeed, the library does this between `getWindow` and `load` - but doing this in your user code is not supported and you'd try it at your own risk.)
271 
272 	The audio thread is created if `wantAudio` is true and is communicated to via the `audio` object in your game class. The library manages it for you and the methods in the `audio` object tell it what to do. You are permitted to call these from your `update` function, or to load sound assets from your `load` function.
273 
274 	Finally, the game thread is responsible for running your `update` function at a regular interval. The library coordinates sharing your game state between it and the graphics thread with a mutex. You can get more fine-grained control over this by overriding `updateWithManualLock`. The default is for `drawFrame` and `update` to never run simultaneously to keep data sharing to a minimum, but if you know what you're doing, you can make the lock time very very small by limiting the amount of writable data is actually shared. The default is what it is to keep things simple for you and should work most the time, though.
275 
276 	Most computer programs are written either as batch processors or as event-driven applications. Batch processors do their work when requested, then exit. Event-driven applications, including many video games, wait for something to happen, like the user pressing a key or clicking the mouse, respond to it, then go back to waiting. These might do some animations, but this is the exception to its run time, not the rule. You are assumed to be waiting for events, but can `requestAnimationFrame` for the special occasions.
277 
278 	But this is the rule for the third category of programs: time-driven programs, and many video games fall into this category. This is what `arsd.game` tries to make easy. It assumes you want a timed `update` and a steady stream of animation frames, and if you want to make an exception, you can pause updates until an event comes in. FIXME: `pauseUntilNextInput`.
279 
280 	$(H3 Webassembly implementation)
281 
282 	See_Also:
283 		[arsd.ttf.OpenGlLimitedFont]
284 
285 	History:
286 		The [GameHelperBase], indeed most the module, was completely redesigned in November 2022. If you
287 		have code that depended on the old way, you're probably better off keeping a copy of the old module
288 		and not updating it again.
289 
290 		However, if you want to update it, you can approximate the old behavior by making a single `GameScreen`
291 		and moving most your code into it, especially the `drawFrame` and `update` methods, and returning that
292 		as the `firstScreen`.
293 +/
294 module arsd.game;
295 
296 /+
297 	Platformer demo:
298 		dance of sugar plum fairy as you are the fairy jumping around
299 	Board game demo:
300 		good old chess
301 	3d first person demo:
302 		orbit simulator. your instruments show the spacecraft orientation relative to direction of motion (0 = prograde, 180 = retrograde yaw then the pitch angle relative to the orbit plane with up just being a thing) and your orbit params (apogee, perigee, phase, etc. also show velocity and potential energy relative to planet). and your angular velocity in three dimensions
303 
304 		you just kinda fly around. goal is to try to actually transfer to another station successfully.
305 
306 		play blue danube song lol
307 
308 +/
309 
310 
311 // i will want to keep a copy of these that the events update, then the pre-frame update call just copies it in
312 // just gotta remember potential cross-thread issues; the write should prolly be protected by a mutex so it all happens
313 // together when the frame begins
314 struct VirtualJoystick {
315 	// the mouse sets one thing and the right stick sets another
316 	// both will update it, so hopefully people won't move mouse and joystick at the same time.
317 	private float[2] currentPosition_ = 0.0;
318 	private float[2] positionLastAsked_ = 0.0;
319 
320 	float[2] currentPosition() {
321 		return currentPosition_;
322 	}
323 
324 	float[2] changeInPosition() {
325 		auto tmp = positionLastAsked_;
326 		positionLastAsked_ = currentPosition_;
327 		return [currentPosition_[0] - tmp[0], currentPosition_[1] - tmp[1]];
328 	}
329 
330 }
331 
332 struct MouseAccess {
333 	// the mouse buttons can be L and R on the virtual gamepad
334 	int[2] currentPosition_;
335 }
336 
337 struct KeyboardAccess {
338 	// state based access
339 
340 	int lastChange; // in terms of the game clock's frame counter
341 
342 	void startRecordingCharacters() {
343 
344 	}
345 
346 	string getRecordedCharacters() {
347 		return "";
348 	}
349 
350 	void stopRecordingCharacters() {
351 
352 	}
353 }
354 
355 struct MousePath {
356 	static struct Waypoint {
357 		// Duration timestamp
358 		// x, y
359 		// button flags
360 	}
361 
362 	Waypoint[] path;
363 
364 }
365 
366 struct JoystickPath {
367 	static struct Waypoint {
368 		// Duration timestamp
369 		// x, y
370 		// button flags
371 	}
372 
373 	Waypoint[] path;
374 }
375 
376 /++
377 	See [GameScreen] for the thing you are supposed to use. This is just for internal use by the arsd.game library.
378 +/
379 class GameScreenBase {
380 	abstract inout(GameHelperBase) game() inout;
381 	abstract void update();
382 	abstract void drawFrame(float interpolate);
383 	abstract void load();
384 
385 	private bool loaded;
386 }
387 
388 /+
389 	you ask for things to be done - foo();
390 	and other code asks you to do things - foo() { }
391 
392 
393 	Recommended drawing methods:
394 		old opengl
395 		new opengl
396 		nanovega
397 
398 	FIXME:
399 		for nanovega, load might want a withNvg()
400 		both load and drawFrame might want a nvgFrame()
401 
402 		game.nvgFrame((nvg) {
403 
404 		});
405 +/
406 
407 /++
408 	Tip: if your screen is a generic component reused across many games, you might pass `GameHelperBase` as the `Game` parameter.
409 +/
410 class GameScreen(Game) : GameScreenBase {
411 	private Game game_;
412 
413 	// convenience accessors
414 	final AudioOutputThread audio() {
415 		if(this is null || game is null) return AudioOutputThread.init;
416 		return game.audio;
417 	}
418 
419 	final VirtualController snes() {
420 		if(this is null || game is null) return VirtualController.init;
421 		return game.snes;
422 	}
423 
424 	/+
425 		manual draw mode turns off the automatic timer to render and only
426 		draws when you specifically trigger it. might not be worth tho.
427 	+/
428 
429 
430 	// You are not supposed to call this.
431 	final void setGame(Game game) {
432 		assert(game_ is null);
433 		assert(game !is null);
434 		this.game_ = game;
435 	}
436 
437 	/++
438 		Gives access to your game object for use through the screen.
439 	+/
440 	public override inout(Game) game() inout {
441 		if(game_ is null)
442 			throw new Exception("The game screen isn't showing!");
443 		return game_;
444 	}
445 
446 	/++
447 		`update`'s responsibility is to:
448 
449 		$(LIST
450 			* Process player input
451 			* Update game state - object positions, do collision detection, etc.
452 			* Run any character AI
453 			* Kick off any audio associated with changes in this update
454 			* Transition to other screens if appropriate
455 		)
456 
457 		It is NOT supposed to:
458 
459 		$(LIST
460 			* draw - that's the job of [drawFrame]
461 			* load files, bind textures, or similar - that's the job of [load]
462 			* set uniforms or other OpenGL objects - do one-time things in [load] and per-frame things in [drawFrame]
463 		)
464 	+/
465 	override abstract void update();
466 
467 	/++
468 		`drawFrame`'s responsibility is to draw a single frame. It can use the `interpolate` method to smooth animations between updates.
469 
470 		It should NOT change any variables in the game state or attempt to do things like collision detection - that's [update]'s job. When interpolating, just assume the objects are going to keep doing what they're doing.
471 
472 		It should also NOT load any files, create textures, or any other setup task - [load] is supposed to have already done that.
473 	+/
474 	override abstract void drawFrame(float interpolate);
475 
476 	/++
477 		Load your graphics and other assets in this function. You are allowed to draw to the screen while loading, but note you'll have to manage things like buffer swapping yourself if you do. [drawFrame] and [update] will be paused until loading is complete. This function will be called exactly once per screen object, right as it is first shown.
478 	+/
479 	override void load() {}
480 }
481 
482 /// ditto
483 //alias GenericGameScreen = GameScreen!GameHelperBase;
484 
485 ///
486 unittest {
487 	// The TitleScreen has a simple job: show the title until the user presses start. After that, it will progress to the GameplayScreen.
488 
489 	static // exclude from docs
490 	class DemoGame : GameHelperBase {
491 		// I put this inside DemoGame for this demo, but you could define them in separate files if you wanted to
492 		static class TitleScreen : GameScreen!DemoGame {
493 			override void update() {
494 				// you can always access your main Game object through the screen objects
495 				if(game.snes[VirtualController.Button.Start]) {
496 					//game.showScreen(new GameplayScreen());
497 				}
498 			}
499 
500 			override void drawFrame(float interpolate) {
501 
502 			}
503 		}
504 
505 		// and the minimum boilerplate the game itself must provide for the library
506 		// is the window it wants to use and the first screen to load into it.
507 		override TitleScreen firstScreen() {
508 			return new TitleScreen();
509 		}
510 
511 		override SimpleWindow getWindow() {
512 			auto window = create2dWindow("Demo game");
513 			return window;
514 		}
515 	}
516 
517 	void main() {
518 		runGame!DemoGame();
519 	}
520 
521 	main(); // exclude from docs
522 }
523 
524 /+
525 	Networking helper: just send/receive messages and manage some connections
526 
527 	It might offer a controller queue you can put local and network events in to get fair lag and transparent ultiplayer
528 
529 	split screen?!?!
530 
531 +/
532 
533 /+
534 	ADD ME:
535 	Animation helper like audio style. Your game object
536 	has a particular image attached as primary.
537 
538 	You can be like `animate once` or `animate indefinitely`
539 	and it takes care of it, then set new things and it does that too.
540 +/
541 
542 public import arsd.gamehelpers;
543 public import arsd.color;
544 public import arsd.simpledisplay;
545 public import arsd.simpleaudio;
546 
547 import std.math;
548 public import core.time;
549 
550 public import arsd.joystick;
551 
552 /++
553 	Creates a simple 2d opengl simpledisplay window. It sets the matrix for pixel coordinates and enables alpha blending and textures.
554 +/
555 SimpleWindow create2dWindow(string title, int width = 512, int height = 512) {
556 	auto window = new SimpleWindow(width, height, title, OpenGlOptions.yes);
557 
558 	window.setAsCurrentOpenGlContext();
559 
560 	glEnable(GL_BLEND);
561 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
562 	glClearColor(0,0,0,0);
563 	glDepthFunc(GL_LEQUAL);
564 
565 	glMatrixMode(GL_PROJECTION);
566 	glLoadIdentity();
567 	glOrtho(0, width, height, 0, 0, 1);
568 
569 	glMatrixMode(GL_MODELVIEW);
570 	glLoadIdentity();
571 	glDisable(GL_DEPTH_TEST);
572 	glEnable(GL_TEXTURE_2D);
573 
574 	window.windowResized = (newWidth, newHeight) {
575 		int x, y, w, h;
576 
577 		// FIXME: this works for only square original sizes
578 		if(newWidth < newHeight) {
579 			w = newWidth;
580 			h = newWidth * height / width;
581 			x = 0;
582 			y = (newHeight - h) / 2;
583 		} else {
584 			w = newHeight * width / height;
585 			h = newHeight;
586 			x = (newWidth - w) / 2;
587 			y = 0;
588 		}
589 
590 		glViewport(x, y, w, h);
591 		window.redrawOpenGlSceneSoon();
592 	};
593 
594 	return window;
595 }
596 
597 /++
598 	This is the base class for your game. Create a class based on this, then pass it to [runGame].
599 +/
600 abstract class GameHelperBase {
601 	/++
602 		Implement this to draw.
603 
604 		The `interpolateToNextFrame` argument tells you how close you are to the next frame. You should
605 		take your current state and add the estimated next frame things multiplied by this to get smoother
606 		animation. interpolateToNextFrame will always be >= 0 and < 1.0.
607 
608 		History:
609 			Previous to August 27, 2022, this took no arguments. It could thus not interpolate frames!
610 	+/
611 	deprecated("Move to void drawFrame(float) in a GameScreen instead") void drawFrame(float interpolateToNextFrame) {
612 		if(currentScreen is null)
613 			return;
614 		if(!currentScreen.loaded) {
615 			// FIXME: unpause the update thread when it is done
616 			currentScreen.load();
617 			currentScreen.loaded = true;
618 			return;
619 		}
620 		currentScreen.drawFrame(interpolateToNextFrame);
621 	}
622 
623 	ushort snesRepeatRate() { return ushort.max; }
624 	ushort snesRepeatDelay() { return snesRepeatRate(); }
625 
626 	/++
627 		Implement this to update your game state by a single fixed timestep. You should
628 		check for user input state here.
629 
630 		Return true if something visibly changed to queue a frame redraw asap.
631 
632 		History:
633 			Previous to August 27, 2022, this took an argument. This was a design flaw.
634 	+/
635 	deprecated("Move to void update in a GameScreen instead") bool update() { return false; }
636 
637 	/+
638 		override this to have more control over synchronization
639 
640 		its main job is to lock on `this` and update what [update] changes
641 		and call `bookkeeping` while inside the lock
642 
643 		but if you have some work that can be done outside the lock - things
644 		that are read-only on the game state - you might split it up here and
645 		batch your update. as long as nothing that the [drawFrame] needs is mutated
646 		outside the lock you'll be ok.
647 
648 		History:
649 			Added November 12, 2022
650 	+/
651 	bool updateWithManualLock(scope void delegate() bookkeeping) shared {
652 		if(currentScreen is null)
653 			return false;
654 		synchronized(this) {
655 			(cast() this).currentScreen.update();
656 			bookkeeping();
657 			return false;
658 		}
659 	}
660 	//abstract void fillAudioBuffer(short[] buffer);
661 
662 	/++
663 		Returns the main game window. This function will only be
664 		called once if you use runGame. You should return a window
665 		here like one created with `create2dWindow`.
666 	+/
667 	abstract SimpleWindow getWindow();
668 
669 	/++
670 		Override this and return true to initialize the audio system. If you return `true`
671 		here, the [audio] member can be used.
672 	+/
673 	bool wantAudio() { return false; }
674 
675 	/++
676 		Override this and return true if you are compatible with separate render and update threads.
677 	+/
678 	bool multithreadCompatible() { return true; }
679 
680 	/// You must override [wantAudio] and return true for this to be valid;
681 	AudioOutputThread audio;
682 
683 	this() {
684 		audio = AudioOutputThread(wantAudio());
685 	}
686 
687 	protected bool redrawForced;
688 
689 	private GameScreenBase currentScreen;
690 
691 	/+
692 	// it will also need a configuration in time and such
693 	enum ScreenTransition {
694 		none,
695 		crossFade
696 	}
697 	+/
698 
699 	/++
700 		Shows the given screen, making it actively responsible for drawing and updating,
701 		optionally through the given transition effect.
702 	+/
703 	void showScreen(this This, Screen)(Screen cs, GameScreenBase transition = null) {
704 		cs.setGame(cast(This) this);
705 		currentScreen = cs;
706 		// FIXME: pause the update thread here, and fast forward the game clock when it is unpaused
707 		// (this actually SHOULD be called from the update thread, except for the initial load... and even that maybe it will then)
708 		// but i have to be careful waiting here because it can deadlock with teh mutex still locked.
709 	}
710 
711 	/++
712 		Returns the first screen of your game.
713 	+/
714 	abstract GameScreenBase firstScreen();
715 
716 	/++
717 		Returns the number of game updates per second your game is designed for.
718 
719 		This isn't necessarily the number of frames drawn per second, which may be more
720 		or less due to frame skipping and interpolation, but it is the number of times
721 		your screen's update methods will be called each second.
722 
723 		You actually want to make this as small as possible without breaking your game's
724 		physics and feeling of responsiveness to the controls. Remember, the display FPS
725 		is different - you can interpolate frames for smooth animation. What you want to
726 		ensure here is that the design fps is big enough that you don't have problems like
727 		clipping through walls or sluggishness in player control, but not so big that the
728 		computer is busy doing collision detection, etc., all the time and has no time
729 		left over to actually draw the game.
730 
731 		I personally find 20 actually works pretty well, though the default set here is 60
732 		due to how common that number is. You are encouraged to override this and use what
733 		works for you.
734 	+/
735 	int designFps() { return 60; }
736 
737 	/// Forces a redraw even if update returns false
738 	final public void forceRedraw() {
739 		redrawForced = true;
740 	}
741 
742 	/// These functions help you handle user input. It offers polling functions for
743 	/// keyboard, mouse, joystick, and virtual controller input.
744 	///
745 	/// The virtual digital controllers are best to use if that model fits you because it
746 	/// works with several kinds of controllers as well as keyboards.
747 
748 	JoystickUpdate[4] joysticks;
749 	ref JoystickUpdate joystick1() { return joysticks[0]; }
750 
751 	bool[256] keyboardState;
752 
753 	// FIXME: add a mouse position and delta thing too.
754 
755 	/++
756 
757 	+/
758 	VirtualController snes;
759 }
760 
761 /++
762 	The virtual controller is based on the SNES. If you need more detail, try using
763 	the joystick or keyboard and mouse members directly.
764 
765 	```
766 	 l          r
767 
768 	 U          X
769 	L R  s  S  Y A
770 	 D          B
771 	```
772 
773 	For Playstation and XBox controllers plugged into the computer,
774 	it picks those buttons based on similar layout on the physical device.
775 
776 	For keyboard control, arrows and WASD are mapped to the d-pad (ULRD in the diagram),
777 	Q and E are mapped to the shoulder buttons (l and r in the diagram).So are U and P.
778 
779 	Z, X, C, V (for when right hand is on arrows) and K,L,I,O (for left hand on WASD) are mapped to B,A,Y,X buttons.
780 
781 	G is mapped to select (s), and H is mapped to start (S).
782 
783 	The space bar and enter keys are also set to button A, with shift mapped to button B.
784 
785 	Additionally, the mouse is mapped to the virtual joystick, and mouse buttons left and right are mapped to shoulder buttons L and R.
786 
787 
788 	Only player 1 is mapped to the keyboard.
789 +/
790 struct VirtualController {
791 	ushort previousState;
792 	ushort state;
793 
794 	// for key repeat
795 	ushort truePreviousState;
796 	ushort lastStateChange;
797 	bool repeating;
798 
799 	///
800 	enum Button {
801 		Up, Left, Right, Down,
802 		X, A, B, Y,
803 		Select, Start, L, R
804 	}
805 
806 	@nogc pure nothrow @safe:
807 
808 	/++
809 		History: Added April 30, 2020
810 	+/
811 	bool justPressed(Button idx) const {
812 		auto before = (previousState & (1 << (cast(int) idx))) ? true : false;
813 		auto after = (state & (1 << (cast(int) idx))) ? true : false;
814 		return !before && after;
815 	}
816 	/++
817 		History: Added April 30, 2020
818 	+/
819 	bool justReleased(Button idx) const {
820 		auto before = (previousState & (1 << (cast(int) idx))) ? true : false;
821 		auto after = (state & (1 << (cast(int) idx))) ? true : false;
822 		return before && !after;
823 	}
824 
825 	/+
826 	+/
827 
828 	VirtualJoystick stick;
829 
830 	///
831 	bool opIndex(Button idx) const {
832 		return (state & (1 << (cast(int) idx))) ? true : false;
833 	}
834 	private void opIndexAssign(bool value, Button idx) {
835 		if(value)
836 			state |= (1 << (cast(int) idx));
837 		else
838 			state &= ~(1 << (cast(int) idx));
839 	}
840 }
841 
842 struct ButtonCheck {
843 	bool wasPressed() {
844 		return false;
845 	}
846 	bool wasReleased() {
847 		return false;
848 	}
849 	bool wasClicked() {
850 		return false;
851 	}
852 	bool isHeld() {
853 		return false;
854 	}
855 
856 	bool opCast(T : bool)() {
857 		return isHeld();
858 	}
859 }
860 
861 /++
862 	Deprecated, use the other overload instead.
863 
864 	History:
865 		Deprecated on May 9, 2020. Instead of calling
866 		`runGame(your_instance);` run `runGame!YourClass();`
867 		instead. If you needed to change something in the game
868 		ctor, make a default constructor in your class to do that
869 		instead.
870 +/
871 deprecated("Use runGame!YourGameType(updateRate, redrawRate); instead now.")
872 void runGame()(GameHelperBase game, int targetUpdateRate = 20, int maxRedrawRate = 0) { assert(0, "this overload is deprecated, use runGame!YourClass instead"); }
873 
874 /++
875 	Runs your game. It will construct the given class and destroy it at end of scope.
876 	Your class must have a default constructor and must implement [GameHelperBase].
877 	Your class should also probably be `final` for a small, but easy performance boost.
878 
879 	$(TIP
880 		If you need to pass parameters to your game class, you can define
881 		it as a nested class in your `main` function and access the local
882 		variables that way instead of passing them explicitly through the
883 		constructor.
884 	)
885 
886 	Params:
887 	targetUpdateRate = The number of game state updates you get per second. You want this to be quick enough that players don't feel input lag, but conservative enough that any supported computer can keep up with it easily.
888 	maxRedrawRate = The maximum draw frame rate. 0 means it will only redraw after a state update changes things. It will be automatically capped at the user's monitor refresh rate. Frames in between updates can be interpolated or skipped.
889 +/
890 void runGame(T : GameHelperBase)(int targetUpdateRate = 0, int maxRedrawRate = 0) {
891 
892 	auto game = new T();
893 	scope(exit) .destroy(game);
894 
895 	if(targetUpdateRate == 0)
896 		targetUpdateRate = game.designFps();
897 
898 	// this is a template btw because then it can statically dispatch
899 	// the members instead of going through the virtual interface.
900 
901 	auto window = game.getWindow();
902 	game.showScreen(game.firstScreen());
903 
904 	auto lastUpdate = MonoTime.currTime;
905 	bool isImmediateUpdate;
906 
907 	int joystickPlayers;
908 
909 	window.redrawOpenGlScene = null;
910 
911 	/*
912 		The game clock should always be one update ahead of the real world clock.
913 
914 		If it is behind the real world clock, it needs to run update faster, so it will
915 		double up on its timer to try to update and skip some render frames to make cpu time available.
916 		Generally speaking the render should never be more than one full frame ahead of the game clock,
917 		and since the game clock should always be a bit ahead of the real world clock, if the game clock
918 		is behind the real world clock, time to skip.
919 
920 		If there's a huge jump in the real world clock - more than a couple seconds between
921 		updates - this probably indicates the computer went to sleep or something. We can't
922 		catch up, so this will just resync the clock to real world and not try to catch up.
923 	*/
924 	MonoTime gameClock;
925 	// FIXME: render thread should be lower priority than the ui thread
926 
927 	int rframeCounter = 0;
928 	auto drawer = delegate bool() {
929 		if(gameClock is MonoTime.init)
930 			return false; // can't draw uninitialized info
931 		auto time = MonoTime.currTime;
932 		if(gameClock + (1000.msecs / targetUpdateRate) < time) {
933 			import std.stdio; writeln("frame skip ", gameClock, " vs ", time);
934 			return false; // we're behind on updates, skip this frame
935 		}
936 
937 		if(false && isImmediateUpdate) {
938 			game.drawFrame(0.0);
939 			isImmediateUpdate = false;
940 		} else {
941 			auto now = MonoTime.currTime - lastUpdate;
942 			Duration nextFrame = msecs(1000 / targetUpdateRate);
943 			auto delta = cast(float) ((nextFrame - now).total!"usecs") / cast(float) nextFrame.total!"usecs";
944 
945 			if(delta < 0) {
946 				return false; // the render is too far ahead of the updater! time to skip frames to let it catch up
947 			}
948 
949 			game.drawFrame(1.0 - delta);
950 		}
951 
952 		rframeCounter++;
953 		if(rframeCounter % 60 == 0) {
954 			import std.stdio;
955 			writeln("frame");
956 		}
957 
958 		return true;
959 	};
960 
961 	import core.thread;
962 	import core..volatile;
963 	Thread renderThread;
964 	Thread updateThread;
965 
966 	// shared things to communicate with threads
967 	ubyte exit;
968 	ulong newWindowSize;
969 	ubyte loadRequired; // if the screen changed and you need to call load again in the render thread
970 
971 	ubyte workersPaused;
972 	// Event unpauseRender; // maybe a manual reset so you set it then reset after unpausing
973 	// Event unpauseUpdate;
974 
975 	// the input buffers should prolly be double buffered generally speaking
976 
977 	// FIXME: i might just want an asset cache thing
978 	// FIXME: ffor audio, i want to be able to play a sound to completion without necessarily letting it play twice simultaneously and then replay it later. this would be a sound effect thing. but you might also play it twice anyway if there's like two shots so meh. and then i'll need BGM controlling in the game and/or screen.
979 
980 	Timer renderTimer;
981 	Timer updateTimer;
982 
983 	auto updater = delegate() {
984 		if(gameClock is MonoTime.init) {
985 			gameClock = MonoTime.currTime;
986 		}
987 
988 		foreach(p; 0 .. joystickPlayers) {
989 			version(linux)
990 				readJoystickEvents(joystickFds[p]);
991 			auto update = getJoystickUpdate(p);
992 
993 			if(p == 0) {
994 				static if(__traits(isSame, Button, PS1Buttons)) {
995 					// PS1 style joystick mapping compiled in
996 					with(Button) with(VirtualController.Button) {
997 						// so I did the "wasJustPressed thing because it interplays
998 						// better with the keyboard as well which works on events...
999 						if(update.buttonWasJustPressed(square)) game.snes[Y] = true;
1000 						if(update.buttonWasJustPressed(triangle)) game.snes[X] = true;
1001 						if(update.buttonWasJustPressed(cross)) game.snes[B] = true;
1002 						if(update.buttonWasJustPressed(circle)) game.snes[A] = true;
1003 						if(update.buttonWasJustPressed(select)) game.snes[Select] = true;
1004 						if(update.buttonWasJustPressed(start)) game.snes[Start] = true;
1005 						if(update.buttonWasJustPressed(l1)) game.snes[L] = true;
1006 						if(update.buttonWasJustPressed(r1)) game.snes[R] = true;
1007 						// note: no need to check analog stick here cuz joystick.d already does it for us (per old playstation tradition)
1008 						if(update.axisChange(Axis.horizontalDpad) < 0 && update.axisPosition(Axis.horizontalDpad) < -8) game.snes[Left] = true;
1009 						if(update.axisChange(Axis.horizontalDpad) > 0 && update.axisPosition(Axis.horizontalDpad) > 8) game.snes[Right] = true;
1010 						if(update.axisChange(Axis.verticalDpad) < 0 && update.axisPosition(Axis.verticalDpad) < -8) game.snes[Up] = true;
1011 						if(update.axisChange(Axis.verticalDpad) > 0 && update.axisPosition(Axis.verticalDpad) > 8) game.snes[Down] = true;
1012 
1013 						if(update.buttonWasJustReleased(square)) game.snes[Y] = false;
1014 						if(update.buttonWasJustReleased(triangle)) game.snes[X] = false;
1015 						if(update.buttonWasJustReleased(cross)) game.snes[B] = false;
1016 						if(update.buttonWasJustReleased(circle)) game.snes[A] = false;
1017 						if(update.buttonWasJustReleased(select)) game.snes[Select] = false;
1018 						if(update.buttonWasJustReleased(start)) game.snes[Start] = false;
1019 						if(update.buttonWasJustReleased(l1)) game.snes[L] = false;
1020 						if(update.buttonWasJustReleased(r1)) game.snes[R] = false;
1021 						if(update.axisChange(Axis.horizontalDpad) > 0 && update.axisPosition(Axis.horizontalDpad) > -8) game.snes[Left] = false;
1022 						if(update.axisChange(Axis.horizontalDpad) < 0 && update.axisPosition(Axis.horizontalDpad) < 8) game.snes[Right] = false;
1023 						if(update.axisChange(Axis.verticalDpad) > 0 && update.axisPosition(Axis.verticalDpad) > -8) game.snes[Up] = false;
1024 						if(update.axisChange(Axis.verticalDpad) < 0 && update.axisPosition(Axis.verticalDpad) < 8) game.snes[Down] = false;
1025 					}
1026 
1027 				} else static if(__traits(isSame, Button, XBox360Buttons)) {
1028 				static assert(0);
1029 					// XBox style mapping
1030 					// the reason this exists is if the programmer wants to use the xbox details, but
1031 					// might also want the basic controller in here. joystick.d already does translations
1032 					// so an xbox controller with the default build actually uses the PS1 branch above.
1033 					/+
1034 					case XBox360Buttons.a: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? true : false;
1035 					case XBox360Buttons.b: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? true : false;
1036 					case XBox360Buttons.x: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? true : false;
1037 					case XBox360Buttons.y: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? true : false;
1038 
1039 					case XBox360Buttons.lb: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? true : false;
1040 					case XBox360Buttons.rb: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? true : false;
1041 
1042 					case XBox360Buttons.back: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? true : false;
1043 					case XBox360Buttons.start: return (what.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? true : false;
1044 					+/
1045 				}
1046 			}
1047 
1048 			game.joysticks[p] = update;
1049 		}
1050 
1051 		int runs;
1052 
1053 		again:
1054 
1055 		auto now = MonoTime.currTime;
1056 		bool changed;
1057 		changed = (cast(shared)game).updateWithManualLock({ lastUpdate = now; });
1058 		auto stateChange = game.snes.truePreviousState ^ game.snes.state;
1059 		game.snes.previousState = game.snes.state;
1060 		game.snes.truePreviousState = game.snes.state;
1061 
1062 		if(stateChange == 0) {
1063 			game.snes.lastStateChange++;
1064 			auto r = game.snesRepeatRate();
1065 			if(r != typeof(r).max && !game.snes.repeating && game.snes.lastStateChange == game.snesRepeatDelay()) {
1066 				game.snes.lastStateChange = 0;
1067 				game.snes.repeating = true;
1068 			} else if(r != typeof(r).max && game.snes.repeating && game.snes.lastStateChange == r) {
1069 				game.snes.lastStateChange = 0;
1070 				game.snes.previousState = 0;
1071 			}
1072 		} else {
1073 			game.snes.repeating = false;
1074 		}
1075 
1076 		if(game.redrawForced) {
1077 			changed = true;
1078 			game.redrawForced = false;
1079 		}
1080 
1081 		gameClock += 1.seconds / targetUpdateRate;
1082 
1083 		if(++runs < 3 && gameClock < MonoTime.currTime)
1084 			goto again;
1085 
1086 		// FIXME: rate limiting
1087 		// FIXME: triple buffer it.
1088 		if(changed) {
1089 			isImmediateUpdate = true;
1090 			window.redrawOpenGlSceneSoon();
1091 		}
1092 	};
1093 
1094 	//window.vsync = false;
1095 
1096 	const maxRedrawTime = maxRedrawRate > 0 ? (1000.msecs / maxRedrawRate) : 4.msecs;
1097 
1098 	if(game.multithreadCompatible()) {
1099 		window.redrawOpenGlScene = null;
1100 		renderThread = new Thread({
1101 			// FIXME: catch exception and inform the parent
1102 			int frames = 0;
1103 
1104 			Duration renderTime;
1105 			Duration flipTime;
1106 			Duration renderThrottleTime;
1107 
1108 			while(!volatileLoad(&exit)) {
1109 				MonoTime start = MonoTime.currTime;
1110 				{
1111 					window.mtLock();
1112 					scope(exit)
1113 						window.mtUnlock();
1114 					window.setAsCurrentOpenGlContext();
1115 				}
1116 
1117 				bool actuallyDrew;
1118 
1119 				synchronized(game)
1120 					actuallyDrew = drawer();
1121 
1122 				MonoTime end = MonoTime.currTime;
1123 				if(actuallyDrew) {
1124 					window.mtLock();
1125 					scope(exit)
1126 						window.mtUnlock();
1127 					window.swapOpenGlBuffers();
1128 				}
1129 				// want to ensure the vsync wait occurs here, outside the window and locks
1130 				// some impls will do it on glFinish, some on the next touch of the
1131 				// front buffer, hence the clear being done here.
1132 				if(actuallyDrew) {
1133 					glFinish();
1134 					clearOpenGlScreen(window);
1135 				}
1136 				MonoTime flip = MonoTime.currTime;
1137 
1138 				renderTime += end - start;
1139 				flipTime += flip - end;
1140 
1141 				if(flip - start < maxRedrawTime) {
1142 					renderThrottleTime += maxRedrawTime - (flip - start);
1143 					Thread.sleep(maxRedrawTime - (flip - start));
1144 				}
1145 				frames++;
1146 				//import std.stdio; if(frames % 60 == 0) writeln("frame");
1147 			}
1148 
1149 			import std.stdio;
1150 			writeln("Average render time: ", renderTime / frames);
1151 			writeln("Average flip time: ", flipTime / frames);
1152 			writeln("Average throttle time: ", renderThrottleTime / frames);
1153 		});
1154 
1155 		updateThread = new Thread({
1156 			// FIXME: catch exception and inform the parent
1157 			int frames;
1158 
1159 			joystickPlayers = enableJoystickInput();
1160 			scope(exit) closeJoysticks();
1161 
1162 			Duration updateTime;
1163 			Duration waitTime;
1164 
1165 			while(!volatileLoad(&exit)) {
1166 				MonoTime start = MonoTime.currTime;
1167 				updater();
1168 				MonoTime end = MonoTime.currTime;
1169 
1170 				updateTime += end - start;
1171 
1172 				frames++;
1173 				import std.stdio; if(frames % game.designFps == 0) writeln("update");
1174 
1175 				const now = MonoTime.currTime - lastUpdate;
1176 				Duration nextFrame = msecs(1000) / targetUpdateRate;
1177 				const sleepTime = nextFrame - now;
1178 				if(sleepTime.total!"msecs" <= 0) {
1179 					// falling behind on update...
1180 				} else {
1181 					waitTime += sleepTime;
1182 					// import std.stdio; writeln(sleepTime);
1183 					Thread.sleep(sleepTime);
1184 				}
1185 			}
1186 
1187 			import std.stdio;
1188 			writeln("Average update time:" , updateTime / frames);
1189 			writeln("Average wait time:" , waitTime / frames);
1190 		});
1191 	} else {
1192 		// single threaded, vsync a bit dangeresque here since it
1193 		// puts the ui thread to sleep!
1194 		window.vsync = false;
1195 	}
1196 
1197 	// FIXME: when single threaded, set the joystick here
1198 	// actually just always do the joystick in the event thread regardless
1199 
1200 	int frameCounter;
1201 
1202 	auto first = window.visibleForTheFirstTime;
1203 	window.visibleForTheFirstTime = () {
1204 		if(first)
1205 			first();
1206 
1207 		if(updateThread) {
1208 			updateThread.start();
1209 		} else {
1210 			updateTimer = new Timer(1000 / targetUpdateRate, {
1211 				frameCounter++;
1212 				updater();
1213 			});
1214 		}
1215 
1216 		if(renderThread) {
1217 			window.suppressAutoOpenglViewport = true; // we don't want the context being pulled back by the other thread now, we'll check it over here.
1218 			// FIXME: set viewport prior to render if width/height changed
1219 			window.releaseCurrentOpenGlContext(); // need to let the render thread take it
1220 			renderThread.start();
1221 		} else {
1222 			window.redrawOpenGlScene = { drawer(); };
1223 			renderTimer = new Timer(1000 / 60, { window.redrawOpenGlSceneSoon(); });
1224 		}
1225 	};
1226 
1227 	window.onClosing = () {
1228 		volatileStore(&exit, 1);
1229 
1230 		if(updateTimer) {
1231 			updateTimer.dispose();
1232 			updateTimer = null;
1233 		}
1234 		if(renderTimer) {
1235 			renderTimer.dispose();
1236 			renderTimer = null;
1237 		}
1238 
1239 		if(renderThread) {
1240 			renderThread.join();
1241 			renderThread = null;
1242 		}
1243 		if(updateThread) {
1244 			updateThread.join();
1245 			updateThread = null;
1246 		}
1247 	};
1248 
1249 	window.eventLoop(0,
1250 		delegate (KeyEvent ke) {
1251 			game.keyboardState[ke.hardwareCode] = ke.pressed;
1252 
1253 			with(VirtualController.Button)
1254 			switch(ke.key) {
1255 				case Key.Up, Key.W: game.snes[Up] = ke.pressed; break;
1256 				case Key.Down, Key.S: game.snes[Down] = ke.pressed; break;
1257 				case Key.Left, Key.A: game.snes[Left] = ke.pressed; break;
1258 				case Key.Right, Key.D: game.snes[Right] = ke.pressed; break;
1259 				case Key.Q, Key.U: game.snes[L] = ke.pressed; break;
1260 				case Key.E, Key.P: game.snes[R] = ke.pressed; break;
1261 				case Key.Z, Key.K: game.snes[B] = ke.pressed; break;
1262 				case Key.Space, Key.Enter, Key.X, Key.L: game.snes[A] = ke.pressed; break;
1263 				case Key.C, Key.I: game.snes[Y] = ke.pressed; break;
1264 				case Key.V, Key.O: game.snes[X] = ke.pressed; break;
1265 				case Key.G: game.snes[Select] = ke.pressed; break;
1266 				case Key.H: game.snes[Start] = ke.pressed; break;
1267 				case Key.Shift, Key.Shift_r: game.snes[B] = ke.pressed; break;
1268 				default:
1269 			}
1270 		}
1271 	);
1272 }
1273 
1274 /++
1275 	Simple class for putting a TrueColorImage in as an OpenGL texture.
1276 +/
1277 // Doesn't do mipmapping btw.
1278 final class OpenGlTexture {
1279 	private uint _tex;
1280 	private int _width;
1281 	private int _height;
1282 	private float _texCoordWidth;
1283 	private float _texCoordHeight;
1284 
1285 	/// Calls glBindTexture
1286 	void bind() {
1287 		glBindTexture(GL_TEXTURE_2D, _tex);
1288 	}
1289 
1290 	/// For easy 2d drawing of it
1291 	void draw(Point where, int width = 0, int height = 0, float rotation = 0.0, Color bg = Color.white) {
1292 		draw(where.x, where.y, width, height, rotation, bg);
1293 	}
1294 
1295 	///
1296 	void draw(float x, float y, int width = 0, int height = 0, float rotation = 0.0, Color bg = Color.white) {
1297 		glPushMatrix();
1298 		glTranslatef(x, y, 0);
1299 
1300 		if(width == 0)
1301 			width = this.originalImageWidth;
1302 		if(height == 0)
1303 			height = this.originalImageHeight;
1304 
1305 		glTranslatef(cast(float) width / 2, cast(float) height / 2, 0);
1306 		glRotatef(rotation, 0, 0, 1);
1307 		glTranslatef(cast(float) -width / 2, cast(float) -height / 2, 0);
1308 
1309 		glColor4f(cast(float)bg.r/255.0, cast(float)bg.g/255.0, cast(float)bg.b/255.0, cast(float)bg.a / 255.0);
1310 		glBindTexture(GL_TEXTURE_2D, _tex);
1311 		glBegin(GL_QUADS); 
1312 			glTexCoord2f(0, 0); 				glVertex2i(0, 0);
1313 			glTexCoord2f(texCoordWidth, 0); 		glVertex2i(width, 0); 
1314 			glTexCoord2f(texCoordWidth, texCoordHeight); 	glVertex2i(width, height); 
1315 			glTexCoord2f(0, texCoordHeight); 		glVertex2i(0, height); 
1316 		glEnd();
1317 
1318 		glBindTexture(GL_TEXTURE_2D, 0); // unbind the texture
1319 
1320 		glPopMatrix();
1321 	}
1322 
1323 	/// Use for glTexCoord2f
1324 	float texCoordWidth() { return _texCoordWidth; }
1325 	float texCoordHeight() { return _texCoordHeight; } /// ditto
1326 
1327 	/// Returns the texture ID
1328 	uint tex() { return _tex; }
1329 
1330 	/// Returns the size of the image
1331 	int originalImageWidth() { return _width; }
1332 	int originalImageHeight() { return _height; } /// ditto
1333 
1334 	// explicitly undocumented, i might remove this
1335 	TrueColorImage from;
1336 
1337 	/// Make a texture from an image.
1338 	this(TrueColorImage from) {
1339 		bindFrom(from);
1340 	}
1341 
1342 	/// Generates from text. Requires ttf.d
1343 	/// pass a pointer to the TtfFont as the first arg (it is template cuz of lazy importing, not because it actually works with different types)
1344 	this(T, FONT)(FONT* font, int size, in T[] text) if(is(T == char)) {
1345 		bindFrom(font, size, text);
1346 	}
1347 
1348 	/// Creates an empty texture class for you to use with [bindFrom] later
1349 	/// Using it when not bound is undefined behavior.
1350 	this() {}
1351 
1352 
1353 
1354 	/// After you delete it with dispose, you may rebind it to something else with this.
1355 	void bindFrom(TrueColorImage from) {
1356 		assert(from !is null);
1357 		assert(from.width > 0 && from.height > 0);
1358 
1359 		import core.stdc.stdlib;
1360 
1361 		_width = from.width;
1362 		_height = from.height;
1363 
1364 		this.from = from;
1365 
1366 		auto _texWidth = _width;
1367 		auto _texHeight = _height;
1368 
1369 		const(ubyte)* data = from.imageData.bytes.ptr;
1370 		bool freeRequired = false;
1371 
1372 		// gotta round them to the nearest power of two which means padding the image
1373 		if((_texWidth & (_texWidth - 1)) || (_texHeight & (_texHeight - 1))) {
1374 			_texWidth = nextPowerOfTwo(_texWidth);
1375 			_texHeight = nextPowerOfTwo(_texHeight);
1376 
1377 			auto n = cast(ubyte*) malloc(_texWidth * _texHeight * 4);
1378 			if(n is null) assert(0);
1379 			scope(failure) free(n);
1380 
1381 			auto size = from.width * 4;
1382 			auto advance = _texWidth * 4;
1383 			int at = 0;
1384 			int at2 = 0;
1385 			foreach(y; 0 .. from.height) {
1386 				n[at .. at + size] = from.imageData.bytes[at2 .. at2+ size];
1387 				at += advance;
1388 				at2 += size;
1389 			}
1390 
1391 			data = n;
1392 			freeRequired = true;
1393 
1394 			// the rest of data will be initialized to zeros automatically which is fine.
1395 		}
1396 
1397 		glGenTextures(1, &_tex);
1398 		glBindTexture(GL_TEXTURE_2D, tex);
1399 
1400 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1401 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1402 		
1403 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1404 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1405 		
1406 		glTexImage2D(
1407 			GL_TEXTURE_2D,
1408 			0,
1409 			GL_RGBA,
1410 			_texWidth, // needs to be power of 2
1411 			_texHeight,
1412 			0,
1413 			GL_RGBA,
1414 			GL_UNSIGNED_BYTE,
1415 			data);
1416 
1417 		assert(!glGetError());
1418 
1419 		_texCoordWidth = cast(float) _width / _texWidth;
1420 		_texCoordHeight = cast(float) _height / _texHeight;
1421 
1422 		if(freeRequired)
1423 			free(cast(void*) data);
1424 		glBindTexture(GL_TEXTURE_2D, 0);
1425 	}
1426 
1427 	/// ditto
1428 	void bindFrom(T, FONT)(FONT* font, int size, in T[] text) if(is(T == char)) {
1429 		assert(font !is null);
1430 		int width, height;
1431 		auto data = font.renderString(text, size, width, height);
1432 		auto image = new TrueColorImage(width, height);
1433 		int pos = 0;
1434 		foreach(y; 0 .. height)
1435 		foreach(x; 0 .. width) {
1436 			image.imageData.bytes[pos++] = 255;
1437 			image.imageData.bytes[pos++] = 255;
1438 			image.imageData.bytes[pos++] = 255;
1439 			image.imageData.bytes[pos++] = data[0];
1440 			data = data[1 .. $];
1441 		}
1442 		assert(data.length == 0);
1443 
1444 		bindFrom(image);
1445 	}
1446 
1447 	/// Deletes the texture. Using it after calling this is undefined behavior
1448 	void dispose() {
1449 		glDeleteTextures(1, &_tex);
1450 		_tex = 0;
1451 	}
1452 
1453 	~this() {
1454 		if(_tex > 0)
1455 			dispose();
1456 	}
1457 }
1458 
1459 /+
1460 	FIXME: i want to do stbtt_GetBakedQuad for ASCII and use that
1461 	for simple cases especially numbers. for other stuff you can
1462 	create the texture for the text above.
1463 +/
1464 
1465 ///
1466 void clearOpenGlScreen(SimpleWindow window) {
1467 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);
1468 }
1469 
1470