Skip to content

Repository files navigation

moanpoint

An app that makes every interaction with your trackpoint awkward.

moanpoint listens to your laptop's pointing stick (ThinkPad TrackPoint, pointing stick, etc.) and plays moaning sounds whose volume scales with how hard you push it.


Quick Install

One command. It clones the repo, installs uv and any missing build tools, and sets up the background service:

curl -LsSf https://raw.githubusercontent.com/nicolasgutierrezdev/moanpoint/main/install.sh | bash

Want a specific voice? Pass arguments through bash -s --:

curl -LsSf https://raw.githubusercontent.com/nicolasgutierrezdev/moanpoint/main/install.sh | bash -s -- --voice mia

Prefer to read the script first (recommended, as always):

git clone https://github.com/nicolasgutierrezdev/moanpoint.git
cd moanpoint
./install.sh

The installer will:

  1. Install missing build tools for your distro (Arch, Debian/Ubuntu, Fedora, openSUSE)
  2. Install uv if you don't have it
  3. Add your user to the input group (needed to read the pointing stick device)
  4. Install Python dependencies into a project-local .venv, reusing your system Python when it is usable
  5. Install, enable and start a systemd user service

It is safe to re-run at any time.

Note: If the installer added you to the input group, log out and back in, then run systemctl --user restart moanpoint.

Installer options

Usage: ./install.sh [options]

Options:
  --voice NAME   Install with a specific voice (e.g. mia, sofia).
  --linger       Enable lingering so moanpoint starts at boot without login.
  --no-service   Install dependencies only, skip the systemd service.
  -h, --help     Show this help.

Try It Without Installing

main.py carries PEP 723 inline dependencies, so uv can run it straight from a fresh clone — no virtualenv, no uv sync, nothing to clean up afterwards:

uv run main.py

Requirements

  • Linux (evdev and udev are Linux-only)
  • A laptop with a pointing stick / TrackPoint
  • git and curl
  • A C compiler and kernel headers (the installer installs these for you)
  • systemd — optional, only for running as a background service
  • A working audio setup (PulseAudio or PipeWire)

Python itself is not a prerequisite. The installer uses your system Python if it is 3.10+ and has the development headers (Python.h); otherwise uv downloads a suitable interpreter automatically.

System packages

The installer handles this. The table is only for manual setups:

Distro Command
Arch / Manjaro sudo pacman -S --needed base-devel git curl uv
Debian / Ubuntu sudo apt install build-essential git curl
Fedora sudo dnf install gcc kernel-headers git curl
openSUSE sudo zypper install gcc linux-glibc-devel git curl

Arch ships uv in the extra repository. On other distros the installer pulls it from astral.sh.

Dependencies

Managed by uv and pinned in uv.lock. No manual pip installs needed.

Package Version Purpose
evdev >=1.9.3 Read raw input events from the pointing stick
pygame-ce >=2.5.7 Load and play WAV audio, control volume

evdev has no prebuilt wheels, so it compiles a small C extension during installation — that's the only reason a compiler is needed.

pygame-ce is the community fork of pygame and a drop-in replacement — the code still does import pygame. It is used here because it ships wheels for current Python releases. Upstream pygame has none past 3.13, so it would build from source and quietly leave out pygame.mixer unless SDL2_mixer's headers happen to be installed, giving you an install with no sound.


Manual Usage

Run directly without installing as a service:

uv run main.py

Options

usage: moanpoint [-h] [--voice NAME] [--list-voices]

options:
  -h, --help      show this help message and exit
  --voice NAME    Voice to use (e.g. 'mia', 'sofia'). Overrides MOANPOINT_VOICE env var.
  --list-voices   List available voices and exit.

Voice Configuration

List available voices

uv run main.py --list-voices

Output:

Available voices:
  luna
  mia
  sofia

Select a voice

uv run main.py --voice sofia

You can also use the bare filename with extension:

uv run main.py --voice sofia.wav

Via environment variable

Set MOANPOINT_VOICE in your shell or service file:

MOANPOINT_VOICE=mia uv run main.py

Priority order: --voice argument > MOANPOINT_VOICE env var > default (luna).

If a voice file cannot be found, moanpoint falls back to luna with a warning.


Running as a Service

After running install.sh, the service is already running. Use standard systemctl commands:

# Check status
systemctl --user status moanpoint

# Stop
systemctl --user stop moanpoint

# Start
systemctl --user start moanpoint

# Restart (e.g. after changing the voice)
systemctl --user restart moanpoint

# Follow the logs
journalctl --user -u moanpoint -f

# Disable autostart
systemctl --user disable moanpoint

The unit runs the project's .venv/bin/python directly, so uv doesn't need to be on the PATH at boot.

Changing the voice after installation

Easiest way is to re-run the installer:

./install.sh --voice sofia

Or edit ~/.config/systemd/user/moanpoint.service by hand, uncomment the Environment= line and set your voice:

Environment=MOANPOINT_VOICE=sofia

Then reload and restart:

systemctl --user daemon-reload
systemctl --user restart moanpoint

Autostart on boot (without login)

For the service to automatically start when your system boots (even without logging in), enable lingering:

sudo loginctl enable-linger $USER

./install.sh --linger does this for you.


Adding Your Own Voices

Drop any .wav file into the voices/ directory and refer to it by its stem name:

cp /path/to/myfavorite.wav voices/myfavorite.wav
uv run main.py --voice myfavorite

Uninstall

./uninstall.sh

Stops and removes the service and deletes the .venv. It prints optional follow-up steps (leaving the input group, disabling lingering, removing the downloaded copy).

If you installed with the curl one-liner, the repo was cloned to ~/.local/share/moanpoint (or $XDG_DATA_HOME/moanpoint), and the uninstaller lives there:

~/.local/share/moanpoint/uninstall.sh
rm -rf ~/.local/share/moanpoint

Run it from the checkout — unlike install.sh, it can't be piped from curl.


License

MIT — see LICENSE.

About

An app that makes every interaction with your trackpoint awkward.

Resources

Stars

24 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages