1 /* 2 Copyright (c) 2019 Mateusz Muszyński 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module dagon.core.input; 30 31 import std.stdio; 32 import std.ascii; 33 import std.conv : to; 34 import std.math : abs; 35 import std.algorithm.searching : startsWith; 36 import std.string : isNumeric; 37 import dlib.core.memory; 38 import dlib.container.dict; 39 import dlib.container.array; 40 import dlib.text.lexer; 41 import dlib.text.unmanagedstring; 42 import dagon.core.event; 43 import dagon.core.libs; 44 import dagon.core.ownership; 45 import dagon.core.keycodes; 46 import dagon.resource.asset; 47 import dagon.resource.config; 48 49 enum BindingType 50 { 51 None, 52 Keyboard, 53 MouseButton, 54 MouseAxis, 55 GamepadButton, 56 GamepadAxis, 57 VirtualAxis // axis created from two buttons 58 } 59 60 struct Binding 61 { 62 BindingType type; 63 union 64 { 65 int key; 66 int button; 67 int axis; 68 69 private struct Vaxis 70 { 71 BindingType typePos; 72 int pos; 73 BindingType typeNeg; 74 int neg; 75 } 76 Vaxis vaxis; // virtual axis 77 } 78 } 79 80 class InputManager 81 { 82 EventManager eventManager; 83 84 alias Bindings = DynamicArray!Binding; 85 86 Dict!(Bindings, string) bindings; 87 88 Configuration config; 89 90 this(EventManager em) 91 { 92 eventManager = em; 93 bindings = dict!(Bindings, string)(); 94 95 config = New!Configuration(null); 96 if (!config.fromFile("input.conf")) 97 { 98 writeln("Warning: no \"input.conf\" found"); 99 } 100 101 foreach(name, value; config.props.props) 102 { 103 addBindings(name, value.data); 104 } 105 } 106 107 ~this() 108 { 109 foreach(name, bind; bindings) 110 { 111 bind.free(); 112 } 113 Delete(bindings); 114 Delete(config); 115 } 116 117 private Binding parseBinding(Lexer lexer) 118 { 119 // Binding format consist of device type and name(or number) 120 // coresponding to button or axis of this device 121 // eg. kb_up, kb_w, ma_0, mb_1, gb_a, gb_x, ga_leftx, ga_lefttrigger 122 // kb -> keybaord, by name 123 // ma -> mouse axis, by number 124 // mb -> mouse button, by number 125 // ga -> gamepad axis, by name 126 // gb -> gamepad button, by name 127 // va -> virtual axis, special syntax: va(kb_up, kb_down) 128 129 BindingType type = BindingType.None; 130 int result = -1; 131 132 auto lexeme = lexer.getLexeme(); 133 134 switch(lexeme) 135 { 136 case "kb": type = BindingType.Keyboard; break; 137 case "ma": type = BindingType.MouseAxis; break; 138 case "mb": type = BindingType.MouseButton; break; 139 case "ga": type = BindingType.GamepadAxis; break; 140 case "gb": type = BindingType.GamepadButton; break; 141 case "va": type = BindingType.VirtualAxis; break; 142 143 default: goto fail; 144 } 145 146 lexeme = lexer.getLexeme(); 147 148 if (type != BindingType.VirtualAxis) 149 { 150 if (lexeme != "_") 151 goto fail; 152 153 lexeme = lexer.getLexeme(); 154 155 if(lexeme.isNumeric) 156 { 157 result = to!int(lexeme); 158 } 159 else 160 { 161 String svalue = String(lexeme); 162 char* cvalue = svalue.cString; 163 switch(type) 164 { 165 case BindingType.Keyboard: result = cast(int)SDL_GetScancodeFromName(cvalue); break; 166 case BindingType.GamepadAxis: result = cast(int)SDL_GameControllerGetAxisFromString(cvalue); break; 167 case BindingType.GamepadButton: result = cast(int)SDL_GameControllerGetButtonFromString(cvalue); break; 168 169 default: break; 170 } 171 172 svalue.free(); 173 } 174 175 if (type != BindingType.None || result > 0) 176 return Binding(type, result); 177 } 178 else 179 { 180 // Virtual axis 181 if (lexeme != "(") 182 goto fail; 183 184 Binding pos = parseBinding(lexer); 185 186 if (pos.type != BindingType.Keyboard && 187 pos.type != BindingType.MouseButton && 188 pos.type != BindingType.GamepadButton) 189 goto fail; 190 191 lexeme = lexer.getLexeme(); 192 if (lexeme != ",") 193 goto fail; 194 195 Binding neg = parseBinding(lexer); 196 197 if (neg.type != BindingType.Keyboard && 198 neg.type != BindingType.MouseButton && 199 neg.type != BindingType.GamepadButton) 200 goto fail; 201 202 lexeme = lexer.getLexeme(); 203 204 if (lexeme != ")") 205 goto fail; 206 207 Binding bind = Binding(type); 208 bind.vaxis.typePos = pos.type; 209 bind.vaxis.pos = pos.key; 210 bind.vaxis.typeNeg = neg.type; 211 bind.vaxis.neg = neg.key; 212 return bind; 213 } 214 215 fail: 216 return Binding(BindingType.None, -1); 217 } 218 219 void addBindings(string name, string value) 220 { 221 auto lexer = New!Lexer(value, ["_", ",", "(", ")"]); 222 lexer.ignoreWhitespaces = true; 223 224 while(true) 225 { 226 Binding b = parseBinding(lexer); 227 if (b.type == BindingType.None && b.key == -1) 228 { 229 writefln("Error: wrong binding format \"%s\"", value); 230 break; 231 } 232 233 if (auto binding = name in bindings) 234 { 235 binding.insertBack(b); 236 } 237 else 238 { 239 auto binds = Bindings(); 240 binds.insertBack(b); 241 bindings[name] = binds; 242 } 243 244 auto lexeme = lexer.getLexeme(); 245 if (lexeme == ",") 246 continue; 247 248 if (lexeme == "") 249 break; 250 } 251 252 Delete(lexer); 253 } 254 255 void clearBindings(string name) 256 { 257 if (auto binding = name in bindings) 258 { 259 binding.removeBack(cast(uint)binding.length); 260 } 261 } 262 263 bool getButton(Binding binding) 264 { 265 switch(binding.type) 266 { 267 case BindingType.Keyboard: 268 if (eventManager.keyPressed[binding.key]) return true; 269 break; 270 271 case BindingType.MouseButton: 272 if (eventManager.mouseButtonPressed[binding.button]) return true; 273 break; 274 275 case BindingType.GamepadButton: 276 if (eventManager.controllerButtonPressed[binding.button]) return true; 277 break; 278 279 default: 280 break; 281 } 282 283 return false; 284 } 285 286 bool getButton(string name) 287 { 288 auto b = name in bindings; 289 if (!b) 290 return false; 291 292 for(int i = 0; i < b.length; i++) 293 { 294 if (getButton((*b)[i])) return true; 295 } 296 297 return false; 298 } 299 300 bool getButtonUp(string name) 301 { 302 auto b = name in bindings; 303 if (!b) 304 return false; 305 306 for(int i = 0; i < b.length; i++) 307 { 308 auto binding = (*b)[i]; 309 switch(binding.type) 310 { 311 case BindingType.Keyboard: 312 if (eventManager.keyUp[binding.key]) return true; 313 break; 314 315 case BindingType.MouseButton: 316 if (eventManager.mouseButtonUp[binding.button]) return true; 317 break; 318 319 case BindingType.GamepadButton: 320 if (eventManager.controllerButtonUp[binding.button]) return true; 321 break; 322 323 default: 324 break; 325 } 326 } 327 328 return false; 329 } 330 331 bool getButtonDown(string name) 332 { 333 auto b = name in bindings; 334 if (!b) 335 return false; 336 337 for(int i = 0; i < b.length; i++) 338 { 339 auto binding = (*b)[i]; 340 341 switch(binding.type) 342 { 343 case BindingType.Keyboard: 344 if (eventManager.keyDown[binding.key]) return true; 345 break; 346 347 case BindingType.MouseButton: 348 if (eventManager.mouseButtonDown[binding.button]) return true; 349 break; 350 351 case BindingType.GamepadButton: 352 if (eventManager.controllerButtonDown[binding.button]) return true; 353 break; 354 355 default: 356 break; 357 } 358 } 359 360 return false; 361 } 362 363 float getAxis(string name) 364 { 365 auto b = name in bindings; 366 if (!b) 367 return false; 368 369 float result = 0.0f; 370 float aresult = 0.0f; // absolute result 371 372 for(int i = 0; i < b.length; i++) 373 { 374 auto binding = (*b)[i]; 375 float value = 0.0f; 376 377 switch(binding.type) 378 { 379 case BindingType.MouseAxis: 380 if (binding.axis == 0) 381 value = eventManager.mouseRelX / (eventManager.windowWidth * 0.5f); // map to -1 to 1 range 382 else if (binding.axis == 1) 383 value = eventManager.mouseRelY / (eventManager.windowHeight * 0.5f); 384 break; 385 386 case BindingType.GamepadAxis: 387 if (eventManager.gameControllerAvailable) 388 value = eventManager.gameControllerAxis(binding.axis); 389 break; 390 391 case BindingType.VirtualAxis: 392 value = getButton(*cast(Binding*)(&binding.vaxis.typePos)) ? 1.0f : 0.0f; 393 value += getButton(*cast(Binding*)(&binding.vaxis.typeNeg)) ? -1.0f : 0.0f; 394 break; 395 396 default: 397 break; 398 } 399 float avalue = abs(value); 400 if (avalue > aresult) 401 { 402 result = value; 403 aresult = avalue; 404 } 405 } 406 407 return result; 408 } 409 }