Browse Source

(DirectX) Improved GameController support (#95)

Pascal Peridont 7 years ago
parent
commit
959b55cffc
2 changed files with 161 additions and 92 deletions
  1. 123 65
      libs/directx/dx/GameController.hx
  2. 38 27
      libs/directx/gamecontroller.c

+ 123 - 65
libs/directx/dx/GameController.hx

@@ -6,10 +6,12 @@ typedef GameControllerPtr = hl.Abstract<"dx_gctrl_device">;
 private class DInputButton {
 	var num : Int;
 	var mask : Int;
+	var axis : Int;
 	
-	public function new( num : Int, mask : Int ){
+	public function new( num : Int, mask : Int, axis : Int ){
 		this.num = num;
 		this.mask = mask;
+		this.axis = axis;
 	}
 }
 
@@ -17,7 +19,6 @@ private class DInputMapping {
 	var guid : Int;
 	var name : hl.Bytes;
 	var button : hl.NativeArray<DInputButton>;
-	var axis : hl.NativeArray<Int>;
 	
 	function new( s : String ){
 		var a = s.split(",");
@@ -26,54 +27,54 @@ private class DInputMapping {
 		var product = Std.parseInt( "0x" + suid.substr(16,4) );
 		guid = (product&0xFF)<<24 | (product&0xFF00)<<8 | (vendor&0xFF)<<8 | (vendor&0xFF00)>>8;
 		name = @:privateAccess a.shift().toUtf8();
-		button = new hl.NativeArray(14);
-		axis = new hl.NativeArray(6);
+		button = new hl.NativeArray(20);
 		
 		for( e in a ){
 			var p = e.split(":");
 			if( p.length != 2 ) continue;
-			if( p[1].charCodeAt(0) == 'a'.code ){
-				var num = Std.parseInt(p[1].substr(1));
-				switch( p[0] ){
-					case "leftx":        axis[0] = num;
-					case "lefty":        axis[1] = num;
-					case "rightx":       axis[2] = num;
-					case "righty":       axis[3] = num;
-					case "lefttrigger":  axis[4] = num;
-					case "righttrigger": axis[5] = num;
-					default:
+			
+			var btn : DInputButton = switch( p[1].charCodeAt(0) ){
+				case 'b'.code: new DInputButton(Std.parseInt(p[1].substr(1)), 0, -1);
+				case 'h'.code: {
+					var ba = p[1].substr(1).split(".");
+					if( ba == null ) 
+						null;
+					else
+						new DInputButton(Std.parseInt(ba[0]), Std.parseInt(ba[1]), -1);
 				}
-			}else{
-				var btn : DInputButton = switch( p[1].charCodeAt(0) ){
-					case 'b'.code: new DInputButton(Std.parseInt(p[1].substr(1)), 0);
-					case 'h'.code: {
-						var ba = p[1].substr(1).split(".");
-						if( ba == null ) 
-							null;
-						else
-							new DInputButton(Std.parseInt(ba[0]), Std.parseInt(ba[1]));
+				case 'a'.code:
+					new DInputButton(-1,-1,Std.parseInt(p[1].substr(1)));
+				default: null;
+			}
+			if( btn == null ) continue;
+
+			switch( p[0] ){
+				case "leftx":        button[0] = btn;
+				case "lefty":        button[1] = btn;
+				case "rightx":       button[2] = btn;
+				case "righty":       button[3] = btn;
+				case "lefttrigger":  button[4] = btn;
+				case "righttrigger": button[5] = btn;
+				default:
+					var idx = switch( p[0] ){
+						case "dpup":          Btn_DPadUp;
+						case "dpdown":        Btn_DPadDown;
+						case "dpleft":        Btn_DPadLeft;
+						case "dpright":       Btn_DPadRight;
+						case "start":         Btn_Start;
+						case "back":          Btn_Back;
+						case "leftstick":     Btn_LeftStick;
+						case "rightstick":    Btn_RightStick;
+						case "leftshoulder":  Btn_LB;
+						case "rightshoulder": Btn_RB;
+						case "a":             Btn_A;
+						case "b":             Btn_B;
+						case "x":             Btn_X;
+						case "y":             Btn_Y;
+						default: null;
 					}
-					default: null;
-				}
-				var idx = switch( p[0] ){
-					case "dpup":          Btn_DPadUp;
-					case "dpdown":        Btn_DPadDown;
-					case "dpleft":        Btn_DPadLeft;
-					case "dpright":       Btn_DPadRight;
-					case "start":         Btn_Start;
-					case "back":          Btn_Back;
-					case "leftstick":     Btn_LeftStick;
-					case "rightstick":    Btn_RightStick;
-					case "leftshoulder":  Btn_LB;
-					case "rightshoulder": Btn_RB;
-					case "a":             Btn_A;
-					case "b":             Btn_B;
-					case "x":             Btn_X;
-					case "y":             Btn_Y;
-					default: null;
-				}
-				if( idx != null && btn != null )
-					button[Type.enumIndex(idx)] = btn;
+					if( idx != null )
+						button[6+Type.enumIndex(idx)] = btn;
 			}
 		}
 	}
@@ -101,7 +102,7 @@ private class DInputMapping {
 		"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
 		"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
 		"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,",
-		"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+		"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
 		"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,",
 		"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,",
 		"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
@@ -143,41 +144,98 @@ enum GameControllerButton {
 
 @:keep @:hlNative("directx")
 class GameController {
-	
-	public static function init(){
-		var mappings = DInputMapping.parseDefaults();
-		gctrlInit( mappings );
-	}
-	
-	public static function detect( onDetect : GameControllerPtr -> hl.Bytes -> Void ){
-		gctrlDetect(onDetect);
-	}
+	public static var CONFIG = {
+		analogX : 14,
+		analogY : 15,
+		ranalogX : 16,
+		ranalogY : 17,
+		LT : 18,
+		RT : 19,
 
+		dpadUp : 0,
+		dpadDown : 1,
+		dpadLeft : 2,
+		dpadRight : 3,
+		start : 4,
+		back : 5,
+		analogClick : 6,
+		ranalogClick : 7,
+		LB : 8,
+		RB : 9,
+		A : 10,
+		B : 11,
+		X : 12,
+		Y : 13,
+
+		names : ["DUp","DDown","DLeft","DRight","Start","Back","LCLK","RCLK","LB","RB","A","B","X","Y","LX","LY","RX","RY","LT","RT"],
+	};
+
+	public static var NUM_BUTTONS = 14;
+	public static var NUM_AXES = 6;
 	
-	public var ptr(default,null) : GameControllerPtr;
+	var ptr(default,null) : GameControllerPtr;
+	public var index : Int;
 	public var name(default,null) : String;
 	public var buttons : haxe.EnumFlags<GameControllerButton>;
-	public var lx : Float;
-	public var ly : Float;
-	public var rx : Float;
-	public var ry : Float;
-	public var lt : Float;
-	public var rt : Float;
+	public var axes : hl.Bytes;
+	var rumbleEnd : Null<Float>;
 	
-	public function new( ptr : GameControllerPtr, name : hl.Bytes ){
-		this.ptr = ptr;
-		this.name = @:privateAccess String.fromUTF8(name);
+	function new(){
 	}
 	
 	public inline function update(){
 		gctrlUpdate(this);
+		if( rumbleEnd != null && haxe.Timer.stamp() > rumbleEnd ){
+			gctrlSetVibration(ptr,0.);
+			rumbleEnd = null;
+		}
 	}
 	
-	public inline function setVibration( strength : Float ){
+	public inline function rumble( strength : Float, time_s : Float ){
 		gctrlSetVibration(ptr,strength);
+		rumbleEnd = strength <= 0 ? null : haxe.Timer.stamp() + time_s;
+	}
+
+	public inline function getAxis( i : Int ) {
+		return (i<0 || i>=NUM_AXES) ? 0. : axes.getF64(i*8);
+	}
+
+	public inline function getButtons(){
+		return buttons.toInt();
 	}
 	
 	// 
+
+	static var UID = 0;
+	static var ALL = [];
+
+	public static function init(){
+		var mappings = DInputMapping.parseDefaults();
+		gctrlInit( mappings );
+	}
+	
+	public static function detect( onDetect : GameController -> Bool -> Void ){
+		gctrlDetect(function(ptr:GameControllerPtr, name:hl.Bytes){
+			if( name != null ){
+				var d = new GameController();
+				d.ptr = ptr;
+				d.name = @:privateAccess String.fromUTF8(name);
+				d.index = UID++;
+				d.axes = new hl.Bytes(NUM_AXES*8);
+				d.update();
+				ALL.push(d);
+				onDetect(d,true);
+			}else{
+				for( d in ALL ){
+					if( d.ptr == ptr ){
+						ALL.remove(d);
+						onDetect(d,false);
+						break;
+					}
+				}
+			}
+		});
+	}
 	
 	static function gctrlInit( mappings : hl.NativeArray<DInputMapping> ){}
 	static function gctrlDetect( onDetect : GameControllerPtr -> hl.Bytes -> Void ){}

+ 38 - 27
libs/directx/gamecontroller.c

@@ -15,6 +15,7 @@ typedef struct {
 	hl_type *t;
 	int num;
 	int mask; // for hat only
+	int axis;
 } dinput_mapping_btn;
 
 typedef struct {
@@ -22,7 +23,6 @@ typedef struct {
 	unsigned int guid;
 	vbyte *name;
 	varray *button; // dinput_mapping_btn
-	varray *axis; // int
 } dinput_mapping;
 
 typedef struct _dx_gctrl_device dx_gctrl_device;
@@ -39,14 +39,11 @@ struct _dx_gctrl_device {
 typedef struct {
 	hl_type *t;
 	dx_gctrl_device *device;
+	int index;
 	vstring *name;
 	int buttons;
-	double lx;
-	double ly;
-	double rx;
-	double ry;
-	double lt;
-	double rt;
+	vbyte *axes;
+	vdynamic *rumbleEnd;
 } dx_gctrl_data;
 
 static dx_gctrl_device *dx_gctrl_devices = NULL;
@@ -93,12 +90,12 @@ static void gctrl_xinput_update(dx_gctrl_data *data) {
 		return;
 
 	data->buttons = (state.Gamepad.wButtons & 0x0FFF) | (state.Gamepad.wButtons & 0xF000) >> 2;
-	data->lx = (double)(state.Gamepad.sThumbLX < -32767 ? -1. : (state.Gamepad.sThumbLX / 32767.));
-	data->ly = (double)(state.Gamepad.sThumbLY < -32767 ? -1. : (state.Gamepad.sThumbLY / 32767.));
-	data->rx = (double)(state.Gamepad.sThumbRX < -32767 ? -1. : (state.Gamepad.sThumbRX / 32767.));
-	data->ry = (double)(state.Gamepad.sThumbRY < -32767 ? -1. : (state.Gamepad.sThumbRY / 32767.));
-	data->lt = (double)(state.Gamepad.bLeftTrigger / 255.);
-	data->rt = (double)(state.Gamepad.bRightTrigger / 255.);
+	((double*)data->axes)[0] = (double)(state.Gamepad.sThumbLX < -32767 ? -1. : (state.Gamepad.sThumbLX / 32767.));
+	((double*)data->axes)[1] = (double)(state.Gamepad.sThumbLY < -32767 ? -1. : (state.Gamepad.sThumbLY / 32767.));
+	((double*)data->axes)[2] = (double)(state.Gamepad.sThumbRX < -32767 ? -1. : (state.Gamepad.sThumbRX / 32767.));
+	((double*)data->axes)[3] = (double)(state.Gamepad.sThumbRY < -32767 ? -1. : (state.Gamepad.sThumbRY / 32767.));
+	((double*)data->axes)[4] = (double)(state.Gamepad.bLeftTrigger / 255.);
+	((double*)data->axes)[5] = (double)(state.Gamepad.bRightTrigger / 255.);
 }
 
 // DirectInput
@@ -216,8 +213,6 @@ static void gctrl_dinput_update(dx_gctrl_data *data) {
 	dinput_mapping *mapping = data->device->dMapping;
 	LPDIRECTINPUTDEVICE8 dDevice = data->device->dDevice;
 
-	int *axis = hl_aptr(mapping->axis, int);
-
 	HRESULT result = IDirectInputDevice8_GetDeviceState(dDevice, sizeof(DIJOYSTATE2), &state);
 	if( result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED ) {
 		IDirectInputDevice8_Acquire(dDevice);
@@ -226,20 +221,36 @@ static void gctrl_dinput_update(dx_gctrl_data *data) {
 	if( result != DI_OK )
 		return;
 	
-	if( mapping->axis->size > 0 ) data->lx = (double)((*(p + axis[0])) / 65535.) * 2 - 1.;
-	if( mapping->axis->size > 1 ) data->ly = (double)((*(p + axis[1])) / 65535.) * -2 + 1.;
-	if( mapping->axis->size > 2 ) data->rx = (double)((*(p + axis[2])) / 65535.) * 2 - 1.;
-	if( mapping->axis->size > 3 ) data->ry = (double)((*(p + axis[3])) / 65535.) * -2 + 1.;
-	if( mapping->axis->size > 4 ) data->lt = (double)((*(p + axis[4])) / 65535.);
-	if( mapping->axis->size > 5 ) data->rt = (double)((*(p + axis[5])) / 65535.);
-
 	data->buttons = 0;
 	for( int i = 0; i < mapping->button->size; i++ ) {
 		dinput_mapping_btn *bmap = hl_aptr(mapping->button, dinput_mapping_btn*)[i];
-		if( bmap->mask == 0 ) {
-			data->buttons |= (state.rgbButtons[bmap->num] > 0 ? 1 : 0) << i;
+		if (!bmap) continue;
+
+		int val;
+		if( bmap->axis < 0 && bmap->mask == 0 ) {
+			val = state.rgbButtons[bmap->num];
+		} else if( bmap->axis < 0 ){
+			val = gctrl_dinput_translatePOV(state.rgdwPOV[bmap->num])&bmap->mask;
 		} else {
-			data->buttons |= ((gctrl_dinput_translatePOV(state.rgdwPOV[bmap->num])&bmap->mask) > 0 ? 1 : 0) << i;
+			val = *(p + bmap->axis);
+		}
+
+		switch (i) {
+			case 0: 
+			case 2:
+				((double*)data->axes)[i] = (double)(val / 65535.) * 2 - 1.; 
+				break;
+			case 1:
+			case 3:
+				((double*)data->axes)[i] = (double)(val / 65535.) * - 2 + 1.;
+				break;
+			case 4:
+			case 5:
+				((double*)data->axes)[i] = bmap->axis < 0 ? (val > 0 ? 1 : 0) : (double)(val / 65535.);
+				break;
+			default: 
+				data->buttons |= (val > 0 ? 1 : 0) << (i - 6);
+				break;
 		}
 	}
 }
@@ -274,7 +285,7 @@ void gctrl_detect_thread(void *p) {
 
 		LeaveCriticalSection(&dx_gctrl_cs);
 
-		Sleep(1);
+		Sleep(300);
 	}
 }
 
@@ -339,5 +350,5 @@ HL_PRIM void HL_NAME(gctrl_set_vibration)(dx_gctrl_device *device, double streng
 #define TGAMECTRL _ABSTRACT(dx_gctrl_device)
 DEFINE_PRIM(_VOID, gctrl_init, _ARR);
 DEFINE_PRIM(_VOID, gctrl_detect, _FUN(_VOID, TGAMECTRL _BYTES));
-DEFINE_PRIM(_VOID, gctrl_update, _OBJ(TGAMECTRL _STRING _I32 _F64 _F64 _F64 _F64 _F64 _F64));
+DEFINE_PRIM(_VOID, gctrl_update, _OBJ(TGAMECTRL _I32 _STRING _I32 _BYTES _NULL(_F64)));
 DEFINE_PRIM(_VOID, gctrl_set_vibration, TGAMECTRL _F64);