Module: Przn::EchoesClient

Defined in:
lib/przn/echoes_client.rb

Overview

Thin wrappers around Echoes-specific OSC 7772 commands the presenter uses to set up extended-display mode. Other terminals ignore OSC 7772, so each method silently fails (returns nil / false) when not running inside Echoes.

Constant Summary collapse

OSC =
"\e]7772"
BEL =
"\a"
REPLY_TIMEOUT_S =
0.5

Class Method Summary collapse

Class Method Details

.display_info(io_in: $stdin, io_out: $stdout) ⇒ Object

Ask Echoes how many displays are attached and a tiny descriptor for each. Returns an Array of Hashes like [0, w: 1920, h: 1080, …] or nil when no reply arrives within the timeout (non-Echoes terminal, or an Echoes that doesn’t speak this command yet).



22
23
24
25
26
27
28
29
30
# File 'lib/przn/echoes_client.rb', line 22

def display_info(io_in: $stdin, io_out: $stdout)
  io_out.write("#{OSC};display-info#{BEL}")
  io_out.flush if io_out.respond_to?(:flush)
  reply = read_osc_reply(io_in)
  return nil unless reply
  JSON.parse(reply, symbolize_names: true)
rescue JSON::ParserError
  nil
end

.open_window(display:, argv:, fullscreen: true, io_out: $stdout) ⇒ Object

Open a new Echoes window on the given display, running ‘argv` (an Array of strings — argv is the executable). `fullscreen:` is a hint. Returns true if the request was emitted; nothing in the protocol confirms success synchronously.



36
37
38
39
40
41
42
43
44
45
# File 'lib/przn/echoes_client.rb', line 36

def open_window(display:, argv:, fullscreen: true, io_out: $stdout)
  # `pack('m0')` is strict (no-newline) base64 — same as
  # Base64.strict_encode64 but without pulling in the base64 stdlib,
  # which is no longer a default gem in Ruby 3.4+.
  payload = [JSON.generate(argv)].pack('m0')
  args = "display=#{display}:program=#{payload}:fullscreen=#{fullscreen ? 'yes' : 'no'}"
  io_out.write("#{OSC};open-window;#{args}#{BEL}")
  io_out.flush if io_out.respond_to?(:flush)
  true
end

.read_osc_reply(io_in) ⇒ Object

Read an OSC reply up to ST or BEL. Returns the payload string or nil on timeout. Echoes replies follow the same ‘e]7772;…a` shape it accepts.

Stdin defaults to canonical (line-buffered) mode in a shell context, so ‘getc` would block waiting for a newline that an OSC reply never sends. Put the input in raw mode for the duration of the read; IO#raw saves and restores termios automatically.



54
55
56
57
58
59
60
# File 'lib/przn/echoes_client.rb', line 54

def read_osc_reply(io_in)
  if io_in.respond_to?(:raw) && io_in.respond_to?(:tty?) && io_in.tty?
    io_in.raw { read_osc_reply_inner(io_in) }
  else
    read_osc_reply_inner(io_in)
  end
end

.read_osc_reply_inner(io_in) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/przn/echoes_client.rb', line 62

def read_osc_reply_inner(io_in)
  Timeout.timeout(REPLY_TIMEOUT_S) do
    buf = +""
    loop do
      c = io_in.getc
      return nil if c.nil?
      break if c == BEL
      if c == "\e"
        nxt = io_in.getc
        break if nxt == "\\"
        buf << c << nxt
      else
        buf << c
      end
    end
    buf.sub(/\A\e?\]?7772;[\w-]+;/, '')
  end
rescue Timeout::Error
  nil
end