Skip to content

Instantly share code, notes, and snippets.

@NeoCat
Created April 29, 2011 21:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save NeoCat/949054 to your computer and use it in GitHub Desktop.
Save NeoCat/949054 to your computer and use it in GitHub Desktop.
Share input within multiple terminals
#!/usr/bin/ruby
# share-input.rb: share input within multiple terminals
# Author: NeoCat
require 'socket'
require 'pty'
require "thread"
require "curses"
def server(sock, sock_name)
puts "relay server started."
$sock_name = sock_name
$clients = []
def stop_client(client)
client.close
$mutex.synchronize {
$clients.delete client
#p $clients
quit if $clients.empty?
}
end
def quit
puts "relay server stopped."
File.unlink $sock_name
exit
end
Signal.trap(:CHLD, 'IGNORE')
Signal.trap(:HUP) { quit }
Signal.trap(:INT) { quit }
Signal.trap(:TERM) { quit }
$mutex = Mutex.new
while new_client = sock.accept
$mutex.synchronize { $clients << new_client }
#p $clients
Thread.new(new_client) do |client|
begin
while data = client.readpartial(4096)
$clients.each do |c|
begin
c.write data
rescue
#puts "error on write: #$!"
stop_client c
end
end
end
rescue
#puts "error on read: #$!"
stop_client client
end
end
end
end
def make_connection(sock_id)
sock_name = "/tmp/sock.pty-relay."+sock_id.to_s
# launch a server unless it already exists
sock = nil
begin
sock = UNIXServer.open(sock_name)
rescue
end
if sock
fork { server(sock, sock_name) }
end
# client mode
return UNIXSocket.open(sock_name)
end
def relay_pty(sock, cmd)
$child_pid = nil
def quit
Curses::noraw
exit
end
Signal.trap(:CHLD, 'IGNORE')
Signal.trap(:HUP) { quit }
Signal.trap(:INT) { quit }
Signal.trap(:TERM) { quit }
Curses::init_screen
Curses::raw
p = PTY.spawn "export PTY_RELAY=#$$; stty rows #{Curses::lines} columns #{Curses::cols}; " + (cmd || "exec $SHELL")
$child_pid = p[2]
$p = p
single_mode = false
until p[0].closed? || STDIN.closed? || sock.closed?
s = IO.select [p[0], STDIN, sock]
if s0 = s[0][0]
out = nil
if single_mode
out = s0 === sock ? p[1] : s0 === STDIN ? p[1] : STDOUT
else
out = s0 === sock ? p[1] : s0 === STDIN ? sock : STDOUT
end
begin
if s0 === STDIN
data = s0.readpartial(1)
if !single_mode and data == "\C-]"
single_mode = true
elsif single_mode and data == "\r"
single_mode = false
out.write data
else
out.write data
end
else
out.write s0.readpartial(4096)
out.flush
end
rescue
#puts "error on relay: #$!"
p[0].close
p[1].close
end
end
end
quit
end
if ARGV[0] == '-h'
puts <<-EOD
#$0: share input within multiple terminals
usage: #$0 [server-id] [command]
- Execute #$0 in multiple terminal to share the input.
- The login shell or the specified command is started.
- If you hit ^], the input is not shared until you hit Enter key
and only applied to current terminal.
EOD
exit
end
if ENV['PTY_RELAY']
STDERR.puts "Cannot launch #$0 inside another #$0 !"
exit 1
end
sock_id = 0
if ARGV[0] =~ /^\d+$/
sock_id = ARGV[0].to_i
ARGV.shift
end
if sock = make_connection(sock_id)
relay_pty(sock, ARGV[0] && ARGV * " ")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment