1 module cat.wheel.events;
2 
3 import std.string;
4 import std.array;
5 import core.sync.mutex;
6 import bindbc.sdl;
7 
8 import cat.wheel.except;
9 
10 /**
11  * Initialize the SDL library. Call this once, and before anything else.
12  * systems: The subsystems to initialize
13  */
14 void initSDL(uint systems) {
15 	SDL_Init(systems).check;
16 }
17 
18 /**
19  * Initialize one or more subsystems of SDL.
20  * systems: The subsystems to initialize
21  */
22 void initSystem(uint systems) {
23 	SDL_InitSubSystem(systems).check;
24 }
25 
26 /**
27  * Shuts down the SDL library and all initialized subsystems
28  */
29 void quitSDL() {
30 	SDL_Quit();
31 }
32 
33 /**
34  * Shuts down specific subsystems of SDL
35  * systems: The subsystems to quit
36  */
37 void quitSystem(uint systems) {
38 	SDL_QuitSubSystem(systems);
39 }
40 
41 /**
42  * Arguments to event handler delegates
43  */
44 class EventArgs {}
45 
46 /**
47  * The argument to the default PUMP event
48  */
49 class PumpEventArgs : EventArgs {
50 	package this(SDL_Event e) { event = e; }
51 
52 	/**
53 	 * The event
54 	 */
55 	public SDL_Event event;
56 }
57 
58 /**
59  * Represents an SDL event handler, which controls the main thread and calls delegate functions specified by the program
60  */
61 class Handler {
62 	/**
63 	 * Adds a delegate function to be called by this handler when a specific event occurs
64 	 */
65 	void addDelegate(void delegate(EventArgs) del, int event) nothrow @safe {
66 		_delegates[event] ~= del;
67 	}
68 
69 	/**
70 	 * Forces the main loop to stop
71 	 */
72 	void stop() nothrow pure @safe {
73 		_doContinue = false;
74 	}
75 
76 	/**
77 	 * Starts the main loop for this Handler. The loop will run until the stop function has been called
78 	 */
79 	void handle() {
80 		runDelegates(ED_START);
81 
82 		while (_doContinue) {
83 			runDelegates(ED_PRE_PUMP);
84 
85 			_eventsSDL = _eventsSDL.init;
86 			auto appender = appender(_eventsSDL);
87 
88 			SDL_Event e;
89 			while (SDL_PollEvent(&e)) {
90 				appender.put(e);
91 
92 				runDelegates(ED_PUMP, new PumpEventArgs(e));
93 			}
94 
95 			runDelegates(ED_POST_PUMP);
96 
97 			const int currentTime = SDL_GetTicks();
98 			_deltaTime = _lastTick - currentTime;
99 			_lastTick = currentTime;
100 
101 			runDelegates(ED_PRE_TICK);
102 			runDelegates(ED_TICK);
103 			runDelegates(ED_POST_TICK);
104 		}
105 
106 		runDelegates(ED_STOP);
107 	}
108 
109 	/**
110 	 * Dispatches an event to be run by user-defined delegate functions
111 	 * event: Event UID
112 	 */
113 	void callEvent(uint event, EventArgs arg = new EventArgs()) {
114 		runDelegates(event, arg);
115 	}
116 
117 	/**
118 	 * The amount of time it took between the previous frame and this frame
119 	 * This function should only be called on the main thread.
120 	 */
121 	@property time() nothrow @safe {
122 		return _deltaTime;
123 	}
124 
125 	/**
126 	 * The SDL events that happened this frame.
127 	 * This function should only be called on the main thread.
128 	 */
129 	@property events() nothrow @safe {
130 		return _eventsSDL;
131 	}
132 
133 private:
134 	void delegate(EventArgs)[][int] _delegates;
135 
136 	void runDelegates(uint type, EventArgs args = new EventArgs()) {
137 		if (type in _delegates) {
138 			foreach (d; _delegates[type]) {
139 				d(args);
140 			}
141 		}
142 	}
143 
144 	shared(bool) _doContinue = true;
145 	shared(SDL_Event[]) _eventsSDL;
146 
147 	int _deltaTime;
148 	int _lastTick;
149 }
150 
151 enum : uint {
152 	/// Run once at the start of handling
153 	ED_START = 0b000,
154 	ED_PRE_PUMP = 0b001,
155 	ED_PUMP = 0b010,
156 	ED_POST_PUMP = 0b011,
157 	ED_PRE_TICK = 0b100,
158 	ED_TICK = 0b101,
159 	ED_POST_TICK = 0b110,
160 	ED_STOP = 0b111
161 }