Class: Agentilda::Keyboard
- Inherits:
-
Object
- Object
- Agentilda::Keyboard
- Defined in:
- lib/agentilda/keyboard.rb
Overview
Listens for single keypresses while a run is in flight, and turns them into Control broadcasts.
The listener is a thread reading STDIN in raw mode, which only makes sense when STDIN is a terminal: piped input must never be eaten one byte at a time, and a run under cron has nobody at the keys. Keyboard.listen returns nil in both cases and every caller treats nil as "no keyboard".
Raw mode swallows Ctrl-C along with everything else, so ETX is forwarded to the main thread as the Interrupt it would have been — the listener must never make a run harder to kill than it was without one.
Constant Summary collapse
- BINDINGS =
[ ["h ?", "this help"], ["w", "ask every running agent to wrap up as fast as possible"], ["n", "ask agents to write out what they have and stop; the loop continues"], ["q", "write out, stop everything, and quit after a #{Control::GRACE}s grace"], ["ctrl-c", "interrupt the run, as ever"] ].freeze
Class Method Summary collapse
-
.listen(input: $stdin) ⇒ Agentilda::Keyboard?
A running listener, or nil when STDIN is not a terminal.
Instance Method Summary collapse
- #handle(key) ⇒ void
-
#help ⇒ String
The bindings, one per line, widest key first.
-
#initialize(input: $stdin) ⇒ Keyboard
constructor
A new instance of Keyboard.
- #start ⇒ self
- #stop ⇒ void
Constructor Details
#initialize(input: $stdin) ⇒ Keyboard
Returns a new instance of Keyboard.
36 37 38 |
# File 'lib/agentilda/keyboard.rb', line 36 def initialize(input: $stdin) @input = input end |
Class Method Details
.listen(input: $stdin) ⇒ Agentilda::Keyboard?
Returns a running listener, or nil when STDIN is not a terminal.
29 30 31 32 33 |
# File 'lib/agentilda/keyboard.rb', line 29 def self.listen(input: $stdin) return nil unless input.tty? new(input:).start end |
Instance Method Details
#handle(key) ⇒ void
This method returns an undefined value.
59 60 61 62 63 64 65 66 67 |
# File 'lib/agentilda/keyboard.rb', line 59 def handle(key) case key when "h", "?" then UI.popup("Keys", help) when "w" then acted("w — agents asked to wrap up") { Control.wrap_up! } when "n" then acted("n — agents asked to write out and stop") { Control.stop! } when "q" then acted("q — quitting; #{Control::GRACE}s grace to write out") { Control.quit! } when "\u0003" then Thread.main.raise(Interrupt) end end |
#help ⇒ String
Returns the bindings, one per line, widest key first.
70 71 72 73 |
# File 'lib/agentilda/keyboard.rb', line 70 def help width = BINDINGS.map { |key, _| key.length }.max BINDINGS.map { |key, does| "#{key.ljust(width)} #{does}" }.join("\n") end |
#start ⇒ self
41 42 43 44 45 46 47 48 49 |
# File 'lib/agentilda/keyboard.rb', line 41 def start @thread = Thread.new do Thread.current.report_on_exception = false loop { handle(@input.getch) } rescue IOError, Errno::EIO # STDIN went away; a keyboard with no keys just stops listening. end self end |
#stop ⇒ void
This method returns an undefined value.
52 53 54 55 |
# File 'lib/agentilda/keyboard.rb', line 52 def stop @thread&.kill @thread = nil end |