26 August 2026

Connecting a Nintendo Switch Pro Controller Wirelessly to Ubuntu 26.04

(Courtesy of the Kagi Assistant and saved for posterity...)

Context & Symptoms

System: Ubuntu 26.04 (Linux), using the in-kernel hid_nintendo driver (057e:2009), connecting a Nintendo Switch Pro Controller over Bluetooth.

Symptoms: The controller paired successfully and hid_nintendo bound to it in dmesg, but two problems appeared:

  1. The controller would immediately disconnect when evtest (or any program) tried to read from the device, with the error: expected 24 bytes, got -1 — No such device.
  2. After fixing the disconnect, evtest could see the controller's input node but no button or stick events appeared when pressing anything. The controller worked perfectly when connected via USB-C cable — the problem was Bluetooth-only.

The root causes were two separate BlueZ/Bluetooth configuration issues: a firmware quirk in the controller that rejects adapters with unexpected names, and BlueZ's default UHID transport silently failing to forward HID input reports to the kernel.


Step 1: Rename the Bluetooth adapter to “Nintendo”

The Pro Controller's firmware has a quirk where it disconnects if the Bluetooth adapter name doesn't match what it expects. Renaming the adapter satisfies the handshake.

Edit /etc/bluetooth/main.conf:

sudo nano /etc/bluetooth/main.conf

Under [General], set:

[General]
Name = Nintendo
Alias = Nintendo

Restart Bluetooth:

sudo systemctl restart bluetooth

This fixed the immediate disconnection observed when evtest tried to read from the device.


Step 2: Disable UHID (use kernel HIDP instead)

BlueZ's default UHID (userspace HID) transport was creating the input device but not forwarding input reports from the controller to the kernel — so evtest saw the device but no button events ever arrived. Switching to kernel HIDP fixed this.

The telltale sign in dmesg was the input device path containing /devices/virtual/misc/uhid/, indicating the UHID transport was in use:

input: Pro Controller as /devices/virtual/misc/uhid/0005:057E:2009.000E/input/input31

Edit /etc/bluetooth/input.conf:

sudo nano /etc/bluetooth/input.conf

Under [General], set:

[General]
UserspaceHID=false

Restart Bluetooth:

sudo systemctl restart bluetooth

Note: This requires the hidp kernel module. On stock Ubuntu it should be available, but if the controller fails to connect after this change, load it manually:

sudo modprobe hidp
echo "hidp" | sudo tee /etc/modules-load.d/hidp.conf

Step 3: Re-pair the controller

Remove the old pairing and re-pair fresh, since the Bluetooth configuration has changed:

bluetoothctl
[bluetooth]# remove <MAC_ADDRESS>
[bluetooth]# scan on
# (hold the Sync button on the Pro Controller until LEDs flash)
[bluetooth]# pair <MAC_ADDRESS>
[bluetooth]# trust <MAC_ADDRESS>
[bluetooth]# connect <MAC_ADDRESS>
[bluetooth]# quit

The trust command is important — without it, the controller may disconnect after pairing.


Step 4: Verify

Confirm the input device is no longer on the UHID path:

dmesg | grep -i "057e:2009" | tail -10

The input: line should now reference a real Bluetooth device path rather than /devices/virtual/misc/uhid/.

Test with:

sudo evtest

Select “Pro Controller” (not “Pro Controller (IMU)”) and press buttons — events should now stream.


Summary of problems and fixes

Problem Cause Fix
Controller disconnects when evtest opens it BT adapter name doesn't match controller's firmware expectation Rename adapter to “Nintendo” in main.conf
evtest sees the device but no button events BlueZ UHID transport creates input node but doesn't forward HID reports Set UserspaceHID=false in input.conf to use kernel HIDP
USB worked but Bluetooth didn't USB bypasses BlueZ entirely; Bluetooth was broken at the BlueZ → UHID bridge Same — switching to HIDP bypasses the broken bridge

14 August 2026

Maternal Oddity

(I spotted this parody in my file of old writings. Evidently, it's from June 2018.)

Ground Control to Major Mom
Ground Control to Major Mom
Take your protein pills and put your make-up on
Ground Control to Major Mom (ten, nine, eight, seven, six)
Commencing countdown, footwear on (five, four, three)
Odor suspicion! Did you wash your hands too? (two, one, liftoff)

This is Ground Control to Major Mom
We've really made the bed
And the kiddos want to know which shirts to wear.
Now it's time to leave the castle if you dare.

"This is Major Mom in Complete Control
I'm stepping out the door
And no one's listening to what I say
And I'm haunted by forgetting something today

For here
Is coffee in my right hand
No silence in the room
The toddler is blue
And there's nothing I can do

Though we've passed one hundred thousand smiles
I've misgivings still
And I hope my husband knows which way to go
That fool better love me very much he knows"

Ground Control to Major Mom
Your mobile's dead, there's something wrong
Can you hear me, Major Mom?
Can you hear me, Major Mom?
Can you hear me, Major Mom?
Can you...

"Here am I coffee in my right hand
No silence in the room
The toddler is blue
And there's nothing I can do"

04 August 2026

The Alexandrian Moment

For giggles, I put a short story up as an Amazon Kindle offering: The Alexandrian Moment

30 July 2026

Workaround gamescope invisible cursor bug in Ubuntu 26.04

The bug

On Ubuntu 26.04 (resolute, which ships gamescope 3.16.20), launching a game through gamescope with --force-grab-cursor can make the mouse pointer vanish. Upstream: https://github.com/ValveSoftware/gamescope/issues/2180

Dropping --force-grab-cursor avoids the bug, but that flag is what confines the pointer to the game window on a multi-monitor setup, so removing it is not an acceptable fix here.

A Workaround

Only one thing worked for me: build the older questing gamescope 3.16.15 and run it instead of resolute's 3.16.20.

Everything else raised on #2180 and related threads failed or did not apply:

  • --hide-cursor-delay 99999 — no effect.
  • WLR_NO_HARDWARE_CURSORS=1 — no effect.
  • Borderless-windowed / backend switching — unreliable.
  • winecfg / protontricks "capture mouse in fullscreen" — Proton/Wine only, so no help for a native-Linux game.
  • Downgrade further, to 3.14.x — unnecessary; 3.16.15 sufficed.

Both 3.16.15 and 3.16.20 are 3.16.x, so the regression landed in a commit between those two tags.

Procedure for Agents

Build gamescope 3.16.15-2 from Ubuntu questing sources and install, self-contained, under ~/Stow/gamescope-questing. No root, no system changes. resolute dropped wlroots 0.18 (ships 0.19), which 3.16.15 needs — Debian patch 0006 wires gamescope to the system libwlroots-0.18-dev, and the Debian tarball strips the vendored subprojects/wlroots — so build wlroots 0.18 from its matching Ubuntu source into the same prefix.

Sources

All from the Ubuntu archive. Confirm each download's checksum against the Checksums-Sha256 field of its .dsc.

Prerequisites

Install both packages' Build-Depends (see each .dsc). gamescope's libwlroots-0.18-dev is supplied by the wlroots build below; wlroots' libcairo2-dev is needed only for examples, which are disabled.

Build

Prefix for both: PREFIX="$HOME/Stow/gamescope-questing". Apply each package's Debian quilt patches before configuring:

while read p; do patch -p1 -i debian/patches/$p; done < debian/patches/series

1. wlroots 0.18.2 -> $PREFIX

meson setup build --prefix="$PREFIX" --libdir=lib --buildtype=release \
  -Dexamples=false -Dwerror=false
ninja -C build install

Installs libwlroots-0.18.so, headers under include/wlroots-0.18, and lib/pkgconfig/wlroots-0.18.pc.

2. gamescope 3.16.15 -> $PREFIX

Two source edits after patching:

  • src/meson.build: patch 0006 hardcodes /usr/include/wlroots-0.18 in reshade_include. Replace with join_paths(get_option('prefix'), 'include', 'wlroots-0.18').
  • src/meson.build: vcs_tag() has no git tree, so the --version banner is blank. After gamescope_version_conf = configuration_data() add gamescope_version_conf.set('VCS_TAG', '3.16.15').
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:\
/usr/lib/x86_64-linux-gnu/pkgconfig:\
/usr/lib/pkgconfig:\
/usr/share/pkgconfig"
meson setup build --prefix="$PREFIX" --libdir=lib --bindir=bin --buildtype=release \
  -Dpipewire=enabled -Denable_openvr_support=false \
  -Dcpp_link_args='-Wl,-rpath,$ORIGIN/../lib' \
  -Dc_link_args='-Wl,-rpath,$ORIGIN/../lib'
ninja -C build install

Choices, matching the Debian rules except where noted:

  • -Dpipewire=enabled -Denable_openvr_support=false from Debian rules.
  • bindir=bin, libdir=lib (Debian uses /usr/games) so the tree suits a stow package.
  • PKG_CONFIG_PATH puts $PREFIX first so the staged wlroots-0.18 wins.
  • rpath $ORIGIN/../lib lets gamescope find its bundled libwlroots-0.18.so both in the raw prefix and after stowing.

Verify

readelf -d "$PREFIX/bin/gamescope" | grep RUNPATH        # $ORIGIN/../lib
ldd "$PREFIX/bin/gamescope" | grep wlroots               # resolves inside $PREFIX
"$PREFIX/bin/gamescope" --version                        # gamescope version 3.16.15