summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh
diff options
context:
space:
mode:
authorSimponic <loganhunt@simponic.xyz>2023-01-11 14:21:48 -0700
committerSimponic <loganhunt@simponic.xyz>2023-01-11 14:21:48 -0700
commit628c6d95a32ba0dea67ab046a480ddfca7432c9b (patch)
tree4405ecd2fbee6027897706884d70ac5dd5de418b /lib/chessh/ssh
parent2ce03d4796ad54f332c70ff88f0a9f3a164bbc4a (diff)
downloadchessh-628c6d95a32ba0dea67ab046a480ddfca7432c9b.tar.gz
chessh-628c6d95a32ba0dea67ab046a480ddfca7432c9b.zip
Done moving menu to genserver architecture
Diffstat (limited to 'lib/chessh/ssh')
-rw-r--r--lib/chessh/ssh/client/client.ex79
-rw-r--r--lib/chessh/ssh/client/menu.ex67
-rw-r--r--lib/chessh/ssh/client/screen.ex25
-rw-r--r--lib/chessh/ssh/tui.ex33
4 files changed, 119 insertions, 85 deletions
diff --git a/lib/chessh/ssh/client/client.ex b/lib/chessh/ssh/client/client.ex
index a5f7bec..216af71 100644
--- a/lib/chessh/ssh/client/client.ex
+++ b/lib/chessh/ssh/client/client.ex
@@ -1,22 +1,18 @@
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"
- ]
+ @min_terminal_width 64
+ @min_terminal_height 31
+ @max_terminal_width 200
+ @max_terminal_height 100
defmodule State do
defstruct tui_pid: nil,
@@ -27,10 +23,10 @@ defmodule Chessh.SSH.Client do
end
@impl true
- def init([%State{tui_pid: tui_pid} = state]) do
+ def init([%State{} = state]) do
{:ok, screen_pid} =
GenServer.start_link(Chessh.SSH.Client.Menu, [
- %Chessh.SSH.Client.Menu.State{tui_pid: tui_pid}
+ %Chessh.SSH.Client.Menu.State{client_pid: self()}
])
{:ok, %{state | screen_processes: [screen_pid]}}
@@ -76,27 +72,36 @@ defmodule Chessh.SSH.Client do
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(
+ :refresh,
+ %State{} = state
+ ) do
+ render(state)
+ {:noreply, state}
+ end
+
+ def handle(
+ {:send_to_ssh, data},
+ %State{width: width, height: height, tui_pid: tui_pid} = state
+ ) do
+ case get_terminal_dim_msg(width, height) do
+ {true, msg} -> send(tui_pid, {:send_data, msg})
+ {false, _} -> send(tui_pid, {:send_data, data})
+ end
+
+ {: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})
+ case get_terminal_dim_msg(width, height) do
+ {true, msg} -> send(tui_pid, {:send_data, msg})
+ {false, _} -> send(screen_pid, {:render, width, height})
end
- {:noreply, new_state}
+ {:noreply, %State{state | width: width, height: height}}
end
def keymap(key) do
@@ -113,4 +118,30 @@ defmodule Chessh.SSH.Client do
x -> x
end
end
+
+ defp get_terminal_dim_msg(width, height) do
+ case {height > @max_terminal_height, height < @min_terminal_height,
+ width > @max_terminal_width, width < @min_terminal_width} do
+ {true, _, _, _} -> {true, @clear_codes ++ ["The terminal height is too large."]}
+ {_, true, _, _} -> {true, @clear_codes ++ ["The terminal height is too small."]}
+ {_, _, true, _} -> {true, @clear_codes ++ ["The terminal width is too large"]}
+ {_, _, _, true} -> {true, @clear_codes ++ ["The terminal width is too small."]}
+ {false, false, false, false} -> {false, nil}
+ end
+ end
+
+ defp render(%State{
+ tui_pid: tui_pid,
+ width: width,
+ height: height,
+ screen_processes: [screen_pid | _]
+ }) do
+ {out_of_range, msg} = get_terminal_dim_msg(width, height)
+
+ if out_of_range && msg do
+ send(tui_pid, {:send_data, msg})
+ else
+ send(screen_pid, {:render, width, height})
+ end
+ end
end
diff --git a/lib/chessh/ssh/client/menu.ex b/lib/chessh/ssh/client/menu.ex
index a69ad88..7e2ffbc 100644
--- a/lib/chessh/ssh/client/menu.ex
+++ b/lib/chessh/ssh/client/menu.ex
@@ -5,18 +5,10 @@ defmodule Chessh.SSH.Client.Menu do
require Logger
defmodule State do
- defstruct dy: 0,
- dx: 0,
- tui_pid: nil,
+ defstruct client_pid: nil,
selected: 0
end
- use Chessh.SSH.Client.Screen
-
- def init([%State{} = state | _]) do
- {:ok, state}
- end
-
@logo " Simponic's
dP MP\"\"\"\"\"\"`MM MP\"\"\"\"\"\"`MM M\"\"MMMMM\"\"MM
88 M mmmmm..M M mmmmm..M M MMMMM MM
@@ -25,6 +17,11 @@ defmodule Chessh.SSH.Client.Menu do
88. ... 88 88 88. ... M. .MMM' M M. .MMM' M M MMMMM MM
`88888P' dP dP `88888P' Mb. .dM Mb. .dM M MMMMM MM
MMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMM"
+ use Chessh.SSH.Client.Screen
+
+ def init([%State{} = state | _]) do
+ {:ok, state}
+ end
# @options [
# {"Option 1", {Chessh.SSH.Client.Board, [%Chessh.SSH.Client.Board.State{}]}},
@@ -36,12 +33,7 @@ defmodule Chessh.SSH.Client.Menu do
{"Option 3", {}}
]
- def handle_info({:render, width, height}, %State{} = state) do
- render(width, height, state)
- {:noreply, state}
- end
-
- def handle_info({:input, width, height, action}, %State{selected: selected} = state) do
+ def input(width, height, action, %State{client_pid: client_pid, selected: selected} = state) do
new_state =
case(action) do
:up ->
@@ -61,36 +53,43 @@ defmodule Chessh.SSH.Client.Menu do
state
end
- render(width, height, new_state)
- {:noreply, new_state}
+ send(client_pid, {:send_to_ssh, render_state(width, height, new_state)})
+ new_state
+ end
+
+ def render(width, height, %State{client_pid: client_pid} = state) do
+ send(client_pid, {:send_to_ssh, render_state(width, height, state)})
+ state
end
- def render(width, height, %State{tui_pid: tui_pid, dy: dy, dx: dx, selected: selected}) do
- text = String.split(@logo, "\n")
+ defp render_state(
+ width,
+ height,
+ %State{selected: selected}
+ ) do
+ logo_lines = String.split(@logo, "\n")
{logo_width, logo_height} = Utils.text_dim(@logo)
- {y, x} = center_rect({logo_width, logo_height + length(text)}, {width, height})
+ {y, x} = center_rect({logo_width, logo_height + length(logo_lines)}, {width, height})
- rendered =
+ [ANSI.clear()] ++
Enum.flat_map(
- Enum.zip(1..length(text), text),
+ Enum.zip(1..length(logo_lines), logo_lines),
fn {i, line} ->
[
- ANSI.cursor(y + i + dy, x + dx),
+ ANSI.cursor(y + i, x),
line
]
end
) ++
- Enum.flat_map(
- Enum.zip(0..(length(@options) - 1), @options),
- fn {i, {option, _}} ->
- [
- ANSI.cursor(y + length(text) + i + dy, x + dx),
- if(i == selected, do: ANSI.format([:light_cyan, "* #{option}"]), else: option)
- ]
- end
- ) ++ [ANSI.home()]
-
- send(tui_pid, {:send_data, rendered})
+ Enum.flat_map(
+ Enum.zip(0..(length(@options) - 1), @options),
+ fn {i, {option, _}} ->
+ [
+ ANSI.cursor(y + length(logo_lines) + i + 1, x),
+ if(i == selected, do: ANSI.format([:bright, :light_cyan, "+ #{option}"]), else: option)
+ ]
+ end
+ ) ++ [ANSI.home()]
end
defp wrap_around(index, delta, length) do
diff --git a/lib/chessh/ssh/client/screen.ex b/lib/chessh/ssh/client/screen.ex
index 2dd9f9c..8eb9f21 100644
--- a/lib/chessh/ssh/client/screen.ex
+++ b/lib/chessh/ssh/client/screen.ex
@@ -1,21 +1,18 @@
defmodule Chessh.SSH.Client.Screen do
- @callback handle_info(
- {:render, width :: integer(), height :: integer()},
- state :: any()
- ) ::
- {:noreply, any()}
- @callback handle_info({:input, width :: integer(), height :: integer(), action :: any()}) ::
- {:noreply, any()}
-
- # @callback render(state :: Chessh.SSH.Client.State.t() | any()) :: any()
- # @callback handle_input(action :: any(), state :: Chessh.SSH.Client.State.t()) ::
- # Chessh.SSH.Client.State.t()
+ @callback render(width :: integer(), height :: integer(), state :: any()) :: any()
+ @callback input(width :: integer(), height :: integer(), action :: any(), state :: any()) ::
+ any()
defmacro __using__(_) do
quote do
@behaviour Chessh.SSH.Client.Screen
use GenServer
+ @clear_codes [
+ IO.ANSI.clear(),
+ IO.ANSI.home()
+ ]
+
@ascii_chars Application.compile_env!(:chessh, :ascii_chars_json_file)
|> File.read!()
|> Jason.decode!()
@@ -26,6 +23,12 @@ defmodule Chessh.SSH.Client.Screen do
div(parent_width - rect_width, 2)
}
end
+
+ def handle_info({:render, width, height}, state),
+ do: {:noreply, render(width, height, state)}
+
+ def handle_info({:input, width, height, action}, state),
+ do: {:noreply, input(width, height, action, state)}
end
end
end
diff --git a/lib/chessh/ssh/tui.ex b/lib/chessh/ssh/tui.ex
index 5d43760..c1c763b 100644
--- a/lib/chessh/ssh/tui.ex
+++ b/lib/chessh/ssh/tui.ex
@@ -26,7 +26,7 @@ defmodule Chessh.SSH.Tui do
{:ok, init_state}
end
- def handle_msg({:ssh_channel_up, channel_id, connection_ref}, state) do
+ def handle_msg({:ssh_channel_up, channel_id, connection_ref}, %State{} = state) do
Logger.debug("SSH channel up #{inspect(:ssh.connection_info(connection_ref))}")
connected_player =
@@ -54,7 +54,7 @@ defmodule Chessh.SSH.Tui do
:syn.join(:player_sessions, {:session, session.id}, self())
{:ok,
- %{
+ %State{
state
| channel_id: channel_id,
connection_ref: connection_ref,
@@ -66,7 +66,7 @@ defmodule Chessh.SSH.Tui do
def handle_msg(
{:EXIT, client_pid, _reason},
- %{client_pid: client_pid, channel_id: channel_id} = state
+ %State{client_pid: client_pid, channel_id: channel_id} = state
) do
send(client_pid, :quit)
{:stop, channel_id, state}
@@ -74,7 +74,7 @@ defmodule Chessh.SSH.Tui do
def handle_msg(
{:send_data, data},
- %{connection_ref: connection_ref, channel_id: channel_id} = state
+ %State{connection_ref: connection_ref, channel_id: channel_id} = state
) do
:ssh_connection.send(connection_ref, channel_id, data)
{:ok, state}
@@ -82,7 +82,7 @@ defmodule Chessh.SSH.Tui do
def handle_msg(
:session_closed,
- %{connection_ref: connection_ref, channel_id: channel_id} = state
+ %State{connection_ref: connection_ref, channel_id: channel_id} = state
) do
:ssh_connection.send(connection_ref, channel_id, @session_closed_message)
{:stop, channel_id, state}
@@ -94,7 +94,7 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, _connection_handler, {:data, _channel_id, _type, data}},
- state
+ %State{} = state
) do
send(state.client_pid, {:data, data})
{:ok, state}
@@ -103,7 +103,7 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, connection_handler,
{:pty, channel_id, want_reply?, {_term, width, height, _pixwidth, _pixheight, _opts}}},
- state
+ %State{client_pid: client_pid} = state
) do
Logger.debug("#{inspect(state.player_session)} has requested a PTY")
:ssh_connection.reply_request(connection_handler, want_reply?, :success, channel_id)
@@ -128,12 +128,12 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, _connection_handler,
{:window_change, _channel_id, width, height, _pixwidth, _pixheight}},
- %{client_pid: client_pid} = state
+ %State{client_pid: client_pid} = state
) do
send(client_pid, {:resize, {width, height}})
{:ok,
- %{
+ %State{
state
| width: width,
height: height
@@ -157,12 +157,13 @@ defmodule Chessh.SSH.Tui do
}
])
- {:ok, %{state | client_pid: client_pid}}
+ send(client_pid, :refresh)
+ {:ok, %State{state | client_pid: client_pid}}
end
def handle_ssh_msg(
{:ssh_cm, connection_handler, {:exec, channel_id, want_reply?, cmd}},
- state
+ %State{} = state
) do
:ssh_connection.reply_request(connection_handler, want_reply?, :success, channel_id)
Logger.debug("EXEC #{cmd}")
@@ -171,7 +172,7 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, _connection_handler, {:eof, _channel_id}},
- state
+ %State{} = state
) do
Logger.debug("EOF")
{:ok, state}
@@ -179,7 +180,7 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, _connection_handler, {:signal, _channel_id, signal}},
- state
+ %State{} = state
) do
Logger.debug("SIGNAL #{signal}")
{:ok, state}
@@ -187,7 +188,7 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, _connection_handler, {:exit_signal, channel_id, signal, err, lang}},
- state
+ %State{} = state
) do
Logger.debug("EXIT SIGNAL #{signal} #{err} #{lang}")
{:stop, channel_id, state}
@@ -195,7 +196,7 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, _connection_handler, {:exit_STATUS, channel_id, status}},
- state
+ %State{} = state
) do
Logger.debug("EXIT STATUS #{status}")
{:stop, channel_id, state}
@@ -203,7 +204,7 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
msg,
- %{channel_id: channel_id} = state
+ %State{channel_id: channel_id} = state
) do
Logger.debug("UNKOWN MESSAGE #{inspect(msg)}")
{:stop, channel_id, state}