- Client struct sends JSON requests to daemon over Unix socket - EnsureDaemon auto-starts the daemon if not running (retry 50ms x 20) - Switch handler uses FuzzyMatch + SwitchToWorkspace via i3 IPC - InitWorkspaceResolver wires BuildTerminalWorkspaceMap + ResolveWorkspace - sysattr_linux.go for Setsid detach on daemon spawn
85 lines
1.9 KiB
Go
85 lines
1.9 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
)
|
|
|
|
// Client communicates with the vmux daemon over a Unix socket.
|
|
type Client struct {
|
|
sockPath string
|
|
}
|
|
|
|
// NewClient creates a client targeting the given socket path.
|
|
func NewClient(sockPath string) *Client {
|
|
return &Client{sockPath: sockPath}
|
|
}
|
|
|
|
// Send transmits a Request to the daemon and returns the Response.
|
|
func (c *Client) Send(req Request) (*Response, error) {
|
|
conn, err := net.DialTimeout("unix", c.sockPath, 2*time.Second)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("dial daemon: %w", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
conn.SetDeadline(time.Now().Add(5 * time.Second))
|
|
|
|
if err := json.NewEncoder(conn).Encode(req); err != nil {
|
|
return nil, fmt.Errorf("send request: %w", err)
|
|
}
|
|
|
|
var resp Response
|
|
if err := json.NewDecoder(conn).Decode(&resp); err != nil {
|
|
return nil, fmt.Errorf("read response: %w", err)
|
|
}
|
|
|
|
return &resp, nil
|
|
}
|
|
|
|
// EnsureDaemon checks if a daemon is already running on sockPath.
|
|
// If not, it spawns one in the background and waits until it's reachable.
|
|
func EnsureDaemon(sockPath string) error {
|
|
// Already running?
|
|
conn, err := net.DialTimeout("unix", sockPath, 200*time.Millisecond)
|
|
if err == nil {
|
|
conn.Close()
|
|
return nil
|
|
}
|
|
|
|
// Spawn daemon in background
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return fmt.Errorf("resolve executable: %w", err)
|
|
}
|
|
|
|
cmd := exec.Command(exe, "daemon")
|
|
cmd.SysProcAttr = newSysProcAttr()
|
|
cmd.Stdout = nil
|
|
cmd.Stderr = nil
|
|
cmd.Stdin = nil
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return fmt.Errorf("start daemon: %w", err)
|
|
}
|
|
|
|
// Detach: don't wait for the child
|
|
go cmd.Wait()
|
|
|
|
// Retry until daemon is reachable (50ms x 20 = 1s max)
|
|
for i := 0; i < 20; i++ {
|
|
time.Sleep(50 * time.Millisecond)
|
|
conn, err := net.DialTimeout("unix", sockPath, 200*time.Millisecond)
|
|
if err == nil {
|
|
conn.Close()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("daemon did not start within 1s")
|
|
}
|