Class: Controller

Inherits:
Object
  • Object
show all
Defined in:
lib/controller.rb

Overview

A spawned command that is controlling the terminal window,

and on request receives events (mouse buttons etc.

Instance Method Summary collapse

Constructor Details

#initialize(term, config = {}) ⇒ Controller

Returns a new instance of Controller.



6
7
8
9
10
# File 'lib/controller.rb', line 6

def initialize(term, config = {})
  @term = term
  @config = config
  @shell = determine_shell
end

Instance Method Details

#determine_shellObject



12
13
14
15
# File 'lib/controller.rb', line 12

def determine_shell
  # Try config file first, then ENV["SHELL"], then fallback to /bin/sh
  @config[:shell] || ENV["SHELL"] || "/bin/sh"
end

#device_attr_primaryObject

Device Attributes replies. Each query has a distinct reply type; sending the wrong type (e.g. the DA3 DCS below in answer to a DA1/DA2 query) makes hosts like tmux fail to consume it, so it leaks into the pane as visible text.

DA1 (CSI c): identify as a VT100 with Advanced Video Option.



49
50
# File 'lib/controller.rb', line 49

def device_attr_primary   = @wr.write("\e[?1;2c")
# DA2 (CSI > c): terminal id 0 (VT100), firmware version, cartridge 0.

#device_attr_secondaryObject

DA2 (CSI > c): terminal id 0 (VT100), firmware version, cartridge 0.



51
52
# File 'lib/controller.rb', line 51

def device_attr_secondary = @wr.write("\e[>0;10;1c")
# DA3 (CSI = c): DECRPTUI unit id, a DCS string (! | DDDDDDDD ST).

#device_attr_tertiaryObject

DA3 (CSI = c): DECRPTUI unit id, a DCS string (! | DDDDDDDD ST).



53
# File 'lib/controller.rb', line 53

def device_attr_tertiary  = @wr.write("\x1bP!|00000000\x1b\\")

#keypress(data) ⇒ Object



62
# File 'lib/controller.rb', line 62

def keypress(data) = @wr.write(data)

#mouse_digits(event, x, y, release) ⇒ Object



72
73
74
# File 'lib/controller.rb', line 72

def mouse_digits(event, x, y, release)
  @wr.write("\e[<#{event};#{x + 1};#{y + 1}#{release ? "m" : "M"}")
end

#mouse_report(mode, event, x, y, release) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/controller.rb', line 64

def mouse_report(mode, event, x, y, release)
  case mode
  when :digits then mouse_digits(event, x, y, release)
  else # Currently only x10
    mouse_x10(event, x, y)
  end
end

#mouse_x10(event, x, y) ⇒ Object



76
77
78
79
# File 'lib/controller.rb', line 76

def mouse_x10(event, x, y)
  raise "FIXME; untested and likely broken; Test w/htop"
  @wr.write("\e[M#{event.to_i.chr}#{x.chr}#{y.chr}")
end

#paste(data) ⇒ Object

These are semantically different, though practically similar currently. These are separate so they can be treated differently (bracketed etc.) in the future



61
# File 'lib/controller.rb', line 61

def paste(data)    = @wr.write(data)

#readObject



36
37
38
39
40
41
# File 'lib/controller.rb', line 36

def read
  @master.read_nonblock(128)
rescue IO::EAGAINWaitReadable
  IO.select([@master], [], [], nil)
  retry
end

#report_position(x, y) ⇒ Object



56
# File 'lib/controller.rb', line 56

def report_position(x, y) = @wr.write("\e[#{y + 1};#{x + 1}R")

#report_size(w, h) ⇒ Object



55
# File 'lib/controller.rb', line 55

def report_size(w, h) = (@master.winsize = [h, w])

#run(*args) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/controller.rb', line 17

def run(*args)
  cmd = args.empty? ? @shell : [@shell, '-c', args.join(' ')]
  @master, @wr, @pid = *PTY.spawn(*cmd)

  Thread.new do
    loop do
      begin
        @term.write(self.read)
        Thread.pass
      rescue Errno::EIO
        # The child closed the pty (it exited): EIO on read is normal
        # here. Exit with the child's status.
        # FIXME: Not sure if this really belongs *here*?
        exit(Process.wait(@pid))
      end
    end
  end
end