From 0fa25d513336d7d22f6e10fb728a263e1e4dc849 Mon Sep 17 00:00:00 2001 From: t-gabor Date: Thu, 16 May 2019 13:11:11 +0200 Subject: [PATCH 1/4] linux: gamepad and keyboard with uinput. --- Linux/3DSController_uinput.py | 219 ++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100755 Linux/3DSController_uinput.py diff --git a/Linux/3DSController_uinput.py b/Linux/3DSController_uinput.py new file mode 100755 index 0000000..51ac263 --- /dev/null +++ b/Linux/3DSController_uinput.py @@ -0,0 +1,219 @@ +#!/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 = 8889 + +btn_gamepad_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, +} + +keboard_matrix = [ + uinput.KEY_GRAVE, uinput.KEY_1, uinput.KEY_2, uinput.KEY_3, uinput.KEY_4, uinput.KEY_5, uinput.KEY_6, uinput.KEY_7, uinput.KEY_8, uinput.KEY_9, uinput.KEY_0, uinput.KEY_BACKSPACE, + uinput.KEY_Q, uinput.KEY_W, uinput.KEY_E, uinput.KEY_R, uinput.KEY_T, uinput.KEY_Y, uinput.KEY_U, uinput.KEY_I, uinput.KEY_O, uinput.KEY_P, uinput.KEY_ENTER, uinput.KEY_ENTER, + uinput.KEY_A, uinput.KEY_S, uinput.KEY_D, uinput.KEY_F, uinput.KEY_G, uinput.KEY_H, uinput.KEY_J, uinput.KEY_K, uinput.KEY_L, uinput.KEY_MINUS, uinput.KEY_ENTER, uinput.KEY_ENTER, + uinput.KEY_Z, uinput.KEY_X, uinput.KEY_C, uinput.KEY_V, uinput.KEY_B, uinput.KEY_N, uinput.KEY_M, uinput.KEY_COMMA, uinput.KEY_DOT, uinput.KEY_SLASH, uinput.KEY_ENTER, uinput.KEY_ENTER, + uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE, uinput.KEY_SPACE +] + +btn_keyboard_map = { + "A": uinput.KEY_A, + "B": uinput.KEY_B, + "X": uinput.KEY_X, + "Y": uinput.KEY_TAB, + "L": uinput.KEY_LEFTSHIFT, + "R": uinput.KEY_RIGHTSHIFT, + "ZL": uinput.KEY_Q, + "ZR": uinput.KEY_W, + "Left": uinput.KEY_LEFT, + "Right": uinput.KEY_RIGHT, + "Up": uinput.KEY_UP, + "Down": uinput.KEY_DOWN, + "Start": uinput.KEY_ENTER, + "Select": uinput.KEY_ESC +} + +gamepad_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.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, + ]) + +keyboard_device = uinput.Device(keboard_matrix + btn_keyboard_map.values()) + +######################################################## +# 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 + +def currentKeyboardKey(x, y): + if x>=1 and x<=312 and y>=78 and y<=208: + xi = int(x*12/320) + yi = int((y-78)*12/320) + return keboard_matrix[yi*12 + xi] + else: return None + +def action_key(key): + if key in keboard_matrix: + keyboard_device.emit_click(key) + return + +def press_key(key): + keyboard_device.emit(key, 1) + +def release_key(key): + keyboard_device.emit(key,0) + +def press_btn(btn): + gamepad_device.emit(btn, 1) + +def release_btn(btn): + gamepad_device.emit(btn, 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 +keyboard_prevkey = currentKeyboardKey(0, 0) + +while True: + rawdata, addr = sock.recvfrom(4096) + rawdata = bytearray(rawdata) + #print("received message", rawdata, "from", addr) + + if rawdata[0]==command.CONNECT: + pass # CONNECT packets are empty + + if rawdata[0]==command.KEYS: + fields = struct.unpack(" Date: Thu, 16 May 2019 13:12:54 +0200 Subject: [PATCH 2/4] Exit requires Left key too. Start + Select is the default combination in EmulationStation to quit games. --- 3DS/source/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3DS/source/main.c b/3DS/source/main.c index c444347..2958b2d 100644 --- a/3DS/source/main.c +++ b/3DS/source/main.c @@ -153,7 +153,7 @@ int main(void) { //receiveBuffer(sizeof(struct packet)); - if((kHeld & KEY_START) && (kHeld & KEY_SELECT)) longjmp(exitJmp, 1); + if((kHeld & KEY_START) && (kHeld & KEY_SELECT) && (kHeld & KEY_L)) longjmp(exitJmp, 1); gfxFlushBuffers(); gspWaitForVBlank(); From 31b84046d0022c9baa8f4a83b1286bbe893755a1 Mon Sep 17 00:00:00 2001 From: t-gabor Date: Thu, 23 May 2019 16:27:16 +0200 Subject: [PATCH 3/4] Load IP settings from 3DSController.alt.ini when Left trigger is pressed. --- 3DS/include/settings.h | 2 +- 3DS/source/main.c | 8 ++++++-- 3DS/source/settings.c | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/3DS/include/settings.h b/3DS/include/settings.h index f5d87df..b818a11 100644 --- a/3DS/include/settings.h +++ b/3DS/include/settings.h @@ -12,4 +12,4 @@ extern struct settings defaultSettings; extern Handle fileHandle; -bool readSettings(void); +bool readSettings(const char *settingsFile); diff --git a/3DS/source/main.c b/3DS/source/main.c index 2958b2d..40e928f 100644 --- a/3DS/source/main.c +++ b/3DS/source/main.c @@ -65,7 +65,7 @@ int main(void) { drawString(10, 10, "Waiting for WiFi connection..."); drawString(10, 20, "Ensure you are in range of an access point,"); drawString(10, 30, "and that wireless communications are enabled."); - drawString(10, 50, "You can alternatively press Start and Select to exit."); + drawString(10, 50, "You can alternatively press Start and Select and Left Trigger to exit."); u32 kHeld = hidKeysHeld(); if((kHeld & KEY_START) && (kHeld & KEY_SELECT)) longjmp(exitJmp, 1); @@ -80,7 +80,11 @@ int main(void) { gfxFlushBuffers(); gfxSwapBuffers(); - if(!readSettings()) { + hidScanInput(); + u32 kHeld = hidKeysHeld(); + const char* settingsFile = "/3DSController.ini"; + const char* settingsFileAlt = "/3DSController.alt.ini"; + if(!readSettings(kHeld & KEY_L ? settingsFileAlt : settingsFile)) { hang("Could not read 3DSController.ini!"); } diff --git a/3DS/source/settings.c b/3DS/source/settings.c index 8018436..5b19a3a 100644 --- a/3DS/source/settings.c +++ b/3DS/source/settings.c @@ -35,12 +35,12 @@ static bool getSetting(char *name, char *src, char *dest) { return false; } -bool readSettings(void) { +bool readSettings(const char *settingsFile) { char *buffer = NULL; u64 size; u32 bytesRead; - FS_Path filePath = fsMakePath(PATH_ASCII, "/3DSController.ini"); + FS_Path filePath = fsMakePath(PATH_ASCII, settingsFile); Result ret = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), filePath, FS_OPEN_READ, 0x00000000); if(ret) return false; From a57d864ae49fe85ff1c77d146949b0a759f19662 Mon Sep 17 00:00:00 2001 From: t-gabor Date: Fri, 24 May 2019 10:17:23 +0200 Subject: [PATCH 4/4] Add mouse support, use left trigger to right click. --- Linux/3DSController_uinput.py | 43 +++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/Linux/3DSController_uinput.py b/Linux/3DSController_uinput.py index 51ac263..fa15532 100755 --- a/Linux/3DSController_uinput.py +++ b/Linux/3DSController_uinput.py @@ -9,6 +9,7 @@ # CONFIGURABLE REGION START - Don't touch anything above # ########################################################## port = 8889 +mouse_speed = 4 btn_gamepad_map = { "A": uinput.BTN_A, @@ -75,6 +76,13 @@ uinput.BTN_MODE, ]) +mouse_device = uinput.Device([ + uinput.REL_X, + uinput.REL_Y, + uinput.BTN_LEFT, + uinput.BTN_RIGHT + ]) + keyboard_device = uinput.Device(keboard_matrix + btn_keyboard_map.values()) ######################################################## @@ -148,12 +156,17 @@ def press_btn(btn): def release_btn(btn): gamepad_device.emit(btn, 0) +def move_mouse(x,y): + x=int(x) + y=int(y) + if not x and not y: return + mouse_device.emit(uinput.REL_X, x) + mouse_device.emit(uinput.REL_Y, y) 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 @@ -210,10 +223,30 @@ def release_btn(btn): if oldkeys & (1<