Measure the width of text in the terminal and build simple layouts!
2.8 kB
91 lines
1-module(tui_ffi).
2-export([tui_app/4]).
3
4-behaviour(gen_event).
5-export([init/1, handle_event/2, handle_info/2, handle_call/2, terminate/2]).
6-export([run_read_loop/1]).
7
8%% This implements tui_app using the new OTP 28 features.
9%% To try this out, build erlang from this branch (or what until the PR is merged):
10%%
11%% Branch: https://github.com/garazdawi/otp/blob/lukas/kernel/shell-improvements
12%% PR: https://github.com/erlang/otp/pull/8962
13%%
14%% Also, please heart the PR <3
15%%
16%% To be able to exit properly on CTRL+C, you have to invoke erlang with the +Bc flag:
17%%
18%% ERL_FLAGS="+Bc" gleam run -m human_rights
19%%
20%% Just `gleam run` also works, but will bring up the break handler menu on CTRL+C.
21%% If you abort, your terminal will still be somewhat broken; a `reset` fixes that.
22
23tui_app(State, OnResize, OnData, View) ->
24 io:setopts(standard_io, [binary, {encoding, utf8}]),
25 ok = shell:start_interactive({noshell, raw}),
26 io:put_chars(<<"\e[?1049h\e[?25l"/utf8>>),
27
28 ok = gen_event:swap_sup_handler(
29 erl_signal_server,
30 {erl_signal_handler, []},
31 {tui_ffi, [self()]}),
32 os:set_signal(sigwinch, handle),
33 %% os:set_signal(sigterm, handle),
34
35 spawn_link(tui_ffi, run_read_loop, [self()]),
36
37 loop(State, OnResize, OnData, View),
38 io:put_chars(<<"\e[?25h\e[?1049l"/utf8>>),
39 init:stop(),
40 nil.
41
42loop(State, OnResize, OnData, View) ->
43 %% io:put_chars(<<"\e[0;0H"/utf8>>),
44 io:put_chars(string:replace(View(State), <<"\n"/utf8>>, <<"\r\n"/utf8>>, all)),
45 %% io:put_chars(unicode:characters_to_binary(integer_to_list(os:system_time()))),
46 receive
47 { data, Chars } ->
48 case handle_input(Chars, State, OnData) of
49 stop -> stop;
50 NewState ->
51 loop(NewState, OnResize, OnData, View)
52 end;
53
54 { signal, sigint } -> stop;
55 { signal, sigterm } -> stop;
56
57 { signal, sigwinch } ->
58 {ok, Rows} = io:rows(),
59 {ok, Cols} = io:columns(),
60 NewState = OnResize(State, Rows, Cols),
61 loop(NewState, OnResize, OnData, View)
62 end.
63
64handle_input(<<>>, State, _OnData) -> State;
65handle_input(<<3, _rest/bits>>, _State, _OnData) -> stop;
66handle_input(<<"q"/utf8, _rest/bits>>, _State, _OnData) -> stop;
67handle_input(Rest, State, OnData) -> OnData(State, Rest).
68
69%% input handler
70
71run_read_loop(Pid) ->
72 Chars = io:get_chars("", 64),
73 Pid ! { data, Chars },
74 run_read_loop(Pid).
75
76%% signal handler
77
78init({[Pid], _}) -> {ok, {Pid}}.
79
80handle_event(Signal, {Pid} = State) ->
81 Pid ! { signal, Signal },
82 {ok, State}.
83
84handle_info(_, State) ->
85 {ok, State}.
86
87handle_call(_Request, State) ->
88 {ok, ok, State}.
89
90terminate(_Args, _State) ->
91 ok.