Skip to content

Instantly share code, notes, and snippets.

@jrunning
Created January 12, 2018 16:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jrunning/b2fb863555c9e35c426d2fec25d9ac27 to your computer and use it in GitHub Desktop.
Save jrunning/b2fb863555c9e35c426d2fec25d9ac27 to your computer and use it in GitHub Desktop.
terminal-eyes.rb
at_exit { stop! }
require "io/console"
$last_click_pos = nil
LEFT_EYE_OFFSET = -3
RIGHT_EYE_OFFSET = 3
EYE_CENTER_OFFSET = 2
# Xterm control sequences
CTL_START_TRACKING = "\e[?1003h".freeze
CTL_STOP_TRACKING = "\e[?1003l".freeze
CTL_CLEAR_CANVAS = "\e[2J".freeze
CTL_MOVE_CURSOR = "\e[%d;%dH".freeze
# CTL_HIDE_CURSOR = "\e[?25l".freeze
# CTL_SHOW_CURSOR = "\e[?25h".freeze
def set_cursor_position(x, y)
printf CTL_MOVE_CURSOR, y, x
end
def start!
# print CTL_HIDE_CURSOR
print CTL_CLEAR_CANVAS
print CTL_START_TRACKING
end
def stop!
print CTL_STOP_TRACKING
# print CTL_SHOW_CURSOR
end
# Parse Xterm control sequences
MATCH_SEQUENCE = /\e\[M(?<event>[C#])(?<x>.)(?<y>.)/
EVENT_CODE_RELEASE = "#".freeze
def decode_sequence(str)
return unless MATCH_SEQUENCE =~ str
{ event_code: $~["event"],
x: $~["x"].ord - 32,
y: $~["y"].ord - 32,
}
end
# \e[5D = move cursor 5 left
# \e[1B = move cursor 1 down
EYE =
".---.\e[5D\e[1B" \
"|cde|\e[5D\e[1B" \
"|bf7|\e[5D\e[1B" \
"|a98|\e[5D\e[1B" \
"'---'"
def eye(target_x, target_y)
# Calculate angle w/ x-axis
hyp = Math.sqrt(target_y**2 + target_x**2)
theta = Math.asin(target_y / hyp)
# Convert to eighths of a turn
eighths = 4 * theta / Math::PI
octant_num =
if target_x < 0
4 - eighths
elsif target_y < 0
8 + eighths
else
eighths
end
# Convert octant 0–7 to 7–e (avoids conflict w/ nums in control seqs), or f for center
octant_code = octant_num.nan? ? 15 : octant_num.round % 8 + 7
EYE.tr(octant_code.to_s(16), "0").tr("789a-f", " ")
end
def draw_eyes(mouse_x, mouse_y)
base_x, y = $last_click_pos
[ base_x - LEFT_EYE_OFFSET, base_x - RIGHT_EYE_OFFSET ].each do |x|
set_cursor_position(x - EYE_CENTER_OFFSET, y - EYE_CENTER_OFFSET)
print eye(mouse_x - x, mouse_y - y)
end
end
def handle_event(event_code:, x:, y:)
print CTL_CLEAR_CANVAS
$last_click_pos = [ x, y ] if event_code == EVENT_CODE_RELEASE
draw_eyes(x, y) unless $last_click_pos.nil?
end
start!
input = ""
while char = STDIN.getch
exit 130 if char == "\003" # Ctrl+C
input << char
if info = decode_sequence(input)
handle_event(info)
input = ""
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment