Skip to content
atomicstackPublic

About

A re-implementation of the gotmux library, but using tmux's control mode.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

73 Commits

Folders and files

Repository files navigation

gotmuxcc

gotmuxcc is a drop-in replacement for github.com/GianlucaP106/gotmux. It mirrors the public API of the original library while internally using a persistent tmux control-mode (tmux -C) connection instead of spawning a new tmux process for every command. This dramatically reduces overhead when issuing many tmux operations from Go.

Features

  • API-compatible with gotmux — public structs, method signatures, and return types match the original library so switching requires only a module path change.
  • Persistent control-mode connection — a single tmux -C subprocess is kept alive for the lifetime of the Tmux handle; all commands flow over stdin/stdout pipes.
  • Concurrency-safe router — commands are dispatched, correlated with %begin/%end/%error frames, and returned to callers without external locking. The router automatically absorbs the initial control-mode handshake so the first user command is never mismatched.
  • Notification stream — Notifications() delivers every tmux control-mode notification (%window-add, %layout-change, %session-changed, …) as a typed value, without ever blocking the connection. The stream outlives reconnects (DisconnectedNotification, ReconnectedNotification), reports what a slow consumer missed (OverflowNotification) and passes unrecognised lines through (UnknownNotification).
  • Subscriptions — Subscribe/Unsubscribe wrappers for refresh-client -B with helpers for session, window, and pane targets, plus SubscribeServerTopology for a server-wide change signal.
  • Flow control — pause-after mode, per-pane output control, and arbitrary control flag management via SetControlFlags.
  • Client & window sizing — SetClientSize, SetWindowSize, and ClearWindowSize for programmatic layout control.
  • Custom format queries — ListSessionsFormat, ListWindowsFormat, and ListPanesFormat accept arbitrary -F format strings and -f filters.
  • Target-string APIs — Tmux.SplitWindow, Tmux.SelectLayout, Tmux.SelectPane, Tmux.SelectWindow, and Tmux.GlobalOption accept raw tmux target strings, enabling bulk session/window/pane creation during restore without needing materialized objects.
  • Debug tracing — set GOTMUXCC_TRACE=1 (or component names like router,transport) to write structured debug logs to a trace file.

Getting Started

Requires Go 1.22+ and tmux 3.2 or later (structured queries rely on the n: format modifier).

Install the module:

go get github.com/atomicstack/gotmuxcc/gotmuxcc

Create a client bound to the default tmux socket:

package main

import (
    "fmt"

    "github.com/atomicstack/gotmuxcc/gotmuxcc"
)

func main() {
    tmux, err := gotmuxcc.DefaultTmux()
    if err != nil {
        panic(err)
    }
    defer tmux.Close()

    info, err := tmux.GetServerInformation()
    if err != nil {
        panic(err)
    }

    fmt.Printf("tmux version: %s\n", info.Version)
}

To point at a custom socket:

tmux, err := gotmuxcc.NewTmux("/path/to/socket")

To run a particular tmux build, or give it a particular environment:

tmux, err := gotmuxcc.NewTmuxWithOptions("",
    gotmuxcc.WithTmuxBinary("/opt/tmux/bin/tmux"),
    gotmuxcc.WithEnv(os.Environ()))

A client never starts a tmux server: the control client runs with -N, which makes that impossible. If nothing is running on the socket, the constructor still succeeds and returns a client with no connection: ListSessions reports no sessions, HasSession is false, and other commands return an error wrapping gotmuxcc.ErrNoServer. NewSession starts the server the way plain tmux would, and the control-mode connection is opened on the next command. The connection is also reopened transparently if tmux exits (for example when its last session is killed) and a session is created again later.

Errors

A command tmux rejects returns a *gotmuxcc.CommandError carrying tmux's message (errors.As); a client that has been closed returns gotmuxcc.ErrClosed; a socket with no server returns gotmuxcc.ErrNoServer (errors.Is). Transport failures are none of these. Session.AttachSession runs tmux attach-session on the calling process's terminal, as gotmux does.

Notifications

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

for n := range tmux.NotificationsContext(ctx) {
    switch n := n.(type) {
    case *gotmuxcc.WindowAddNotification:
        fmt.Println("new window", n.WindowID)
    case *gotmuxcc.OverflowNotification, *gotmuxcc.ReconnectedNotification:
        // state unknown: refetch whatever the stream was keeping current
    }
}

Each call to Notifications is an independent subscriber with its own buffer (WithNotificationBuffer); nothing runs when nobody is listening. While a subscriber exists the client redials on its own after tmux exits and announces the new connection with a ReconnectedNotification.

tmux scopes some of what it sends to the session the control client is attached to: %layout-change only covers that session's windows, windows in other sessions arrive as %unlinked-window-*, and every refresh-client -B subscription is evaluated against that session alone. A pane split or killed in another session therefore produces no notification by itself. SubscribeServerTopology registers a session-level subscription whose format loops over every session, window and pane, so one connection still gets a change signal for the whole server, at most once a second. The client also sets no-detach-on-destroy so the connection survives its session being destroyed while others exist (WithNoDetachOnDestroy(false) opts out).

Architecture

The library is organized into five layers (top to bottom):

Layer Package Responsibility
Public API gotmuxcc/ Tmux, Session, Window, Pane types that mirror gotmux
Query Builder gotmuxcc/query.go Fluent command assembly with #{...} format variables
Router gotmuxcc/router.go Concurrency-safe dispatch, frame parsing, event streaming
Control Bridge gotmuxcc/control_bridge.go Wires the transport interface to the concrete implementation
Control Transport internal/control/ Launches and manages the tmux -C subprocess

The controlTransport interface (defined in gotmuxcc/tmux.go) abstracts the tmux pipe. In production it is satisfied by internal/control.Transport; in tests it is replaced by fake or recording transports injected via WithDialer().

Testing

Unit tests use fake control transports and do not require tmux:

make unit

Integration tests spin up isolated tmux servers with temporary sockets and require tmux to be installed:

make integration

Other targets:

make cover   # Generate coverage report to coverage.out
make clean   # Remove local .gocache and .gomodcache

See docs/testing.md for details on running tests inside restricted sandboxes.

Documentation

  • docs/api_inventory.md — one-to-one API compatibility catalog with gotmux
  • docs/testing.md — test prerequisites, integration test setup, coverage notes

License

MIT License. See LICENSE for details.

About

A re-implementation of the gotmux library, but using tmux's control mode.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages