jc2mouse is a Linux userspace tool for using Nintendo Switch 2 Joy-Con 2 controllers and the Nintendo Switch 2 Pro Controller over BLE, with support for bondless / no-pairing workflows.
It currently supports:
- Single Joy-Con 2 mode
- mouse mode
- compact gamepad mode
- Combined Joy-Con 2 mode
- Left + Right as one full virtual controller
- optional right-side mouse overlay
- Nintendo Switch 2 Pro Controller mode
- full gamepad via uinput
Originally, this project depended on a patched BlueZ session-mode workflow to work around BLE / GATT behavior seen on some systems.
On newer systems, stock BlueZ may already work well enough for jc2mouse without needing the patched session daemon.
Because of that, the project now treats patched BlueZ as:
- an optional fallback
- a troubleshooting path
- a way to test behavior against a custom BlueZ build when stock behavior is unreliable
So the normal recommendation is:
- Try jc2mouse with your stock Bluetooth stack first
- If your controller still drops, fails to enumerate correctly, or behaves inconsistently, then try the patched BlueZ session mode
- Optical mouse motion
- Mouse buttons from controller buttons
- Stick-based scroll
- Toggle mouse/gamepad mode with C
- Optical mouse motion
- Mouse buttons from controller buttons
- Stick-based scroll
- Toggle mouse/gamepad mode with hold L + ZL
- Left + Right exposed as one full virtual gamepad
- Xbox-style face-button layout for better Linux / Steam compatibility
- Right stick, left stick, shoulders, triggers, d-pad, etc.
- Right-only mouse overlay:
- press C on the Right Joy-Con
- Right side becomes mouse
- Left side continues as the left half of the controller
- Auto-detected from the main CLI when supported advertisements are visible
- Exposed as a full virtual gamepad
- Can also be run directly through the dedicated ns2pro path if needed
src/jc2mouse/cli.py— CLI, discovery, session handlingsrc/jc2mouse/driver.py— Joy-Con 2 driver logicsrc/jc2mouse/ns2pro.py— Switch 2 Pro Controller driversrc/jc2mouse/mapper.py— developer button-mapping helperscripts/setup.sh— installs helper/service filesscripts/jc2-session.sh— session-mode helperscripts/build_bluez.sh— optional patched BlueZ build/install scriptsystemd/jc2-bluetooth.service— optional patched session-mode bluetoothd unit
- Linux
- Python 3.10+ recommended
- root privileges for uinput / Bluetooth control
- working BlueZ / D-Bus environment
uinputavailable
From the repo root:
python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .sudo scripts/setup.shThis installs:
/usr/local/sbin/jc2-session/etc/systemd/system/jc2-bluetooth.service
Note: installing these files does not force you to use patched BlueZ. It just prepares the optional fallback path.
Only do this if stock Bluetooth is unreliable on your system, or if you want to test the custom session-mode path.
sudo scripts/build_bluez.shThis installs a patched bluetoothd under:
/opt/jc2mouse/bluez
jc2mouse --help
jc2mouse run --help
jc2mouse scan --helpIf using a virtualenv under sudo:
sudo -E .venv/bin/jc2mouse --helpThis is now the recommended first test.
sudo -E .venv/bin/jc2mouse run --auto --session offsudo -E .venv/bin/jc2mouse run --auto --combined --session offsudo -E .venv/bin/jc2mouse run --auto --side left --session off
sudo -E .venv/bin/jc2mouse run --auto --side right --session offsudo -E .venv/bin/jc2mouse run --auto --verbose --session offsudo -E .venv/bin/jc2mouse run --auto --verbose --session off 2>&1 | tee debug/jc2mouse.logIf stock Bluetooth is unstable, try session mode.
sudo jc2-session startsudo jc2-session statussudo jc2-session stopsudo -E .venv/bin/jc2mouse run --autoThe default --session auto behavior will attempt to use the optional session helper path when appropriate.
If you explicitly want to avoid any session changes:
sudo -E .venv/bin/jc2mouse run --auto --session offsudo -E .venv/bin/jc2mouse run --auto --session offsudo -E .venv/bin/jc2mouse run --auto --combined --session offsudo -E .venv/bin/jc2mouse scan --timeout 8sudo -E .venv/bin/jc2mouse run --auto --session offIf the advertised device is recognized as an NS2 Pro controller, the CLI will route into the ns2pro driver path automatically.
sudo -E .venv/bin/jc2mouse ns2pro --mac 38:C6:CE:29:60:44 --session offPress:
Ctrl+C
The CLI will stop cleanly.
If you also want the controller disconnected on exit, use:
--disconnect-on-exitExamples:
sudo -E .venv/bin/jc2mouse run --auto --session off --disconnect-on-exit
sudo -E .venv/bin/jc2mouse run --auto --combined --session off --disconnect-on-exitIf session mode is failing but stock Bluetooth works, just run with:
--session offThen inspect:
systemctl status jc2-bluetooth.service --no-pager -l
journalctl -u jc2-bluetooth.service -n 100 --no-pagersudo systemctl unmask bluetooth.service
sudo systemctl enable --now bluetooth.service- hold the controller PAIR button
- avoid pressing other buttons while it is connecting
- try
scanfirst - use
--askif multiple compatible devices are advertising
On some systems, if the first Joy-Con falls out of pairing state before the second side is ready, you may need to press PAIR again on the side that dropped.
If a Joy-Con enters mouse mode but optical data does not become active immediately, re-run and try again. This is known to occasionally happen during bring-up and is not always a permanent failure.
- This project name started from the Joy-Con 2 mouse workflow, but it now also supports broader Nintendo Switch 2 controller use cases.
- The optional patched BlueZ path is still kept in the repo because it remains useful for regression testing and for systems where stock behavior is not sufficient.
See repository license / project preferences.