- Go 92.1%
- Roff 6.7%
- Makefile 1.2%
| Filename | Latest commit message | Latest commit date |
|---|---|
| .forgejo/workflows | |
| extra | |
| plugins | |
| .gitignore | |
| app.go | |
| backend.go | |
| BUILD.md | |
| BUILDNOTES.md | |
| config.go | |
| debug.go | |
| display_x11.go | |
| go.mod | |
| go.sum | |
| gofer.1 | |
| input.go | |
| main.go | |
| Makefile | |
| README.md | |
| render.go | |
| TEST_PLAN.md | |
| wayland.go |
Gofer
A fast, minimal application launcher written in pure Go. Supports both Wayland and X11.
Features
- Pure Go — Zero C library dependencies, no CGo, compiles to a single static binary
- Wayland + X11 — Auto-detects your display server, works on both
- Native rendering — SHM on Wayland, MIT-SHM on X11, no GPU libraries required
- Application search — Indexes
.desktopfiles from standard XDG paths - File navigation — Path completion and home directory fuzzy search
- Web search — Configurable search engine prefixes (Google, GitHub, YouTube, etc.)
- Command runner — Execute shell commands directly; GUI apps launch detached, CLI commands open in your terminal
- Text input mode — Prompt for user input from scripts via
--input - Dmenu mode — Pipe any list into gofer for dmenu-style selection
- Theming — 10 built-in themes with full custom theme support
- Nerd Font support — Optional icon rendering with Nerd Fonts
- Keyboard + mouse — Full keyboard navigation, key repeat, and mouse click support
- Scrolling — Scroll indicator and full arrow key navigation through long result lists
Installation
Binary release (recommended)
Download the latest binary from the releases page, make it executable, and move it to your PATH:
tar -xzf gofer-v1.0.0-linux-x86_64.tar.gz
chmod +x gofer
sudo mv gofer /usr/local/bin/gofer
No Go installation required. The binary is fully static with no external dependencies.
From source
git clone https://codeberg.org/JakeAtLinux/Gofer.git
# or
git clone https://gitlab.com/jped/Gofer.git
cd Gofer
go mod tidy
make build
sudo make install
Or manually:
CGO_ENABLED=0 go build -ldflags="-X main.version=$(git describe --tags) -s -w" -o gofer .
sudo cp gofer /usr/local/bin/
Requirements
- Go 1.21+ (build only — not needed to run the binary)
- A running Wayland compositor or X11 server
- Optional: Nerd Font for icons
Usage
gofer # launcher mode
echo -e "A\nB\nC" | gofer # dmenu / pipe mode
gofer --input="Enter name:" # text input prompt, prints result to stdout
gofer --theme=nord # launch with a specific theme
gofer --themes # list all available themes
gofer --version # print version
gofer --debug # verbose logging to stderr
Text input mode
--input opens a simple text prompt and prints what the user types to stdout.
Useful for scripting:
vm_name=$(gofer --input="Enter VM name:")
choice=$(echo -e "Option A\nOption B" | gofer)
result=$(gofer --input="Search term:") | xargs my-command
Pressing Enter prints the typed text. Pressing Escape prints nothing.
Command runner
| Input | Behaviour |
|---|---|
firefox |
Launches detached (has a .desktop file) |
lsblk |
Opens in your terminal emulator |
>lsblk |
Force terminal mode (the > prefix) |
github.com |
Opens in browser |
https://example.com |
Opens in browser |
The > prefix always forces a command to run in a terminal window regardless of type.
After the command exits, the terminal stays open so you can read the output.
Configuration
Config is stored at ~/.config/gofer/config.toml and created automatically on first run.
# Active theme
theme = "catppuccin"
# Placeholder text shown when search box is empty
prompt_text = "search..."
# Show plugin-type icon boxes next to results
show_icons = false
# Enable Nerd Font icons
nerd_font = false
# Terminal emulator for CLI commands (auto-detected if not set)
# terminal = "foot"
[layout]
width = 680
height = 500
header_height = 64
footer_height = 28
row_height = 54
max_results = 50
[font]
# Optional: path to a custom TTF/OTF font file
# path = "/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Regular.ttf"
size_prompt = 20
size_title = 13
size_sub = 11
size_hint = 10
# Search engine prefixes
[[engines]]
prefix = "g:"
name = "Google"
url = "https://www.google.com/search?q=%s"
[[engines]]
prefix = "d:"
name = "DuckDuckGo"
url = "https://duckduckgo.com/?q=%s"
[[engines]]
prefix = "gh:"
name = "GitHub"
url = "https://github.com/search?q=%s"
Keyboard shortcuts
| Key | Action |
|---|---|
| Type | Filter results |
↑ ↓ |
Navigate results |
Tab / Shift+Tab |
Navigate / switch tabs |
Enter |
Launch selected |
Escape |
Close |
>command |
Force run in terminal |
| Click | Launch clicked result |
Web search prefixes
| Prefix | Engine |
|---|---|
g: |
|
d: |
DuckDuckGo |
gh: |
GitHub |
yt: |
YouTube |
r: |
|
w: |
Wikipedia |
Bare URLs open directly without a prefix — type github.com and press Enter.
Tab filters
| Tab | Content |
|---|---|
| All | Combined results from all sources |
| Apps | Installed desktop applications |
| Web | Web searches and URLs |
| Run | Command runner |
| Menu | Dmenu / pipe mode results |
Theming
Built-in themes
| Theme | Description |
|---|---|
catppuccin |
Mocha-inspired pastel palette (default) |
dracula |
Dark purple |
nord |
Arctic blue-grey |
gruvbox |
Retro earth tones |
tokyonight |
Neon night city |
rosepine |
Rosé pine |
onedark |
One Dark Pro |
solarized |
Solarized dark |
everforest |
Evergreen forest |
light |
Minimal light theme |
Switch themes at runtime:
gofer --theme=nord
gofer --themes # list all with active marker
Custom themes
Define your own themes in config.toml:
theme = "mytheme"
[themes.mytheme]
background = "#1a1b26"
header = "#16161e"
footer = "#16161e"
selected = "#292e42"
divider = "#292e42"
text = "#c0caf5"
subtext = "#565f89"
hint = "#565f89"
border = "#292e42"
accent_apps = "#bb9af7"
accent_files = "#e0af68"
accent_runner = "#9ece6a"
accent_web = "#7aa2f7"
accent_dmenu = "#f7768e"
Multiple custom themes can be defined and switched between with theme = "name".
Compositor setup
Niri
window-rule {
match app-id="gofer"
open-floating true
default-column-width { fixed 680; }
default-window-height { fixed 500; }
}
binds {
Mod+Space { spawn "gofer"; }
}
Sway
for_window [app_id="gofer"] floating enable, resize set 680 500, move position center
bindsym $mod+space exec gofer
Hyprland
windowrulev2 = float, class:^(gofer)$
windowrulev2 = size 680 500, class:^(gofer)$
windowrulev2 = center, class:^(gofer)$
bind = $mainMod, SPACE, exec, gofer
Scripting and dmenu workflows
Any script written for dmenu or rofi works with gofer — replace dmenu or rofi -dmenu with gofer.
Power menu:
choice=$(printf 'Lock\nSuspend\nReboot\nShutdown' | gofer)
case "$choice" in
Lock) swaylock ;;
Suspend) systemctl suspend ;;
Reboot) systemctl reboot ;;
Shutdown) systemctl poweroff ;;
esac
Nested menus:
main_menu() {
choice=$(printf 'Settings\nPower\nApps' | gofer)
case "$choice" in
Settings) settings_menu ;;
Power) power_menu ;;
Apps) gofer ;;
esac
}
SSH host picker:
grep "^Host " ~/.ssh/config | awk '{print $2}' | gofer | xargs -I{} foot ssh {}
Architecture
gofer/
├── main.go Entry point, env bootstrap, mode detection
├── wayland.go Wayland backend (display, surface, SHM, keyboard, pointer)
├── display_x11.go X11 backend (window, MIT-SHM, keyboard, pointer)
├── debug.go Shared debug logging
├── render.go Software renderer → XRGB8888 pixel buffer
├── app.go UI state, search, key handling, scroll
├── input.go Linux scancode → rune conversion
├── config.go Config loading, built-in and custom themes
└── plugins/
├── plugin.go Plugin interface, action types, terminal detection
├── apps.go .desktop file indexing
├── files.go Path completion, home search
└── misc.go Runner, web search, dmenu
How rendering works
gofer uses shared memory (SHM) rendering — the only path that needs zero GPU libraries:
Wayland:
- Allocate an anonymous memfd file, twice the frame size (double buffer)
- Register with
wl_shmas a pool - Create two
wl_bufferobjects pointing to each half - Each frame: render pixels into the inactive buffer, attach, commit
X11:
- Allocate two System V shared memory segments via
shmget - Register both with X11 via the MIT-SHM extension
- Each frame: render pixels into the inactive segment, blit with
ShmPutImage
In both cases the CPU renders directly into shared RAM — no OpenGL, no Vulkan, no EGL.
Display auto-detection
WAYLAND_DISPLAY set → Wayland backend
DISPLAY set → X11 backend
neither → error: no display found
Troubleshooting
"no xdg_wm_base advertised" Your compositor doesn't support the required XDG shell protocol:
WAYLAND_DEBUG=1 gofer 2>&1 | grep xdg
Empty results when launched from a keybind
XDG_DATA_DIRS may not be inherited. gofer bootstraps it automatically, but verify:
env -i HOME=$HOME WAYLAND_DISPLAY=$WAYLAND_DISPLAY gofer
Wrong terminal used for commands Set your preferred terminal in config:
terminal = "kitty"
Or set the $TERMINAL environment variable.
Custom font not loading Specify the full path to the TTF/OTF file:
[font]
path = "/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Regular.ttf"
Debug mode
gofer --debug
Prints connection info, globals, configure events, and paint calls to stderr.
☕ Donations
While all donations are greatly appreciated, they are 100% voluntary. Thank you to everyone who supports the repo and work, both through donation and through use!
