-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Common Win32 gamepad API + test (#1355)
* Common Win32 gamepad API + test * Fix KStructure memory handling in Node.JS and Android * Minor * Fix KStructure.fixedBytes alignment * Improve toString in some scenarios
- Loading branch information
Showing
15 changed files
with
293 additions
and
324 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 0 additions & 66 deletions
66
korgw/src/commonMain/kotlin/com/soywiz/korev/gamepad/XInputGameControllerCommon.kt
This file was deleted.
Oops, something went wrong.
137 changes: 137 additions & 0 deletions
137
korgw/src/commonMain/kotlin/com/soywiz/korgw/win32/Win32GameControllerCommon.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package com.soywiz.korgw.win32 | ||
|
||
import com.soywiz.kmem.* | ||
import com.soywiz.kmem.dyn.* | ||
import com.soywiz.korev.* | ||
import com.soywiz.korio.lang.* | ||
|
||
internal class Win32XInputEventAdapterCommon(val xinput: XInput?, val joy: Joy32?) { | ||
private val controllers = Array(GamepadInfo.MAX_CONTROLLERS) { GamepadInfo(it) } | ||
|
||
fun updateGamepads(emitter: GamepadInfoEmitter) { | ||
if (xinput == null) return | ||
|
||
emitter.dispatchGamepadUpdateStart() | ||
kmemScoped { | ||
val state = XInputState(allocBytes(XInputState().size)) | ||
for (n in 0 until GamepadInfo.MAX_CONTROLLERS) { | ||
val connected = xinput.XInputGetState(n, state) == XInput.SUCCESS | ||
val gamepad = controllers[n] | ||
if (connected) { | ||
val buttons = state.wButtons.toInt() and 0xFFFF | ||
|
||
gamepad.setDigital(GameButton.UP, buttons, XInput.GAMEPAD_DPAD_UP) | ||
gamepad.setDigital(GameButton.DOWN, buttons, XInput.GAMEPAD_DPAD_DOWN) | ||
gamepad.setDigital(GameButton.LEFT, buttons, XInput.GAMEPAD_DPAD_LEFT) | ||
gamepad.setDigital(GameButton.RIGHT, buttons, XInput.GAMEPAD_DPAD_RIGHT) | ||
gamepad.setDigital(GameButton.BACK, buttons, XInput.GAMEPAD_BACK) | ||
gamepad.setDigital(GameButton.START, buttons, XInput.GAMEPAD_START) | ||
gamepad.setDigital(GameButton.LEFT_THUMB, buttons, XInput.GAMEPAD_LEFT_THUMB) | ||
gamepad.setDigital(GameButton.RIGHT_THUMB, buttons, XInput.GAMEPAD_RIGHT_THUMB) | ||
gamepad.setDigital(GameButton.LEFT_SHOULDER, buttons, XInput.GAMEPAD_LEFT_SHOULDER) | ||
gamepad.setDigital(GameButton.RIGHT_SHOULDER, buttons, XInput.GAMEPAD_RIGHT_SHOULDER) | ||
gamepad.setDigital(GameButton.XBOX_A, buttons, XInput.GAMEPAD_A) | ||
gamepad.setDigital(GameButton.XBOX_B, buttons, XInput.GAMEPAD_B) | ||
gamepad.setDigital(GameButton.XBOX_X, buttons, XInput.GAMEPAD_X) | ||
gamepad.setDigital(GameButton.XBOX_Y, buttons, XInput.GAMEPAD_Y) | ||
gamepad.rawButtons[GameButton.LEFT_TRIGGER.index] = convertUByteRangeToDouble(state.bLeftTrigger) | ||
gamepad.rawButtons[GameButton.RIGHT_TRIGGER.index] = convertUByteRangeToDouble(state.bRightTrigger) | ||
gamepad.rawButtons[GameButton.LX.index] = GamepadInfo.withoutDeadRange(convertShortRangeToDouble(state.sThumbLX)) | ||
gamepad.rawButtons[GameButton.LY.index] = GamepadInfo.withoutDeadRange(convertShortRangeToDouble(state.sThumbLY)) | ||
gamepad.rawButtons[GameButton.RX.index] = GamepadInfo.withoutDeadRange(convertShortRangeToDouble(state.sThumbRX)) | ||
gamepad.rawButtons[GameButton.RY.index] = GamepadInfo.withoutDeadRange(convertShortRangeToDouble(state.sThumbRY)) | ||
|
||
if (gamepad.name == null) { | ||
kmemScoped { | ||
val joyCapsW = JoyCapsW(allocBytes(JoyCapsW.SIZE)) | ||
if (joy?.joyGetDevCapsW(n, joyCapsW, JoyCapsW.SIZE) == 0) { | ||
gamepad.name = joyCapsW.name | ||
} | ||
} | ||
} | ||
emitter.dispatchGamepadUpdateAdd(gamepad) | ||
} | ||
} | ||
emitter.dispatchGamepadUpdateEnd() | ||
} | ||
} | ||
|
||
private fun GamepadInfo.setDigital(button: GameButton, buttons: Int, bit: Int) { | ||
this.rawButtons[button.index] = if (buttons.hasBitSet(bit)) 1f else 0f | ||
} | ||
|
||
private fun convertShortRangeToDouble(value: Short): Float = value.toFloat().convertRangeClamped(Short.MIN_VALUE.toFloat(), Short.MAX_VALUE.toFloat(), -1f, +1f) | ||
private fun convertUByteRangeToDouble(value: Byte): Float = (value.toInt() and 0xFF).toFloat().convertRangeClamped(0f, 255f, 0f, +1f) | ||
} | ||
|
||
// Used this as reference: | ||
// https://github.com/fantarama/JXInput/blob/86356e7a4037bbb1f3478c7333555e00b3601bde/XInputJNA/src/main/java/com/microsoft/xinput/XInput.java | ||
internal class XInputState(pointer: KPointer? = null) : KStructure(pointer) { | ||
var dwPacketNumber by int() // offset: 0 | ||
var wButtons by short() // offset: 4 | ||
var bLeftTrigger by byte() // offset: 6 | ||
var bRightTrigger by byte() // offset: 7 | ||
var sThumbLX by short() // offset: 8 | ||
var sThumbLY by short() // offset: 10 | ||
var sThumbRX by short() // offset: 12 | ||
var sThumbRY by short() // offset: 14 | ||
override fun toString(): String = | ||
"XInputState(dwPacketNumber=$dwPacketNumber, wButtons=$wButtons, bLeftTrigger=$bLeftTrigger, bRightTrigger=$bRightTrigger, sThumbLX=$sThumbLX, sThumbLY=$sThumbLY, sThumbRX=$sThumbRX, sThumbRY=$sThumbRY)" | ||
} | ||
|
||
internal class JoyCapsW(pointer: KPointer? = null) : KStructure(pointer) { | ||
companion object { | ||
val SIZE = 728 | ||
} | ||
|
||
var wMid: Short by short() | ||
var wPid: Short by short() | ||
var szPname by fixedBytes(32 * 2) | ||
var name: String | ||
get() = szPname.toString(Charsets.UTF16_LE).trimEnd('\u0000').also { | ||
//println("JoyCapsW.name='$it'") | ||
} | ||
set(value) { | ||
szPname = run { | ||
ByteArray(szPname.size).also { | ||
val new = value.toByteArray(Charsets.UTF16_LE) | ||
arraycopy(new, 0, it, 0, new.size) | ||
} | ||
} | ||
|
||
} | ||
override fun toString(): String = | ||
"JoyCapsW(name=$name)" | ||
} | ||
|
||
internal fun interface XInput { | ||
fun XInputGetState(dwUserIndex: Int, pState: XInputState): Int // pState: XInputState | ||
|
||
companion object { | ||
const val SUCCESS = 0 | ||
const val ERROR_DEVICE_NOT_CONNECTED = 0x0000048F | ||
|
||
const val GAMEPAD_DPAD_UP = 0 | ||
const val GAMEPAD_DPAD_DOWN = 1 | ||
const val GAMEPAD_DPAD_LEFT = 2 | ||
const val GAMEPAD_DPAD_RIGHT = 3 | ||
const val GAMEPAD_START = 4 | ||
const val GAMEPAD_BACK = 5 | ||
const val GAMEPAD_LEFT_THUMB = 6 | ||
const val GAMEPAD_RIGHT_THUMB = 7 | ||
const val GAMEPAD_LEFT_SHOULDER = 8 | ||
const val GAMEPAD_RIGHT_SHOULDER = 9 | ||
const val GAMEPAD_UNKNOWN_10 = 10 | ||
const val GAMEPAD_UNKNOWN_11 = 11 | ||
const val GAMEPAD_A = 12 | ||
const val GAMEPAD_B = 13 | ||
const val GAMEPAD_X = 14 | ||
const val GAMEPAD_Y = 15 | ||
|
||
const val SIZE = 16 | ||
} | ||
} | ||
|
||
internal fun interface Joy32 { | ||
fun joyGetDevCapsW(uJoyID: Int, pjc: JoyCapsW, cbjc: Int): Int // pjc: JoyCapsW | ||
} |
Oops, something went wrong.