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.



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

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.



64
65
# File 'lib/controller.rb', line 64

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).



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

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

#keypress(data) ⇒ Object



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

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

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



85
86
87
# File 'lib/controller.rb', line 85

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



77
78
79
80
81
82
83
# File 'lib/controller.rb', line 77

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



89
90
91
92
# File 'lib/controller.rb', line 89

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



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

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

#readObject



49
50
51
52
53
54
# File 'lib/controller.rb', line 49

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

#report_position(x, y) ⇒ Object



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

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

#report_size(w, h) ⇒ Object



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

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 = *spawn_child(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

#spawn_child(cmd) ⇒ Object

Spawn the child shell/command with the user’s own gem environment, not rubyterm’s bundle: when rubyterm runs under ‘bundle exec`, BUNDLE_GEMFILE / GEM_PATH leak into children, so a Bundler-based shell (or any tool relying on the system gems) fails to load its gems and exits immediately. Stripping the bundle env restores the pre-bundle-exec behaviour.



41
42
43
44
45
46
47
# File 'lib/controller.rb', line 41

def spawn_child(cmd)
  if defined?(Bundler)
    Bundler.with_unbundled_env { PTY.spawn(*cmd) }
  else
    PTY.spawn(*cmd)
  end
end