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
Current section
Files
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