Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol lib raxol terminal events events_handler.ex
Raw

lib/raxol/terminal/events/events_handler.ex

defmodule Raxol.Terminal.Events.Handler do
@moduledoc """
Handles terminal events and dispatches them to appropriate handlers.
"""
require Raxol.Core.Runtime.Log
alias Raxol.Terminal.ANSI.Window.Manipulation, as: WindowManipulation
@doc """
Handles window-related events.
"""
def handle_window_event(emulator_state, event) do
case event do
{:resize, w, h} ->
handle_resize(emulator_state, w, h)
{:title, title} ->
handle_title_change(emulator_state, title)
{:icon_name, name} ->
handle_icon_name_change(emulator_state, name)
_ ->
{:error, "Unknown window event: #{inspect(event)}"}
end
end
@doc """
Handles mode change events.
"""
def handle_mode_event(emulator_state, event) do
case event do
{:change, new_mode} ->
handle_mode_change(emulator_state, new_mode)
_ ->
{:error, "Unknown mode event: #{inspect(event)}"}
end
end
@doc """
Handles mouse events.
"""
def handle_mouse_event(emulator_state, event) do
case event do
{:click, button, x, y} ->
handle_mouse_click(emulator_state, button, x, y)
{:drag, button, x, y} ->
handle_mouse_drag(emulator_state, button, x, y)
{:release, button, x, y} ->
handle_mouse_release(emulator_state, button, x, y)
_ ->
{:error, "Unknown mouse event: #{inspect(event)}"}
end
end
@doc """
Handles keyboard events.
"""
def handle_keyboard_event(emulator_state, event) do
case event do
{:press, key} ->
handle_key_press(emulator_state, key)
{:release, key} ->
handle_key_release(emulator_state, key)
_ ->
{:error, "Unknown keyboard event: #{inspect(event)}"}
end
end
@doc """
Handles focus events.
"""
def handle_focus_event(emulator_state, event) do
case event do
{:gain} ->
handle_focus_gain(emulator_state)
{:loss} ->
handle_focus_loss(emulator_state)
_ ->
{:error, "Unknown focus event: #{inspect(event)}"}
end
end
@doc """
Generic event handler that dispatches to appropriate handlers.
"""
def handle_event(emulator_state, event) do
case event do
{:window, window_event} ->
handle_window_event(emulator_state, window_event)
{:mode, mode_event} ->
handle_mode_event(emulator_state, mode_event)
{:mouse, mouse_event} ->
handle_mouse_event(emulator_state, mouse_event)
{:keyboard, keyboard_event} ->
handle_keyboard_event(emulator_state, keyboard_event)
{:focus, focus_event} ->
handle_focus_event(emulator_state, focus_event)
_ ->
{:error, "Unknown event type: #{inspect(event)}"}
end
end
# Private helper functions
defp handle_resize(emulator_state, w, h) do
# Update terminal dimensions
updated_state = %{emulator_state | width: w, height: h}
# Clear screen and reset cursor position
commands = [
WindowManipulation.clear_screen(),
WindowManipulation.move_cursor(1, 1)
]
{:ok, updated_state, commands}
end
defp handle_title_change(emulator_state, title) do
# Update window title
updated_state = %{emulator_state | title: title}
# Send title change command
commands = [WindowManipulation.set_title(title)]
{:ok, updated_state, commands}
end
defp handle_icon_name_change(emulator_state, name) do
# Update icon name
updated_state = %{emulator_state | icon_name: name}
# Send icon name change command
commands = [WindowManipulation.set_icon_name(name)]
{:ok, updated_state, commands}
end
defp handle_mode_change(emulator_state, new_mode) do
# Update terminal mode
updated_state = %{emulator_state | mode: new_mode}
# Send mode change command
commands = [WindowManipulation.set_mode(new_mode)]
{:ok, updated_state, commands}
end
defp handle_mouse_click(emulator_state, button, x, y) do
# Handle mouse click
commands = [WindowManipulation.mouse_click(button, x, y)]
{:ok, emulator_state, commands}
end
defp handle_mouse_drag(emulator_state, button, x, y) do
# Handle mouse drag
commands = [WindowManipulation.mouse_drag(button, x, y)]
{:ok, emulator_state, commands}
end
defp handle_mouse_release(emulator_state, button, x, y) do
# Handle mouse release
commands = [WindowManipulation.mouse_release(button, x, y)]
{:ok, emulator_state, commands}
end
defp handle_key_press(emulator_state, key) do
# Handle key press
commands = [WindowManipulation.key_press(key)]
{:ok, emulator_state, commands}
end
defp handle_key_release(emulator_state, key) do
# Handle key release
commands = [WindowManipulation.key_release(key)]
{:ok, emulator_state, commands}
end
defp handle_focus_gain(emulator_state) do
# Handle focus gain
commands = [WindowManipulation.focus_gain()]
{:ok, emulator_state, commands}
end
defp handle_focus_loss(emulator_state) do
# Handle focus loss
commands = [WindowManipulation.focus_loss()]
{:ok, emulator_state, commands}
end
# === Additional Event Handlers ===
@doc """
Handles selection events.
"""
@spec handle_selection_event(any(), any()) ::
{:ok, any()} | {:error, String.t()}
def handle_selection_event(emulator_state, event) do
case event do
%{start_pos: start_pos, end_pos: end_pos, text: text} ->
# Update selection in the emulator state
updated_state =
update_selection_in_state(emulator_state, start_pos, end_pos, text)
{:ok, updated_state}
_ ->
{:error, "Invalid selection event: #{inspect(event)}"}
end
end
defp update_selection_in_state(emulator_state, start_pos, end_pos, _text) do
# Update the screen buffer with the new selection
updated_buffer =
Raxol.Terminal.Buffer.Selection.start(
emulator_state.screen_buffer,
elem(start_pos, 0),
elem(start_pos, 1)
)
updated_buffer =
Raxol.Terminal.Buffer.Selection.update(
updated_buffer,
elem(end_pos, 0),
elem(end_pos, 1)
)
%{emulator_state | screen_buffer: updated_buffer}
end
@doc """
Handles scroll events.
"""
@spec handle_scroll_event(any(), any()) :: {:ok, any()} | {:error, String.t()}
def handle_scroll_event(emulator_state, event) do
case event do
%{direction: direction, delta: delta, position: position} ->
# Update scroll position in the emulator state
updated_state =
update_scroll_in_state(emulator_state, direction, delta, position)
{:ok, updated_state}
_ ->
{:error, "Invalid scroll event: #{inspect(event)}"}
end
end
defp update_scroll_in_state(emulator_state, direction, delta, _position) do
# Apply scroll operation based on direction and delta
case direction do
:vertical ->
case delta > 0 do
true ->
# Scroll down
Raxol.Terminal.Commands.Screen.scroll_down(emulator_state, delta)
false ->
# Scroll up
Raxol.Terminal.Commands.Screen.scroll_up(emulator_state, abs(delta))
end
:horizontal ->
# Horizontal scrolling not yet implemented, return unchanged state
emulator_state
_ ->
emulator_state
end
end
@doc """
Handles paste events.
"""
@spec handle_paste_event(any(), any()) :: {:ok, any()} | {:error, String.t()}
def handle_paste_event(emulator_state, event) do
case event do
%{text: text} ->
# Update the screen buffer with the pasted text
updated_buffer =
Raxol.Terminal.Buffer.Paste.paste(emulator_state.screen_buffer, text)
{:ok, %{emulator_state | screen_buffer: updated_buffer}}
_ ->
{:error, "Invalid paste event: #{inspect(event)}"}
end
end
@doc """
Handles cursor events.
"""
@spec handle_cursor_event(any(), any()) :: {:ok, any()} | {:error, String.t()}
def handle_cursor_event(emulator_state, event) do
case event do
%{visible: visible, style: style, blink: blink, position: position} ->
# Update cursor properties in the emulator state
updated_state =
update_cursor_in_state(
emulator_state,
visible,
style,
blink,
position
)
{:ok, updated_state}
_ ->
{:error, "Invalid cursor event: #{inspect(event)}"}
end
end
defp update_cursor_in_state(emulator_state, visible, style, blink, position) do
cursor = emulator_state.cursor
cursor = Raxol.Terminal.Cursor.Manager.set_visibility(cursor, visible)
cursor = Raxol.Terminal.Cursor.Manager.set_style(cursor, style)
cursor = Raxol.Terminal.Cursor.Manager.set_blink(cursor, blink)
cursor = Raxol.Terminal.Cursor.Manager.set_position(cursor, position)
%{emulator_state | cursor: cursor}
end
@doc """
Handles clipboard events.
"""
@spec handle_clipboard_event(any(), any()) ::
{:ok, any()} | {:error, String.t()}
def handle_clipboard_event(emulator_state, event) do
case event do
%{op: op, content: content} ->
# Handle clipboard operations (copy, cut, etc.)
updated_state = handle_clipboard_operation(emulator_state, op, content)
{:ok, updated_state}
_ ->
{:error, "Invalid clipboard event: #{inspect(event)}"}
end
end
defp handle_clipboard_operation(emulator_state, :copy, _content) do
# Copy selected text to clipboard (implementation depends on system clipboard)
emulator_state
end
defp handle_clipboard_operation(emulator_state, :cut, _content) do
# Cut selected text to clipboard and remove from buffer
emulator_state
end
defp handle_clipboard_operation(emulator_state, _op, _content) do
# Unknown operation, return unchanged state
emulator_state
end
end