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.core.ownership; 39 import dlib.container.dict; 40 import dlib.container.array; 41 import dlib.text.lexer; 42 import dlib.text.str; 43 import dagon.core.event; 44 import dagon.core.bindings; 45 import dagon.core.keycodes; 46 import dagon.core.config; 47 48 enum BindingType 49 { 50 None, 51 Keyboard, 52 MouseButton, 53 MouseAxis, 54 GamepadButton, 55 GamepadAxis, 56 VirtualAxis // axis created from two buttons 57 } 58 59 struct Binding 60 { 61 BindingType type; 62 union 63 { 64 int key; 65 int button; 66 int axis; 67 68 private struct Vaxis 69 { 70 BindingType typePos; 71 int pos; 72 BindingType typeNeg; 73 int neg; 74 } 75 Vaxis vaxis; // virtual axis 76 } 77 } 78 79 class InputManager 80 { 81 EventManager eventManager; 82 83 alias Bindings = Array!Binding; 84 85 Dict!(Bindings, string) bindings; 86 87 Configuration config; 88 89 this(EventManager em) 90 { 91 eventManager = em; 92 bindings = dict!(Bindings, string)(); 93 94 config = New!Configuration(null); 95 if (!config.fromFile("input.conf")) 96 { 97 writeln("Warning: no \"input.conf\" found"); 98 } 99 100 foreach(name, value; config.props.props) 101 { 102 addBindings(name, value.data); 103 } 104 } 105 106 ~this() 107 { 108 foreach(name, bind; bindings) 109 { 110 bind.free(); 111 } 112 Delete(bindings); 113 Delete(config); 114 } 115 116 private Binding parseBinding(Lexer lexer) 117 { 118 // Binding format consist of device type and name(or number) 119 // coresponding to button or axis of this device 120 // eg. kb_up, kb_w, ma_0, mb_1, gb_a, gb_x, ga_leftx, ga_lefttrigger 121 // kb -> keybaord, by name 122 // ma -> mouse axis, by number 123 // mb -> mouse button, by number 124 // ga -> gamepad axis, by name 125 // gb -> gamepad button, by name 126 // va -> virtual axis, special syntax: va(kb_up, kb_down) 127 128 BindingType type = BindingType.None; 129 int result = -1; 130 131 auto lexeme = lexer.getLexeme(); 132 133 switch(lexeme) 134 { 135 case "kb": type = BindingType.Keyboard; break; 136 case "ma": type = BindingType.MouseAxis; break; 137 case "mb": type = BindingType.MouseButton; break; 138 case "ga": type = BindingType.GamepadAxis; break; 139 case "gb": type = BindingType.GamepadButton; break; 140 case "va": type = BindingType.VirtualAxis; break; 141 142 default: goto fail; 143 } 144 145 lexeme = lexer.getLexeme(); 146 147 if (type != BindingType.VirtualAxis) 148 { 149 if (lexeme != "_") 150 goto fail; 151 152 lexeme = lexer.getLexeme(); 153 154 if(lexeme.isNumeric) 155 { 156 result = to!int(lexeme); 157 } 158 else 159 { 160 String svalue = String(lexeme); 161 const(char)* cvalue = svalue.ptr; 162 switch(type) 163 { 164 case BindingType.Keyboard: result = cast(int)SDL_GetScancodeFromName(cvalue); break; 165 case BindingType.GamepadAxis: result = cast(int)SDL_GameControllerGetAxisFromString(cvalue); break; 166 case BindingType.GamepadButton: result = cast(int)SDL_GameControllerGetButtonFromString(cvalue); break; 167 168 default: break; 169 } 170 171 svalue.free(); 172 } 173 174 if (type != BindingType.None || result > 0) 175 return Binding(type, result); 176 } 177 else 178 { 179 // Virtual axis 180 if (lexeme != "(") 181 goto fail; 182 183 Binding pos = parseBinding(lexer); 184 185 if (pos.type != BindingType.Keyboard && 186 pos.type != BindingType.MouseButton && 187 pos.type != BindingType.GamepadButton) 188 goto fail; 189 190 lexeme = lexer.getLexeme(); 191 if (lexeme != ",") 192 goto fail; 193 194 Binding neg = parseBinding(lexer); 195 196 if (neg.type != BindingType.Keyboard && 197 neg.type != BindingType.MouseButton && 198 neg.type != BindingType.GamepadButton) 199 goto fail; 200 201 lexeme = lexer.getLexeme(); 202 203 if (lexeme != ")") 204 goto fail; 205 206 Binding bind = Binding(type); 207 bind.vaxis.typePos = pos.type; 208 bind.vaxis.pos = pos.key; 209 bind.vaxis.typeNeg = neg.type; 210 bind.vaxis.neg = neg.key; 211 return bind; 212 } 213 214 fail: 215 return Binding(BindingType.None, -1); 216 } 217 218 void addBindings(string name, string value) 219 { 220 auto lexer = New!Lexer(value, ["_", ",", "(", ")"]); 221 lexer.ignoreWhitespaces = true; 222 223 while(true) 224 { 225 Binding b = parseBinding(lexer); 226 if (b.type == BindingType.None && b.key == -1) 227 { 228 writefln("Error: wrong binding format \"%s\"", value); 229 break; 230 } 231 232 if (auto binding = name in bindings) 233 { 234 binding.insertBack(b); 235 } 236 else 237 { 238 auto binds = Bindings(); 239 binds.insertBack(b); 240 bindings[name] = binds; 241 } 242 243 auto lexeme = lexer.getLexeme(); 244 if (lexeme == ",") 245 continue; 246 247 if (lexeme == "") 248 break; 249 } 250 251 Delete(lexer); 252 } 253 254 void clearBindings(string name) 255 { 256 if (auto binding = name in bindings) 257 { 258 binding.removeBack(cast(uint)binding.length); 259 } 260 } 261 262 bool getButton(Binding binding) 263 { 264 switch(binding.type) 265 { 266 case BindingType.Keyboard: 267 if (eventManager.keyPressed[binding.key]) return true; 268 break; 269 270 case BindingType.MouseButton: 271 if (eventManager.mouseButtonPressed[binding.button]) return true; 272 break; 273 274 case BindingType.GamepadButton: 275 if (eventManager.controllerButtonPressed[binding.button]) return true; 276 break; 277 278 default: 279 break; 280 } 281 282 return false; 283 } 284 285 bool getButton(string name) 286 { 287 auto b = name in bindings; 288 if (!b) 289 return false; 290 291 for(int i = 0; i < b.length; i++) 292 { 293 if (getButton((*b)[i])) return true; 294 } 295 296 return false; 297 } 298 299 bool getButtonUp(string name) 300 { 301 auto b = name in bindings; 302 if (!b) 303 return false; 304 305 for(int i = 0; i < b.length; i++) 306 { 307 auto binding = (*b)[i]; 308 switch(binding.type) 309 { 310 case BindingType.Keyboard: 311 if (eventManager.keyUp[binding.key]) return true; 312 break; 313 314 case BindingType.MouseButton: 315 if (eventManager.mouseButtonUp[binding.button]) return true; 316 break; 317 318 case BindingType.GamepadButton: 319 if (eventManager.controllerButtonUp[binding.button]) return true; 320 break; 321 322 default: 323 break; 324 } 325 } 326 327 return false; 328 } 329 330 bool getButtonDown(string name) 331 { 332 auto b = name in bindings; 333 if (!b) 334 return false; 335 336 for(int i = 0; i < b.length; i++) 337 { 338 auto binding = (*b)[i]; 339 340 switch(binding.type) 341 { 342 case BindingType.Keyboard: 343 if (eventManager.keyDown[binding.key]) return true; 344 break; 345 346 case BindingType.MouseButton: 347 if (eventManager.mouseButtonDown[binding.button]) return true; 348 break; 349 350 case BindingType.GamepadButton: 351 if (eventManager.controllerButtonDown[binding.button]) return true; 352 break; 353 354 default: 355 break; 356 } 357 } 358 359 return false; 360 } 361 362 float getAxis(string name) 363 { 364 auto b = name in bindings; 365 if (!b) 366 return false; 367 368 float result = 0.0f; 369 float aresult = 0.0f; // absolute result 370 371 for(int i = 0; i < b.length; i++) 372 { 373 auto binding = (*b)[i]; 374 float value = 0.0f; 375 376 switch(binding.type) 377 { 378 case BindingType.MouseAxis: 379 if (binding.axis == 0) 380 value = eventManager.mouseRelX / (eventManager.windowWidth * 0.5f); // map to -1 to 1 range 381 else if (binding.axis == 1) 382 value = eventManager.mouseRelY / (eventManager.windowHeight * 0.5f); 383 break; 384 385 case BindingType.GamepadAxis: 386 if (eventManager.gameControllerAvailable) 387 value = eventManager.gameControllerAxis(binding.axis); 388 break; 389 390 case BindingType.VirtualAxis: 391 value = getButton(*cast(Binding*)(&binding.vaxis.typePos)) ? 1.0f : 0.0f; 392 value += getButton(*cast(Binding*)(&binding.vaxis.typeNeg)) ? -1.0f : 0.0f; 393 break; 394 395 default: 396 break; 397 } 398 float avalue = abs(value); 399 if (avalue > aresult) 400 { 401 result = value; 402 aresult = avalue; 403 } 404 } 405 406 return result; 407 } 408 }