From 720989e829a673aae3e88832ab98563aa5e39b4e Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 21 Dec 2017 05:29:59 +0100 Subject: [PATCH] Add remote control of slide deck --- slides/autopilot/autotest.py | 82 +++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/slides/autopilot/autotest.py b/slides/autopilot/autotest.py index 30995dc0..6db08426 100755 --- a/slides/autopilot/autotest.py +++ b/slides/autopilot/autotest.py @@ -43,9 +43,6 @@ class State(object): next_step=self.next_step), f, default_flow_style=False) -state = State() - - def hrule(): return "="*int(subprocess.check_output(["tput", "cols"])) @@ -99,6 +96,54 @@ class Slide(object): def debug(self): logging.debug("\n{}\n{}\n{}".format(hrule(), self.content, hrule())) +# Synchronize slides in a remote browser +class Remote(object): + + def __init__(self): + self.slide_on_screen = 0 + + # Directly go to a specific slide + def goto(self, slide_number): + subprocess.check_call(["./gotoslide.js", str(slide_number)]) + self.slide_on_screen = slide_number + focus_slides() + + # Offer the opportunity to go step by step to the given slide + def catchup(self, slide_number): + if self.slide_on_screen > slide_number: + return self.goto(slide_number) + while self.slide_on_screen < slide_number: + if state.interactive: + print("Catching up on slide: {} -> {}" + .format(self.slide_on_screen, slide_number)) + print("z Zoom to target slide") + print("n/➡️/⎵ Next slide") + print("p/⬅️ Previous slide") + command = click.getchar() + else: + command = "z" + if command == "z": + self.goto(slide_number) + elif command in ("n", "\x1b[C", " "): + self.goto(self.slide_on_screen+1) + elif command in ("p", "\x1b[D"): + self.goto(self.slide_on_screen-1) + + +def focus_slides(): + subprocess.check_call(["i3-msg", "workspace", "3"]) + subprocess.check_call(["i3-msg", "workspace", "1"]) + time.sleep(1) + +def focus_terminal(): + subprocess.check_call(["i3-msg", "workspace", "2"]) + subprocess.check_call(["i3-msg", "workspace", "1"]) + time.sleep(1) + + +remote = Remote() +state = State() + def ansi(code): return lambda s: "\x1b[{}m{}\x1b[0m".format(code, s) @@ -171,7 +216,20 @@ def setup_tmux_and_ssh(): slides = [] content = open(sys.argv[1]).read() + +# OK, this part is definitely hackish, and will break if the +# excludedClasses parameter is not on a single line. +excluded_classes = re.findall("excludedClasses: (\[.*\])", content) +excluded_classes = set(eval(excluded_classes[0])) + for slide in re.split("\n---?\n", content): + slide_classes = re.findall("class: (.*)", slide) + if slide_classes: + slide_classes = slide_classes[0].split(",") + slide_classes = [c.strip() for c in slide_classes] + if excluded_classes & set(slide_classes): + logging.info("Skipping excluded slide.") + continue slides.append(Slide(slide)) actions = [] @@ -225,17 +283,20 @@ while state.next_step < len(actions): # Remove extra spaces (we don't want them in the terminal) and carriage returns data = data.strip() + # Synchronize the remote slides + remote.catchup(slide.number) + print(hrule()) print(slide.content.replace(snippet.content, ansi(7)(snippet.content))) print(hrule()) if state.interactive: print("simulate_type:{} verify_status:{}".format(state.simulate_type, state.verify_status)) print("[{}/{}] Shall we execute that snippet above?".format(state.next_step, len(actions))) - print("y/⏎/→ Execute snippet") + print("y/⏎ Execute snippet") print("p/← Previous snippet") - print("s Skip snippet") - print("t Toggle typist simulation") - print("v Toggle verification of exit status") + print("n/→ Next snippet") + print("s Simulate keystrokes") + print("v Validate exit status") print("g Go to a specific snippet") print("q Quit") print("c Continue non-interactively until next error") @@ -249,11 +310,11 @@ while state.next_step < len(actions): logging.info("Stripping ` from snippet.") data = data.replace('`', '') - if command == "s": + if command in ("n", "\x1b[C"): state.next_step += 1 elif command in ("p", "\x1b[D"): state.next_step -= 1 - elif command == "t": + elif command == "s": state.simulate_type = not state.simulate_type elif command == "v": state.verify_status = not state.verify_status @@ -264,7 +325,8 @@ while state.next_step < len(actions): elif command == "c": # continue until next timeout state.interactive = False - elif command in ("y", "\r", " ", "\x1b[C"): + elif command in ("y", "\r", " "): + focus_terminal() logging.info("Running with method {}: {}".format(method, data)) if method == "keys": send_keys(data)