1 /** 2 * Defines InputSystem and State, which are used for defining an input device (keyboard, mouse, gamepad, etc.) 3 */ 4 module dash.utility.input.inputsystem; 5 import dash.utility.input.input; 6 7 import std.typecons, std.traits; 8 9 package: 10 /// The type each button is stored as. 11 alias ButtonStorageType = bool; 12 /// The type each axis is stored as. 13 alias AxisStorageType = float; 14 15 /** 16 * Defines a system of inputs, buttons, axes or both. 17 * 18 * Params: 19 * ButtonEnum = The enum of buttons for this system. 20 * AxisEnum = The enum of axes for this system. 21 */ 22 final abstract class InputSystem( ButtonEnum, AxisEnum ) 23 { 24 static: 25 public: 26 /// Whether or not the system has buttons. 27 enum HasButtons = !is( ButtonEnum == void ); 28 /// Whether or not the system has axes. 29 enum HasAxes = !is( AxisEnum == void ); 30 31 package: 32 /** 33 * Initialize all states and events. 34 */ 35 void initialize() 36 { 37 static if( HasButtons ) 38 { 39 buttonCurrent.reset(); 40 buttonPrevious.reset(); 41 buttonStaging.reset(); 42 43 foreach( key; buttonEvents.keys ) 44 buttonEvents.remove( key ); 45 } 46 static if( HasAxes ) 47 { 48 axisCurrent.reset(); 49 axisStaging.reset(); 50 51 foreach( key; axisEvents.keys ) 52 axisEvents.remove( key ); 53 } 54 } 55 56 /** 57 * Update all events and states. 58 */ 59 void update() 60 { 61 static if( HasButtons ) 62 { 63 auto diffButtons = buttonStaging - buttonCurrent; 64 buttonPrevious = buttonCurrent; 65 buttonCurrent = buttonStaging; 66 67 foreach( state; diffButtons ) 68 if( auto buttonEvent = state[ 0 ] in buttonEvents ) 69 foreach( event; *buttonEvent ) 70 event( state[ 0 ], state[ 1 ] ); 71 72 Input.processDiffs!(typeof(this))( diffButtons ); 73 } 74 static if( HasAxes ) 75 { 76 auto diffAxis = axisStaging - axisCurrent; 77 axisCurrent = axisStaging; 78 79 foreach( state; diffAxis ) 80 if( auto axisEvent = state[ 0 ] in axisEvents ) 81 foreach( event; *axisEvent ) 82 event( state[ 0 ], state[ 1 ] ); 83 84 Input.processDiffs!(typeof(this))( diffAxis ); 85 } 86 } 87 88 // If we have buttons 89 static if( HasButtons ) 90 { 91 public: 92 /// The enum of buttons that the input system has. 93 alias Buttons = ButtonState.Inputs; 94 /// A delegate that takes the changed button and the new state. 95 alias ButtonEvent = void delegate( Buttons, ButtonStorageType ); 96 /// A delegate that takes the changed button. 97 alias ButtonStateEvent = void delegate( Buttons ); 98 99 /** 100 * Check if a given button is down. 101 * 102 * Params: 103 * buttonCode = The button to check. 104 * checkPrevious = Whether or not to make sure the button was down last frame. 105 * 106 * Returns: The state of the button. 107 */ 108 ButtonStorageType getButtonState( Buttons buttonCode ) 109 { 110 return buttonCurrent[ buttonCode ]; 111 } 112 113 /** 114 * Check if a given button is down. 115 * 116 * Params: 117 * buttonCode = The code of the button to check. 118 * checkPrevious = Whether or not to make sure the button was down last frame. 119 * 120 * Returns: The state of the button. 121 */ 122 ButtonStorageType isButtonDown( Buttons buttonCode, bool checkPrevious = false ) 123 { 124 return buttonCurrent[ buttonCode ] && ( !checkPrevious || !buttonPrevious[ buttonCode ] ); 125 } 126 127 /** 128 * Check if a given button is up. 129 * 130 * Params: 131 * buttonCode = The code of the button to check. 132 * checkPrevious = Whether or not to make sure the button was up last frame. 133 * 134 * Returns: The state of the button. 135 */ 136 ButtonStorageType isButtonUp( Buttons buttonCode, bool checkPrevious = false ) 137 { 138 return !buttonCurrent[ buttonCode ] && ( !checkPrevious || buttonPrevious[ buttonCode ] ); 139 } 140 141 /** 142 * Add an event to be fired when the given button changes. 143 * 144 * Params: 145 * buttonCode = The code of the button to add the event to. 146 * func = The function to call when the button state changes. 147 */ 148 deprecated( "Use Input.addButtonEvent with a binding instead." ) 149 void addButtonEvent( Buttons buttonCode, ButtonEvent func ) 150 { 151 buttonEvents[ buttonCode ] ~= func; 152 } 153 154 /** 155 * Add a button event only when the button is down. 156 */ 157 deprecated( "Use Input.addButtonDownEvent with a binding instead." ) 158 void addButtonDownEvent( Buttons buttonCode, ButtonStateEvent func ) 159 { 160 addButtonEvent( buttonCode, ( Buttons buttonCode, ButtonStorageType newState ) { if( newState ) func( buttonCode ); } ); 161 } 162 163 /** 164 * Add a button event only when the button is up. 165 */ 166 deprecated( "Use Input.addButtonUpEvent with a binding instead." ) 167 void addButtonUpEvent( Buttons buttonCode, ButtonStateEvent func ) 168 { 169 addButtonEvent( buttonCode, ( Buttons buttonCode, ButtonStorageType newState ) { if( !newState ) func( buttonCode ); } ); 170 } 171 172 /** 173 * Sets the state of the button to be assigned at the beginning of next frame. 174 * Should only be called from a window controller. 175 */ 176 void setButtonState( Buttons buttonCode, ButtonStorageType newState ) 177 { 178 // HACK: Don't mind me. 179 import dash.utility.input.mouse, dash.core.dgame, dash.graphics.graphics; 180 import dash.utility.bindings.awesomium; 181 version( Windows ) { 182 static if( is( Buttons == MouseButtons ) ) 183 { 184 if( buttonCode == MouseButtons.Left ) 185 { 186 auto ui = DGame.instance.activeScene.ui; 187 auto mousePos = Input.mousePos; 188 auto offset = ( Graphics.width * (mousePos.y - 1) + mousePos.x ) * 4; 189 auto transparency = ui.view.glBuffer[ offset + 3 ]; 190 191 import dash.utility.output; 192 tracef( "Transparency at point %d, %d: %d", cast(int)mousePos.x, cast(int)( Graphics.height - mousePos.y ), transparency ); 193 194 if( ui && newState && transparency > 0 ) 195 { 196 awe_webview_inject_mouse_down( ui.view.webView, awe_mousebutton.AWE_MB_LEFT ); 197 } 198 else 199 { 200 buttonStaging[ buttonCode ] = newState; 201 202 if( !newState ) 203 { 204 awe_webview_inject_mouse_up( ui.view.webView, awe_mousebutton.AWE_MB_LEFT ); 205 } 206 } 207 } 208 else 209 { 210 buttonStaging[ buttonCode ] = newState; 211 } 212 } 213 else 214 { 215 buttonStaging[ buttonCode ] = newState; 216 } 217 } 218 else 219 { 220 buttonStaging[ buttonCode ] = newState; 221 } 222 } 223 224 private: 225 /// The struct storing the state of the buttons. 226 alias ButtonState = State!( ButtonStorageType, ButtonEnum ); 227 228 /// The state of the buttons as of the beginning of the current frame. 229 ButtonState buttonCurrent; 230 /// The state of the buttons for the last frame. 231 ButtonState buttonPrevious; 232 /// The state of the buttons that has not been applied yet. 233 ButtonState buttonStaging; 234 235 /// The events tied to the buttons of this system. 236 ButtonEvent[][ Buttons ] buttonEvents; 237 } 238 239 // If we have axes 240 static if( HasAxes ) 241 { 242 public: 243 /// The enum of axes that the input system has. 244 alias Axes = AxisState.Inputs; 245 /// A delegate that takes the changed axis and the new state. 246 alias AxisEvent = void delegate( Axes, AxisStorageType ); 247 248 /** 249 * Get the state of a given axis. 250 * 251 * Params: 252 * axis = The axis to get the state of. 253 * 254 * Returns: The state of the axis. 255 */ 256 AxisStorageType getAxisState( Axes axis ) 257 { 258 return axisCurrent[ axis ]; 259 } 260 261 /** 262 * Add an event to be fired when the given axis changes. 263 * 264 * Params: 265 * axis = The name of the input to add the event to. 266 * event = The event to trigger when the axis state changes. 267 */ 268 deprecated( "Use Input.addAxisEvent with a binding instead." ) 269 void addAxisEvent( Axes axis, AxisEvent event ) 270 { 271 axisEvents[ axis ] ~= event; 272 } 273 274 /** 275 * Sets the state of the axis to be assigned at the beginning of next frame. 276 * Should only be called from a window controller. 277 */ 278 void setAxisState( Axes axisCode, AxisStorageType newState ) 279 { 280 axisStaging[ axisCode ] = newState; 281 } 282 283 private: 284 /// The struct storing the state of the axes. 285 alias AxisState = State!( AxisStorageType, AxisEnum ); 286 /// The state of the axes as of the beginning of the current frame. 287 AxisState axisCurrent; 288 /// The state of the axes that has not been applied yet. 289 AxisState axisStaging; 290 291 /// The events tied to the axes of this system. 292 AxisEvent[][ Axes ] axisEvents; 293 } 294 } 295 296 /** 297 * Represents the state of an input method (ie. keyboard, gamepad, etc.). 298 * 299 * Params: 300 * T = The type being stored (ie. bool for keys, floats for axes, etc.). 301 * totalSize = The number of inputs to store. 302 */ 303 struct State( T, InputEnum ) if( is( InputEnum == enum ) ) 304 { 305 private: 306 // enum totalSize = Inputs.END; 307 enum totalSize = EnumMembers!Inputs.length; 308 alias StorageType = T; 309 alias Inputs = InputEnum; 310 public: 311 T[ size_t ] keys; 312 313 ref typeof(this) opAssign( const ref typeof(this) other ) 314 { 315 foreach(size_t index, T value; other.keys) 316 { 317 keys[index] = value; 318 } 319 320 return this; 321 } 322 323 T opIndex( size_t keyCode ) 324 { 325 // If the key being pressed doesn't have 326 // an entry in keys, add it, and set it 327 // to the default value 328 if( !( keyCode in keys ) ) 329 keys[ keyCode ] = cast(T)0; 330 331 return keys[ keyCode ]; 332 } 333 334 T opIndexAssign( T newValue, size_t keyCode ) 335 { 336 keys[ keyCode ] = newValue; 337 338 return newValue; 339 } 340 341 Tuple!( InputEnum, T )[] opBinary( string Op : "-" )( ref typeof(this) other ) 342 { 343 Tuple!( InputEnum, T )[] differences; 344 345 foreach( size_t index, T value; keys) 346 if( value != other[ index ] ) 347 differences ~= tuple( cast(InputEnum)index, value ); 348 349 return differences; 350 } 351 352 void reset() 353 { 354 foreach( ii; keys.keys ) 355 keys[ ii ] = cast(T)0; 356 } 357 }