Packages

Terminal emulation and driver infrastructure for Raxol. ANSI parsing, screen buffers, command processing, cursor management, input handling, session management, and termbox2 NIF integration.

Current section

Files

Jump to
raxol_terminal lib raxol terminal parser states dcs_entry_state.ex
Raw

lib/raxol/terminal/parser/states/dcs_entry_state.ex

defmodule Raxol.Terminal.Parser.States.DCSEntryState do
@moduledoc """
Handles the :dcs_entry state of the terminal parser.
"""
alias Raxol.Terminal.Emulator
alias Raxol.Terminal.Parser.ParserState, as: State
require Raxol.Core.Runtime.Log
@doc """
Processes input when the parser is in the :dcs_entry state.
Similar to CSI Entry - collects params/intermediates/final byte.
"""
@spec handle(Emulator.t(), State.t(), binary()) ::
{:continue, Emulator.t(), State.t(), binary()}
| {:finished, Emulator.t(), State.t()}
| {:incomplete, Emulator.t(), State.t()}
def handle(emulator, %State{state: :dcs_entry} = parser_state, input) do
case input do
<<>> -> {:incomplete, emulator, parser_state}
<<byte, rest::binary>> -> handle_byte(emulator, parser_state, byte, rest)
end
end
defp handle_byte(emulator, parser_state, byte, rest) do
case categorize_byte(byte) do
:param ->
handle_param_byte(emulator, parser_state, byte, rest)
:separator ->
handle_separator(emulator, parser_state, rest)
:intermediate ->
handle_intermediate_byte(emulator, parser_state, byte, rest)
:final ->
handle_final_byte(emulator, parser_state, byte, rest)
:can_sub ->
handle_can_sub(emulator, parser_state, rest)
:ignored ->
handle_ignored_byte(emulator, parser_state, byte, rest)
:unhandled ->
handle_unhandled_byte(emulator, parser_state, byte, rest)
end
end
defp categorize_byte(byte) do
cond do
param_byte?(byte) -> :param
byte == ?; -> :separator
intermediate_byte?(byte) -> :intermediate
final_byte?(byte) -> :final
can_sub?(byte) -> :can_sub
ignored_byte?(byte) -> :ignored
true -> :unhandled
end
end
defp param_byte?(byte), do: byte >= ?0 and byte <= ?9
defp intermediate_byte?(byte), do: byte >= 0x20 and byte <= 0x2F
defp final_byte?(byte), do: byte >= 0x40 and byte <= 0x7E
defp can_sub?(byte), do: byte == 0x18 or byte == 0x1A
defp ignored_byte?(byte),
do:
(byte >= 0 and byte <= 23 and byte != 0x18 and byte != 0x1A) or
(byte >= 27 and byte <= 31) or byte == 127
defp handle_param_byte(emulator, parser_state, byte, rest) do
next_state = %{
parser_state
| params_buffer: parser_state.params_buffer <> <<byte>>
}
{:continue, emulator, next_state, rest}
end
defp handle_separator(emulator, parser_state, rest) do
next_state = %{
parser_state
| params_buffer: parser_state.params_buffer <> <<?;>>
}
{:continue, emulator, next_state, rest}
end
defp handle_intermediate_byte(emulator, parser_state, byte, rest) do
next_state = %{
parser_state
| intermediates_buffer: parser_state.intermediates_buffer <> <<byte>>
}
{:continue, emulator, next_state, rest}
end
defp handle_final_byte(emulator, parser_state, byte, rest) do
Raxol.Core.Runtime.Log.debug(
"DCSEntryState: Found final byte #{byte}, transitioning to dcs_passthrough with rest=#{inspect(rest)}"
)
next_state = %{
parser_state
| state: :dcs_passthrough,
final_byte: byte,
payload_buffer: ""
}
{:continue, emulator, next_state, rest}
end
defp handle_can_sub(emulator, parser_state, rest) do
Raxol.Core.Runtime.Log.debug("Ignoring CAN/SUB byte in DCS Entry")
next_state = %{parser_state | state: :ground}
{:continue, emulator, next_state, rest}
end
defp handle_ignored_byte(emulator, parser_state, byte, rest) do
Raxol.Core.Runtime.Log.debug("Ignoring C0/DEL byte #{byte} in DCS Entry")
{:continue, emulator, parser_state, rest}
end
defp handle_unhandled_byte(emulator, parser_state, byte, rest) do
Raxol.Core.Runtime.Log.warning_with_context(
"Unhandled byte #{byte} in DCS Entry state, returning to ground.",
%{}
)
next_state = %{parser_state | state: :ground}
{:continue, emulator, next_state, rest}
end
end