1 module des.app.joystick; 2 3 import std.conv : to; 4 5 import derelict.sdl2.sdl; 6 7 import des.util.arch; 8 import des.util.logsys; 9 import des.util.stdext..string; 10 import des.app.evproc; 11 12 import des.app.base : DesAppException; 13 14 /// 15 class Joystick : DesObject 16 { 17 mixin DES; 18 private: 19 bool is_open; 20 21 protected: 22 /// 23 int index; 24 /// 25 SDL_Joystick *dev; 26 27 string joy_name; 28 29 /// 30 short[] axis_vals; 31 /// 32 ivec2[] ball_vals; 33 /// 34 bool[] button_vals; 35 /// 36 HatState[] hat_vals; 37 38 public: 39 40 /// 41 Signal!( uint, short ) axisChange; 42 /// 43 Signal!( uint, ivec2 ) ballChange; 44 /// 45 Signal!( uint, bool ) buttonChange; 46 /// 47 Signal!( uint, HatState ) hatChange; 48 49 /// 50 enum HatState 51 { 52 CENTERED = SDL_HAT_CENTERED, /// `SDL_HAT_CENTERED` 53 UP = SDL_HAT_UP, /// `SDL_HAT_UP` 54 RIGHT = SDL_HAT_RIGHT, /// `SDL_HAT_RIGHT` 55 DOWN = SDL_HAT_DOWN, /// `SDL_HAT_DOWN` 56 LEFT = SDL_HAT_LEFT, /// `SDL_HAT_LEFT` 57 RIGHTUP = SDL_HAT_RIGHTUP, /// `SDL_HAT_RIGHTUP` 58 RIGHTDOWN = SDL_HAT_RIGHTDOWN, /// `SDL_HAT_RIGHTDOWN` 59 LEFTUP = SDL_HAT_LEFTUP, /// `SDL_HAT_LEFTUP` 60 LEFTDOWN = SDL_HAT_LEFTDOWN, /// `SDL_HAT_LEFTDOWN` 61 }; 62 63 /// 64 this( int index ) 65 { 66 this.index = index; 67 open(); 68 } 69 70 /// 71 void open() { if( !isOpen ) selfConstruct(); } 72 73 pure 74 { 75 const 76 { 77 @property 78 { 79 /// 80 string name() { return joy_name; } 81 /// 82 bool isOpen() { return is_open; } 83 } 84 85 /// 86 short axis( uint i ) { return axis_vals[i]; } 87 /// 88 ivec2 ball( uint i ) { return ball_vals[i]; } 89 /// 90 bool button( uint i ) { return button_vals[i]; } 91 /// 92 HatState hat( uint i ) { return hat_vals[i]; } 93 } 94 } 95 96 package: 97 /// 98 void updateAxis( uint i, short v ) 99 { 100 axis_vals[i] = v; 101 axisChange( i, v ); 102 } 103 /// 104 void updateBall( uint i, in ivec2 v ) 105 { 106 ball_vals[i] = v; 107 ballChange( i, v ); 108 } 109 /// 110 void updateButton( uint i, bool v ) 111 { 112 button_vals[i] = v; 113 buttonChange( i, v ); 114 } 115 /// 116 void updateHat( uint i, uint v ) 117 { 118 auto vv = cast(HatState)v; 119 hat_vals[i] = vv; 120 hatChange( i, vv ); 121 } 122 123 protected: 124 125 /// 126 override void selfConstruct() 127 { 128 dev = SDL_JoystickOpen( index ); 129 if( !dev ) 130 throw new DesAppException( "Error open joystick: " ~ toDString( SDL_GetError() ) ); 131 132 logger.Debug( "joy opened: ", dev ); 133 134 joy_name = SDL_JoystickName( dev ).toDString; 135 136 createVals(); 137 setVals(); 138 139 is_open = true; 140 logger.Debug( "pass" ); 141 } 142 143 /// 144 void createVals() 145 { 146 axis_vals = new short[]( SDL_JoystickNumAxes( dev ) ); 147 logger.trace( "axis vals created: ", axis_vals.length ); 148 ball_vals = new ivec2[]( SDL_JoystickNumBalls( dev ) ); 149 logger.trace( "ball vals created: ", ball_vals.length ); 150 button_vals = new bool[]( SDL_JoystickNumButtons( dev ) ); 151 logger.trace( "ball vals setted: ", button_vals.length ); 152 hat_vals = to!(HatState[])( new byte[]( SDL_JoystickNumHats( dev ) ) ); 153 logger.trace( "hat vals setted: ", hat_vals.length ); 154 } 155 156 /// without calling change signals 157 void setVals() 158 { 159 setAxes(); 160 setBalls(); 161 setButtons(); 162 setHats(); 163 } 164 165 /// 166 void setAxes() 167 { 168 foreach( int i, ref axis; axis_vals ) 169 axis = SDL_JoystickGetAxis( dev, i ); 170 } 171 172 /// 173 void setBalls() 174 { 175 foreach( int i, ref ball; ball_vals ) 176 SDL_JoystickGetBall( dev, i, ball.data.ptr, ball.data.ptr+1 ); 177 } 178 179 /// 180 void setButtons() 181 { 182 foreach( int i, ref button; button_vals ) 183 button = cast(bool)SDL_JoystickGetButton( dev, i ); 184 } 185 186 /// 187 void setHats() 188 { 189 foreach( int i, ref hat; hat_vals ) 190 hat = cast(HatState)SDL_JoystickGetHat( dev, i ); 191 } 192 193 /// 194 override void selfDestroy() 195 { 196 SDL_JoystickClose( dev ); 197 is_open = false; 198 } 199 } 200 201 /// 202 class JoyEventProcessor : BaseSDLEventProcessor 203 { 204 mixin DES; 205 206 /// number of joystick in `devlist` 207 Signal!( uint ) added; 208 /// number of joystick in `devlist` 209 Signal!( uint ) removed; 210 211 /// number of joystick in `devlist`, number of axis, value 212 Signal!( uint, uint, short ) axisChange; 213 /// number of joystick in `devlist`, number of ball, relative move 214 Signal!( uint, uint, ivec2 ) ballChange; 215 /// number of joystick in `devlist`, number of button, pressed 216 Signal!( uint, uint, bool ) buttonChange; 217 /// number of joystick in `devlist`, number of hat, state 218 Signal!( uint, uint, Joystick.HatState ) hatChange; 219 220 221 /// list of registred devices 222 Joystick[uint] devlist; 223 224 this() 225 { 226 if( SDL_InitSubSystem( SDL_INIT_JOYSTICK ) < 0 ) 227 throw new DesAppException( "Error initializing SDL joystick subsystem: " ~ toDString( SDL_GetError() ) ); 228 229 SDL_JoystickEventState( SDL_ENABLE ); 230 231 int num_joys = SDL_NumJoysticks(); 232 foreach( index; 0 .. num_joys ) 233 addJoystick( index ); 234 235 logger.Debug( "pass" ); 236 } 237 238 bool procSDLEvent( in SDL_Event ev ) 239 { 240 switch( ev.type ) 241 { 242 case SDL_JOYAXISMOTION: 243 with( ev.jaxis ) 244 updateAxis( which, axis, value ); 245 return true; 246 247 case SDL_JOYBALLMOTION: 248 with( ev.jball ) 249 updateBall( which, ball, ivec2( xrel, yrel ) ); 250 return true; 251 252 case SDL_JOYHATMOTION: 253 with( ev.jhat ) 254 updateHat( which, hat, value ); 255 return true; 256 257 case SDL_JOYBUTTONDOWN: 258 case SDL_JOYBUTTONUP: 259 with( ev.jbutton ) 260 updateButton( which, button, cast(bool)state ); 261 return true; 262 263 case SDL_JOYDEVICEADDED: 264 addJoystick( ev.jdevice.which ); 265 return true; 266 267 case SDL_JOYDEVICEREMOVED: 268 removeJoystick( ev.jdevice.which ); 269 return true; 270 271 default: return false; 272 } 273 } 274 275 protected: 276 277 void updateAxis( uint ind, uint axis, short value ) 278 { 279 if( auto j = devlist.get( ind, null ) ) 280 j.updateAxis( axis, value ); 281 else 282 logger.warn( "no joystick device in list: ", ind ); 283 axisChange( ind, axis, value ); 284 } 285 286 void updateBall( uint ind, uint ball, ivec2 rel ) 287 { 288 if( auto j = devlist.get( ind, null ) ) 289 j.updateBall( ball, rel ); 290 else 291 logger.warn( "no joystick device in list: ", ind ); 292 ballChange( ind, ball, rel ); 293 } 294 295 void updateHat( uint ind, uint hat, uint value ) 296 { 297 if( auto j = devlist.get( ind, null ) ) 298 j.updateHat( hat, value ); 299 else 300 logger.warn( "no joystick device in list: ", ind ); 301 hatChange( ind, hat, cast(Joystick.HatState)value ); 302 } 303 304 void updateButton( uint ind, uint btn, bool value ) 305 { 306 if( auto j = devlist.get( ind, null ) ) 307 j.updateButton( btn, value ); 308 else 309 logger.warn( "no joystick device in list: ", ind ); 310 buttonChange( ind, btn, value ); 311 } 312 313 void addJoystick( uint ind ) 314 { 315 auto j = newEMM!Joystick(ind); 316 auto i = SDL_JoystickInstanceID( j.dev ); 317 devlist[i] = j; 318 added( i ); 319 logger.Debug( "[%d]", i ); 320 } 321 322 void removeJoystick( uint ind ) 323 { 324 if( ind in devlist ) 325 { 326 auto j = devlist[ind]; 327 devlist.remove(ind); 328 j.destroy(); 329 removed( ind ); 330 logger.Debug( "[%d]", ind ); 331 } 332 } 333 }