wip: can send messages and scripts over the IPC link.
This commit is contained in:
parent
a898d370ac
commit
244dedfb9b
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -24,3 +24,5 @@ scenic_driver_renderling-*.tar
|
|||
|
||||
# Temporary files, for example, from tests.
|
||||
/tmp/
|
||||
|
||||
priv/__scenic
|
||||
|
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"cSpell.words": ["Renderling"]
|
||||
"cSpell.words": ["centered", "Renderling"]
|
||||
}
|
||||
|
|
15
config/config.exs
Normal file
15
config/config.exs
Normal file
|
@ -0,0 +1,15 @@
|
|||
import Config
|
||||
|
||||
if Mix.env() in [:dev, :test] do
|
||||
config :scenic_driver_renderling, :viewport,
|
||||
name: :main_viewport,
|
||||
size: {700, 600},
|
||||
default_scene: ExampleScene,
|
||||
drivers: [
|
||||
module: Scenic.Driver.Renderling.Window,
|
||||
name: :local,
|
||||
window: [resizeable: false, title: "Example Application"]
|
||||
]
|
||||
|
||||
config :scenic, :assets, module: ExampleAssets
|
||||
end
|
|
@ -1,11 +1,74 @@
|
|||
defmodule Scenic.Driver.Renderling.Window.Config do
|
||||
defstruct [:title, :resizeable, :width, :height, :server_path]
|
||||
defstruct name: nil,
|
||||
limit_ms: nil,
|
||||
layer: nil,
|
||||
opacity: nil,
|
||||
debug: false,
|
||||
debugger: "",
|
||||
debug_fps: 0,
|
||||
antialias: true,
|
||||
position: nil,
|
||||
window: nil,
|
||||
cursor: false,
|
||||
key_map: Scenic.KeyMap.USEnglish,
|
||||
on_close: :restart,
|
||||
input_blacklist: [],
|
||||
server_path: nil
|
||||
|
||||
defmodule Position do
|
||||
defstruct scaled: false, centered: false, orientation: :normal
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
scaled: boolean,
|
||||
centered: boolean,
|
||||
orientation: :normal | :left | :right | :upside_down
|
||||
}
|
||||
|
||||
def init(opts), do: struct(__MODULE__, opts)
|
||||
end
|
||||
|
||||
defmodule Window do
|
||||
defstruct title: "Scenic Window", resizeable: false, width: nil, height: nil
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
title: String.t(),
|
||||
resizeable: false,
|
||||
width: nil | non_neg_integer(),
|
||||
height: nil | non_neg_integer()
|
||||
}
|
||||
def init(opts), do: struct(__MODULE__, opts)
|
||||
end
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
title: String.t(),
|
||||
resizeable: boolean,
|
||||
width: non_neg_integer(),
|
||||
height: non_neg_integer(),
|
||||
server_path: String.t()
|
||||
name: atom | String.t(),
|
||||
limit_ms: non_neg_integer(),
|
||||
layer: integer,
|
||||
opacity: integer,
|
||||
debug: boolean,
|
||||
debugger: String.t(),
|
||||
debug_fps: integer,
|
||||
antialias: boolean,
|
||||
position: Position.t(),
|
||||
window: Window.t(),
|
||||
cursor: boolean,
|
||||
key_map: module,
|
||||
on_close: :restart | :stop_driver | :stop_viewport | :stop_system | :halt_system,
|
||||
input_blacklist: [String.t()]
|
||||
}
|
||||
|
||||
def init(opts) do
|
||||
attrs =
|
||||
opts
|
||||
|> Keyword.update(:position, Position.init([]), &Position.init/1)
|
||||
|> Keyword.update(:window, Window.init([]), &Window.init/1)
|
||||
|> Keyword.put(:server_path, compute_server_path())
|
||||
|
||||
struct(__MODULE__, attrs)
|
||||
end
|
||||
|
||||
defp compute_server_path do
|
||||
:scenic_driver_renderling
|
||||
|> :code.priv_dir()
|
||||
|> Path.join("native/renderling_window_server")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
defmodule Scenic.Driver.Renderling.Window.Nif do
|
||||
use Rustler, otp_app: :scenic_driver_renderling, crate: :renderling_window
|
||||
|
||||
def init(_cfg), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def init(_config), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def update_scene(_script, _script_id, _resource), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def reset_scene(_resource), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def del_scripts(_script_ids, _resource), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def clear_color(_color, _resource), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def terminate(_resource), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def request_input(_input_classes, _resource), do: :erlang.nif_error(:nif_not_loaded)
|
||||
end
|
||||
|
|
117
lib/scenic_driver_rendering/window/window.ex
Normal file
117
lib/scenic_driver_rendering/window/window.ex
Normal file
|
@ -0,0 +1,117 @@
|
|||
defmodule Scenic.Driver.Renderling.Window do
|
||||
@moduledoc """
|
||||
The Scenic for windowed applications.
|
||||
"""
|
||||
|
||||
alias Scenic.ViewPort
|
||||
alias Scenic.Driver.Renderling.Window.{Config, Nif}
|
||||
|
||||
use Scenic.Driver
|
||||
require Logger
|
||||
|
||||
@default_limit 29
|
||||
@default_layer 0
|
||||
@default_opacity 255
|
||||
|
||||
@position_schema [
|
||||
scaled: [type: :boolean, default: false],
|
||||
centered: [type: :boolean, default: false],
|
||||
orientation: [type: {:in, [:normal, :left, :right, :upside_down]}, default: :normal]
|
||||
]
|
||||
|
||||
@window_schema [
|
||||
title: [type: :string, default: "Scenic Window"],
|
||||
resizeable: [type: :boolean, default: false]
|
||||
]
|
||||
|
||||
@opts_schema [
|
||||
name: [type: {:or, [:atom, :string]}],
|
||||
limit_ms: [type: :non_neg_integer, default: @default_limit],
|
||||
layer: [type: :integer, default: @default_layer],
|
||||
opacity: [type: :integer, default: @default_opacity],
|
||||
debug: [type: :boolean, default: false],
|
||||
debugger: [type: :string, default: ""],
|
||||
debug_fps: [type: :integer, default: 0],
|
||||
antialias: [type: :boolean, default: true],
|
||||
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
|
||||
end
|
10
mix.exs
10
mix.exs
|
@ -6,6 +6,7 @@ defmodule Scenic.Driver.Renderling.MixProject do
|
|||
app: :scenic_driver_renderling,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.16",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
|
@ -21,9 +22,12 @@ defmodule Scenic.Driver.Renderling.MixProject do
|
|||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:rustler, "~> 0.32", runtime: false}
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
{:rustler, "~> 0.32", runtime: false},
|
||||
{:nimble_options, ">= 0.0.0"},
|
||||
{:scenic, "~> 0.11"}
|
||||
]
|
||||
end
|
||||
|
||||
defp elixirc_paths(env) when env in [:dev, :test], do: ["lib", "test/support"]
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
end
|
||||
|
|
6
mix.lock
6
mix.lock
|
@ -1,5 +1,11 @@
|
|||
%{
|
||||
"elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"},
|
||||
"ex_image_info": {:hex, :ex_image_info, "0.2.4", "610002acba43520a9b1cf1421d55812bde5b8a8aeaf1fe7b1f8823e84e762adb", [:mix], [], "hexpm", "fd1a7e02664e3b14dfd3b231d22fdd48bd3dd694c4773e6272b3a6228f1106bc"},
|
||||
"font_metrics": {:hex, :font_metrics, "0.5.1", "10ce0b8b1bf092a2d3d307e05a7c433787ae8ca7cd1f3cf959995a628809a994", [:mix], [{:nimble_options, "~> 0.3", [hex: :nimble_options, repo: "hexpm", optional: false]}], "hexpm", "192e4288772839ae4dadccb0f5b1d5c89b73a0c3961ccea14b6181fdcd535e54"},
|
||||
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
||||
"nimble_options": {:hex, :nimble_options, "0.5.2", "42703307b924880f8c08d97719da7472673391905f528259915782bb346e0a1b", [:mix], [], "hexpm", "4da7f904b915fd71db549bcdc25f8d56f378ef7ae07dc1d372cbe72ba950dce0"},
|
||||
"rustler": {:hex, :rustler, "0.32.1", "f4cf5a39f9e85d182c0a3f75fa15b5d0add6542ab0bf9ceac6b4023109ebd3fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "b96be75526784f86f6587f051bc8d6f4eaff23d6e0f88dbcfe4d5871f52946f7"},
|
||||
"scenic": {:hex, :scenic, "0.11.2", "5d29aac74ee85899651716af47f72d167d064939fc2b4e514cd7c43810293cda", [:make, :mix], [{:elixir_make, "~> 0.7.7", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:ex_image_info, "~> 0.2.4", [hex: :ex_image_info, repo: "hexpm", optional: false]}, {:font_metrics, "~> 0.5.0", [hex: :font_metrics, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.4 or ~> 0.4.0 or ~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:truetype_metrics, "~> 0.6", [hex: :truetype_metrics, repo: "hexpm", optional: false]}], "hexpm", "6f846cfe8457163d28ad6b8b147d251bd15524c249ddad3e75608cbdb739beaa"},
|
||||
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
||||
"truetype_metrics": {:hex, :truetype_metrics, "0.6.1", "9119a04dc269dd8f63e85e12e4098f711cb7c5204a420f4896f40667b9e064f6", [:mix], [{:font_metrics, "~> 0.5", [hex: :font_metrics, repo: "hexpm", optional: false]}], "hexpm", "5711d4a3e4fc92eb073326fbe54208925d35168dc9b288c331ee666a8a84759b"},
|
||||
}
|
||||
|
|
13
native/renderling_window/.vscode/settings.json
vendored
Normal file
13
native/renderling_window/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"Bezier",
|
||||
"centered",
|
||||
"cmds",
|
||||
"Codepoint",
|
||||
"Miter",
|
||||
"Rect",
|
||||
"Renderling",
|
||||
"Rrect",
|
||||
"Stdio"
|
||||
]
|
||||
}
|
1
native/renderling_window/Cargo.lock
generated
1
native/renderling_window/Cargo.lock
generated
|
@ -2044,6 +2044,7 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"rustler_codegen",
|
||||
"rustler_sys",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -14,11 +14,11 @@ anyhow = "1"
|
|||
clap = {version = "4", features = ["derive"]}
|
||||
ipc-channel = "0.18.0"
|
||||
renderling = "0.4.1"
|
||||
rustler = "0.32.1"
|
||||
rustler = {version = "0.32.1", features = ["serde", "nif_version_2_16"]}
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
wgpu = "0.20.0"
|
||||
winit = "0.30.0"
|
||||
|
||||
[[bin]]
|
||||
name = "server"
|
||||
name = "renderling_window_server"
|
||||
path = "src/server.rs"
|
||||
|
|
407
native/renderling_window/src/elixir_types.rs
Normal file
407
native/renderling_window/src/elixir_types.rs
Normal file
|
@ -0,0 +1,407 @@
|
|||
use rustler::{NifStruct, NifTaggedEnum, NifTuple, NifUnitEnum, NifUntaggedEnum, Term};
|
||||
|
||||
/// Structures received from BEAM.
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum WindowOrientation {
|
||||
Normal,
|
||||
Left,
|
||||
Right,
|
||||
UpsideDown,
|
||||
}
|
||||
|
||||
#[derive(NifStruct, Debug, Clone)]
|
||||
#[module = "Scenic.Driver.Renderling.Window.Config.Position"]
|
||||
pub struct WindowPosition {
|
||||
pub scaled: bool,
|
||||
pub centered: bool,
|
||||
pub orientation: WindowOrientation,
|
||||
}
|
||||
|
||||
#[derive(NifStruct, Debug, Clone)]
|
||||
#[module = "Scenic.Driver.Renderling.Window.Config.Window"]
|
||||
pub struct WindowConfig {
|
||||
pub title: String,
|
||||
pub resizeable: bool,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum OnCloseAction {
|
||||
Restart,
|
||||
StopDriver,
|
||||
StopViewport,
|
||||
StopSystem,
|
||||
HaltSystem,
|
||||
}
|
||||
|
||||
#[derive(NifStruct, Debug, Clone)]
|
||||
#[module = "Scenic.Driver.Renderling.Window.Config"]
|
||||
pub struct DriverConfig {
|
||||
pub limit_ms: u32,
|
||||
pub layer: u32,
|
||||
pub opacity: u32,
|
||||
pub debug: bool,
|
||||
pub debugger: String,
|
||||
pub debug_fps: u32,
|
||||
pub antialias: bool,
|
||||
pub position: WindowPosition,
|
||||
pub window: WindowConfig,
|
||||
pub cursor: bool,
|
||||
pub on_close: OnCloseAction,
|
||||
pub input_blacklist: Vec<String>,
|
||||
pub server_path: String,
|
||||
}
|
||||
|
||||
#[derive(NifTaggedEnum, Debug, Clone)]
|
||||
pub enum Color {
|
||||
ColorG(u8),
|
||||
ColorGa((u8, u8)),
|
||||
ColorRgb((u8, u8, u8)),
|
||||
ColorRgba((u8, u8, u8, u8)),
|
||||
ColorHsv((f32, f32, f32)),
|
||||
ColorHsl((f32, f32, f32)),
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Pos(pub u32, pub u32);
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum TextAlign {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum TextBase {
|
||||
Top,
|
||||
Middle,
|
||||
Alphabetic,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum FillStroke {
|
||||
Fill,
|
||||
Stroke,
|
||||
FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawLine {
|
||||
pub x0: u32,
|
||||
pub y0: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawQuad {
|
||||
pub x0: u32,
|
||||
pub y0: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub x2: u32,
|
||||
pub y2: u32,
|
||||
pub x3: u32,
|
||||
pub y3: u32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Quad {
|
||||
pub x0: u32,
|
||||
pub y0: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub x2: u32,
|
||||
pub y2: u32,
|
||||
pub x3: u32,
|
||||
pub y3: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawRect {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Rect {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawRrect {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub radius: u32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Rrect {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub radius: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawSector {
|
||||
pub radius: u32,
|
||||
pub radians: f32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Sector {
|
||||
pub radius: u32,
|
||||
pub radians: f32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawArc {
|
||||
pub radius: u32,
|
||||
pub radians: f32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawCircle {
|
||||
pub radius: u32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Circle {
|
||||
pub radius: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawEllipse {
|
||||
pub radius0: u32,
|
||||
pub radius1: u32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Ellipse {
|
||||
pub radius0: u32,
|
||||
pub radius1: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct SpriteDrawCmd {
|
||||
pub src_xy: Pos,
|
||||
pub src_wh: Pos,
|
||||
pub dst_xy: Pos,
|
||||
pub dst_wh: Pos,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawSprite {
|
||||
pub src_id: String,
|
||||
pub cmds: Vec<SpriteDrawCmd>,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawTriangle {
|
||||
pub x0: u32,
|
||||
pub y0: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub x2: u32,
|
||||
pub y2: u32,
|
||||
pub fill_stroke: FillStroke,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Triangle {
|
||||
pub x0: u32,
|
||||
pub y0: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub x2: u32,
|
||||
pub y2: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct ArcTo {
|
||||
pub x0: u32,
|
||||
pub y0: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub radius: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct BezierTo {
|
||||
pub cp1x: u32,
|
||||
pub cp1y: u32,
|
||||
pub cp2x: u32,
|
||||
pub cp2y: u32,
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct QuadraticTo {
|
||||
pub cpx: u32,
|
||||
pub cpy: u32,
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Scale {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Transform {
|
||||
pub a: f32,
|
||||
pub b: f32,
|
||||
pub c: f32,
|
||||
pub d: f32,
|
||||
pub e: f32,
|
||||
pub f: f32,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct FillLinear {
|
||||
pub start_x: u32,
|
||||
pub start_y: u32,
|
||||
pub end_x: u32,
|
||||
pub end_y: u32,
|
||||
pub color_start: Color,
|
||||
pub color_end: Color,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct FillRadial {
|
||||
pub center_x: u32,
|
||||
pub center_y: u32,
|
||||
pub inner_radius: u32,
|
||||
pub outer_radius: u32,
|
||||
pub color_start: Color,
|
||||
pub color_end: Color,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
|
||||
pub struct StrokeLinear {
|
||||
pub start_x: u32,
|
||||
pub start_u: u32,
|
||||
pub end_x: u32,
|
||||
pub end_y: u32,
|
||||
pub color_start: Color,
|
||||
pub color_end: Color,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct StrokeRadial {
|
||||
pub center_x: u32,
|
||||
pub center_y: u32,
|
||||
pub inner_radius: u32,
|
||||
pub outer_radius: u32,
|
||||
pub color_start: Color,
|
||||
pub color_end: Color,
|
||||
}
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum Cap {
|
||||
Butt,
|
||||
Round,
|
||||
Square,
|
||||
}
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum Join {
|
||||
Bevel,
|
||||
Round,
|
||||
Miter,
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Scissor {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(NifTaggedEnum, Debug, Clone)]
|
||||
pub enum ScriptItem {
|
||||
PushState,
|
||||
PopState,
|
||||
PushPopState,
|
||||
Clear(Color),
|
||||
DrawLine(DrawLine),
|
||||
DrawQuad(DrawQuad),
|
||||
DrawRect(DrawRect),
|
||||
DrawRrect(DrawRrect),
|
||||
DrawSector(DrawSector),
|
||||
DrawArc(DrawArc),
|
||||
DrawCircle(DrawCircle),
|
||||
DrawEllipse(DrawEllipse),
|
||||
DrawSprites(DrawSprite),
|
||||
DrawText(String),
|
||||
DrawTriangle(DrawTriangle),
|
||||
Script(String),
|
||||
BeginPath,
|
||||
ClosePath,
|
||||
FillPath,
|
||||
StrokePath,
|
||||
MoveTo(Pos),
|
||||
LineTo(Pos),
|
||||
ArcTo(ArcTo),
|
||||
BezierTo(BezierTo),
|
||||
QuadraticTo(QuadraticTo),
|
||||
Quad(Quad),
|
||||
Rect(Rect),
|
||||
Rrect(Rrect),
|
||||
Sector(Sector),
|
||||
Circle(Circle),
|
||||
Ellipse(Ellipse),
|
||||
Triangle(Triangle),
|
||||
Scale(Scale),
|
||||
Rotate(f32),
|
||||
Translate(Pos),
|
||||
Transform(Transform),
|
||||
FillColor(Color),
|
||||
FillLinear(FillLinear),
|
||||
FillRadial(FillRadial),
|
||||
FillImage(String),
|
||||
FillStream(String),
|
||||
StrokeColor(Color),
|
||||
StrokeLinear(StrokeLinear),
|
||||
StrokeRadial(StrokeRadial),
|
||||
StrokeImage(String),
|
||||
StrokeStream(String),
|
||||
StrokeWidth(u32),
|
||||
Cap(Cap),
|
||||
Join(Join),
|
||||
MiterLimit(u32),
|
||||
Scissor(Scissor),
|
||||
Font(String),
|
||||
FontSize(u32),
|
||||
TextAlign(TextAlign),
|
||||
TextBase(TextBase),
|
||||
}
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum InputClass {
|
||||
CursorButton,
|
||||
CursorScroll,
|
||||
CursorPos,
|
||||
Codepoint,
|
||||
Key,
|
||||
Viewport,
|
||||
Relative,
|
||||
Led,
|
||||
Switch,
|
||||
}
|
|
@ -1,63 +1,36 @@
|
|||
mod elixir_types;
|
||||
mod message;
|
||||
|
||||
use crate::message::{IpcExchange, Message};
|
||||
use crate::elixir_types::{Color, DriverConfig, InputClass, ScriptItem};
|
||||
use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender};
|
||||
use rustler::types::OwnedBinary;
|
||||
use rustler::{Atom, Env, Error, NifStruct, ResourceArc, Term};
|
||||
use std::env;
|
||||
use std::io::stdout;
|
||||
use rustler::{Env, NifResult, ResourceArc, Term};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(NifStruct)]
|
||||
#[module = "Scenic.Driver.Renderling.Window.Config"]
|
||||
pub struct WindowConfig {
|
||||
pub title: String,
|
||||
pub resizeable: bool,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub server_path: String,
|
||||
}
|
||||
use std::thread::sleep;
|
||||
|
||||
struct IpcResourceInner {
|
||||
ipc_channel_name: String,
|
||||
sender: IpcSender<Message>,
|
||||
receiver: IpcReceiver<Message>,
|
||||
sender: IpcSender<message::Message>,
|
||||
receiver: IpcReceiver<message::Message>,
|
||||
handle: Child,
|
||||
}
|
||||
|
||||
impl Drop for IpcResource {
|
||||
fn drop(&mut self) {
|
||||
println!("Shutting down server.");
|
||||
let mut mutex = self.0.lock().unwrap();
|
||||
// mutex.sender.send(Message::Shutdown).unwrap();
|
||||
mutex.handle.try_wait().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct IpcResource(Mutex<IpcResourceInner>);
|
||||
|
||||
#[rustler::nif]
|
||||
fn init(cfg: WindowConfig) -> ResourceArc<IpcResource> {
|
||||
let (ipc, ipc_channel_name) = IpcOneShotServer::<IpcExchange>::new().unwrap();
|
||||
fn init(cfg: DriverConfig) -> ResourceArc<IpcResource> {
|
||||
let (ipc, ipc_channel_name) = IpcOneShotServer::<message::IpcExchange>::new().unwrap();
|
||||
|
||||
let handle = Command::new(cfg.server_path)
|
||||
let handle = Command::new(cfg.server_path.clone())
|
||||
.arg("--ipc-channel")
|
||||
.arg(&ipc_channel_name)
|
||||
.stdout(Stdio::inherit())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let (_, IpcExchange { sender, receiver }) = ipc.accept().unwrap();
|
||||
let (_, message::IpcExchange { sender, receiver }) = ipc.accept().unwrap();
|
||||
|
||||
sender
|
||||
.send(Message::Init {
|
||||
title: cfg.title,
|
||||
resizeable: cfg.resizeable,
|
||||
width: cfg.width,
|
||||
height: cfg.height,
|
||||
})
|
||||
.unwrap();
|
||||
sender.send(cfg.into()).unwrap();
|
||||
|
||||
ResourceArc::new(IpcResource(Mutex::new(IpcResourceInner {
|
||||
ipc_channel_name,
|
||||
|
@ -67,6 +40,94 @@ fn init(cfg: WindowConfig) -> ResourceArc<IpcResource> {
|
|||
})))
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn update_scene(script: Term, script_id: String, resource: ResourceArc<IpcResource>) {
|
||||
let script = script
|
||||
.into_list_iterator()
|
||||
.unwrap()
|
||||
.map(|term| {
|
||||
term.decode::<ScriptItem>()
|
||||
.map(|script_item| script_item.into())
|
||||
})
|
||||
.collect::<NifResult<Vec<message::ScriptItem>>>()
|
||||
.unwrap();
|
||||
|
||||
let mutex = resource.0.lock().unwrap();
|
||||
mutex
|
||||
.sender
|
||||
.send(message::Message::UpdateScene(script_id, script))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn clear_color(color: Color, resource: ResourceArc<IpcResource>) {
|
||||
let mutex = resource.0.lock().unwrap();
|
||||
mutex
|
||||
.sender
|
||||
.send(message::Message::ClearColor(color.into()))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn reset_scene(resource: ResourceArc<IpcResource>) {
|
||||
let mutex = resource.0.lock().unwrap();
|
||||
mutex.sender.send(message::Message::ResetScene).unwrap();
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn del_scripts(ids: Term, resource: ResourceArc<IpcResource>) {
|
||||
let ids = ids
|
||||
.into_list_iterator()
|
||||
.unwrap()
|
||||
.map(|x| x.decode::<String>())
|
||||
.collect::<NifResult<Vec<String>>>()
|
||||
.unwrap();
|
||||
|
||||
let mutex = resource.0.lock().unwrap();
|
||||
mutex
|
||||
.sender
|
||||
.send(message::Message::DeleteScripts(ids))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn request_input(classes: Term, resource: ResourceArc<IpcResource>) {
|
||||
let classes = classes
|
||||
.into_list_iterator()
|
||||
.unwrap()
|
||||
.map(|term| {
|
||||
term.decode::<InputClass>()
|
||||
.map(|input_class| input_class.into())
|
||||
})
|
||||
.collect::<NifResult<Vec<message::InputClass>>>()
|
||||
.unwrap();
|
||||
|
||||
let mutex = resource.0.lock().unwrap();
|
||||
mutex
|
||||
.sender
|
||||
.send(message::Message::RequestInput(classes))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn terminate(resource: ResourceArc<IpcResource>) {
|
||||
println!("Shutting down server.");
|
||||
let mut mutex = resource.0.lock().unwrap();
|
||||
if let None = mutex.handle.try_wait().unwrap() {
|
||||
mutex.sender.send(message::Message::Shutdown).unwrap();
|
||||
drop(mutex);
|
||||
std::thread::spawn(move || {
|
||||
// We give the server 100 millis to shut down and then we just kill
|
||||
// it.
|
||||
sleep(std::time::Duration::from_micros(100));
|
||||
let mut mutex = resource.0.lock().unwrap();
|
||||
if let None = mutex.handle.try_wait().unwrap() {
|
||||
mutex.handle.kill().unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn load(env: Env, _: Term) -> bool {
|
||||
rustler::resource!(IpcResource, env);
|
||||
true
|
||||
|
@ -74,6 +135,13 @@ fn load(env: Env, _: Term) -> bool {
|
|||
|
||||
rustler::init!(
|
||||
"Elixir.Scenic.Driver.Renderling.Window.Nif",
|
||||
[init],
|
||||
[
|
||||
init,
|
||||
update_scene,
|
||||
reset_scene,
|
||||
del_scripts,
|
||||
clear_color,
|
||||
terminate
|
||||
],
|
||||
load = load
|
||||
);
|
||||
|
|
|
@ -1,15 +1,655 @@
|
|||
use crate::elixir_types;
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Orientation {
|
||||
Normal,
|
||||
Left,
|
||||
Right,
|
||||
UpsideDown,
|
||||
}
|
||||
|
||||
impl From<elixir_types::WindowOrientation> for Orientation {
|
||||
fn from(orientation: elixir_types::WindowOrientation) -> Self {
|
||||
match orientation {
|
||||
elixir_types::WindowOrientation::Normal => Orientation::Normal,
|
||||
elixir_types::WindowOrientation::Left => Orientation::Left,
|
||||
elixir_types::WindowOrientation::Right => Orientation::Right,
|
||||
elixir_types::WindowOrientation::UpsideDown => Orientation::UpsideDown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum OnClose {
|
||||
Restart,
|
||||
StopDriver,
|
||||
StopViewport,
|
||||
StopSystem,
|
||||
HaltSystem,
|
||||
}
|
||||
|
||||
impl From<elixir_types::OnCloseAction> for OnClose {
|
||||
fn from(on_close: elixir_types::OnCloseAction) -> Self {
|
||||
match on_close {
|
||||
elixir_types::OnCloseAction::Restart => OnClose::Restart,
|
||||
elixir_types::OnCloseAction::StopDriver => OnClose::StopDriver,
|
||||
elixir_types::OnCloseAction::StopViewport => OnClose::StopViewport,
|
||||
elixir_types::OnCloseAction::StopSystem => OnClose::StopSystem,
|
||||
elixir_types::OnCloseAction::HaltSystem => OnClose::HaltSystem,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Color {
|
||||
Gray(u8),
|
||||
GrayAlpha(u8, u8),
|
||||
Rgb(u8, u8, u8),
|
||||
RgbA(u8, u8, u8, u8),
|
||||
Hsv(f32, f32, f32),
|
||||
Hsl(f32, f32, f32),
|
||||
}
|
||||
|
||||
impl From<elixir_types::Color> for Color {
|
||||
fn from(color: elixir_types::Color) -> Self {
|
||||
match color {
|
||||
elixir_types::Color::ColorG(g) => Color::Gray(g),
|
||||
elixir_types::Color::ColorGa((g, a)) => Color::GrayAlpha(g, a),
|
||||
elixir_types::Color::ColorRgb((r, g, b)) => Color::Rgb(r, g, b),
|
||||
elixir_types::Color::ColorRgba((r, g, b, a)) => Color::RgbA(r, g, b, a),
|
||||
elixir_types::Color::ColorHsv((h, s, v)) => Color::Hsv(h, s, v),
|
||||
elixir_types::Color::ColorHsl((h, s, l)) => Color::Hsl(h, s, l),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum InputClass {
|
||||
CursorButton,
|
||||
CursorScroll,
|
||||
CursorPos,
|
||||
Codepoint,
|
||||
Key,
|
||||
Viewport,
|
||||
Relative,
|
||||
Led,
|
||||
Switch,
|
||||
}
|
||||
|
||||
impl From<elixir_types::InputClass> for InputClass {
|
||||
fn from(input_class: elixir_types::InputClass) -> Self {
|
||||
match input_class {
|
||||
elixir_types::InputClass::CursorButton => InputClass::CursorButton,
|
||||
elixir_types::InputClass::CursorScroll => InputClass::CursorScroll,
|
||||
elixir_types::InputClass::CursorPos => InputClass::CursorPos,
|
||||
elixir_types::InputClass::Codepoint => InputClass::Codepoint,
|
||||
elixir_types::InputClass::Key => InputClass::Key,
|
||||
elixir_types::InputClass::Viewport => InputClass::Viewport,
|
||||
elixir_types::InputClass::Relative => InputClass::Relative,
|
||||
elixir_types::InputClass::Led => InputClass::Led,
|
||||
elixir_types::InputClass::Switch => InputClass::Switch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum FillStroke {
|
||||
Fill,
|
||||
Stroke,
|
||||
FillStroke,
|
||||
}
|
||||
|
||||
impl From<elixir_types::FillStroke> for FillStroke {
|
||||
fn from(fill_stroke: elixir_types::FillStroke) -> Self {
|
||||
match fill_stroke {
|
||||
elixir_types::FillStroke::Fill => FillStroke::Fill,
|
||||
elixir_types::FillStroke::FillStroke => FillStroke::FillStroke,
|
||||
elixir_types::FillStroke::Stroke => FillStroke::Stroke,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SpriteDrawCmd {
|
||||
src_x: u32,
|
||||
src_y: u32,
|
||||
src_w: u32,
|
||||
src_h: u32,
|
||||
dst_x: u32,
|
||||
dst_y: u32,
|
||||
dst_w: u32,
|
||||
dst_h: u32,
|
||||
}
|
||||
|
||||
impl From<elixir_types::SpriteDrawCmd> for SpriteDrawCmd {
|
||||
fn from(cmd: elixir_types::SpriteDrawCmd) -> Self {
|
||||
SpriteDrawCmd {
|
||||
src_x: cmd.src_xy.0,
|
||||
src_y: cmd.src_xy.1,
|
||||
src_w: cmd.src_wh.0,
|
||||
src_h: cmd.src_wh.1,
|
||||
dst_x: cmd.dst_xy.0,
|
||||
dst_y: cmd.dst_xy.1,
|
||||
dst_w: cmd.dst_wh.0,
|
||||
dst_h: cmd.dst_wh.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Cap {
|
||||
Butt,
|
||||
Round,
|
||||
Square,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Join {
|
||||
Bevel,
|
||||
Round,
|
||||
Miter,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum TextAlign {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum TextBase {
|
||||
Top,
|
||||
Middle,
|
||||
Alphabetic,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum ScriptItem {
|
||||
PushState,
|
||||
PopState,
|
||||
PushPopState,
|
||||
Clear(Color),
|
||||
DrawLine {
|
||||
x0: u32,
|
||||
y0: u32,
|
||||
x1: u32,
|
||||
y1: u32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawQuad {
|
||||
x0: u32,
|
||||
y0: u32,
|
||||
x1: u32,
|
||||
y1: u32,
|
||||
x2: u32,
|
||||
y2: u32,
|
||||
x3: u32,
|
||||
y3: u32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawRect {
|
||||
width: u32,
|
||||
height: u32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawRRect {
|
||||
width: u32,
|
||||
height: u32,
|
||||
radius: u32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawSector {
|
||||
radius: u32,
|
||||
radians: f32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawArc {
|
||||
radius: u32,
|
||||
radians: f32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawCircle {
|
||||
radius: u32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawEllipse {
|
||||
radius0: u32,
|
||||
radius1: u32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
DrawSprite {
|
||||
src_id: String,
|
||||
cmds: Vec<SpriteDrawCmd>,
|
||||
},
|
||||
DrawText(String),
|
||||
DrawTriangle {
|
||||
x0: u32,
|
||||
y0: u32,
|
||||
x1: u32,
|
||||
y1: u32,
|
||||
x2: u32,
|
||||
y2: u32,
|
||||
fill_stroke: FillStroke,
|
||||
},
|
||||
Script(String),
|
||||
BeginPath,
|
||||
ClosePath,
|
||||
FillPath,
|
||||
StrokePath,
|
||||
MoveTo {
|
||||
x: u32,
|
||||
y: u32,
|
||||
},
|
||||
LineTo {
|
||||
x: u32,
|
||||
y: u32,
|
||||
},
|
||||
ArcTo {
|
||||
x0: u32,
|
||||
y0: u32,
|
||||
x1: u32,
|
||||
y1: u32,
|
||||
radius: u32,
|
||||
},
|
||||
BezierTo {
|
||||
cp1x: u32,
|
||||
cp1y: u32,
|
||||
cp2x: u32,
|
||||
cp2y: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
},
|
||||
QuadraticTo {
|
||||
cpx: u32,
|
||||
cpy: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
},
|
||||
Quad {
|
||||
x0: u32,
|
||||
y0: u32,
|
||||
x1: u32,
|
||||
y1: u32,
|
||||
x2: u32,
|
||||
y2: u32,
|
||||
x3: u32,
|
||||
y3: u32,
|
||||
},
|
||||
Rect {
|
||||
width: u32,
|
||||
height: u32,
|
||||
},
|
||||
RRect {
|
||||
width: u32,
|
||||
height: u32,
|
||||
radius: u32,
|
||||
},
|
||||
Sector {
|
||||
radius: u32,
|
||||
radians: f32,
|
||||
},
|
||||
Circle {
|
||||
radius: u32,
|
||||
},
|
||||
Ellipse {
|
||||
radius0: u32,
|
||||
radius1: u32,
|
||||
},
|
||||
Triangle {
|
||||
x0: u32,
|
||||
y0: u32,
|
||||
x1: u32,
|
||||
y1: u32,
|
||||
x2: u32,
|
||||
y2: u32,
|
||||
},
|
||||
Scale {
|
||||
x: f32,
|
||||
y: f32,
|
||||
},
|
||||
Rotate {
|
||||
radians: f32,
|
||||
},
|
||||
Translate {
|
||||
x: u32,
|
||||
y: u32,
|
||||
},
|
||||
Transform {
|
||||
a: f32,
|
||||
b: f32,
|
||||
c: f32,
|
||||
d: f32,
|
||||
e: f32,
|
||||
f: f32,
|
||||
},
|
||||
FillColor(Color),
|
||||
FillLinear {
|
||||
start_x: u32,
|
||||
start_y: u32,
|
||||
end_x: u32,
|
||||
end_y: u32,
|
||||
color_start: Color,
|
||||
color_end: Color,
|
||||
},
|
||||
FillRadial {
|
||||
center_x: u32,
|
||||
center_y: u32,
|
||||
inner_radius: u32,
|
||||
outer_radius: u32,
|
||||
color_start: Color,
|
||||
color_end: Color,
|
||||
},
|
||||
FillImage(String),
|
||||
StrokeColor(Color),
|
||||
StrokeLinear {
|
||||
start_x: u32,
|
||||
start_u: u32,
|
||||
end_x: u32,
|
||||
end_y: u32,
|
||||
color_start: Color,
|
||||
color_end: Color,
|
||||
},
|
||||
StrokeRadial {
|
||||
center_x: u32,
|
||||
center_y: u32,
|
||||
inner_radius: u32,
|
||||
outer_radius: u32,
|
||||
color_start: Color,
|
||||
color_end: Color,
|
||||
},
|
||||
StrokeImage(String),
|
||||
StrokeStream(String),
|
||||
StrokeWidth(u32),
|
||||
Cap(Cap),
|
||||
Join(Join),
|
||||
MiterLimit(u32),
|
||||
Scissor {
|
||||
width: u32,
|
||||
height: u32,
|
||||
},
|
||||
Font(String),
|
||||
FontSize(u32),
|
||||
TextAlign(TextAlign),
|
||||
TextBase(TextBase),
|
||||
}
|
||||
|
||||
impl From<elixir_types::ScriptItem> for ScriptItem {
|
||||
fn from(script_item: elixir_types::ScriptItem) -> Self {
|
||||
match script_item {
|
||||
elixir_types::ScriptItem::PushState => ScriptItem::PushState,
|
||||
elixir_types::ScriptItem::PopState => ScriptItem::PopState,
|
||||
elixir_types::ScriptItem::PushPopState => ScriptItem::PushPopState,
|
||||
elixir_types::ScriptItem::Clear(color) => ScriptItem::Clear(color.into()),
|
||||
elixir_types::ScriptItem::DrawLine(dl) => ScriptItem::DrawLine {
|
||||
x0: dl.x0,
|
||||
y0: dl.y0,
|
||||
x1: dl.x1,
|
||||
y1: dl.y1,
|
||||
fill_stroke: dl.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawQuad(dq) => ScriptItem::DrawQuad {
|
||||
x0: dq.x0,
|
||||
y0: dq.y0,
|
||||
x1: dq.x1,
|
||||
y1: dq.y1,
|
||||
x2: dq.x2,
|
||||
y2: dq.y2,
|
||||
x3: dq.x3,
|
||||
y3: dq.y3,
|
||||
fill_stroke: dq.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawRect(r) => ScriptItem::DrawRect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
fill_stroke: r.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawRrect(r) => ScriptItem::DrawRRect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
radius: r.radius,
|
||||
fill_stroke: r.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawSector(sector) => ScriptItem::DrawSector {
|
||||
radius: sector.radius,
|
||||
radians: sector.radians,
|
||||
fill_stroke: sector.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawArc(arc) => ScriptItem::DrawArc {
|
||||
radius: arc.radius,
|
||||
radians: arc.radians,
|
||||
fill_stroke: arc.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawCircle(c) => ScriptItem::DrawCircle {
|
||||
radius: c.radius,
|
||||
fill_stroke: c.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawEllipse(e) => ScriptItem::DrawEllipse {
|
||||
radius0: e.radius0,
|
||||
radius1: e.radius1,
|
||||
fill_stroke: e.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawSprites(s) => ScriptItem::DrawSprite {
|
||||
src_id: s.src_id,
|
||||
cmds: s
|
||||
.cmds
|
||||
.iter()
|
||||
.map(|x| x.clone().into())
|
||||
.collect::<Vec<SpriteDrawCmd>>(),
|
||||
},
|
||||
elixir_types::ScriptItem::DrawText(text) => ScriptItem::DrawText(text),
|
||||
elixir_types::ScriptItem::DrawTriangle(t) => ScriptItem::DrawTriangle {
|
||||
x0: t.x0,
|
||||
y0: t.y0,
|
||||
x1: t.x1,
|
||||
y1: t.y1,
|
||||
x2: t.x2,
|
||||
y2: t.y2,
|
||||
fill_stroke: t.fill_stroke.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::Script(id) => ScriptItem::Script(id),
|
||||
elixir_types::ScriptItem::BeginPath => ScriptItem::BeginPath,
|
||||
elixir_types::ScriptItem::ClosePath => ScriptItem::ClosePath,
|
||||
elixir_types::ScriptItem::FillPath => ScriptItem::FillPath,
|
||||
elixir_types::ScriptItem::StrokePath => ScriptItem::StrokePath,
|
||||
elixir_types::ScriptItem::MoveTo(p) => ScriptItem::MoveTo { x: p.0, y: p.1 },
|
||||
elixir_types::ScriptItem::LineTo(p) => ScriptItem::LineTo { x: p.0, y: p.1 },
|
||||
elixir_types::ScriptItem::ArcTo(a) => ScriptItem::ArcTo {
|
||||
x0: a.x0,
|
||||
y0: a.y0,
|
||||
x1: a.x1,
|
||||
y1: a.y1,
|
||||
radius: a.radius,
|
||||
},
|
||||
elixir_types::ScriptItem::BezierTo(b) => ScriptItem::BezierTo {
|
||||
cp1x: b.cp1x,
|
||||
cp1y: b.cp1y,
|
||||
cp2x: b.cp2x,
|
||||
cp2y: b.cp2y,
|
||||
x: b.x,
|
||||
y: b.y,
|
||||
},
|
||||
elixir_types::ScriptItem::QuadraticTo(q) => ScriptItem::QuadraticTo {
|
||||
cpx: q.cpx,
|
||||
cpy: q.cpy,
|
||||
x: q.x,
|
||||
y: q.y,
|
||||
},
|
||||
elixir_types::ScriptItem::Quad(q) => ScriptItem::Quad {
|
||||
x0: q.x0,
|
||||
y0: q.y0,
|
||||
x1: q.x1,
|
||||
y1: q.y1,
|
||||
x2: q.x2,
|
||||
y2: q.y2,
|
||||
x3: q.x3,
|
||||
y3: q.y3,
|
||||
},
|
||||
elixir_types::ScriptItem::Rect(r) => ScriptItem::Rect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
},
|
||||
elixir_types::ScriptItem::Rrect(r) => ScriptItem::RRect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
radius: r.radius,
|
||||
},
|
||||
elixir_types::ScriptItem::Sector(s) => ScriptItem::Sector {
|
||||
radius: s.radius,
|
||||
radians: s.radians,
|
||||
},
|
||||
elixir_types::ScriptItem::Circle(c) => ScriptItem::Circle { radius: c.radius },
|
||||
elixir_types::ScriptItem::Ellipse(e) => ScriptItem::Ellipse {
|
||||
radius0: e.radius0,
|
||||
radius1: e.radius1,
|
||||
},
|
||||
elixir_types::ScriptItem::Triangle(t) => ScriptItem::Triangle {
|
||||
x0: t.x0,
|
||||
y0: t.y0,
|
||||
x1: t.x1,
|
||||
y1: t.y1,
|
||||
x2: t.x2,
|
||||
y2: t.y2,
|
||||
},
|
||||
elixir_types::ScriptItem::Scale(s) => ScriptItem::Scale { x: s.x, y: s.y },
|
||||
elixir_types::ScriptItem::Rotate(r) => ScriptItem::Rotate { radians: r },
|
||||
elixir_types::ScriptItem::Translate(p) => ScriptItem::Translate { x: p.0, y: p.1 },
|
||||
elixir_types::ScriptItem::Transform(t) => ScriptItem::Transform {
|
||||
a: t.a,
|
||||
b: t.b,
|
||||
c: t.c,
|
||||
d: t.d,
|
||||
e: t.e,
|
||||
f: t.f,
|
||||
},
|
||||
elixir_types::ScriptItem::FillColor(c) => ScriptItem::FillColor(c.into()),
|
||||
elixir_types::ScriptItem::FillLinear(l) => ScriptItem::FillLinear {
|
||||
start_x: l.start_x,
|
||||
start_y: l.start_y,
|
||||
end_x: l.end_x,
|
||||
end_y: l.end_y,
|
||||
color_start: l.color_start.into(),
|
||||
color_end: l.color_end.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::FillRadial(r) => ScriptItem::FillRadial {
|
||||
center_x: r.center_x,
|
||||
center_y: r.center_y,
|
||||
inner_radius: r.inner_radius,
|
||||
outer_radius: r.outer_radius,
|
||||
color_start: r.color_start.into(),
|
||||
color_end: r.color_end.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::FillImage(i) => ScriptItem::FillImage(i),
|
||||
elixir_types::ScriptItem::StrokeColor(c) => ScriptItem::StrokeColor(c.into()),
|
||||
elixir_types::ScriptItem::StrokeLinear(l) => ScriptItem::StrokeLinear {
|
||||
start_x: l.start_x,
|
||||
start_u: l.start_u,
|
||||
end_x: l.end_x,
|
||||
end_y: l.end_y,
|
||||
color_start: l.color_start.into(),
|
||||
color_end: l.color_end.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::StrokeRadial(r) => ScriptItem::StrokeRadial {
|
||||
center_x: r.center_x,
|
||||
center_y: r.center_y,
|
||||
inner_radius: r.inner_radius,
|
||||
outer_radius: r.outer_radius,
|
||||
color_start: r.color_start.into(),
|
||||
color_end: r.color_end.into(),
|
||||
},
|
||||
elixir_types::ScriptItem::StrokeImage(s) => ScriptItem::StrokeImage(s),
|
||||
elixir_types::ScriptItem::StrokeStream(s) => ScriptItem::StrokeStream(s),
|
||||
elixir_types::ScriptItem::StrokeWidth(w) => ScriptItem::StrokeWidth(w),
|
||||
elixir_types::ScriptItem::Cap(elixir_types::Cap::Butt) => ScriptItem::Cap(Cap::Butt),
|
||||
elixir_types::ScriptItem::Cap(elixir_types::Cap::Round) => ScriptItem::Cap(Cap::Round),
|
||||
elixir_types::ScriptItem::Cap(elixir_types::Cap::Square) => {
|
||||
ScriptItem::Cap(Cap::Square)
|
||||
}
|
||||
elixir_types::ScriptItem::Join(elixir_types::Join::Bevel) => {
|
||||
ScriptItem::Join(Join::Bevel)
|
||||
}
|
||||
elixir_types::ScriptItem::Join(elixir_types::Join::Round) => {
|
||||
ScriptItem::Join(Join::Round)
|
||||
}
|
||||
elixir_types::ScriptItem::Join(elixir_types::Join::Miter) => {
|
||||
ScriptItem::Join(Join::Miter)
|
||||
}
|
||||
elixir_types::ScriptItem::MiterLimit(l) => ScriptItem::MiterLimit(l),
|
||||
elixir_types::ScriptItem::Scissor(s) => ScriptItem::Scissor {
|
||||
width: s.width,
|
||||
height: s.height,
|
||||
},
|
||||
elixir_types::ScriptItem::Font(s) => ScriptItem::Font(s),
|
||||
elixir_types::ScriptItem::FontSize(u32) => ScriptItem::FontSize(u32),
|
||||
elixir_types::ScriptItem::TextAlign(elixir_types::TextAlign::Left) => {
|
||||
ScriptItem::TextAlign(TextAlign::Left)
|
||||
}
|
||||
elixir_types::ScriptItem::TextAlign(elixir_types::TextAlign::Center) => {
|
||||
ScriptItem::TextAlign(TextAlign::Center)
|
||||
}
|
||||
elixir_types::ScriptItem::TextAlign(elixir_types::TextAlign::Right) => {
|
||||
ScriptItem::TextAlign(TextAlign::Right)
|
||||
}
|
||||
elixir_types::ScriptItem::TextBase(elixir_types::TextBase::Top) => {
|
||||
ScriptItem::TextBase(TextBase::Top)
|
||||
}
|
||||
elixir_types::ScriptItem::TextBase(elixir_types::TextBase::Middle) => {
|
||||
ScriptItem::TextBase(TextBase::Middle)
|
||||
}
|
||||
elixir_types::ScriptItem::TextBase(elixir_types::TextBase::Alphabetic) => {
|
||||
ScriptItem::TextBase(TextBase::Alphabetic)
|
||||
}
|
||||
elixir_types::ScriptItem::TextBase(elixir_types::TextBase::Bottom) => {
|
||||
ScriptItem::TextBase(TextBase::Bottom)
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Message {
|
||||
Shutdown,
|
||||
ResetScene,
|
||||
Init {
|
||||
opacity: u32,
|
||||
debug: bool,
|
||||
antialias: bool,
|
||||
centered: bool,
|
||||
scaled: bool,
|
||||
orientation: Orientation,
|
||||
title: String,
|
||||
resizeable: bool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
on_close: OnClose,
|
||||
},
|
||||
ClearColor(Color),
|
||||
DeleteScripts(Vec<String>),
|
||||
RequestInput(Vec<InputClass>),
|
||||
UpdateScene(String, Vec<ScriptItem>),
|
||||
}
|
||||
|
||||
impl From<elixir_types::DriverConfig> for Message {
|
||||
fn from(driver_config: elixir_types::DriverConfig) -> Self {
|
||||
Message::Init {
|
||||
opacity: driver_config.opacity,
|
||||
debug: driver_config.debug,
|
||||
antialias: driver_config.antialias,
|
||||
centered: driver_config.position.centered,
|
||||
scaled: driver_config.position.scaled,
|
||||
orientation: driver_config.position.orientation.into(),
|
||||
title: driver_config.window.title,
|
||||
resizeable: driver_config.window.resizeable,
|
||||
width: driver_config.window.width,
|
||||
height: driver_config.window.height,
|
||||
on_close: driver_config.on_close.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
mod elixir_types;
|
||||
mod message;
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
use crate::message::{IpcExchange, Message};
|
||||
use clap::Parser;
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
|
||||
use message::{OnClose, Orientation};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize};
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, ModifiersState};
|
||||
|
@ -34,7 +38,7 @@ fn main() -> anyhow::Result<()> {
|
|||
let sender = IpcSender::connect(args.ipc_channel)?;
|
||||
|
||||
let (c2s_send, c2s_recv) = ipc::channel()?;
|
||||
let (s2c_send, s2c_recv) = ipc::channel()?;
|
||||
let (_s2c_send, s2c_recv) = ipc::channel()?;
|
||||
sender.send(IpcExchange {
|
||||
sender: c2s_send,
|
||||
receiver: s2c_recv,
|
||||
|
@ -48,7 +52,12 @@ fn main() -> anyhow::Result<()> {
|
|||
|
||||
std::thread::spawn(move || loop {
|
||||
let event = c2s_recv.recv().unwrap();
|
||||
event_loop_proxy.send_event(event).unwrap();
|
||||
|
||||
match event {
|
||||
Message::Shutdown => exit(0),
|
||||
Message::Init { .. } => unreachable!(),
|
||||
_ => event_loop_proxy.send_event(event).unwrap(),
|
||||
};
|
||||
});
|
||||
|
||||
let mut state = Application::new(msg);
|
||||
|
@ -59,10 +68,17 @@ fn main() -> anyhow::Result<()> {
|
|||
}
|
||||
|
||||
struct Application {
|
||||
opacity: u32,
|
||||
debug: bool,
|
||||
antialias: bool,
|
||||
centered: bool,
|
||||
scaled: bool,
|
||||
orientation: Orientation,
|
||||
title: String,
|
||||
resizeable: bool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
on_close: OnClose,
|
||||
window: Option<Window>,
|
||||
}
|
||||
|
||||
|
@ -70,15 +86,29 @@ impl Application {
|
|||
fn new(event: Message) -> Self {
|
||||
match event {
|
||||
Message::Init {
|
||||
opacity,
|
||||
debug,
|
||||
antialias,
|
||||
centered,
|
||||
scaled,
|
||||
orientation,
|
||||
title,
|
||||
resizeable,
|
||||
width,
|
||||
height,
|
||||
on_close,
|
||||
} => Application {
|
||||
opacity,
|
||||
debug,
|
||||
antialias,
|
||||
centered,
|
||||
scaled,
|
||||
orientation,
|
||||
title,
|
||||
resizeable,
|
||||
width,
|
||||
height,
|
||||
on_close,
|
||||
window: None,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
|
@ -104,7 +134,16 @@ impl ApplicationHandler<Message> for Application {
|
|||
let window_attributes = Window::default_attributes()
|
||||
.with_title(self.title.clone())
|
||||
.with_resizable(self.resizeable)
|
||||
.with_inner_size(LogicalSize::new(self.width, self.height));
|
||||
.with_inner_size(LogicalSize::new(self.width as f32, self.height as f32));
|
||||
|
||||
let window_attributes = if self.centered {
|
||||
let display_size = event_loop.primary_monitor().unwrap().size();
|
||||
let center_x = (display_size.width as f32 / 2.0) - (self.width as f32 / 2.0);
|
||||
let center_y = (display_size.height as f32 / 2.0) - (self.height as f32 / 2.0);
|
||||
window_attributes.with_position(LogicalPosition::new(center_x, center_y))
|
||||
} else {
|
||||
window_attributes
|
||||
};
|
||||
|
||||
self.window = event_loop.create_window(window_attributes).ok();
|
||||
}
|
||||
|
|
Binary file not shown.
BIN
priv/native/renderling_window_server
Executable file
BIN
priv/native/renderling_window_server
Executable file
Binary file not shown.
Binary file not shown.
16
test/support/example.ex
Normal file
16
test/support/example.ex
Normal file
|
@ -0,0 +1,16 @@
|
|||
defmodule Example do
|
||||
@default_viewport [
|
||||
name: :main_viewport,
|
||||
size: {640, 480},
|
||||
default_scene: ExampleScene,
|
||||
drivers: [
|
||||
[
|
||||
module: Scenic.Driver.Renderling.Window,
|
||||
name: :window,
|
||||
window: [resizeable: false, title: "Example Window"]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
def start_link(viewport \\ @default_viewport), do: Scenic.start_link([viewport])
|
||||
end
|
3
test/support/example_assets.ex
Normal file
3
test/support/example_assets.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule ExampleAssets do
|
||||
use Scenic.Assets.Static, otp_app: :scenic_driver_renderling, alias: []
|
||||
end
|
16
test/support/example_scene.ex
Normal file
16
test/support/example_scene.ex
Normal file
|
@ -0,0 +1,16 @@
|
|||
defmodule ExampleScene do
|
||||
@moduledoc false
|
||||
|
||||
use Scenic.Scene
|
||||
alias Scenic.Graph
|
||||
import Scenic.Primitives
|
||||
|
||||
@graph Graph.build()
|
||||
|> text("Hello, World!", font_size: 22, translate: {20, 80})
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def init(scene, _args, _opts) do
|
||||
{:ok, push_graph(scene, @graph)}
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue