Add friendly input handling and add user retrieval

This commit is contained in:
Joey Yakimowich-Payne 2018-06-26 22:37:29 +09:00
commit ae67cc15d3
6 changed files with 522 additions and 16 deletions

View file

@ -1,8 +1,10 @@
import sets, strutils
import libnx/graphics
import libnx/wrapper/hid
import libnx/wrapper/console
import libnx/ext/integer128
import libnx/account
import libnx/input
import libnx/app
proc main() =
@ -25,10 +27,22 @@ proc main() =
let msg = getCurrentExceptionMsg()
echo "\x1b[6;2H" & msg
mainLoop:
let keysDown = hidKeysDown(CONTROLLER_P1_AUTO)
try:
let users = listAllUsers()
echo ""
echo " There are $# users:" % $users.len()
for user in users:
echo " User: " & user.username
except AccountUserListError:
let msg = getCurrentExceptionMsg()
echo msg
if (keysDown and KEY_PLUS.uint64) > 0.uint64:
mainLoop:
let keysDown = keysDown(Controller.P1_AUTO)
if ControllerKey.Plus in keysDown:
break
main()

View file

@ -84,6 +84,8 @@ defines = true
defines = true
search.static_assert = "static_assert"
replace.static_assert = "// static_assert"
search.touch = "touchPosition"
replace.touch = "TouchPosition"
[ipc.h]
defines = true

View file

@ -11,6 +11,8 @@ type
AccountInitError* = object of AccountError
AccountImageSizeError* = object of AccountError
AccountUserProfileError* = object of AccountError
AccountUserCountError* = object of AccountError
AccountUserListError* = object of AccountError
AccountUserNotSelectedError* = object of AccountError
AccountUserDataError* = object of AccountError
AccountActiveUserError* = object of AccountError
@ -120,6 +122,32 @@ proc loadImage*(user: User): AccountImage =
proc userID*(user: User): string =
$user.id
proc getUser*(userID: u128): User =
ensureEnabled()
result = new(User)
result.id = userID
result.selected = false
var
prof: AccountProfile = getProfileHelper(userID)
userData: AccountUserData
profBase: AccountProfileBase
let res = accountProfileGet(prof.addr, userData.addr, profBase.addr).newResult
if res.failed:
raiseEx(AccountUserDataError, "Error, could not get user data: " & res.description)
result.username = profBase.username.join("")
result.lastEdited = profBase.lastEditTimestamp
result.iconID = userData.iconID
result.iconBgColorID = userData.iconBackgroundColorID
result.miiID = userData.miiID
prof.close()
proc getActiveUser*(): User =
ensureEnabled()
result = new(User)
@ -171,3 +199,31 @@ proc getProfile*(user: User): Profile =
result.service = newService(prof.s)
prof.close()
proc getUserCount*(): int32 =
var count: int32
let res = accountGetUserCount(count.addr).newResult
if res.failed:
raiseEx(
AccountUserCountError,
"Error, could not get user count: " & res.description
)
result = count
proc listAllUsers*(): seq[User] =
result = @[]
let numUsers = getUserCount()
var userIDs: array[ACC_USER_LIST_SIZE, u128]
let res = accountListAllUsers(userIDs[0].addr).newResult
if res.failed:
raiseEx(
AccountUserListError,
"Error, could not list users: " & res.description
)
for i in 0 ..< numUsers:
result.add(getUser(userIDs[i]))

346
src/libnx/input.nim Normal file
View file

@ -0,0 +1,346 @@
import macros, strutils, sets
import libnx/wrapper/hid
from libnx/wrapper/types import BIT
import libnx/utils
import libnx/results
import libnx/service
import libnx/results
niceify(HidMouseButton, "Hid:MOUSE_")
niceify(HidKeyboardModifier, "Hid:KBD_MOD_")
niceify(HidKeyboardScancode, "HidKeyboardScancode:KeyboardKey:KBD_:")
niceify(HidControllerType, "Hid:TYPE_")
niceify(HidControllerLayoutType, "Hid:LAYOUT_")
niceify(HidControllerColorDescription, "Hid:COLORS_")
niceify(HidControllerKeys, "HidControllerKeys:ControllerKey:KEY_:")
niceify(HidControllerJoystick, "Hid:JOYSTICK_")
niceify(HidControllerID, "HidControllerID:Controller:CONTROLLER_:")
export TouchPosition, JoystickPosition, MousePosition
export JOYSTICK_MAX, JOYSTICK_MIN
type
InputError* = object of Exception
ControllerSelectError* = object of InputError
TouchScreenHeader* = ref object
timestampTicks*: uint64
numEntries*: uint64
latestEntry*: uint64
maxEntryIndex*: uint64
timestamp*: uint64
TouchScreenEntryHeader* = ref object
timestamp*: uint64
numTouches*: uint64
TouchScreenEntryTouch* = ref object
timestamp*: uint64
padding*: uint32
touchIndex*: uint32
x*: uint32
y*: uint32
diameterX*: uint32
diameterY*: uint32
angle*: uint32
padding2*: uint32
TouchScreenEntry* = ref object
header*: TouchScreenEntryHeader
touches*: Buffer[HidTouchScreenEntryTouch]
unk*: uint64
TouchScreen* = ref object
header*: TouchScreenHeader
entries*: Buffer[HidTouchScreenEntry]
padding*: Buffer[uint8]
MouseHeader* = ref object
timestampTicks*: uint64
numEntries*: uint64
latestEntry*: uint64
maxEntryIndex*: uint64
type
MouseEntry* = ref object
timestamp*: uint64
timestamp2*: uint64
position*: MousePosition
buttons*: uint64
type
Mouse* = ref object
header*: MouseHeader
entries*: Buffer[MouseEntry]
padding*: Buffer[uint8]
KeyboardHeader* = ref object
timestampTicks*: uint64
numEntries*: uint64
latestEntry*: uint64
maxEntryIndex*: uint64
KeyboardEntry* = ref object
timestamp*: uint64
timestamp2*: uint64
modifier*: uint64
keys*: Buffer[uint32]
KeyboardSection* = ref object
header*: KeyboardHeader
entries*: array[17, KeyboardEntry]
padding*: array[0x00000028, uint8]
ControllerMAC* = object
timestamp*: uint64
mac*: array[0x00000008, uint8]
unk*: uint64
timestamp2*: uint64
ControllerHeader* = object
`type`* {.importc: "type".}: uint32
isHalf* {.importc: "isHalf".}: uint32
singleColorsDescriptor* {.importc: "singleColorsDescriptor".}: uint32
singleColorBody* {.importc: "singleColorBody".}: uint32
singleColorButtons* {.importc: "singleColorButtons".}: uint32
splitColorsDescriptor* {.importc: "splitColorsDescriptor".}: uint32
leftColorBody* {.importc: "leftColorBody".}: uint32
leftColorButtons* {.importc: "leftColorButtons".}: uint32
rightColorBody* {.importc: "rightColorBody".}: uint32
rightColorbuttons* {.importc: "rightColorbuttons".}: uint32
ControllerLayoutHeader* = object
timestampTicks* {.importc: "timestampTicks".}: uint64
numEntries* {.importc: "numEntries".}: uint64
latestEntry* {.importc: "latestEntry".}: uint64
maxEntryIndex* {.importc: "maxEntryIndex".}: uint64
HidControllerInputEntry* = object
timestamp* {.importc: "timestamp".}: uint64
timestamp_2* {.importc: "timestamp_2".}: uint64
buttons* {.importc: "buttons".}: uint64
joysticks* {.importc: "joysticks".}: array[JOYSTICK_NUM_STICKS, JoystickPosition]
connectionState* {.importc: "connectionState".}: uint64
ControllerLayoutSection* = ref object
header*: ControllerLayoutHeader
entries* {.importc: "entries".}: array[17, HidControllerInputEntry]
ControllerSection* = ref object
header* {.importc: "header".}: ControllerHeader
layouts* {.importc: "layouts".}: Buffer[ControllerLayoutSection]
unk_1*: Buffer[uint8]
macLeft*: HidControllerMAC
macRight*: HidControllerMAC
unk_2* {.importc: "unk_2".}: array[0x00000DF8, uint8]
InputSharedMemory* = ref object
header*: Buffer[uint8]
touchscreen*: TouchScreen
mouse*: Mouse
keyboard*: KeyboardSection
controllerSerials*: Buffer[uint8]
controllers*: Buffer[ControllerSection]
VibrationDeviceInfo* = ref object
unk_x0*: uint32
unk_x4*: uint32 ## /< 0x1 for left-joycon, 0x2 for right-joycon.
VibrationValue* = ref object
ampLow*: float ## /< Low Band amplitude. 1.0f: Max amplitude.
freqLow*: float ## /< Low Band frequency in Hz.
ampHigh*: float ## /< High Band amplitude. 1.0f: Max amplitude.
freqHigh*: float ## /< High Band frequency in Hz.
proc init*(): Result = hidInitialize().newResult
proc exit*() = hidExit()
proc reset*() = hidReset()
proc getSessionService*(): Service =
newService(hidGetSessionService()[])
proc getSharedMemory*(): ptr HidSharedMemory =
result = cast[ptr HidSharedMemory](hidGetSharedMemAddr())
proc setControllerLayout*(id: Controller, layoutType: ControllerLayoutType) =
hidSetControllerLayout(HidControllerID(id), HidControllerLayoutType(layoutType))
proc getControllerLayout*(id: Controller): ControllerLayoutType =
hidGetControllerLayout(HidControllerID(id)).ControllerLayoutType
proc scanInput*() = scanInput()
proc keysHeld*(id: Controller): HashSet[ControllerKey] =
result = initSet[ControllerKey]()
var raw = hidKeysHeld(HidControllerID(id))
for i in ControllerKey.low.int ..< ControllerKey.size:
let bit = raw and 0x1
if bit == 1:
result.incl(ControllerKey(BIT(i)))
raw = raw shr 1
proc keysDown*(id: Controller): HashSet[ControllerKey] =
result = initSet[ControllerKey]()
var raw = hidKeysDown(HidControllerID(id))
for i in ControllerKey.low.int ..< ControllerKey.size:
let bit = raw and 0x1
if bit == 1:
result.incl(ControllerKey(BIT(i)))
raw = raw shr 1
proc keysUp*(id: Controller): HashSet[ControllerKey] =
result = initSet[ControllerKey]()
var raw = hidKeysUp(HidControllerID(id))
for i in ControllerKey.low.int ..< ControllerKey.size:
let bit = raw and 0x1
if bit == 1:
result.incl(ControllerKey(BIT(i)))
raw = raw shr 1
proc mouseButtonsHeld*(): HashSet[MouseButton] =
result = initSet[MouseButton]()
var raw = hidMouseButtonsHeld()
for i in MouseButton.low.int ..< MouseButton.size:
let bit = raw and 0x1
if bit == 1:
result.incl(MouseButton(BIT(i)))
raw = raw shr 1
proc mouseButtonsDown*(): HashSet[MouseButton] =
result = initSet[MouseButton]()
var raw = hidMouseButtonsDown()
for i in MouseButton.low.int ..< MouseButton.size:
let bit = raw and 0x1
if bit == 1:
result.incl(MouseButton(BIT(i)))
raw = raw shr 1
proc mouseButtonsUp*(): HashSet[MouseButton] =
result = initSet[MouseButton]()
var raw = hidMouseButtonsUp()
for i in MouseButton.low.int ..< MouseButton.size:
let bit = raw and 0x1
if bit == 1:
result.incl(MouseButton(BIT(i)))
raw = raw shr 1
proc mouseRead*(): MousePosition =
var pos: ptr MousePosition
hidMouseRead(pos)
result = pos[]
proc keyboardModifierHeld*(modifier: KeyboardModifier): bool =
hidKeyboardModifierHeld(modifier.HidKeyboardModifier)
proc keyboardModifierDown*(modifier: KeyboardModifier): bool =
hidKeyboardModifierDown(modifier.HidKeyboardModifier)
proc keyboardModifierUp*(modifier: KeyboardModifier): bool =
hidKeyboardModifierUp(modifier.HidKeyboardModifier)
proc keyboardHeld*(key: KeyboardKey): bool =
hidKeyboardHeld(key.HidKeyboardScancode)
proc keyboardDown*(key: KeyboardKey): bool =
hidKeyboardDown(key.HidKeyboardScancode)
proc keyboardUp*(key: KeyboardKey): bool =
hidKeyboardUp(key.HidKeyboardScancode)
proc touchCount*(): uint32 =
hidTouchCount()
proc touchRead*(pointId: uint32): TouchPosition =
var touch: ptr TouchPosition
hidTouchRead(touch, pointId)
result = touch[]
proc joystickRead*(id: Controller, stick: ControllerJoystick): JoystickPosition =
var pos: ptr JoystickPosition
hidJoystickRead(pos, id.HidControllerID, stick.HidControllerJoystick)
result = pos[]
## / This can be used to check what CONTROLLER_P1_AUTO uses.
## / Returns 0 when CONTROLLER_PLAYER_1 is connected, otherwise returns 1 for
## handheld-mode.
proc getHandheldMode*(): bool =
hidGetHandheldMode()
## / Use this if you want to use a single joy-con as a dedicated CONTROLLER_PLAYER_*.
## / When used, both joy-cons in a pair should be used with this (CONTROLLER_PLAYER_1
## and CONTROLLER_PLAYER_2 for example).
## / id must be CONTROLLER_PLAYER_*.
proc setJoyConModeSingle*(id: Controller): Result =
if not (id in {Controller.Player1 .. Controller.Player8}):
raiseEx(ControllerSelectError, "Must be controller 1-8")
hidSetNpadJoyAssignmentModeSingleByDefault(id.HidControllerId).newResult
## / Use this if you want to use a pair of joy-cons as a single CONTROLLER_PLAYER_*.
## Only necessary if you want to use this mode in your application after \ref
## hidSetNpadJoyAssignmentModeSingleByDefault was used with this pair of joy-cons.
## / Used automatically during app startup/exit for all controllers.
## / When used, both joy-cons in a pair should be used with this (CONTROLLER_PLAYER_1
## and CONTROLLER_PLAYER_2 for example).
## / id must be CONTROLLER_PLAYER_*.
proc setJoyconModeDual*(id: Controller): Result =
if not (id in {Controller.Player1 .. Controller.Player8}):
raiseEx(ControllerSelectError, "Must be controller 1-8")
hidSetNpadJoyAssignmentModeDual(id.HidControllerId).newResult
## / Merge two single joy-cons into a dual-mode controller. Use this after \ref
## hidSetNpadJoyAssignmentModeDual, when \ref hidSetNpadJoyAssignmentModeSingleByDefault
## was previously used (this includes using this manually at application exit).
proc mergeSingleJoyAsDualJoy*(id1: Controller; id2: Controller): Result =
hidMergeSingleJoyAsDualJoy(id1.HidControllerID, id2.HidControllerId).newResult
proc initializeVibrationDevices*(VibrationDeviceHandles: ptr uint32;
total_handles: csize; id: HidControllerID;
`type`: HidControllerType): Result =
discard
## / Gets HidVibrationDeviceInfo for the specified VibrationDeviceHandle.
proc getVibrationDeviceInfo*(VibrationDeviceHandle: ptr uint32;
VibrationDeviceInfo: ptr HidVibrationDeviceInfo): Result =
newResult(0)
## / Send the VibrationValue to the specified VibrationDeviceHandle.
proc sendVibrationValue*(VibrationDeviceHandle: ptr uint32;
VibrationValue: ptr HidVibrationValue): Result =
discard
## / Gets the current HidVibrationValue for the specified VibrationDeviceHandle.
proc getActualVibrationValue*(VibrationDeviceHandle: ptr uint32;
VibrationValue: ptr HidVibrationValue): Result =
discard
## / Sets whether vibration is allowed, this also affects the config displayed by
## System Settings.
proc permitVibration*(flag: bool): Result =
discard
## / Gets whether vibration is allowed.
proc isVibrationPermitted*(flag: ptr bool): Result =
discard
## / Send VibrationValues[index] to VibrationDeviceHandles[index], where count is the
## number of entries in the VibrationDeviceHandles/VibrationValues arrays.
proc sendVibrationValues*(VibrationDeviceHandles: ptr uint32;
VibrationValues: ptr HidVibrationValue; count: csize): Result =
discard

View file

@ -109,19 +109,26 @@ proc newResult*(code: uint32): Result =
let resCode = MAKERESULT(moduleCode, descCode)
var description = ""
case module
of Module.Invalid:
discard
of Module.Kernel:
result.kernelError = KernelError(resCode)
description = $result.kernelError
of Module.Libnx:
result.libnxError = LibnxError(resCode)
description = $result.libnxError
of Module.LibnxNvidia:
result.libnxNvidiaError = LibnxNvidiaError(resCode)
description = $result.libnxNvidiaError
try:
case module
of Module.Invalid:
discard
of Module.Kernel:
result.kernelError = KernelError(resCode)
description = $result.kernelError
of Module.Libnx:
result.libnxError = LibnxError(resCode)
description = $result.libnxError
of Module.LibnxNvidia:
result.libnxNvidiaError = LibnxNvidiaError(resCode)
description = $result.libnxNvidiaError
except:
echo "Converting to result failed: " & getCurrentExceptionMsg()
echo "Code: " & $code
echo "ModuleCode: " & $moduleCode
echo "Module: " & $module
echo "DescCode: " & $descCode
echo "ResultCode: " & $resCode
result.module = $module
result.description = description

View file

@ -1,4 +1,84 @@
import macros, strutils, math
proc size*(enumTy: typedesc): int =
# Returns the number of items in a bit set enum
log2(float64(enumTy.high)).int + 1
template recursive(node, action: untyped): untyped {.dirty.} =
## recursively iterate over AST nodes and perform an
## action on them
proc helper(child: NimNode): NimNode {.gensym.}=
action
result = child.copy()
for c in child.children:
if child.kind == nnkCall and c.kind == nnkDotExpr:
# ignore dot expressions that are also calls
continue
result.add helper(c)
discard helper(node)
macro niceify*(enumType: typed, replaceNameField: string): untyped =
##
## niceify(HidMouseButton, "Hid:MOUSE_") transforms this:
##
## HidMouseButton* {.size: sizeof(cint)} = enum
## MOUSE_LEFT = BIT(0), MOUSE_RIGHT = BIT(1), MOUSE_MIDDLE = BIT(2),
## MOUSE_FORWARD = BIT(3), MOUSE_BACK = BIT(4)
##
## into this:
##
## MouseButton {.size: 4, pure.} = enum
## Left = MOUSE_LEFT, Right = MOUSE_RIGHT, Middle = MOUSE_MIDDLE,
## Forward = MOUSE_FORWARD,
## Back = MOUSE_BACK
##
## If any fields begin with a number, the first letter of the name after
## the colon will be used in addition. (128 -> M128 in this case)
let replaceSplit = replaceNameField.strVal.split(":")
var
replaceName: string
replaceField: string
replaceNameWith = ""
replaceFieldWith = ""
if replaceSplit.len == 2:
replaceName = replaceSplit[0]
replaceField = replaceSplit[1]
else:
replaceName = replaceSplit[0]
replaceNameWith = replaceSplit[1]
replaceField = replaceSplit[2]
replaceFieldWith = replaceSplit[3]
var node = enumType.getImpl().copy()
recursive(node):
if child.kind == nnkPragmaExpr:
child[0] = postFix(
ident(child[0].strVal.replace($replaceName, replaceNameWith)),
"*"
)
if child.kind == nnkPragma:
child.add(ident("pure"))
if child.kind == nnkEnumFieldDef:
let
lhs = child[0]
rhs = child[1]
name = lhs.strVal
var
newName = name.replace($replaceField, replaceFieldWith)
.normalize().capitalizeAscii()
firstChar = newName[0]
if firstChar in Digits:
newName = replaceField[0] & newName
child[0] = ident(newName)
child[1] = ident(name)
return newNimNode(nnkTypeSection).add(node)
type
BufferError* = object of Exception
@ -8,6 +88,7 @@ type
len*: int
data*: ptr UncheckedArray[T]
template raiseEx*(ty: untyped, message: string): untyped =
raise newException(ty, message)