# How witr Handles Signal Operations in the TUI: Kill, Terminate, Pause, and Renice

> Discover how witr's TUI manages signal operations like kill, terminate, pause, and renice using platform-agnostic syscall wrappers for efficient process control.

- Repository: [Pranshu Parmar/witr](https://github.com/pranshuparmar/witr)
- Tags: internals
- Published: 2026-08-09

---

**witr implements process control signals directly in its TUI layer through platform-agnostic syscall wrappers located in [`internal/tui/actions.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/actions.go), using `syscall.Kill` for POSIX signals and `syscall.Setpriority` for niceness adjustments.**

The open-source process monitor **witr** (pranshuparmar/witr) provides terminal-based process management through an intuitive Text User Interface (TUI). Understanding how **witr handles signal operations** reveals a lightweight architecture that leverages Go's standard library to deliver cross-platform process control without external dependencies.

## Core Signal Implementation in actions.go

The foundation of witr's signal operations resides in [`internal/tui/actions.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/actions.go), where dedicated helper functions map user commands to POSIX signals. Each operation uses the internal `sendSignal` helper to deliver signals via Go's `syscall` package.

### Signal Dispatch Functions

The file defines specific wrappers for each destructive operation at the following locations:

- **`killProcess(pid int)`** – Sends **SIGKILL** (actions.go L13-L14)
- **`termProcess(pid int)`** – Sends **SIGTERM** (actions.go L13-L14)
- **`pauseProcess(pid int)`** – Sends **SIGSTOP** (actions.go L15)
- **`resumeProcess(pid int)`** – Sends **SIGCONT** (actions.go L16)

These functions delegate to a unified `sendSignal(pid int, sig syscall.Signal)` implementation that invokes `syscall.Kill` to transmit the signal to the target process. This abstraction ensures compatibility across Linux, macOS, and BSD systems by handling platform-specific system call details internally.

### Renice Implementation

For priority adjustment, **`setNice(pid, value int)`** (actions.go L30-L35) handles renice operations. Rather than sending a signal, this function calls `syscall.Setpriority` (or the equivalent `setpriority` syscall) to modify the process's niceness value within the standard -20 to 19 range.

```go
// Simplified representation of the actions.go implementation
func sendSignal(pid int, sig syscall.Signal) error {
    return syscall.Kill(pid, sig)
}

func setNice(pid, value int) error {
    // Validation and syscall.Setpriority logic
    return syscall.Setpriority(syscall.PRIO_PROCESS, pid, value)
}

```

## TUI State Management and User Interaction

The signal workflow integrates with the TUI through [`internal/tui/update.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/update.go), which manages confirmation dialogs and action state to prevent accidental process termination.

### Action Confirmation Flow

When a user initiates a destructive operation (pressing **k** for kill, **t** for terminate, **p** for pause, or **r** for resume), the update loop follows this sequence:

1. Sets `m.pendingAction` to the appropriate enum (e.g., `actionKill`, `actionPause`)
2. Displays a confirmation prompt to prevent accidental termination
3. Upon confirmation, invokes the corresponding helper from [`actions.go`](https://github.com/pranshuparmar/witr/blob/main/actions.go)
4. Captures errors in `execErr` and surfaces status messages

The pause implementation at line 1293 demonstrates this pattern: `execErr = pauseProcess(pid)` executes only after explicit user confirmation (update.go L1290-L1295).

### Renice Input Handling

Unlike instantaneous signals, renice requires user input for the new priority value. The [`internal/tui/view.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/view.go) file renders an input prompt (view.go L421-L425) requesting a niceness value. After validation through **`validateNiceValue`**, [`update.go`](https://github.com/pranshuparmar/witr/blob/main/update.go) executes `setNice(pid, val)` (update.go L1249-L1264), displaying confirmation messages like "PID 123 reniced to -5" upon success.

## Cross-Platform Syscall Architecture

witr's signal operations rely on Go's `syscall` package for direct kernel communication. The `sendSignal` function uses `syscall.Kill` for immediate signal delivery, while `setNice` leverages `syscall.Setpriority` for scheduling adjustments. This design eliminates CGO dependencies while maintaining native performance characteristics across POSIX-compliant operating systems.

## Summary

- **Signal dispatch** occurs through `sendSignal` in [`internal/tui/actions.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/actions.go), wrapping `syscall.Kill` for POSIX compliance
- **Individual operations** (kill, terminate, pause, resume) use dedicated helpers that map to SIGKILL, SIGTERM, SIGSTOP, and SIGCONT respectively
- **State management** in [`internal/tui/update.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/update.go) enforces confirmation dialogs via `pendingAction` before executing destructive actions
- **Renice functionality** combines input rendering in [`view.go`](https://github.com/pranshuparmar/witr/blob/main/view.go) with priority adjustment via `syscall.Setpriority` in [`actions.go`](https://github.com/pranshuparmar/witr/blob/main/actions.go)
- **Cross-platform support** comes from Go's standard library abstraction of system calls across Linux, macOS, and BSD

## Frequently Asked Questions

### What signals does witr use for process termination?

witr implements two termination methods: `termProcess` sends **SIGTERM** for graceful shutdown, while `killProcess` sends **SIGKILL** for immediate termination. Both functions reside in [`internal/tui/actions.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/actions.go) and utilize `syscall.Kill` for signal delivery.

### How does witr prevent accidental process kills?

The TUI requires explicit confirmation through the `pendingAction` state machine in [`internal/tui/update.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/update.go). When a user presses a destructive key, the interface displays a confirmation prompt and only executes `killProcess` or `termProcess` after receiving affirmative input, preventing accidental signal transmission.

### Can witr adjust process priority on macOS and Linux?

Yes. The `setNice` function in [`internal/tui/actions.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/actions.go) uses `syscall.Setpriority`, which works on both Linux and macOS (as well as other BSD variants). The implementation handles the standard Unix niceness range of -20 (highest priority) to 19 (lowest priority).

### Where does witr handle the UI rendering for signal operations?

Signal confirmation prompts and renice input fields are rendered in [`internal/tui/view.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/view.go), while the state transitions and business logic execute in [`internal/tui/update.go`](https://github.com/pranshuparmar/witr/blob/main/internal/tui/update.go). This separation follows the Model-View-Update pattern common in TUI applications.