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 }