#!/usr/bin/env escript %% needs: %% - line parsing %% - keyboard input %% - drawing to screen -mode(compile). % john state -record(js, {rcv = <<>> :: binary(), kbdq = [] :: iolist(), ibuf = [] :: string(), tsock = none :: none | gen_tcp:socket(), nsock = none :: none | enoise:noise_socket()}). main([]) -> main_ii(6969); main([PortNum]) -> main_ii(list_to_integer(PortNum)). main_ii(PortNum) -> TcpOpts = [binary, {packet, 0}, {active, once}], {ok, TSock} = gen_tcp:connect("localhost", PortNum, TcpOpts), ok = inet:setopts(TSock, [{active, once}]), {ok, NSock, _HandshakeState} = enoise:connect(TSock, noise_options()), % need keyboard process Self = self(), spawn_link(fun() -> kbd_loop(Self) end), j_loop(#js{tsock = TSock, nsock = NSock}). noise_options() -> % totally safe crypto KP = enoise_keypair:new(dh25519), [{noise, "Noise_NN_25519_ChaChaPoly_BLAKE2b"}, {e, KP}]. j_loop(JS = #js{rcv = Rcv, tsock = TSock, nsock = NSock}) -> ok = inet:setopts(TSock, [{active, once}]), receive {noise, NSock, Bytes} -> NewRcv = <>, io:format("NewRcv = ~p~n", [NewRcv]), NewJS = j_received(NewRcv, JS), j_loop(NewJS); {kbd, line, Line} -> NewJS = j_kbdln(Line, JS), j_loop(NewJS); {tcp_closed, TSock} -> io:format("tcp socket closed unexpectedly, exiting~n"), exit(tcp_closed); Unknown -> ok = io:format("unknown message ~tp~n", [Unknown]), j_loop(JS) end. j_kbdln(Line, JS = #js{nsock = NS}) -> FinalMsg = unicode:characters_to_binary([string:chomp(Line), "\r\n"]), ok = enoise:send(NS, FinalMsg), JS. % not the time for readline % % user hit return, flush queue % j_kbd("\r", JS = #js{kbdq = KQ, nsock = NS}) -> % FinalMsg = unicode:characters_to_binary([KQ, "\r\n"]), % ok = enoise:send(NS, FinalMsg), % NewJS = JS#js{kbdq = []} % NewJS; % % user hit something else, add to queue % % fixme: parse ANSI escape sequences here, maybe xterm % j_kbd(Str1, JS = #js{kbdq = KQ}) -> % JS#js{kbdq = KQ ++ Str1}. j_received(Rcv, JS) -> case takeln(Rcv) of none -> JS#js{rcv = Rcv}; {line, Line, Rcv2} -> ok = j_rln(Line), j_received(Rcv2, JS) end. j_rln(Line) -> io:format("< ~ts~n", [string:chomp(Line)]). takeln(X) -> takeln(<<>>, X). takeln(Acc, <<"\r\n", Rest/binary>>) -> {line, <>, Rest}; takeln(Acc, <>) -> takeln(<>, Rest); takeln(_Acc, <<>>) -> none. kbd_loop(Parent) -> Line = io:get_line("> "), Parent ! {kbd, line, Line}, kbd_loop(Parent). %kbd_loop(Parent) -> % Char = io:get_chars("", 1), % Parent ! {kbd, Char}, % kbd_loop(Parent).