scenic_driver_renderling/lib/scenic_driver_rendering/window/window.ex
James Harton 9c1b4d917e
All checks were successful
continuous-integration/drone/push Build is passing
improvement: Wire up input events to the viewport.
2024-05-17 21:08:46 +12:00

191 lines
4.9 KiB
Elixir

defmodule Scenic.Driver.Renderling.Window do
@moduledoc """
The Scenic for windowed applications.
"""
alias Scenic.{ViewPort, ViewPort.Input}
alias Scenic.Driver.Renderling.Window.{Config, Nif}
use Scenic.Driver
require Logger
@position_schema [
centered: [type: :boolean, default: false],
full_screen: [type: :boolean, default: false],
maximized: [type: :boolean, default: false],
orientation: [type: {:in, [:normal, :left, :right, :upside_down]}, default: :normal],
scaled: [type: :boolean, default: false]
]
@window_schema [
title: [type: :string, default: "Scenic Window"],
resizeable: [type: :boolean, default: false]
]
@opts_schema [
name: [type: {:or, [:atom, :string]}],
position: [type: :keyword_list, keys: @position_schema, default: []],
window: [type: :keyword_list, keys: @window_schema, default: []],
cursor: [type: :boolean, default: false],
key_map: [type: :atom, default: Scenic.KeyMap.USEnglish],
on_close: [
type:
{:or, [:mfa, {:in, [:restart, :stop_driver, :stop_viewport, :stop_system, :halt_system]}]},
default: :restart
],
input_blacklist: [type: {:list, :string}, default: []]
]
@doc false
@impl Scenic.Driver
def validate_opts(opts), do: NimbleOptions.validate(opts, @opts_schema)
@doc false
@impl true
def init(driver, opts) do
{width, height} = driver.viewport.size
config =
opts
|> Keyword.update(
:window,
[width: width, height: height],
&Keyword.merge(&1, width: width, height: height)
)
|> Config.init()
driver = assign(driver, config: config, server: Nif.init(config))
Process.flag(:trap_exit, true)
Logger.info("#{inspect(__MODULE__)}: start #{inspect(opts)}, pid: #{inspect(self())}")
{:ok, driver}
end
@doc false
@impl true
def update_scene(ids, driver) do
Enum.reduce_while(ids, {:ok, driver}, fn id, {:ok, driver} ->
case ViewPort.get_script(driver.viewport, id) do
{:ok, script} ->
Nif.update_scene(script, id, driver.assigns.server)
{:cont, {:ok, driver}}
{:error, reason} ->
{:halt, {:error, reason}}
end
end)
end
@doc false
@impl true
def reset_scene(driver) do
Nif.reset_scene(driver.assigns.server)
{:ok, driver}
end
@doc false
@impl true
def del_scripts(ids, driver) do
Nif.del_scripts(ids, driver.assigns.server)
{:ok, driver}
end
@doc false
@impl true
def clear_color(color, driver) do
Nif.clear_color(color, driver.assigns.server)
{:ok, driver}
end
@doc false
@impl GenServer
def terminate(_, driver) do
Nif.terminate(driver.assigns.server)
end
@doc false
@impl GenServer
def handle_info({:shutdown, reason}, driver) do
Logger.debug(fn -> "Received shutdown from `scenic_window_server`: #{inspect(reason)}" end)
case driver.assigns.config.on_close do
:restart ->
{:stop, :normal, driver}
:stop_driver ->
ViewPort.stop_driver(driver.viewport, self())
{:stop, :normal, driver}
:stop_viewport ->
ViewPort.stop(driver.viewport)
{:stop, :normal, driver}
:stop_system ->
System.stop(0)
{:stop, :normal, driver}
:halt_system ->
System.halt(0)
{:stop, :normal, driver}
{module, _fun, 1} ->
module.fun(:shutdown)
{:stop, :normal, driver}
end
end
def handle_info({:codepoint, codepoint, mods}, driver) do
Input.send(driver.viewport, {:codepoint, {<<codepoint::utf8>>, mods}})
{:noreply, driver}
end
def handle_info({:key, key, :pressed, mods}, driver) do
Input.send(driver.viewport, {:key, {key, 1, mods}})
{:noreply, driver}
end
def handle_info({:key, key, :released, mods}, driver) do
Input.send(driver.viewport, {:key, {key, 0, mods}})
{:noreply, driver}
end
def handle_info({:cursor_button, btn, :pressed, mods, pos}, driver) do
Input.send(driver.viewport, {:cursor_button, {btn, 1, mods, pos}})
{:noreply, driver}
end
def handle_info({:cursor_button, btn, :released, mods, pos}, driver) do
Input.send(driver.viewport, {:cursor_button, {btn, 0, mods, pos}})
{:noreply, driver}
end
def handle_info({:cursor_scroll, offset, pos}, driver) do
Input.send(driver.viewport, {:cursor_scroll, offset, pos})
{:noreply, driver}
end
def handle_info({:cursor_pos, pos}, driver) do
Input.send(driver.viewport, {:cursor_pos, pos})
{:noreply, driver}
end
def handle_info({:viewport, event, pos}, driver) when event in [:enter, :exit, :reshape] do
Input.send(driver.viewport, {:viewport, {event, pos}})
{:noreply, driver}
end
def handle_info({:relative, pos}, driver) do
Input.send(driver.viewport, {:relative, pos})
{:noreply, driver}
end
def handle_info(msg, driver) do
Logger.debug(fn ->
"Unhandled message #{inspect(msg)}"
end)
{:noreply, driver}
end
end