From 4b11d955cb43b98deb368c0e8aa1048a5ae8b07f Mon Sep 17 00:00:00 2001 From: Andy Dixon Date: Wed, 7 Oct 2026 12:32:27 +0100 Subject: [PATCH] Letter keys instead of function keys h help, / search, s sort, t tree, z all, c command line, i swap in, o swap out, F flush, k signal, q quit. --- CHANGELOG.md | 7 +++++++ README.md | 8 ++++---- docs/swaptop.1 | 26 +++++++++++++------------- main.go | 38 ++++++++++---------------------------- 4 files changed, 34 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b04b4f0..4475a75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 1.0.1 — 2026-10-07 + +### Changed +- Plain letter keys instead of function keys: `h` help, `/` search, `s` sort, + `t` tree, `z` all, `c` command line, `i` swap in, `o` swap out, `F` flush, + `k` signal, `q` quit. + ## 1.0.0 — 2026-10-07 First release. diff --git a/README.md b/README.md index 1a4b131..61ba617 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ zram figures appear when present. Each process shows its SWAP, proportional SWPPSS, DELTA since the last refresh, RSS and how much of it is in swap; `Enter` breaks a process down by mapping (heap, stacks, libraries, files). -Actions, each confirmed first: swap a process back in (`F7`), page a process -out (`F8`, `process_madvise`, Linux 5.10+), flush every swap device with -swapoff+swapon (`F2`), send a signal (`F9`). Search (`/`), sort (`F6`), tree -view (`F5`), zero-swap toggle (`z`), full command line (`c`). Settings +Actions, each confirmed first: swap a process back in (`i`), page a process +out (`o`, `process_madvise`, Linux 5.10+), flush every swap device with +swapoff+swapon (`F`), send a signal (`k`). Search (`/`), sort (`s`), tree +view (`t`), zero-swap toggle (`z`), full command line (`c`), help (`h`). Settings persist in `~/.config/swaptop/config`. See `man swaptop`. Run it as root to see every process and use the actions: diff --git a/docs/swaptop.1 b/docs/swaptop.1 index 34c15e4..2cf5542 100644 --- a/docs/swaptop.1 +++ b/docs/swaptop.1 @@ -1,5 +1,5 @@ .\" Manual page for swaptop. View with: man -l docs/swaptop.1 -.TH SWAPTOP 1 "October 2026" "swaptop 1.0.0" "User Commands" +.TH SWAPTOP 1 "October 2026" "swaptop 1.0.1" "User Commands" .SH NAME swaptop \- htop\-style view of which processes are using swap, and how .SH SYNOPSIS @@ -26,8 +26,8 @@ and .IR /proc/PID/smaps . Nothing is written anywhere except the configuration file (see .BR FILES ). -Actions that change memory placement are available from the function keys -and always ask for confirmation first. +Actions that change memory placement are bound to single letter keys and +always ask for confirmation first. .PP Running as root (\fBsudo swaptop\fR) is recommended: without it the kernel hides the detailed figures and memory of other users' processes (see @@ -147,19 +147,19 @@ Move the selection. .B Enter Per\-mapping breakdown of the selected process. \fBEsc\fR or \fBq\fR returns. .TP -.B F1, h, ? +.B h, ? Help. .TP -.B F3, / +.B / Incremental search over command line, name, user and PID. \fBEnter\fR keeps the filter, \fBEsc\fR clears it. .TP -.B F5, t +.B t Toggle tree view. In tree view a process is shown when it or any of its descendants matches, so parents stay for context; processes without swap are dimmed. .TP -.B F6, s +.B s Cycle the sort column. .TP .B z @@ -168,12 +168,12 @@ Toggle between processes with swap only (the default) and all processes. .B c Toggle between the command name and the full command line. .TP -.B F10, q +.B q Quit, saving the current settings. .SH ACTIONS Each action asks for confirmation before doing anything. .TP -.B F7, i \(em swap in +.B i \(em swap in Faults every swapped page of the selected process back into RAM by reading it through .IR /proc/PID/mem . @@ -183,7 +183,7 @@ so nothing else is touched. Afterwards the process runs at full speed again. Its swap slots usually remain allocated as swap cache (yellow in the bar) until the kernel needs them or the device is flushed. .TP -.B F8, o \(em swap out +.B o \(em swap out Asks the kernel to reclaim the selected process's resident memory now, with .BR process_madvise (2) .BR MADV_PAGEOUT @@ -191,7 +191,7 @@ Asks the kernel to reclaim the selected process's resident memory now, with pages are simply dropped. The process keeps running and faults its memory back as it touches it. Useful for parking an idle but large process. .TP -.B F2, F \(em flush +.B F \(em flush Empties every swap device completely by calling .BR swapoff (2) and then @@ -204,7 +204,7 @@ priority is kept; other .BR swapon (8) options such as discard are not reapplied. Needs root. .TP -.B F9, k \(em signal +.B k \(em signal Opens a picker of common signals (TERM, KILL, INT, HUP, QUIT, USR1, USR2, STOP, CONT) to send to the selected process. .SH PERMISSIONS @@ -258,7 +258,7 @@ option. .SH NOTES Swap cache explains most apparent mismatches: a process can show 0 in \fBSWAP\fR while the device's usage barely drops, because the slots are kept -until needed. Flushing (\fBF2\fR) is the only way to release them on demand. +until needed. Flushing (\fBF\fR) is the only way to release them on demand. .PP The \fBSWAP\fR column of processes sharing memory (database workers, for example) double counts that memory, by design, so that each process shows diff --git a/main.go b/main.go index 2c69385..8f830c9 100644 --- a/main.go +++ b/main.go @@ -97,7 +97,7 @@ func sortIndex(name string) int { func main() { cfg := loadConfig() flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: swaptop [-d SECONDS] [-s COLUMN] [-t] [-z] [--version]\n\nhtop-style view of swap usage per process. Press F1 inside for keys.\n\n") + fmt.Fprintf(os.Stderr, "Usage: swaptop [-d SECONDS] [-s COLUMN] [-t] [-z] [--version]\n\nhtop-style view of swap usage per process. Press h inside for keys.\n\n") flag.PrintDefaults() } delay := flag.Float64("d", cfg.Refresh, "refresh interval in seconds") @@ -399,24 +399,6 @@ func (a *app) key(ev *tcell.EventKey) bool { } case tcell.KeyEscape: a.view = viewList - case tcell.KeyF1: - a.view = viewHelp - case tcell.KeyF2: - a.askFlush() - case tcell.KeyF3: - a.searching = true - case tcell.KeyF5: - a.toggleTree() - case tcell.KeyF6: - a.cycleSort() - case tcell.KeyF7: - a.askSwapIn() - case tcell.KeyF8: - a.askSwapOut() - case tcell.KeyF9: - a.askKill() - case tcell.KeyF10: - return true case tcell.KeyRune: switch ev.Rune() { case 'q': @@ -515,7 +497,7 @@ func (a *app) askSwapIn() { } }) if err == nil { - set(fmt.Sprintf("Swapped in %s (%d) in %s. Its slots stay 'cached' until the kernel drops them or you flush (F2).", + set(fmt.Sprintf("Swapped in %s (%d) in %s. Its slots stay 'cached' until the kernel drops them or you flush (F).", p.Comm, p.PID, time.Since(start).Round(100*time.Millisecond))) } return err @@ -861,14 +843,14 @@ func (a *app) drawHelp() { " magenta not owned by any process: tmpfs/shm files, or slots whose owner we cannot read", "", "Actions (each asks for confirmation)", - " F7 i swap in: fault every swapped page of the process back into RAM (reads /proc/PID/mem)", - " F8 o swap out: ask the kernel to reclaim the process's pages now (process_madvise MADV_PAGEOUT, Linux 5.10+)", - " F2 F flush: swapoff + swapon every device, emptying swap completely (root, needs enough free RAM)", - " F9 k send a signal (picker)", + " i swap in: fault every swapped page of the process back into RAM (reads /proc/PID/mem)", + " o swap out: ask the kernel to reclaim the process's pages now (process_madvise MADV_PAGEOUT, Linux 5.10+)", + " F flush: swapoff + swapon every device, emptying swap completely (root, needs enough free RAM)", + " k send a signal (picker)", "", "Keys", " Up/Down PgUp/PgDn Home/End move Enter per-mapping breakdown of the selected process", - " F3 / search F6 s sort F5 t tree view z show/hide processes with no swap c full command line F10 q quit", + " / search s sort t tree view z show/hide processes with no swap c full command line h help q quit / back", "", "Settings (sort, tree, z, c, refresh) are saved to " + configPath() + " on exit.", "Non-root: SWPPSS and actions only work for your own processes (kernel.yama.ptrace_scope).", @@ -891,13 +873,13 @@ func (a *app) drawBottom(w, h int) { a.put(0, y, "Search: "+a.filter+"_", stWarn) return } - keys := [][2]string{{"F1", "Help"}, {"F2", "Flush"}, {"F3", "Search"}, {"F5", "Tree"}, {"F6", "SortBy"}, {"F7", "SwapIn"}, {"F8", "SwapOut"}, {"F9", "Kill"}, {"F10", "Quit"}} + keys := [][2]string{{"h", "Help"}, {"/", "Search"}, {"s", "Sort"}, {"t", "Tree"}, {"z", "All"}, {"c", "Cmd"}, {"i", "SwapIn"}, {"o", "SwapOut"}, {"F", "Flush"}, {"k", "Kill"}, {"q", "Quit"}} if a.view != viewList { - keys = [][2]string{{"Esc", "Back"}, {"F7", "SwapIn"}, {"F8", "SwapOut"}, {"F9", "Kill"}, {"F10", "Quit"}} + keys = [][2]string{{"q", "Back"}, {"i", "SwapIn"}, {"o", "SwapOut"}, {"k", "Kill"}} } x := 0 for _, k := range keys { - x = a.put(x, y, fmt.Sprintf("%3s", k[0]), stKey) + x = a.put(x, y, " "+k[0], stKey) x = a.put(x, y, fmt.Sprintf("%-7s", k[1]), stFn) } }