Metadata-Version: 2.4
Name: pttman
Version: 0.4.0
Summary: Push-to-talk microphone control with a daemon-backed command queue
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# pttman

Push-to-talk microphone control for PipeWire.

`pttman` runs a small user service that serializes mute, unmute, and toggle
requests over a Unix datagram socket so rapid key presses do not race each
other.

## Requirements

- PipeWire with PulseAudio compatibility (`pipewire-pulse`)
- `pactl` (from `pipewire-pulse` or `pulseaudio-utils`)
- One of: `systemd` or `OpenRC`

## Installation

### systemd

```bash
uv tool install pttman
pttman install-service
systemctl --user start pttman.service
```

This installs `pttman` to `~/.local/bin/`, copies the systemd user service into
place, and enables it. After installing, point your push-to-talk key at the
client binary, and make sure `~/.local/bin` is on your PATH.

### OpenRC user service (0.60+, Alpine edge, etc.)

```bash
uv tool install pttman
pttman install-service
rc-service --user pttman start
```

On OpenRC 0.60 or newer, `install-service` automatically installs a user-level
service to `~/.config/rc/init.d/pttman`. Make sure `~/.local/bin` is on your
PATH.

### OpenRC system service (older OpenRC)

```bash
sudo uv pip install --system --break-system-packages pttman
sudo pttman install-service
sudo rc-service pttman start
```

On OpenRC versions before 0.60, `install-service` installs a system-level init
script to `/etc/init.d/pttman` and adds it to the default runlevel. The service
uses `supervise-daemon` for process supervision with automatic restart.

To configure the user and environment for the daemon, create
`/etc/conf.d/pttman`:

```sh
command_user="youruser"
supervise_daemon_args="--env XDG_RUNTIME_DIR=/run/user/1000"
```

Replace `1000` with your user's UID (`id -u youruser`).

### Alternative: install.sh (systemd)

```bash
git clone https://github.com/mwolson/pttman.git
cd pttman
./install.sh
systemctl --user start pttman.service
```

This copies `pttman` to `~/.local/bin/` and installs and enables the user
service.

### Optional: set defaults

By default, pttman operates on all audio sources. You can optionally save a
preferred source so that pttman controls only that one:

```bash
pttman list-sources
pttman set-default-source alsa_input.usb-046d_BRIO-03.pro-input-0
```

This writes to `~/.config/pttman.conf` and signals the running daemon to pick up
the change.

### xremap

On Arch Linux, install the xremap variant that matches your desktop environment
(only one should be installed):

```bash
paru -S xremap-gnome-bin     # GNOME
paru -S xremap-hyprland-bin  # Hyprland
paru -S xremap-kde-bin       # KDE Plasma
paru -S xremap-niri-bin      # Niri
paru -S xremap-wlroots-bin   # wlroots-based compositors (sway, etc.)
```

Then configure a push-to-talk key binding:

```yaml
modmap:
  - name: Push-to-talk
    remap:
      F9:
        skip_key_event: true
        press: { launch: ["/home/your-user/.local/bin/pttman", "unmute"] }
        release: { launch: ["/home/your-user/.local/bin/pttman", "mute"] }
```

Pressing F9 (as configured above, which is conveniently labeled with a mic icon
on some laptop keyboards) tells the daemon to unmute. Releasing F9 tells it to
mute again.

You can also route your compositor's mic-mute key through pttman. For example,
in niri's `keybinds.kdl` this implements mic toggle (rather than push-to-talk):

```kdl
XF86AudioMicMute  allow-when-locked=true { spawn "/home/your-user/.local/bin/pttman" "toggle"; }
```

You can check the current microphone state with:

```bash
pttman status
```

### Corsair mice on Arch Linux

If you use a Corsair mouse and want one of its extra buttons to behave like F9,
`ckb-next` is a straightforward way to do it.

On Arch Linux, install `ckb-next` with:

```bash
sudo pacman -S ckb-next
```

Then launch `ckb-next`, select your mouse, pick the button you want to use for
push-to-talk, and remap that button to `F9`.

After that, xremap can keep using the F9 rule above, and your Corsair mouse
button will trigger push-to-talk through `pttman`.

If you specifically want the upstream development build instead of the packaged
release, the `ckb-next` project wiki also lists `ckb-next-git` for Arch-based
systems.

## Commands

pttman uses subcommands for one-off operations. With no subcommand, it runs the
daemon.

```text
pttman                                 Run the daemon (default)
pttman get-default-source              Print the default source from the config file
pttman install-service                 Install and enable the service (systemd or OpenRC)
pttman list-sources                    List available audio sources
pttman mute                            Mute the microphone
pttman set-default-source SOURCE       Save default source and signal the daemon
pttman status                          Print the current microphone state
pttman toggle                          Toggle the microphone mute state
pttman uninstall-service               Disable and remove the service (systemd or OpenRC)
pttman unmute                          Unmute the microphone
```

Aliases:

- `release` for `mute`
- `press` and `talk` for `unmute`

### Options

These flags apply to the daemon and to action commands (`mute`, `unmute`,
`toggle`, `status`):

```text
--source SOURCE     Audio source name to control (default: config file, then all sources)
--all-sources       Operate on all audio sources (overrides --source from config)
```

`--source` and `--all-sources` are mutually exclusive.

## Configuration File

`pttman` reads defaults from `~/.config/pttman.conf` (or
`$XDG_CONFIG_HOME/pttman.conf`). The file uses one flag per line:

```text
--source=alsa_input.usb-046d_BRIO-03.pro-input-0
```

Supported flags:

- `--source=NAME` -- control only this source
- `--all-sources=true` -- control all sources (the default when no config file
  exists)

These are mutually exclusive. Unrecognized flags cause an error at startup.
Command-line arguments always take precedence over the config file.

`set-default-source` automatically signals the running daemon to reload the
config file and update the source for future operations.

## Service

### systemd

```bash
systemctl --user start pttman.service
systemctl --user status pttman.service
journalctl --user -u pttman.service -f
```

### OpenRC (user, 0.60+)

```bash
rc-service --user pttman start
rc-service --user pttman status
```

### OpenRC (system, <0.60)

```bash
sudo rc-service pttman start
rc-service pttman status
```

The daemon listens on `$XDG_RUNTIME_DIR/pttman.sock`. If the daemon is not
running, client commands fall back to direct `pactl` execution.

## Development

### Testing

Unit tests:

```bash
bun run test
```

Integration tests (requires Docker):

```bash
bun run test:integration
```

Both:

```bash
bun run test:all
```

The integration tests use Docker containers to verify the systemd and OpenRC
service files work end-to-end (install, start, stop, uninstall).

### Hooks

```bash
lefthook install
lefthook run pre-commit --all-files
```

The pre-commit hook runs `uvx ruff check`, `uvx ty check`, and the unit test
suite.

## License

MIT
