3dscontroller-ng/3DS_Controller.py

201 lines
5.2 KiB
Python
Executable File

#!/usr/bin/env python
# Compatible with both Python 2.7.6 and 3.4.3
from __future__ import print_function
import socket, struct, time
import uinput
##########################################################
# CONFIGURABLE REGION START - Don't touch anything above #
##########################################################
port = 42020
#Valid values can be found in any of these locations on your Linux system (some may not exist):
# /usr/include/linux/input-event-codes.h
#The virtual device is defined on the device variable and the mapping of the keys on btn_map
#RECAP keynames (DO NOT TOUCH) are the keys that we expect commming from the 3ds, device is
#the virtual device that we define and btn_map maps what we recieve with our virtual device
btn_map = {
"A": uinput.BTN_A,
"B": uinput.BTN_B,
"X": uinput.BTN_X,
"Y": uinput.BTN_Y,
"L": uinput.BTN_TL,
"R": uinput.BTN_TR,
"ZL": uinput.BTN_THUMBL,
"ZR": uinput.BTN_THUMBR,
"Left": uinput.BTN_DPAD_LEFT,
"Right": uinput.BTN_DPAD_RIGHT,
"Up": uinput.BTN_DPAD_UP,
"Down": uinput.BTN_DPAD_DOWN,
"Start": uinput.BTN_START,
"Select": uinput.BTN_SELECT,
"Tap": uinput.BTN_MODE,
# "Gyroscope X": uinput.REL_RX,
# "Gyroscope Y": uinput.REL_RY,
# "Gyroscope Z": uinput.REL_RZ,
}
# gyro_events = {
# uinput.EV_REL: [
# uinput.REL_RX,
# uinput.REL_RY,
# uinput.REL_RZ
# ],
# uinput.EV_SYN: uinput.SYN_REPORT,
# }
device = uinput.Device([
uinput.ABS_X + (-159, 159, 0, 10),
uinput.ABS_Y + (-159, 159, 0, 10),
uinput.ABS_RX + (-146, 146, 0, 16),
uinput.ABS_RY + (-146, 146, 0, 16),
uinput.ABS_VOLUME + (0, 63, 0, 0),
uinput.BTN_A,
uinput.BTN_B,
uinput.BTN_X,
uinput.BTN_Y,
uinput.BTN_TL,
uinput.BTN_TR,
uinput.BTN_THUMBL,
uinput.BTN_THUMBR,
uinput.BTN_DPAD_LEFT,
uinput.BTN_DPAD_RIGHT,
uinput.BTN_DPAD_UP,
uinput.BTN_DPAD_DOWN,
uinput.BTN_START,
uinput.BTN_SELECT,
uinput.BTN_MODE,
], name="3DS")
deviceGyro = uinput.Device([
uinput.REL_RX,
uinput.REL_RY,
uinput.REL_RZ
], name="3DS Gyroscope")
########################################################
# CONFIGURABLE REGION END - Don't touch anything below #
########################################################
def pprint(obj):
import pprint
pprint.PrettyPrinter().pprint(obj)
class x: pass
command = x()
command.CONNECT = 0
command.KEYS = 1
command.SCREENSHOT = 2
keynames = [
"A", "B", "Select", "Start", "Right", "Left", "Up", "Down",
"R", "L", "X", "Y", None, None, "ZL", "ZR",
None, None, None, None, "Tap", None, None, None,
"CSRight", "CSLeft", "CSUp", "CSDown", "CRight", "CLeft", "CUp", "CDown",
]
keys = x()
keys.A = 1<<0
keys.B = 1<<1
keys.Select = 1<<2
keys.Start = 1<<3
keys.Right = 1<<4
keys.Left = 1<<5
keys.Up = 1<<6
keys.Down = 1<<7
keys.R = 1<<8
keys.L = 1<<9
keys.X = 1<<10
keys.Y = 1<<11
keys.ZL = 1<<14 # (new 3DS only)
keys.ZR = 1<<15 # (new 3DS only)
keys.Tap = 1<<20 # Not actually provided by HID
keys.CSRight = 1<<24 # c-stick (new 3DS only)
keys.CSLeft = 1<<25 # c-stick (new 3DS only)
keys.CSUp = 1<<26 # c-stick (new 3DS only)
keys.CSDown = 1<<27 # c-stick (new 3DS only)
keys.CRight = 1<<28 # circle pad
keys.CLeft = 1<<29 # circle pad
keys.CUp = 1<<30 # circle pad
keys.CDown = 1<<31 # circle pad
keys.GyroRight = 1<<32 # circle pad
keys.GyroLeft = 1<<33 # circle pad
keys.GyroUp = 1<<34 # circle pad
keys.GyroDown = 1<<35 # circle pad
def press_key(key):
device.emit(key, 1)
def release_key(key):
device.emit(key,0)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("", port))
prevkeys = 0
touch_start = 0
touch_last_x = 0
touch_last_y = 0
while True:
rawdata, addr = sock.recvfrom(4092)
rawdata = bytearray(rawdata)
print("received message", rawdata, "from", addr)
if rawdata[0]==command.CONNECT:
print("Connected with 3DS at address",addr)
pass # CONNECT packets are empty
if rawdata[0]==command.KEYS:
fields = struct.unpack("<bbxxIhhHHhhBxxxhhhxx", rawdata)
data = {
"command": fields[0],
"keyboardActive": fields[1],
"keys": fields[2],
"circleX": fields[3],
"circleY": fields[4],
"touchX": fields[5],
"touchY": fields[6],
"cstickX": fields[7],
"cstickY": fields[8],
"vol": fields[9],
"gyroX": fields[10],
"gyroY": fields[11],
"gyroZ": fields[12]
}
pprint(data)
newkeys = data["keys"] & ~prevkeys
oldkeys = ~data["keys"] & prevkeys
prevkeys = data["keys"]
for btnid in range(16):
if newkeys & (1<<btnid):
press_key(btn_map[keynames[btnid]])
if oldkeys & (1<<btnid):
release_key(btn_map[keynames[btnid]])
if newkeys & keys.Tap:
press_key(btn_map["Tap"])
if oldkeys & keys.Tap:
release_key(btn_map["Tap"])
device.emit(uinput.ABS_X, data["circleX"], syn=False)
device.emit(uinput.ABS_Y, 0-data["circleY"])
device.emit(uinput.ABS_RX, data["cstickX"], syn=False)
device.emit(uinput.ABS_RY, data["cstickY"])
device.emit(uinput.ABS_VOLUME, data["vol"])
# device.emit(uinput.EV_SYN, 0)
deviceGyro.emit(uinput.REL_X, data["gyroX"], syn=False)
deviceGyro.emit(uinput.REL_Y, data["gyroY"])
deviceGyro.emit(uinput.REL_Z, data["gyroZ"])
# deviceGyro.emit(uinput.EV_SYN, 0)
if rawdata[0]==command.SCREENSHOT:
pass # unused by both 3DS and PC applications