import gleam/erlang/process.{type Name, type Subject} import gleam/otp/actor import gleam/result import gleam/string import input/key.{type Key} import mpv/control as mpv_control import ui/control as ui_control pub type State(ui_mpv, mpv_ui, ui_input_keys, ui_input_stream, exit) { State( ui_mpv: Subject(mpv_control.Control), mpv_ui: Subject(ui_control.Control), ui_input_keys: Subject(Key), ui_input_stream: Subject(List(String)), exit: Subject(Nil), ) } pub fn new( ui_mpv_name: Name(mpv_control.Control), mpv_ui_name: Name(ui_control.Control), ui_input_keys_name: Name(Key), ui_input_stream_name: Name(List(String)), exit: Subject(Nil), ) -> Result(Nil, String) { let ui_mpv: Subject(mpv_control.Control) = process.named_subject(ui_mpv_name) let mpv_ui: Subject(ui_control.Control) = process.named_subject(mpv_ui_name) let ui_input_keys: Subject(Key) = process.named_subject(ui_input_keys_name) let ui_input_stream: Subject(List(String)) = process.named_subject(ui_input_stream_name) case actor.new(State(ui_mpv, mpv_ui, ui_input_keys, ui_input_stream, exit)) |> actor.on_message(handle_message) |> actor.start { Error(start_error) -> Error("Could not start ui: " <> string.inspect(start_error)) Ok(actor.Started(data: ui, ..)) -> { echo "ui started" process.spawn(fn() { let assert Ok(_) = process.register(process.self(), ui_input_keys_name) handle_key(ui, ui_input_keys) }) process.spawn(fn() { let assert Ok(_) = process.register(process.self(), mpv_ui_name) handle_mpv_control(ui, mpv_ui) }) process.spawn(fn() { let assert Ok(_) = process.register(process.self(), ui_input_stream_name) temp_input_stream(ui_input_stream) }) Ok(Nil) } } } fn handle_message( state: State(ui_mpv, mpv_ui, ui_input_keys, ui_input_stream, exit), control: ui_control.Control, ) -> actor.Next( State(ui_mpv, mpv_ui, ui_input_keys, ui_input_stream, exit), ui_control.Control, ) { case control { ui_control.Ack -> { echo "ack! use this to re-render?" actor.continue(state) } ui_control.TogglePlayPause -> { process.send(state.mpv_ui, ui_control.TogglePlayPause) case process.receive(state.mpv_ui, 5000) { Error(_) -> echo "mpv not responding: could not toggle pause :(" Ok(_) -> echo "toggled pause" } actor.continue(state) } ui_control.Exit -> { // TODO call `mpv` to exit, wait for response process.send(state.exit, Nil) actor.stop() } } } /// `handle_key` listens to a subject onto which `input` will send messages with /// parsed `Key`s which will be mapped to `Control`s (if possible) fn handle_key( ui: Subject(ui_control.Control), ui_input_keys: Subject(Key), ) -> Nil { let _ = process.receive_forever(ui_input_keys) |> ui_control.from_key |> result.map(process.send(ui, _)) handle_key(ui, ui_input_keys) } fn handle_mpv_control( ui: Subject(ui_control.Control), mpv_ui: Subject(ui_control.Control), ) { let control = process.receive_forever(mpv_ui) process.send(ui, control) handle_mpv_control(ui, mpv_ui) } fn temp_input_stream(ui_input_stream: Subject(List(String))) -> Nil { let stream = process.receive_forever(ui_input_stream) echo "input stream: " <> string.inspect(stream) temp_input_stream(ui_input_stream) }