1 /**
2 Grimoire
3 Copyright (c) 2017 Enalye
4 
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising
7 from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute
11 it freely, subject to the following restrictions:
12 
13 	1. The origin of this software must not be misrepresented;
14 	   you must not claim that you wrote the original software.
15 	   If you use this software in a product, an acknowledgment
16 	   in the product documentation would be appreciated but
17 	   is not required.
18 
19 	2. Altered source versions must be plainly marked as such,
20 	   and must not be misrepresented as being the original software.
21 
22 	3. This notice may not be removed or altered from any source distribution.
23 */
24 
25 module common.event;
26 
27 import std.path;
28 import std..string;
29 import std.conv;
30 import std.utf;
31 
32 version(linux) {
33 	import core.sys.posix.unistd;
34 	import core.sys.posix.signal;
35 }
36 
37 import derelict.sdl2.sdl;
38 
39 import render.window;
40 import ui.widget;
41 import core.vec2;
42 
43 import common.settings;
44 import common.controller;
45 
46 static private {
47 	bool[512] _keys;
48 	bool[8] _buttons;
49 	Vec2f _mousePosition;
50 	Event[] _globalEvents;
51 	int[string] _keysMap;
52 }
53 
54 private bool _isRunning = false;
55 
56 enum EventType: uint {
57 	KeyUp,
58 	KeyDown,
59 	KeyInput,
60 	KeyDelete,
61 	KeyEnter,
62 	KeyDir,
63 	MouseUp,
64 	MouseDown,
65 	MouseUpdate,
66 	MouseWheel,
67 	Quit,
68 	DropFile,
69 	Resize,
70 	ModalOpen,
71 	ModalClose,
72 	ModalApply,
73 	ModalCancel,
74 	Callback
75 }
76 
77 struct Event {
78 	this(EventType _type) {
79 		type = _type;
80 	}
81 
82 	EventType type;
83 	string id;
84 	union {
85 		Vec2f position;
86 		Vec2i coord;
87 		string str;
88 		int ivalue;
89 		string[] sarray;
90 		Widget widget;
91 	}
92 }
93 
94 struct WidgetCallback {
95 	Widget widget;
96 	string id;
97 
98 	void trigger(Widget w) {
99 		Event event;
100 		event.type = EventType.Callback;
101 		event.id = id;
102 		event.widget = w;
103 		widget.onEvent(event);
104 	}
105 
106 	void trigger(string[] array) {
107 		Event event;
108 		event.type = EventType.Callback;
109 		event.id = id;
110 		event.sarray = array;
111 		widget.onEvent(event);
112 	}
113 
114 	void trigger(int value) {
115 		Event event;
116 		event.type = EventType.Callback;
117 		event.id = id;
118 		event.ivalue = value;
119 		widget.onEvent(event);
120 	}
121 }
122 
123 void bindKey(string keyName, uint keyId) {
124 	_keysMap[keyName] = keyId;
125 }
126 
127 bool isKeyDown(string keyName) {
128 	auto keyPtr = keyName in _keysMap;
129 
130 	if(keyPtr is null)
131 		throw new Exception("Undefined key: " ~ keyName);
132 
133 	return _keys[*keyPtr];
134 }
135 
136 bool getKeyDown(string keyName) {
137 	auto keyPtr = keyName in _keysMap;
138 
139 	if(keyPtr is null)
140 		throw new Exception("Undefined key: " ~ keyName);
141 
142 	auto value = _keys[*keyPtr];
143 	_keys[*keyPtr] = false;
144 	return value;
145 }
146 
147 bool isButtonDown(ubyte button) {
148 	return _buttons[button];
149 }
150 
151 Vec2f getMousePos() {
152 	return _mousePosition;
153 }
154 
155 void stopApplication() {
156 	_isRunning = false;
157 }
158 
159 bool isRunning() {
160 	return _isRunning;
161 }
162 
163 void sendEvent(EventType eventType) {
164 	Event event = Event(eventType);
165 	_globalEvents ~= event;
166 }
167 
168 void sendEvent(Event event) {
169 	_globalEvents ~= event;
170 }
171 
172 version(linux)
173 extern(C) void signalHandler(int sig) nothrow @nogc @system {
174 	_isRunning = false;
175 }
176 
177 void initializeEvents() {
178 	version(linux)
179 		signal(SIGINT, &signalHandler);
180 	_isRunning = true;
181 	_mousePosition = Vec2f.zero;
182     initializeControllers();
183 }
184 
185 void destroyEvents() {
186     destroyControllers();
187 }
188 
189 void updateEvents(float deltaTime) {
190     updateControllers(deltaTime);
191 }
192 
193 bool processEvents(IMainWidget mainWidget) {
194 	Event event;
195 	SDL_Event sdlEvent;
196 
197 	if(!_isRunning) {
198 		event.type = EventType.Quit;
199 		mainWidget.onEvent(event);
200         destroyWindow();
201 		return false;
202 	}
203 
204 	//Used to start receiving SDL_TEXTINPUT events
205 	SDL_StartTextInput();
206 	
207 	while (SDL_PollEvent(&sdlEvent)) {
208 		switch (sdlEvent.type) {
209 		case SDL_QUIT:
210 			_isRunning = false;
211 			event.type = EventType.Quit;
212 			mainWidget.onEvent(event);
213 			destroyWindow();
214 			//No operation involving the SDL after this.
215 			return false;
216 		case SDL_KEYDOWN:
217 			if (!_keys[sdlEvent.key.keysym.scancode])
218 				_keys[sdlEvent.key.keysym.scancode] = true;
219 			switch(sdlEvent.key.keysym.scancode) {
220 			case SDL_SCANCODE_DELETE:
221 				event.type = EventType.KeyDelete;
222 				event.ivalue = 1;
223 				mainWidget.onEvent(event);
224 				break;
225 			case SDL_SCANCODE_BACKSPACE:
226 				event.type = EventType.KeyDelete;
227 				event.ivalue = -1;
228 				mainWidget.onEvent(event);
229 				break;
230 			case SDL_SCANCODE_RETURN:
231 				event.type = EventType.KeyEnter;
232 				mainWidget.onEvent(event);
233 				break;
234 			case SDL_SCANCODE_UP:
235 				event.type = EventType.KeyDir;
236 				event.position = Vec2f(0f, -1f);
237 				mainWidget.onEvent(event);
238 				break;
239 			case SDL_SCANCODE_DOWN:
240 				event.type = EventType.KeyDir;
241 				event.position = Vec2f(0f, 1f);
242 				mainWidget.onEvent(event);
243 				break;
244 			case SDL_SCANCODE_LEFT:
245 				event.type = EventType.KeyDir;
246 				event.position = Vec2f(-1f, 0f);
247 				mainWidget.onEvent(event);
248 				break;
249 			case SDL_SCANCODE_RIGHT:
250 				event.type = EventType.KeyDir;
251 				event.position = Vec2f(1f, 0f);
252 				mainWidget.onEvent(event);
253 				break;
254 			default:
255 				break;
256 			}
257 			break;
258 		case SDL_KEYUP:
259 			if (_keys[sdlEvent.key.keysym.scancode])
260 				_keys[sdlEvent.key.keysym.scancode] = false;
261 			break;
262 		case SDL_TEXTINPUT:
263 			string text = to!string(sdlEvent.text.text);
264 			text.length = stride(text);
265 			event.type = EventType.KeyInput;
266 			event.str = text;
267 			mainWidget.onEvent(event);
268 			break;
269 		case SDL_MOUSEMOTION:
270 			_mousePosition.set(cast(float)sdlEvent.motion.x, cast(float)sdlEvent.motion.y);
271 			_mousePosition = getViewVirtualPos(_mousePosition);
272 
273 			event.type = EventType.MouseUpdate;
274 			event.position = _mousePosition;
275 
276 			mainWidget.onEvent(event);
277 			break;
278 		case SDL_MOUSEBUTTONDOWN:
279 			_mousePosition.set(cast(float)sdlEvent.motion.x, cast(float)sdlEvent.motion.y);
280 			_mousePosition = getViewVirtualPos(_mousePosition);
281 			_buttons[sdlEvent.button.button] = true;
282 			
283 			event.type = EventType.MouseDown;
284 			event.position = _mousePosition;
285 
286 			mainWidget.onEvent(event);
287 			break;
288 		case SDL_MOUSEBUTTONUP:
289 			_mousePosition.set(cast(float)sdlEvent.motion.x, cast(float)sdlEvent.motion.y);
290 			_mousePosition = getViewVirtualPos(_mousePosition);
291 			_buttons[sdlEvent.button.button] = false;
292 
293 			event.type = EventType.MouseUp;
294 			event.position = _mousePosition;
295 
296 			mainWidget.onEvent(event);
297 			break;
298 		case SDL_MOUSEWHEEL:
299 			event.type = EventType.MouseWheel;
300 			event.position = Vec2f(sdlEvent.wheel.x, sdlEvent.wheel.y);
301 			mainWidget.onEvent(event);
302 			break;
303 		case SDL_WINDOWEVENT:
304 			switch (sdlEvent.window.event) {
305 				case SDL_WINDOWEVENT_RESIZED:
306 					setWindowSize(Vec2u(sdlEvent.window.data1, sdlEvent.window.data2));
307 					break;
308 				default:
309 					break;
310 			}
311 			break;
312 		case SDL_DROPFILE:
313 			string path = to!string(fromStringz(sdlEvent.drop.file));
314 			size_t index;
315 			while(-1 != (index = path.indexOfAny("%"))) {
316 				if((index + 3) > path.length)
317 					break;
318 				string str = path[index + 1 .. index + 3];
319 				int utfValue = parse!int(str, 16);
320 				char utfChar = to!char(utfValue);
321 
322 				if(index == 0)
323 					path = utfChar ~ path[3 .. $];
324 				else if((index + 3) == path.length)
325 					path = path[0 .. index] ~ utfChar;
326 				else
327 					path = path[0 .. index] ~ utfChar ~ path[index + 3 .. $];		
328 			}
329 
330 			event.type = EventType.DropFile;
331 			event.str = path;
332 			mainWidget.onEvent(event);
333 
334 			SDL_free(sdlEvent.drop.file);
335 			break;
336         case SDL_CONTROLLERDEVICEADDED:
337             addController(sdlEvent.cdevice.which);
338             break;
339         case SDL_CONTROLLERDEVICEREMOVED:
340             removeController(sdlEvent.cdevice.which);
341             break;
342         case SDL_CONTROLLERDEVICEREMAPPED:
343             remapController(sdlEvent.cdevice.which);
344             break;
345         case SDL_CONTROLLERAXISMOTION:
346             setControllerAxis(sdlEvent.caxis.axis, sdlEvent.caxis.value);
347             break;
348         case SDL_CONTROLLERBUTTONDOWN:
349             setControllerButton(sdlEvent.cbutton.button, true);
350             break;
351         case SDL_CONTROLLERBUTTONUP:
352             setControllerButton(sdlEvent.cbutton.button, false);
353             break;
354 		default:
355 			break;
356 		}
357 	}
358 
359 	foreach(Event globalEvent; _globalEvents) {
360 		switch(globalEvent.type) with(EventType) {
361 			case Quit:
362 				_isRunning = false;
363 				mainWidget.onEvent(globalEvent);
364                 destroyWindow();
365 				return false;
366 			default:
367 				mainWidget.onEvent(globalEvent);
368 				break;
369 		}
370 	}
371 	_globalEvents.length = 0;
372 
373 	return true;
374 }