some stupid bug
This commit is contained in:
@@ -208,3 +208,21 @@
|
||||
% 2. Control sequences reference: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
% 3. Historical background: https://invisible-island.net/xterm/modified-keys.html
|
||||
%-define(XTERM_
|
||||
|
||||
% readline keys
|
||||
-define(KBD_ESC, [27]).
|
||||
-define(KBD_C_a, [1]).
|
||||
-define(KBD_C_b, [2]).
|
||||
-define(KBD_C_d, [4]).
|
||||
-define(KBD_C_e, [5]).
|
||||
-define(KBD_C_f, [6]).
|
||||
-define(KBD_C_g, [7]).
|
||||
-define(KBD_C_k, [11]).
|
||||
-define(KBD_C_l, [12]).
|
||||
-define(KBD_C_n, [14]).
|
||||
-define(KBD_C_p, [16]).
|
||||
|
||||
% M-b -> [ESC, $b]
|
||||
-define(KBD_M_b, [?KBD_ESC, $b]).
|
||||
-define(KBD_M_d, [?KBD_ESC, $d]).
|
||||
-define(KBD_M_f, [?KBD_ESC, $f]).
|
||||
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env escript
|
||||
|
||||
%% script for keycode mapping
|
||||
|
||||
main([]) ->
|
||||
ok = shell:start_interactive({noshell, raw}),
|
||||
dum([]).
|
||||
|
||||
dum(Q) ->
|
||||
io:format("~tw\r\n", [Q]),
|
||||
NewChar = io:get_chars("", 1),
|
||||
dum(Q ++ NewChar).
|
||||
+82
-10
@@ -1,18 +1,32 @@
|
||||
#!/usr/bin/env escript
|
||||
|
||||
%% needs:
|
||||
%% - line parsing
|
||||
%% - keyboard input
|
||||
%% - drawing to screen
|
||||
|
||||
-mode(compile).
|
||||
|
||||
% john state
|
||||
-record(js,
|
||||
{tsock = none :: gen_tcp:socket(),
|
||||
nsock = none :: enoise:noise_socket()}).
|
||||
{rcv = <<>> :: binary(),
|
||||
kbdq = [] :: iolist(),
|
||||
ibuf = [] :: string(),
|
||||
tsock = none :: none | gen_tcp:socket(),
|
||||
nsock = none :: none | enoise:noise_socket()}).
|
||||
|
||||
|
||||
main([]) ->
|
||||
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", 6969, TcpOpts),
|
||||
{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() ->
|
||||
@@ -22,15 +36,73 @@ noise_options() ->
|
||||
{e, KP}].
|
||||
|
||||
|
||||
j_loop(JS = #js{nsock = NSock}) ->
|
||||
j_loop(JS = #js{rcv = Rcv, tsock = TSock, nsock = NSock}) ->
|
||||
ok = inet:setopts(TSock, [{active, once}]),
|
||||
receive
|
||||
{noise, NSock, Bytes} ->
|
||||
ok = io:format("< ~tp~n", [Bytes]),
|
||||
j_loop(JS);
|
||||
NewRcv = <<Rcv/binary, Bytes/binary>>,
|
||||
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("~tp unknown message, killing self: ~tp~n",
|
||||
[self(), Unknown]),
|
||||
exit(normal)
|
||||
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, <<Acc/binary, "\r\n">>, Rest};
|
||||
takeln(Acc, <<B:8, Rest/binary>>) ->
|
||||
takeln(<<Acc/binary, B:8>>, 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).
|
||||
|
||||
+41
-20
@@ -5,9 +5,10 @@
|
||||
|
||||
% ho from pimp pov
|
||||
-record(pho,
|
||||
{pid :: pid(),
|
||||
ref :: reference(),
|
||||
name :: string()}).
|
||||
{pid = none :: none | pid(),
|
||||
ref = none :: none | reference(),
|
||||
taken = false :: boolean(),
|
||||
name = none :: none | string()}).
|
||||
|
||||
%% pimp state
|
||||
-record(ps,
|
||||
@@ -25,10 +26,9 @@
|
||||
|
||||
|
||||
main([]) ->
|
||||
io:format("RBX example 4 telnet echo server daemon~n"
|
||||
"`one' part of the many-to-one relationship~n",
|
||||
[]),
|
||||
p_init().
|
||||
p_init(6969);
|
||||
main([PortNum]) ->
|
||||
p_init(list_to_integer(PortNum)).
|
||||
|
||||
|
||||
noise_options() ->
|
||||
@@ -43,9 +43,8 @@ noise_options() ->
|
||||
|
||||
|
||||
% pimp process startup
|
||||
p_init() ->
|
||||
p_init(Port) ->
|
||||
p_logln("pimp startup (p_init)"),
|
||||
Port = 6969,
|
||||
Opts = [binary, {packet, 0}, {active, once}],
|
||||
case gen_tcp:listen(Port, Opts) of
|
||||
{ok, LSock} ->
|
||||
@@ -60,12 +59,12 @@ p_init() ->
|
||||
|
||||
p_loop(PS = #ps{hos = Hos}) ->
|
||||
receive
|
||||
{ho, _HoPid, taken} ->
|
||||
NewPS = p_spawn_ho(PS),
|
||||
{ho, HoPid, taken} ->
|
||||
NewPS = p_taken(HoPid, PS),
|
||||
p_loop(NewPS);
|
||||
{ho, HoPid, sent, Data} ->
|
||||
HoName = p_ho_name(HoPid, PS),
|
||||
SendMessage = io_lib:format("~ts :: ~ts\r\n", [HoName, Data]),
|
||||
SendMessage = io_lib:format("~ts> ~ts\r\n", [HoName, Data]),
|
||||
ok = p_echo_except(HoPid, HoName, SendMessage, Hos),
|
||||
p_loop(PS);
|
||||
Down = {'DOWN', _, _, _, _} ->
|
||||
@@ -76,6 +75,20 @@ p_loop(PS = #ps{hos = Hos}) ->
|
||||
p_loop(PS)
|
||||
end.
|
||||
|
||||
p_taken(Pid, PS = #ps{hos = Hos}) ->
|
||||
Hos_II = mark_taken(Pid, Hos, []),
|
||||
PS_II = PS#ps{hos = Hos_II},
|
||||
PS_III = p_spawn_ho(PS_II),
|
||||
PS_III.
|
||||
|
||||
mark_taken(Pid, [Ho = #pho{pid = Pid} | Rest], Acc) ->
|
||||
NewHo = Ho#pho{taken = true},
|
||||
lists:reverse(Acc) ++ [NewHo | Rest];
|
||||
mark_taken(Pid, [WrongHo | Rest], Acc) ->
|
||||
mark_taken(Pid, Rest, [WrongHo | Acc]);
|
||||
mark_taken(_, [], Acc) ->
|
||||
lists:reverse(Acc).
|
||||
|
||||
% given pid of ho, get her name
|
||||
p_ho_name(HoPid, #ps{hos = Hos}) ->
|
||||
case lists:keyfind(HoPid, #pho.pid, Hos) of
|
||||
@@ -100,7 +113,8 @@ p_spawn_ho(PS = #ps{lsock = LSock, hos = Hos, ho_names = Names}) ->
|
||||
|
||||
% ho down, remove her from roster
|
||||
p_ho_down({'DOWN', _Ref, process, Pid, Info}, PS = #ps{hos = Hos}) ->
|
||||
ok = p_logfln("ho down: ~tp; reason: ~tw", [Pid, Info]),
|
||||
Ho = lists:keyfind(Pid, #pho.pid, Hos),
|
||||
ok = p_logfln("ho down: ~ts; reason: ~tw", [Ho#pho.name, Info]),
|
||||
NewHos = lists:keydelete(Pid, #pho.pid, Hos),
|
||||
NewPS = PS#ps{hos = NewHos},
|
||||
NewPS.
|
||||
@@ -110,7 +124,10 @@ p_ho_down({'DOWN', _Ref, process, Pid, Info}, PS = #ps{hos = Hos}) ->
|
||||
% skip if equal
|
||||
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = SrcPid} | Rest]) ->
|
||||
p_echo_except(SrcPid, SrcName, Data, Rest);
|
||||
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = DstPid} | Rest]) ->
|
||||
% skip if not taken
|
||||
p_echo_except(SrcPid, SrcName, Data, [#pho{taken = false} | Rest]) ->
|
||||
p_echo_except(SrcPid, SrcName, Data, Rest);
|
||||
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = DstPid, taken = true} | Rest]) ->
|
||||
DstPid ! {pimp, send, Data},
|
||||
p_echo_except(SrcPid, SrcName, Data, Rest);
|
||||
p_echo_except(_, _, _, []) ->
|
||||
@@ -147,7 +164,8 @@ h_loop(HS = #hs{name = Name, daddy = Daddy, asock = ASock, nsock = NSock}) ->
|
||||
ok = h_rcvd(Name, Data_II),
|
||||
Daddy ! {ho, self(), sent, Data_II},
|
||||
h_loop(HS);
|
||||
{pimp, send, Data} ->
|
||||
{pimp, send, Data} = M ->
|
||||
io:format("~p received ~p~n", [self(), M]),
|
||||
ok = h_send(Name, NSock, Data),
|
||||
h_loop(HS);
|
||||
{tcp_closed, ASock} ->
|
||||
@@ -160,7 +178,7 @@ h_loop(HS = #hs{name = Name, daddy = Daddy, asock = ASock, nsock = NSock}) ->
|
||||
|
||||
% pimp log in bold
|
||||
p_logln(LogStr) ->
|
||||
Msg = io_lib:format("!! Daddy :: ~ts~n", [LogStr]),
|
||||
Msg = io_lib:format("Daddy: ~ts~n", [LogStr]),
|
||||
PimpChars = ?ANSI_BOLD([Msg]),
|
||||
io:put_chars(PimpChars).
|
||||
|
||||
@@ -170,7 +188,7 @@ p_logfln(LogStr, Args) ->
|
||||
|
||||
% ho log dimmed
|
||||
h_logln(HoName, LogStr) ->
|
||||
FMsg = io_lib:format("!! ~ts :: ~ts~n", [HoName, LogStr]),
|
||||
FMsg = io_lib:format("~ts: ~ts~n", [HoName, LogStr]),
|
||||
HoChars = ?ANSI_DIM([FMsg]),
|
||||
io:put_chars(HoChars).
|
||||
|
||||
@@ -179,12 +197,15 @@ h_logfln(HoName, MsgStr, Args) ->
|
||||
|
||||
% normal: john messages
|
||||
h_rcvd(Name, MsgStr) ->
|
||||
Msg = io_lib:format("<< ~ts :: ~ts~n", [Name, MsgStr]),
|
||||
Msg = io_lib:format("~ts< ~ts~n", [Name, string:chomp(MsgStr)]),
|
||||
io:put_chars(Msg).
|
||||
|
||||
h_send(Name, NSock, Msg) ->
|
||||
ok = io:format(">> ~ts :: ~ts~n", [Name, Msg]),
|
||||
enoise:send(NSock, Msg).
|
||||
FMsg = io_lib:format("~ts> ~ts~n", [Name, string:chomp(Msg)]),
|
||||
HoChars = [?ANSI_DIM(FMsg)],
|
||||
ok = io:put_chars(HoChars),
|
||||
Send = unicode:characters_to_binary([string:chomp(Msg), "\r\n"]),
|
||||
ok = enoise:send(NSock, Send).
|
||||
|
||||
|
||||
init_ho_names() ->
|
||||
|
||||
Reference in New Issue
Block a user