Smoother daemon: no-op reloads, batched placement, muted audio, graceful teardown
- Skip stream restarts when a reload resolves to the identical wall (same URLs, tile geometry, player settings) — saving an unrelated config edit no longer blanks the screen. - Replace per-tile placement polling (a swaymsg fork per tile per second, plus 150ms get_tree polling per tile at startup) with one shared loop: a single get_tree snapshot per tick, re-placing only drifted windows, relaxing to a 2s tick once settled. - Mute streams by default (--no-audio) to skip an audio decoder per stream; opt back in with player.audio: true. - Graceful, parallel mpv teardown via cmd.Cancel/WaitDelay, fixing the double-Wait race between Stop and Supervise and cutting worst-case layout switches from ~2s x N streams to ~2s total. - status: probe mpv IPC outside the daemon mutex so a slow probe can't block layout switches. - View sync: reuse the Protect session across ticks (re-login only on expiry) and pick up view_refresh_seconds changes without a restart. - Health strikes keyed by player, not slot, so they never carry across layout switches; prune stale entries. - New `rtsp-streamer reload` CLI; fix dead sort in `layout ls`; back off on persistent control-socket accept errors; fsync config before the atomic rename (SD-card power-cut safety); bump IPC client deadline; gofmt stragglers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,12 +10,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Rect is a pixel rectangle on the output.
|
||||
// Rect is a pixel rectangle on the output. The JSON tags match sway's
|
||||
// get_tree/get_outputs "rect" objects so it can be decoded directly.
|
||||
type Rect struct {
|
||||
X, Y, W, H int
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
W int `json:"width"`
|
||||
H int `json:"height"`
|
||||
}
|
||||
|
||||
// GridRects splits a WxH area into cols*rows cells in row-major order. The
|
||||
@@ -130,6 +133,7 @@ type treeNode struct {
|
||||
PID int `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
AppID string `json:"app_id"`
|
||||
Rect Rect `json:"rect"`
|
||||
Nodes []treeNode `json:"nodes"`
|
||||
Float []treeNode `json:"floating_nodes"`
|
||||
}
|
||||
@@ -144,44 +148,25 @@ func (n *treeNode) walk(fn func(*treeNode)) {
|
||||
}
|
||||
}
|
||||
|
||||
// hasPID reports whether a window with the given pid currently exists.
|
||||
func (c *Client) hasPID(ctx context.Context, pid int) (bool, error) {
|
||||
// WindowRects returns the geometry of every mapped window keyed by owning
|
||||
// pid, from a single get_tree round trip. The daemon checks all tiles against
|
||||
// one snapshot instead of issuing a swaymsg per window.
|
||||
func (c *Client) WindowRects(ctx context.Context) (map[int]Rect, error) {
|
||||
out, err := exec.CommandContext(ctx, c.bin, "-t", "get_tree", "-r").Output()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("get_tree: %w", err)
|
||||
return nil, fmt.Errorf("get_tree: %w", err)
|
||||
}
|
||||
var root treeNode
|
||||
if err := json.Unmarshal(out, &root); err != nil {
|
||||
return false, err
|
||||
return nil, err
|
||||
}
|
||||
found := false
|
||||
rects := map[int]Rect{}
|
||||
root.walk(func(n *treeNode) {
|
||||
if n.PID == pid && (n.AppID != "" || n.Name != "") {
|
||||
found = true
|
||||
if n.PID != 0 && (n.AppID != "" || n.Name != "") {
|
||||
rects[n.PID] = n.Rect
|
||||
}
|
||||
})
|
||||
return found, nil
|
||||
}
|
||||
|
||||
// WaitForWindow blocks until a window owned by pid maps, or ctx/timeout fires.
|
||||
func (c *Client) WaitForWindow(ctx context.Context, pid int, timeout time.Duration) error {
|
||||
deadline := time.Now().Add(timeout)
|
||||
ticker := time.NewTicker(150 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
ok, err := c.hasPID(ctx, pid)
|
||||
if err == nil && ok {
|
||||
return nil
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
if time.Now().After(deadline) {
|
||||
return fmt.Errorf("window for pid %d did not appear within %s", pid, timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rects, nil
|
||||
}
|
||||
|
||||
// Place floats and positions the window owned by pid at the given rect. Using
|
||||
|
||||
Reference in New Issue
Block a user