Created
November 13, 2015 21:45
-
-
Save bazzargh/a267b97a52f7a1f70c46 to your computer and use it in GitHub Desktop.
hackertyper style presentation tool, leverages ttyrec to let you play back the presentation from random keystrokes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# hackertyper-style playback of series of tty recordings created by | |
# ttyrec. | |
# | |
# usage: | |
# hacker slides/* | |
# | |
# - Type randomly to move forward inside file | |
# - backspace to skip back or prev file | |
# - return to skip ahead or next file | |
# - Ctrl-C to exit | |
# - plays a bell if you go too far (I set term up with a visual bell) | |
# | |
# record with eg: | |
# ttyrec -e 'bash -l' slide9 | |
# | |
# bash -l means you get your real prompt. This tries to keep together | |
# events that occurred within 20ms. So for best results, pause after | |
# typing each command when recording, THEN press return. | |
# | |
# I'd suggest naming files like slides/07_testing rather than eg | |
# slide01. This means they're easily globbed (slide/*) and are sorted, | |
# but the names are meaningful so you can move them around without | |
# having to peek inside all the time. Name your last slide 99_end to | |
# avoid renumbering as the number of slides changes. | |
require 'io/console' | |
class Slide | |
def initialize | |
@slides = [] | |
ARGV.each do |filename| | |
File.open(filename, 'rb') do |f| | |
@slides << parse(f) | |
end | |
end | |
set_slide(0) | |
end | |
def set_slide(s) | |
clear | |
@step = 0 | |
return if s == @slide | |
@slide = [0, [s, @slides.length - 1].min].max | |
@steps = @slides[@slide] | |
end | |
def run | |
begin | |
# save previous state of stty | |
old_state = `stty -g` | |
# ~(ICANON | ECHO | ECHONL) | |
system "stty -icanon -echo -echonl" | |
loop do | |
c = STDIN.getch | |
case c | |
when "\u007F" # backspace | |
backward | |
when "\r" # return | |
forward | |
when "\u0003" # ^C. | |
break | |
else | |
next_step | |
end | |
end | |
rescue => ex | |
puts "#{ex.class}: #{ex.message}" | |
puts ex.backtrace | |
ensure | |
# restore previous state of stty | |
system "stty #{old_state}" | |
end | |
end | |
def forward | |
if last_step? | |
if last_slide? | |
flash | |
else | |
set_slide(@slide + 1) | |
end | |
else | |
while !last_step? | |
next_step | |
end | |
end | |
end | |
def backward | |
if first_step? | |
if first_slide? | |
flash | |
else | |
set_slide(@slide - 1) | |
end | |
else | |
set_slide(@slide) | |
end | |
end | |
def next_step | |
# dump out whatever the next command sequence is | |
if last_step? | |
flash | |
else | |
begin | |
$stdout.write @steps[@step] | |
rescue e | |
@error = e | |
end | |
@step = @step + 1 | |
end | |
end | |
def first_step? | |
@step == 0 | |
end | |
def last_step? | |
@step == @steps.length - 1 | |
end | |
def first_slide? | |
@slide == 0 | |
end | |
def last_slide? | |
@slide == @slides.length - 1 | |
end | |
def flash | |
print "\a" | |
end | |
def clear | |
puts `clear` | |
@step = 0 | |
end | |
def parse(recording) | |
slide = [] | |
# Anything in the same, say, 1/20s should be played | |
# as a single event. | |
pending_data = nil | |
while !recording.eof? | |
sec, usec, len = recording.read(12).unpack('VVV') | |
t = "#{sec}.#{usec}".to_f | |
data = recording.read(len) | |
if pending_data.nil? | |
pending_data = data | |
tstart = t | |
elsif (t - tstart > 0.05) | |
slide << pending_data | |
pending_data = data | |
tstart = t | |
else | |
pending_data << data | |
end | |
end | |
if !pending_data.nil? | |
slide << pending_data | |
end | |
slide | |
end | |
end | |
puts Slide.new.run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment