Fuck League of Legends, fuck Riot and fuck Vanguard.
Relay mouse events from Linux to Windows, over UDP via Pi acting as a HID device.
I use Linux for everything in my life other than League of Legends, so I have an old PC setup as a server for League of Legends. For this I use Sunshine on the Windows PC to host and Moonlight on the Linux machine to remote in. Since everything is wired locally, this works with virtually 0 noticeable latency. The problem is the shitty abomination that Vanguard is blocks the virtual mouse that Sunshine + Moonlight uses.
This project is an attempt to fix this.
- Windows 11 PC
- Sunshine
- HDMI dummy plug
- Auto login - nice if/when we have to reboot
- Framework Laptop
- Raspberry Pi Zero 2 w
- ENC28J60 & Dupont wires optionally for lower latency
- MicroUSB to USB
Build USB Host native:
gcc -o usb_host usb_host.c window_monitor.c # for linux machineInstall ARM cross-compiler:
sudo apt-get install gcc-aarch64-linux-gnu # for Pi (64-bit OS)Cross-compile for ARM:
aarch64-linux-gnu-gcc -o usb_client usb_client.c # for Pi (64-bit)Need to setup the Pi up to act as a USB host, so Windows recognizes it as a mouse.
Enable modules and drivers:
echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt
sudo echo "dwc2" | sudo tee -a /etc/modules
sudo echo "libcomposite" | sudo tee -a /etc/modulesDouble check:
sudo nano /boot/firmware/config.txtand add dtoverlay if it's not under [all]
[all]
dtoverlay=dwc2
Reboot
sudo reboot
lsmod | grep dwc2 # Should now show dwc2Create config scripts:
sudo touch /usr/bin/pi_usb
sudo chmod +x /usr/bin/pi_usbsudo nano /usr/bin/pi_usband enter, which creates the USB gadget when ran:
#!/bin/bash
GADGET_DIR="/sys/kernel/config/usb_gadget/usb_pi"
# Cleanup function
cleanup_gadget() {
if [ -d "$GADGET_DIR" ]; then
echo "Cleaning up existing gadget..."
cd "$GADGET_DIR"
# Unbind from UDC (important to do first!)
if [ -f UDC ]; then
echo "" > UDC 2>/dev/null || true
fi
# Remove symlinks from configs
find configs/*/hid.usb* -type l -delete 2>/dev/null || true
# Remove functions
find functions/hid.usb* -type d -exec rmdir {} \; 2>/dev/null || true
# Remove config strings
find configs/*/strings/* -type d -exec rmdir {} \; 2>/dev/null || true
# Remove configs
find configs/* -type d -exec rmdir {} \; 2>/dev/null || true
# Remove gadget strings
find strings/* -type d -exec rmdir {} \; 2>/dev/null || true
# Remove the gadget itself
cd /sys/kernel/config/usb_gadget/
rmdir usb_pi 2>/dev/null || true
echo "Cleanup complete"
sleep 1
fi
}
# Load required modules
modprobe libcomposite
# Clean up first
cleanup_gadget
# Create fresh gadget
cd /sys/kernel/config/usb_gadget/
mkdir -p usb_pi
cd usb_pi
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
mkdir -p strings/0x409
echo "fedcba9876543210" > strings/0x409/serialnumber
echo "Noah Giles" > strings/0x409/manufacturer
echo "USB Mouse" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "Config 1: HID Mouse" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
# Add HID mouse function
mkdir -p functions/hid.usb0
echo 2 > functions/hid.usb0/protocol
echo 1 > functions/hid.usb0/subclass
echo 4 > functions/hid.usb0/report_length
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x05\\x15\\x00\\x25\\x01\\x95\\x05\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x03\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x02\\x81\\x06\\x09\\x38\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x01\\x81\\x06\\xc0\\xc0 > functions/hid.usb0/report_desc
ln -s functions/hid.usb0 configs/c.1/
# Bind to UDC
UDC_DEVICE=$(ls /sys/class/udc | head -n1)
echo "$UDC_DEVICE" > UDC
echo "USB HID Mouse gadget configured successfully!"
echo "Device: $UDC_DEVICE"
echo "HID device should be at /dev/hidg0"
Create Systemd service, to auto-start if the Pi reboots.
sudo nano /etc/systemd/system/pi_usb.servicewith the following contents:
[Unit]
Description=Run my custom script at startup
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/bin/pi_usb
RemainAfterExit=yes
User=root
[Install]
WantedBy=multi-user.target
and to start the service:
sudo systemctl daemon-reload
sudo systemctl enable pi_usb.service
sudo systemctl start pi_usb.serviceCheck it worked:
# Should show a UDC device
ls /sys/class/udc/
# Should exist now
ls -l /dev/hidg0If they don't show, gg figure it out.
You should have:
- usb_host - Intended for Linux (sending mouse events to Pi)
- usb_client - Intended for Raspberry Pi (receiving mouse events from Linux and forward to Windows)
Copy usb_client to Pi:
scp usb_client noah@usbpi.local:/home/noah/and run using:
sudo ./usb_clientWe should see two log lines for opening the HID device and listening to port 5555.
Find mouse:
cat /proc/bus/input/devicesYou are looking for the event number of your mouse.
Then run:
sudo -E ./usb_host -d /dev/input/event5 -i 192.168.0.102 -w stream.Moonlight -s 1.677sudo -E or sudo HYPRLAND_INSTANCE_SIGNATURE=$HYPRLAND_INSTANCE_SIGNATURE so we can use hyprctl.
event5 = mouse event number
192.168.0.102 = IP of Pi
stream.Moonlight = Class of window (I only want to send mouse events when Moonlight is active)
1.677 = Sensitivity scaling`
Few notes
This requires a mouse plugged into the Windows host, a dummy plug works and is probably desirable.
This will flag malware warnings etc on Windows, C'est la Vie.
sudo pacman -S mingw-w64-gccx86_64-w64-mingw32-gcc -o usb_client_win.exe windows/usb_client_interception.c windows/interception.dll -lws2_32- Download oblitum's interception
- Open command prompt in the
command line installerdirectory - Run
install-interception.exe /install - Reboot
- Copy the usb_client_win.exe & interception.dll to the Windows machine.
- Run usb_client_win.exe (must be in same location as the dll)
- Run the usb_host on Linux host, same as above method for Pi.
