返回资讯中心
外部精选
软件工程
#开发者实时流

Using a PS3 Navigation Controller on Arch Linux

Everything here runs on Arch Linux with X11 and dwm:

Hacker News 最新SavaRocks2026/10/16 · 21 天前21 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

Everything here runs on Arch Linux with X11 and dwm:

I needed to install Bluetooth utilities, Python, the Python input-device library, xdotool, evtest and triggerhappy.

Install the required packages

sudo pacman -S bluez bluez-utils python python-evdev xdotool evtest triggerhappy

Enable Bluetooth and start it immediately

sudo systemctl enable --now bluetooth

Power on the PS3 Navigation Controller and open the Bluetooth control utility:

Inside bluetoothctl, run the following commands:

Enable the Bluetooth adapter

power on

Enable the Bluetooth agent

agent on

Make the agent the default

default-agent

Search for nearby Bluetooth devices

scan on

Once the controller appears, use its Bluetooth address in place of CONTROLLER_MAC:

Pair with the controller

pair CONTROLLER_MAC

Trust the controller for future connections

trust CONTROLLER_MAC

Connect to the controller

connect CONTROLLER_MAC

Stop scanning for devices

scan off

Exit the Bluetooth shell

exit

Linux assigns input devices names such as /dev/input/event19. That number can change after reconnecting the controller or rebooting, so hardcoding it in scripts is not ideal.

Instead, create a udev rule that gives the controller a predictable symlink:

Open a new udev rule file

sudo vim /etc/udev/rules.d/99-navigation-controller.rules

Create a stable symlink for the PS3 Navigation Controller

SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="Navigation Controller", ATTRS{uniq}=="CONTROLLER_MAC", SYMLINK+="input/navigation-controller"

Reload udev rules

sudo udevadm control --reload-rules

Apply the updated rules to connected devices

sudo udevadm trigger

The analog stick reports its position through Linux input events. A Python script reads those events and uses xdotool to move the mouse pointer. I like keeping my custom scripts inside .config/scripts and store them inside my dotfiles repository.

#!/usr/bin/env python3

Read Linux input events

import evdev

Run xdotool commands

import subprocess

Wait before retrying unavailable devices

import time

Use the stable udev symlink

DEVICE = "/dev/input/navigation-controller"

Center position of the analog stick

CENTER_X = 131 CENTER_Y = 128

Ignore small movements around the center

DEADZONE = 15

Maximum number of pixels moved per update

MAX_SPEED = 8

def move_mouse(x, y):

Calculate displacement from the stick's center

dx = x - CENTER_X dy = y - CENTER_Y

Ignore horizontal movement inside the deadzone

if abs(dx) <= DEADZONE: dx = 0

Ignore vertical movement inside the deadzone

if abs(dy) <= DEADZONE: dy = 0

Do nothing when the stick is centered

if dx == 0 and dy == 0: return

Convert stick displacement into mouse movement

mx = int(dx / (128 - DEADZONE) * MAX_SPEED) my = int(dy / (128 - DEADZONE) * MAX_SPEED)

Ensure small horizontal movements still move the pointer

if mx == 0 and dx: mx = 1 if dx > 0 else -1

Ensure small vertical movements still move the pointer

if my == 0 and dy: my = 1 if dy > 0 else -1

Move the pointer relative to its current position

subprocess.run( ["xdotool", "mousemove_relative", "--", str(mx), str(my)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, )

Keep the script running across controller disconnections

while True:

Wait until the controller device becomes available

while True: try: dev = evdev.InputDevice(DEVICE) break except (FileNotFoundError, OSError): time.sleep(3)

Initialize the stick position

x = CENTER_X y = CENTER_Y

try:

Process incoming input events

for event in dev.read_loop():

Only handle analog-axis events

if event.type == evdev.ecodes.EV_ABS:

Update the horizontal stick position

if event.code == evdev.ecodes.ABS_X: x = event.value

Update the vertical stick position

elif event.code == evdev.ecodes.ABS_Y: y = event.value

Move the mouse using the latest stick position

move_mouse(x, y)

except (OSError, IOError):

Close the device after a disconnection

dev.close()

Wait briefly before attempting to reconnect

time.sleep(1)

Now that the analog stick will act as a mouse we can

First of all edit the triggerhappy service:

[Unit] After=bluetooth.target

[Service] ExecStart= ExecStart=/usr/sbin/thd --triggers /etc/triggerhappy/triggers.d/ --device /dev/input/navigation-controller Restart=always RestartSec=3

Make sure you paste the code above between:

Editing /etc/systemd/system/triggerhappy.service.d/override.conf

Anything between here and the comment below will become the contents of the drop-in file

Edits below this comment will be discarded

Together, these settings make the controller listener start after bluetooth is started, use the intended configuration and input device, and recover automatically if the daemon stops. Enable the service with:

Reload systemd unit definitions and overrides

sudo systemctl daemon-reload

Enable triggerhappy at boot and start it now

sudo systemctl enable --now triggerhappy

The analog stick handles mouse movement, but the physical buttons are handled by triggerhappy. I chose the X button to open Netflix.com in Firefox and make it full screen and the O button to open HBOMax.com.

#!/bin/sh

Select the active X display

export DISPLAY=:0

Tell X11 where the user's authentication file is

export XAUTHORITY=/home/sava/.Xauthority

Connect to the user's D-Bus session

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

Open Netflix in a new Firefox window

firefox --new-window https://www.netflix.com/ &

Wait for Firefox to begin opening the window

sleep 1

Toggle Firefox fullscreen

xdotool key F11

Of couse, the HBOMax one the same but the URL is changed when launching firefox.

If you want to bind a different button or simply figure out which button corresponds to which Linux input event, evtest is your friend.

Start monitoring controller input events

sudo evtest /dev/input/navigation-controller

Now press and release the buttons you want to identify. The output will show the event name, code, and value. For example here's what happens when you press the Up arrow on the DPAD:

Event: time 1791890030.090573, -------------- SYN_REPORT ------------ Event: time 1791890030.126617, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005 Event: time 1791890030.126617, type 1 (EV_KEY), code 544 (BTN_DPAD_UP), value 0 Event: time 1791890030.126617, -------------- SYN_REPORT ------------ Event: time 1791890030.202585, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005 Event: time 1791890030.202585, type 1 (EV_KEY), code 544 (BTN_DPAD_UP), value 1

Launch Netflix as the sava user when the X button is pressed

BTN_GAMEPAD 1 sudo -u sava /home/sava/.config/scripts/netflix.sh

Launch HBO Max as the sava user when the O button is pressed

BTN_B 1 sudo -u sava /home/sava/.config/scripts/hbomax.sh

Send Super+Q to dwm to close the focused window when the PS button is pressed

BTN_MODE 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool key super+q

Send the media Play/Pause key when the L1 button is pressed

BTN_TL 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool key XF86AudioPlay

Simulate a right mouse click when the L2 button is pressed

BTN_TL2 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool click 3

Simulate a left mouse click when the analog stick is pressed (L3)

BTN_THUMBL 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool click 1

Send the Left arrow key when the D-pad is pressed left

BTN_DPAD_LEFT 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool key Left

Send the Right arrow key when the D-pad is pressed right

BTN_DPAD_RIGHT 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool key Right

Send the Up arrow key when the D-pad is pressed up

BTN_DPAD_UP 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool key Up

Send the Down arrow key when the D-pad is pressed down

BTN_DPAD_DOWN 1 DISPLAY=:0 XAUTHORITY=/home/sava/.Xauthority xdotool key Down

and start the navigation controller "mouse" script at login:

~/.config/scripts/ps3-mouse.py & exec dwm

And there you have it. If you chose to use a navigation controller yourself and use my triggerhappy config:

It's a fun way to repurpose old hardware and make an Arch Linux machine more comfortable to use from the couch. 🛋️ + 🎮 = 📽 + 🍿

If this tutorial saved you time, sanity, or a mild existential crisis... consider fueling future chaos with a small donation:

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →