1 /**
2  * ae.demo.inputtiming.main
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.demo.inputtiming.main;
15 
16 import core.time;
17 
18 import ae.ui.app.application;
19 import ae.ui.app.main;
20 import ae.ui.shell.shell;
21 import ae.ui.shell.sdl2.shell;
22 import ae.ui.video.renderer;
23 import ae.ui.video.sdl2.video;
24 import ae.utils.fps;
25 import ae.utils.math;
26 import ae.utils.graphics.image;
27 
28 alias ae.utils.math.abs abs; // Issue 314?
29 
30 final class MyApplication : Application
31 {
32 	override string getName() { return "Demo/Input"; }
33 	override string getCompanyName() { return "CyberShadow"; }
34 
35 	Shell shell;
36 
37 	enum BAND_WIDTH = 800;
38 	enum BAND_INTERVAL = 100;
39 	enum BAND_TOP = 50;
40 	enum BAND_HEIGHT = 30;
41 	enum BAND_HNSECS_PER_PIXEL = 200_000;
42 	enum HISTORY_TOP = 200;
43 	enum HISTORY_HEIGHT = 50;
44 
45 	enum Device : int { keyboard, joypad, mouse, max }
46 	enum SampleType : int { precision, duration, max }
47 
48 	int[] history[Device.max][SampleType.max];
49 	enum SAMPLE_COLORS = [BGRX(0, 0, 255), BGRX(0, 255, 0)];
50 
51 	/// Some (precise) time value of the moment, in hnsecs.
52 	@property long now() { return TickDuration.currSystemTick.to!("hnsecs", long); }
53 
54 	override void render(Renderer s)
55 	{
56 		auto x = now / BAND_HNSECS_PER_PIXEL;
57 
58 		s.clear();
59 		s.line(BAND_WIDTH/2, BAND_TOP, BAND_WIDTH/2, BAND_TOP + BAND_HEIGHT, BGRX(0, 0, 255));
60 		foreach (lx; 0..BAND_WIDTH)
61 			if ((lx+x)%BAND_INTERVAL == 0)
62 				s.line(lx, BAND_TOP+BAND_HEIGHT, lx, BAND_TOP+BAND_HEIGHT*2, BGRX(0, 255, 0));
63 
64 		foreach (device, deviceSamples; history)
65 			foreach (sampleType, samples; deviceSamples)
66 			{
67 				auto y = HISTORY_TOP + HISTORY_HEIGHT * (device*2 + sampleType + 1);
68 				foreach (index, sample; samples)
69 				{
70 					if (sample > HISTORY_HEIGHT)
71 						sample = HISTORY_HEIGHT;
72 					s.line(index, y - sample, index, y, SAMPLE_COLORS[sampleType]);
73 				}
74 			}
75 	}
76 
77 	override int run(string[] args)
78 	{
79 		shell = new SDL2Shell(this);
80 		shell.video = new SDL2Video();
81 		shell.run();
82 		shell.video.shutdown();
83 		return 0;
84 	}
85 
86 	long pressed;
87 
88 	void keyDown(Device device)
89 	{
90 		pressed = now;
91 		auto x = cast(int)(pressed / BAND_HNSECS_PER_PIXEL + BAND_WIDTH/2) % BAND_INTERVAL;
92 		if (x > BAND_INTERVAL/2)
93 			x -= BAND_INTERVAL;
94 		history[device][SampleType.precision] ~= abs(x);
95 	}
96 
97 	void keyUp(Device device)
98 	{
99 		auto duration = now - pressed;
100 		history[device][SampleType.duration] ~= cast(int)(duration / BAND_HNSECS_PER_PIXEL);
101 	}
102 
103 	override void handleKeyDown(Key key, dchar character)
104 	{
105 		if (key == Key.esc)
106 			shell.quit();
107 		else
108 			keyDown(Device.keyboard);
109 	}
110 
111 	override void handleKeyUp(Key key)
112 	{
113 		keyUp  (Device.keyboard);
114 	}
115 
116 	override bool needJoystick() { return true; }
117 
118 	override void handleJoyButtonDown(int button)
119 	{
120 		keyDown(Device.joypad);
121 	}
122 
123 	override void handleJoyButtonUp  (int button)
124 	{
125 		keyUp  (Device.joypad);
126 	}
127 
128 	override void handleMouseDown(uint x, uint y, MouseButton button)
129 	{
130 		keyDown(Device.mouse);
131 	}
132 
133 	override void handleMouseUp(uint x, uint y, MouseButton button)
134 	{
135 		keyUp  (Device.mouse);
136 	}
137 
138 	override void handleQuit()
139 	{
140 		shell.quit();
141 	}
142 }
143 
144 shared static this()
145 {
146 	createApplication!MyApplication();
147 }