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/cursor/callbacks.ex
defmodule Raxol.Terminal.Cursor.Callbacks do
@moduledoc """
Handles GenServer callbacks for the cursor manager.
Extracted from Raxol.Terminal.Cursor.Manager to reduce file size.
"""
alias Raxol.Core.Runtime.Log
alias Raxol.Terminal.Cursor.Movement
@cursor_blink_interval_ms 500
@doc """
Handles GenServer call for getting cursor position.
"""
def handle_get_position(state) do
Raxol.Core.Runtime.Log.debug("Getting cursor position: {#{state.row}, #{state.col}}")
{state.position, state}
end
@doc """
Handles GenServer call for setting cursor position.
"""
def handle_set_position(state, row, col) do
Raxol.Core.Runtime.Log.debug(
"Setting cursor position from {#{state.row}, #{state.col}} to {#{row}, #{col}}"
)
new_state = %{state | row: row, col: col, position: {row, col}}
# Debug: log the new state
Raxol.Core.Runtime.Log.debug(
"New cursor state: row=#{new_state.row}, col=#{new_state.col}, position=#{inspect(new_state.position)}"
)
{:ok, new_state}
end
@doc """
Handles GenServer call for moving cursor in a direction.
"""
def handle_move_cursor(state, direction, count) do
new_state =
case direction do
:up -> Movement.move_up(state, count, 80, 24)
:down -> Movement.move_down(state, count, 80, 24)
:left -> Movement.move_left(state, count, 80, 24)
:right -> Movement.move_right(state, count, 80, 24)
end
{:ok, new_state}
end
@doc """
Handles GenServer call for getting cursor visibility.
"""
def handle_get_visibility(state) do
{state.visible, state}
end
@doc """
Handles GenServer call for setting cursor visibility.
"""
def handle_set_visibility(state, visible) do
new_state = %{
state
| visible: visible,
state:
case visible do
true -> :visible
false -> :hidden
end
}
{:ok, new_state}
end
@doc """
Handles GenServer cast for setting cursor visibility.
"""
def handle_set_visibility_cast(state, visible) do
new_state = %{
state
| visible: visible,
state:
case visible do
true -> :visible
false -> :hidden
end
}
{new_state}
end
@doc """
Handles GenServer call for getting cursor style.
"""
def handle_get_style(state) do
{state.style, state}
end
@doc """
Handles GenServer call for setting cursor style.
"""
def handle_set_style(state, style) do
{:ok, %{state | style: style}}
end
@doc """
Handles GenServer call for getting cursor blink state.
"""
def handle_get_blink(state) do
{state.blinking, state}
end
@doc """
Handles GenServer call for setting cursor blink state.
"""
def handle_set_blink(state, blink) do
new_state = %{state | blinking: blink}
_ =
case blink do
true -> schedule_blink()
false -> cancel_blink(state.blink_timer)
end
{:ok, new_state}
end
@doc """
Handles GenServer call for setting custom cursor shape.
"""
def handle_set_custom_shape(state, shape, params) do
{:ok,
%{
state
| style: :custom,
custom_shape: shape,
custom_dimensions: params,
shape: params
}}
end
@doc """
Handles GenServer call for updating cursor position.
"""
def handle_update_position(state, row, col) do
{:ok, %{state | row: row, col: col, position: {row, col}}}
end
@doc """
Handles GenServer call for resetting cursor position.
"""
def handle_reset_position(state) do
{:ok, %{state | row: 0, col: 0, position: {0, 0}}}
end
@doc """
Handles GenServer call for updating position from text.
"""
def handle_update_position_from_text(state, text) do
new_col = state.col + String.length(text)
new_state = %{state | col: new_col, position: {state.row, new_col}}
{:ok, new_state}
end
@doc """
Handles GenServer call for updating blink state.
"""
def handle_update_blink(state) do
new_blink_state = !state.blink
new_state = %{state | blink: new_blink_state}
{new_blink_state, new_state}
end
@doc """
Handles GenServer call for getting cursor state.
"""
def handle_get_state(state) do
{state, state}
end
@doc """
Handles GenServer call for getting cursor state atom.
"""
def handle_get_state_atom(state) do
{state.state, state}
end
@doc """
Handles GenServer call for moving cursor down.
"""
def handle_move_down(state, count, _width, height) do
# Move the cursor down by count lines, respecting margins
new_row = min(Map.get(state, :bottom_margin, height - 1), state.row + count)
new_state = %{state | row: new_row, position: {new_row, state.col}}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor up.
"""
def handle_move_up(state, lines, _width, _height) do
# Move the cursor up by lines, respecting margins
new_row = max(Map.get(state, :top_margin, 0), state.row - lines)
new_state = %{state | row: new_row, position: {new_row, state.col}}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor right.
"""
def handle_move_right(state, cols, _width, _height) do
# Move the cursor right by cols
new_col = state.col + cols
new_state = %{state | col: new_col, position: {state.row, new_col}}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor left.
"""
def handle_move_left(state, cols, _width, _height) do
# Move the cursor left by cols
new_col = max(0, state.col - cols)
new_state = %{state | col: new_col, position: {state.row, new_col}}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor to position.
"""
def handle_move_to(state, row, col) do
# Move cursor to specific position
new_state = %{state | row: row, col: col, position: {row, col}}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor to column.
"""
def handle_move_to_column(state, column) do
# Move cursor to specific column
new_state = %{state | col: column, position: {state.row, column}}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor to column with bounds.
"""
def handle_move_to_column_bounded(state, column, width, _height) do
# Move cursor to specific column with bounds clamping
clamped_col = max(0, min(column, width - 1))
new_state = %{state | col: clamped_col, position: {state.row, clamped_col}}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor to position with bounds.
"""
def handle_move_to_bounded(state, row, col, width, height) do
# Move cursor to specific position with bounds clamping
clamped_row = max(0, min(row, height - 1))
clamped_col = max(0, min(col, width - 1))
new_state = %{
state
| row: clamped_row,
col: clamped_col,
position: {clamped_row, clamped_col}
}
{new_state, new_state}
end
@doc """
Handles GenServer call for moving cursor to bounded position.
"""
def handle_move_to_bounded_position(state, row, col, width, height) do
# Move cursor to specific position with bounds clamping
clamped_row = max(0, min(row, height - 1))
clamped_col = max(0, min(col, width - 1))
new_state = %{
state
| row: clamped_row,
col: clamped_col,
position: {clamped_row, clamped_col}
}
{:ok, new_state}
end
@doc """
Handles GenServer call for setting state atom.
"""
def handle_set_state_atom(state, state_atom) do
new_state =
case state_atom do
:visible -> %{state | visible: true, state: :visible}
:hidden -> %{state | visible: false, state: :hidden}
:blinking -> %{state | blinking: true, blink: true, state: :blinking}
_ -> state
end
{:ok, new_state}
end
@doc """
Handles GenServer call for unknown requests.
"""
def handle_unknown_request(request, state) do
Log.warning("Unknown request: #{inspect(request)}")
{{:error, :unknown_request}, state}
end
@doc """
Handles GenServer info for blink timer.
"""
def handle_blink_info(state, _timer_id) do
case state.blinking do
true ->
new_blink_state = !state.blink
new_state = %{state | blink: new_blink_state}
schedule_blink()
{new_state}
false ->
{state}
end
end
# --- Private Functions ---
defp schedule_blink do
timer_id = System.unique_integer([:positive])
Process.send_after(self(), {:blink, timer_id}, @cursor_blink_interval_ms)
end
defp cancel_blink(nil), do: :ok
defp cancel_blink(timer_id), do: _ = Process.cancel_timer(timer_id)
end