diff options
Diffstat (limited to 'lib/chessh/ssh/client/client.ex')
-rw-r--r-- | lib/chessh/ssh/client/client.ex | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/lib/chessh/ssh/client/client.ex b/lib/chessh/ssh/client/client.ex new file mode 100644 index 0000000..a5f7bec --- /dev/null +++ b/lib/chessh/ssh/client/client.ex @@ -0,0 +1,116 @@ +defmodule Chessh.SSH.Client do + alias IO.ANSI + alias Chessh.SSH.Client.Menu + require Logger + + use GenServer + + @clear_codes [ + ANSI.clear(), + ANSI.reset(), + ANSI.home() + ] + + @max_terminal_width 255 + @max_terminal_height 127 + + @terminal_bad_dim_msg [ + @clear_codes | "The dimensions of your terminal are not within in the valid range" + ] + + defmodule State do + defstruct tui_pid: nil, + width: 0, + height: 0, + player_session: nil, + screen_processes: [] + end + + @impl true + def init([%State{tui_pid: tui_pid} = state]) do + {:ok, screen_pid} = + GenServer.start_link(Chessh.SSH.Client.Menu, [ + %Chessh.SSH.Client.Menu.State{tui_pid: tui_pid} + ]) + + {:ok, %{state | screen_processes: [screen_pid]}} + end + + @impl true + def handle_info(:quit, %State{} = state) do + {:stop, :normal, state} + end + + @impl true + def handle_info(msg, state) do + [burst_ms, burst_rate] = + Application.get_env(:chessh, RateLimits) + |> Keyword.take([:player_session_message_burst_ms, :player_session_message_burst_rate]) + |> Keyword.values() + + case Hammer.check_rate_inc( + "player-session-#{state.player_session.id}-burst-message-rate", + burst_ms, + burst_rate, + 1 + ) do + {:allow, _count} -> + handle(msg, state) + + {:deny, _limit} -> + {:noreply, state} + end + end + + def handle( + {:data, data}, + %State{width: width, height: height, screen_processes: [screen_pid | _]} = state + ) do + action = keymap(data) + + if action == :quit do + {:stop, :normal, state} + else + send(screen_pid, {:input, width, height, action}) + {:noreply, state} + end + end + + # def handle( + # {:refresh, }, + # %State{screen_processes: [screen_pid | _] = screen_processes, width: width, height: height} = state + # ) do + # send(screen_pid, {:render, tui_pid, width, height}) + # {:noreply, state} + # end + + def handle( + {:resize, {width, height}}, + %State{tui_pid: tui_pid, screen_processes: [screen_pid | _]} = state + ) do + new_state = %State{state | width: width, height: height} + + if height <= @max_terminal_height && width <= @max_terminal_width do + send(screen_pid, {:render, width, height}) + else + send(tui_pid, {:send_data, @terminal_bad_dim_msg}) + end + + {:noreply, new_state} + end + + def keymap(key) do + case key do + # Exit keys - C-c and C-d + <<3>> -> :quit + <<4>> -> :quit + # Arrow keys + "\e[A" -> :up + "\e[B" -> :down + "\e[D" -> :left + "\e[C" -> :right + "\r" -> :return + x -> x + end + end +end |