From f70f18fc3fb315d06203ae3928a6fabd753f3363 Mon Sep 17 00:00:00 2001 From: realrasengan Date: Mon, 30 Mar 2026 20:55:37 -0700 Subject: [PATCH] add DialConnRaw + RecvRaw for direct transport access in WASM --- client.go | 42 ++++++++++++++++++++++++++++++++++++++++++ cmd/mosh-wasm/main.go | 12 ++++++------ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index 9b96b69..38167ba 100644 --- a/client.go +++ b/client.go @@ -106,6 +106,48 @@ func DialConnManual(conn Conn, ocb *OCB) (*Client, error) { return c, nil } +// DialConnRaw creates a mosh client with no internal goroutines. +// The caller must call Tick() for sending AND RecvRaw() for receiving. +// Use this when the caller needs raw transport diffs (e.g., for +// framebuffer state tracking in the WASM client). +func DialConnRaw(conn Conn, ocb *OCB) (*Client, error) { + c := &Client{ + conn: conn, + transport: NewTransport(ocb, false), + ocb: ocb, + outputC: make(chan struct{}, 1), + done: make(chan struct{}), + sentActionCounts: make(map[uint64]int), + } + + c.transport.ForceNextSend() + c.tick() + + return c, nil +} + +// RecvRaw reads one datagram from the connection and processes it +// through the transport. Returns the raw diff payload, or nil if +// no complete message was received (timeout, fragment, replay). +// The caller can use Transport().LastRecvOldNum()/LastRecvNewNum() +// to get the state numbers for this diff. +func (c *Client) RecvRaw(timeout time.Duration) []byte { + buf := make([]byte, maxPayload+64) + + c.conn.SetReadDeadline(time.Now().Add(timeout)) + n, err := c.conn.Read(buf) + if err != nil { + return nil + } + if n < minDatagram { + return nil + } + + data := make([]byte, n) + copy(data, buf[:n]) + return c.transport.Recv(data) +} + // Send sends keystrokes to the server. func (c *Client) Send(keys []byte) { c.actionsMu.Lock() diff --git a/cmd/mosh-wasm/main.go b/cmd/mosh-wasm/main.go index f116788..8b74902 100644 --- a/cmd/mosh-wasm/main.go +++ b/cmd/mosh-wasm/main.go @@ -73,7 +73,7 @@ func newSession(url, key string, cols, rows int) (*session, error) { return nil, err } - client, err := mosh.DialConnManual(conn, ocb) + client, err := mosh.DialConnRaw(conn, ocb) if err != nil { conn.Close() return nil, err @@ -141,12 +141,12 @@ func (s *session) jsObject() js.Value { return obj } -// recvLoop reads raw diffs from the mosh client and feeds them -// to the state tracker for framebuffer-based processing. +// recvLoop reads raw transport diffs and feeds them to the state +// tracker for framebuffer-based processing. func (s *session) recvLoop() { for { - out := s.client.Recv(60 * time.Second) - if out == nil { + diff := s.client.RecvRaw(1 * time.Second) + if diff == nil { s.mu.Lock() closed := s.closed s.mu.Unlock() @@ -158,7 +158,7 @@ func (s *session) recvLoop() { t := s.client.Transport() oldNum := t.LastRecvOldNum() newNum := t.LastRecvNewNum() - s.tracker.applyDiff(out, oldNum, newNum) + s.tracker.applyDiff(diff, oldNum, newNum) } }