This website requires JavaScript.
A fast, minimal application launcher for Wayland or X11 written in pure Go. Zero C dependencies, single static binary.
  • Go 92.1%
  • Roff 6.7%
  • Makefile 1.2%
Repository files (latest commit first)
Filename Latest commit message Latest commit date
jake Pedersen d918cbabc7
All checks were successful
Release / Test (push) Successful in 54s
Release / Build and Release (push) Successful in 1m3s
Fix release to only upload tarballs
2026-07-23 20:05:12 -05:00
.forgejo/workflows Fix release to only upload tarballs 2026-07-23 20:05:12 -05:00
extra added extra dir 2026-04-04 11:51:00 -05:00
plugins refactor: extract DisplayBackend interface to reduce Wayland/X11 duplication 2026-06-23 11:27:27 -05:00
.gitignore Update to README.md for X11 support 2026-04-13 10:08:12 -05:00
app.go refactor: extract DisplayBackend interface to reduce Wayland/X11 duplication 2026-06-23 11:27:27 -05:00
backend.go refactor: extract DisplayBackend interface to reduce Wayland/X11 duplication 2026-06-23 11:27:27 -05:00
BUILD.md modify files to address name change from sift to gofer 2026-03-27 13:29:47 -05:00
BUILDNOTES.md modify files to address name change from sift to gofer 2026-03-27 13:29:47 -05:00
config.go refactor: simplify config merging by eliminating duplicate structs 2026-06-23 16:40:20 -05:00
debug.go adding x11 support 2026-04-11 13:50:22 -05:00
display_x11.go fix: handle X11 WM keybinding launches by detecting empty stdin pipes 2026-06-27 08:55:26 -05:00
go.mod adding x11 support 2026-04-11 13:50:22 -05:00
go.sum adding x11 support 2026-04-11 13:50:22 -05:00
gofer.1 added man page 2026-05-14 07:09:25 -05:00
input.go initial commit 2026-03-18 15:50:02 -05:00
main.go fix: handle X11 WM keybinding launches by detecting empty stdin pipes 2026-06-27 08:55:26 -05:00
Makefile added man page 2026-05-14 07:09:25 -05:00
README.md Update to README.md 2026-06-02 06:46:12 -05:00
render.go refactor: extract DisplayBackend interface to reduce Wayland/X11 duplication 2026-06-23 11:27:27 -05:00
TEST_PLAN.md initial commit 2026-03-18 15:50:02 -05:00
wayland.go refactor: extract DisplayBackend interface to reduce Wayland/X11 duplication 2026-06-23 11:27:27 -05:00

Gofer

A fast, minimal application launcher written in pure Go. Supports both Wayland and X11.

screenshot

Go version CGo Wayland + 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 .desktop files 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

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: Google
d: DuckDuckGo
gh: GitHub
yt: YouTube
r: Reddit
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:

  1. Allocate an anonymous memfd file, twice the frame size (double buffer)
  2. Register with wl_shm as a pool
  3. Create two wl_buffer objects pointing to each half
  4. Each frame: render pixels into the inactive buffer, attach, commit

X11:

  1. Allocate two System V shared memory segments via shmget
  2. Register both with X11 via the MIT-SHM extension
  3. 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!

Buy Me a Coffee PayPal Patreon Liberapay

Morty Proxy This is a proxified and sanitized view of the page, visit original site.