refactor: extract renderer from scenic_window_server.
This commit is contained in:
parent
7a812e8e63
commit
78244ffdda
|
@ -58,6 +58,6 @@ defmodule Scenic.Driver.Renderling.Window.Config do
|
|||
defp compute_server_path do
|
||||
:scenic_driver_renderling
|
||||
|> :code.priv_dir()
|
||||
|> Path.join("native/renderling_window_server")
|
||||
|> Path.join("native/scenic_window_server")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
defmodule Scenic.Driver.Renderling.Window.Nif do
|
||||
use Rustler, otp_app: :scenic_driver_renderling, crate: :renderling_window
|
||||
use Rustler, otp_app: :scenic_driver_renderling, crate: :scenic_window
|
||||
|
||||
def init(_config), do: :erlang.nif_error(:nif_not_loaded)
|
||||
def update_scene(_script, _script_id, _resource), do: :erlang.nif_error(:nif_not_loaded)
|
||||
|
|
|
@ -10,9 +10,11 @@ defmodule Scenic.Driver.Renderling.Window do
|
|||
require Logger
|
||||
|
||||
@position_schema [
|
||||
scaled: [type: :boolean, default: false],
|
||||
centered: [type: :boolean, default: false],
|
||||
orientation: [type: {:in, [:normal, :left, :right, :upside_down]}, default: :normal]
|
||||
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 [
|
||||
|
@ -22,7 +24,6 @@ defmodule Scenic.Driver.Renderling.Window do
|
|||
|
||||
@opts_schema [
|
||||
name: [type: {:or, [:atom, :string]}],
|
||||
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],
|
||||
|
|
|
@ -1,760 +0,0 @@
|
|||
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, Eq, PartialEq, Hash)]
|
||||
pub enum ModifierKey {
|
||||
Meta,
|
||||
Alt,
|
||||
Ctrl,
|
||||
Shift,
|
||||
CapsLock,
|
||||
NumLock,
|
||||
ScrollLock,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct Point {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
}
|
||||
|
||||
impl From<winit::dpi::PhysicalPosition<f64>> for Point {
|
||||
fn from(pos: winit::dpi::PhysicalPosition<f64>) -> Self {
|
||||
Point {
|
||||
x: pos.x.round() as u32,
|
||||
y: pos.y.round() as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<winit::dpi::PhysicalSize<u32>> for Point {
|
||||
fn from(pos: winit::dpi::PhysicalSize<u32>) -> Self {
|
||||
Point {
|
||||
x: pos.width,
|
||||
y: pos.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum ViewPortEvent {
|
||||
Enter,
|
||||
Exit,
|
||||
Reshape,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum ElementState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
impl From<winit::event::ElementState> for ElementState {
|
||||
fn from(state: winit::event::ElementState) -> Self {
|
||||
match state {
|
||||
winit::event::ElementState::Pressed => ElementState::Pressed,
|
||||
winit::event::ElementState::Released => ElementState::Released,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum InputEvent {
|
||||
CodePoint {
|
||||
codepoint: u32,
|
||||
modifiers: Vec<ModifierKey>,
|
||||
},
|
||||
Key {
|
||||
key: winit::keyboard::KeyCode,
|
||||
state: ElementState,
|
||||
modifiers: Vec<ModifierKey>,
|
||||
},
|
||||
CursorButton {
|
||||
button: winit::event::MouseButton,
|
||||
state: ElementState,
|
||||
modifiers: Vec<ModifierKey>,
|
||||
position: Point,
|
||||
},
|
||||
CursorScroll {
|
||||
offset: Point,
|
||||
position: Point,
|
||||
},
|
||||
CursorPos {
|
||||
position: Point,
|
||||
},
|
||||
ViewPort {
|
||||
event: ViewPortEvent,
|
||||
position: Point,
|
||||
},
|
||||
Relative {
|
||||
position: Point,
|
||||
},
|
||||
Led {
|
||||
id: String,
|
||||
value: u32,
|
||||
},
|
||||
Switch {
|
||||
id: String,
|
||||
value: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum ShutdownReason {
|
||||
Normal,
|
||||
Terminate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Message {
|
||||
Shutdown(ShutdownReason),
|
||||
ResetScene,
|
||||
Init {
|
||||
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>),
|
||||
InputEvent(InputEvent),
|
||||
}
|
||||
|
||||
impl From<elixir_types::DriverConfig> for Message {
|
||||
fn from(driver_config: elixir_types::DriverConfig) -> Self {
|
||||
Message::Init {
|
||||
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)]
|
||||
pub struct IpcExchange {
|
||||
pub sender: IpcSender<Message>,
|
||||
pub receiver: IpcReceiver<Message>,
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use renderling::{
|
||||
camera::Camera,
|
||||
math::{UVec2, Vec4},
|
||||
slab::Hybrid,
|
||||
stage::Stage,
|
||||
Context,
|
||||
};
|
||||
|
||||
use crate::message::{Color, ScriptItem};
|
||||
|
||||
pub struct Renderer {
|
||||
pub stage: Stage,
|
||||
pub ctx: Context,
|
||||
camera: Hybrid<Camera>,
|
||||
scripts: HashMap<String, Vec<ScriptItem>>,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new(ctx: Context) -> Self {
|
||||
let mut stage = ctx
|
||||
.new_stage()
|
||||
.with_lighting(false)
|
||||
.with_bloom(false)
|
||||
.with_background_color(Vec4::new(1.0, 0.0, 0.0, 1.0));
|
||||
|
||||
let size = ctx.get_size();
|
||||
let camera = stage.new_value(Camera::default_ortho2d(size.x as f32, size.y as f32));
|
||||
let scripts = HashMap::default();
|
||||
|
||||
Self {
|
||||
stage,
|
||||
camera,
|
||||
ctx,
|
||||
scripts,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_size(&mut self, width: u32, height: u32) {
|
||||
self.ctx.set_size(UVec2::new(width, height));
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self) {
|
||||
self.ctx.get_device().poll(wgpu::Maintain::Wait);
|
||||
}
|
||||
|
||||
pub fn render_frame(&mut self) {
|
||||
let frame = self.ctx.get_next_frame().unwrap();
|
||||
self.stage.render(&frame.view());
|
||||
frame.present();
|
||||
}
|
||||
|
||||
pub fn clear_color(&mut self, color: Color) {
|
||||
self.stage.set_background_color(color);
|
||||
}
|
||||
|
||||
pub fn reset_scene(&mut self) {
|
||||
// ?
|
||||
}
|
||||
|
||||
pub fn update_scene(&mut self, script_id: String, script: Vec<ScriptItem>) {
|
||||
self.scripts.insert(script_id, script);
|
||||
}
|
||||
|
||||
pub fn delete_scripts(&mut self, script_ids: Vec<String>) {
|
||||
for script_id in script_ids {
|
||||
self.scripts.remove(&script_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec4> for Color {
|
||||
fn into(self) -> Vec4 {
|
||||
match self {
|
||||
Color::Gray(g) => {
|
||||
let c = g as f32 / 255 as f32;
|
||||
Vec4::new(c, c, c, 1.0)
|
||||
}
|
||||
Color::GrayAlpha(g, a) => {
|
||||
let c = g as f32 / 255 as f32;
|
||||
let a = a as f32 / 255 as f32;
|
||||
Vec4::new(c, c, c, a)
|
||||
}
|
||||
Color::Rgb(r, g, b) => {
|
||||
let r = r as f32 / 255 as f32;
|
||||
let g = g as f32 / 255 as f32;
|
||||
let b = b as f32 / 255 as f32;
|
||||
Vec4::new(r, g, b, 1.0)
|
||||
}
|
||||
Color::RgbA(r, g, b, a) => {
|
||||
let r = r as f32 / 255 as f32;
|
||||
let g = g as f32 / 255 as f32;
|
||||
let b = b as f32 / 255 as f32;
|
||||
let a = a as f32 / 255 as f32;
|
||||
Vec4::new(r, g, b, a)
|
||||
}
|
||||
Color::Hsv(h, s, v) => {
|
||||
let (r, g, b) = hsv::hsv_to_rgb(h as f64, s as f64, v as f64);
|
||||
let r = r as f32 / 255 as f32;
|
||||
let g = g as f32 / 255 as f32;
|
||||
let b = b as f32 / 255 as f32;
|
||||
Vec4::new(r, g, b, 1.0)
|
||||
}
|
||||
Color::Hsl(h, s, l) => {
|
||||
let (r, g, b) = hsl::HSL {
|
||||
h: h as f64,
|
||||
s: s as f64,
|
||||
l: l as f64,
|
||||
}
|
||||
.to_rgb();
|
||||
let r = r as f32 / 255 as f32;
|
||||
let g = g as f32 / 255 as f32;
|
||||
let b = b as f32 / 255 as f32;
|
||||
Vec4::new(r, g, b, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,407 +0,0 @@
|
|||
mod elixir_types;
|
||||
mod message;
|
||||
mod render;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::message::{IpcExchange, Message};
|
||||
use clap::Parser;
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
|
||||
use message::{InputClass, ModifierKey, OnClose, Orientation};
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize};
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey, PhysicalKey};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
use renderling::{math::UVec2, Context};
|
||||
|
||||
use crate::render::Renderer;
|
||||
|
||||
#[cfg(macos_platform)]
|
||||
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use winit::platform::startup_notify::{
|
||||
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct CmdArgs {
|
||||
#[arg(long)]
|
||||
ipc_channel: String,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = CmdArgs::parse();
|
||||
|
||||
println!("Server: Started.");
|
||||
|
||||
let sender = IpcSender::connect(args.ipc_channel)?;
|
||||
|
||||
let (c2s_send, c2s_recv) = ipc::channel()?;
|
||||
let (s2c_send, s2c_recv) = ipc::channel()?;
|
||||
sender.send(IpcExchange {
|
||||
sender: c2s_send,
|
||||
receiver: s2c_recv,
|
||||
})?;
|
||||
|
||||
let msg = c2s_recv.recv()?;
|
||||
|
||||
if let &Message::Init { .. } = &msg {
|
||||
let event_loop = EventLoop::<Message>::with_user_event().build()?;
|
||||
let event_loop_proxy = event_loop.create_proxy();
|
||||
|
||||
std::thread::spawn(move || loop {
|
||||
let event = c2s_recv.recv().unwrap();
|
||||
|
||||
match event {
|
||||
Message::Init { .. } => unreachable!(),
|
||||
Message::Shutdown(message::ShutdownReason::Normal) => exit(0),
|
||||
Message::Shutdown(message::ShutdownReason::Terminate) => exit(1),
|
||||
_ => event_loop_proxy.send_event(event).unwrap(),
|
||||
};
|
||||
});
|
||||
|
||||
let mut state = Application::new(msg, s2c_send);
|
||||
event_loop.run_app(&mut state).map_err(Into::into)
|
||||
} else {
|
||||
panic!("Server: Invalid message received {:?}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
struct Application {
|
||||
antialias: bool,
|
||||
centered: bool,
|
||||
scaled: bool,
|
||||
orientation: Orientation,
|
||||
title: String,
|
||||
resizeable: bool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
on_close: OnClose,
|
||||
window: Option<Arc<Window>>,
|
||||
renderer: Option<Renderer>,
|
||||
input_classes: Vec<InputClass>,
|
||||
sender: IpcSender<Message>,
|
||||
current_modifier_keys: HashSet<message::ModifierKey>,
|
||||
last_cursor_pos: message::Point,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new(event: Message, sender: IpcSender<Message>) -> Self {
|
||||
match event {
|
||||
Message::Init {
|
||||
antialias,
|
||||
centered,
|
||||
scaled,
|
||||
orientation,
|
||||
title,
|
||||
resizeable,
|
||||
width,
|
||||
height,
|
||||
on_close,
|
||||
} => Application {
|
||||
antialias,
|
||||
centered,
|
||||
scaled,
|
||||
orientation,
|
||||
title,
|
||||
resizeable,
|
||||
width,
|
||||
height,
|
||||
on_close,
|
||||
window: None,
|
||||
input_classes: vec![
|
||||
InputClass::CursorButton,
|
||||
InputClass::CursorScroll,
|
||||
InputClass::CursorPos,
|
||||
InputClass::Codepoint,
|
||||
InputClass::Key,
|
||||
InputClass::Viewport,
|
||||
InputClass::Relative,
|
||||
InputClass::Led,
|
||||
InputClass::Switch,
|
||||
],
|
||||
sender,
|
||||
current_modifier_keys: HashSet::default(),
|
||||
last_cursor_pos: message::Point { x: 0, y: 0 },
|
||||
renderer: None,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler<Message> for Application {
|
||||
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: Message) {
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
match event {
|
||||
Message::ClearColor(color) => renderer.clear_color(color),
|
||||
Message::ResetScene => renderer.reset_scene(),
|
||||
Message::UpdateScene(script_id, script) => renderer.update_scene(script_id, script),
|
||||
Message::DeleteScripts(script_ids) => renderer.delete_scripts(script_ids),
|
||||
_ => {
|
||||
println!("unhandled message event {:?}", event)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
_event_loop: &ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
// Keep a record of which modifier keys are pressed at any given time.
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
match event {
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Meta),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Meta),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Meta),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Meta),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Alt),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Alt),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Alt),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Alt),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Control),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Ctrl),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Control),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Ctrl),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Shift),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Shift),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Shift),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Shift),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::CapsLock),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::CapsLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::CapsLock),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::CapsLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::NumLock),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::NumLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::NumLock),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::NumLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::ScrollLock),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::ScrollLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::ScrollLock),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::ScrollLock),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if let PhysicalKey::Code(keycode) = event.physical_key {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::Key {
|
||||
key: keycode,
|
||||
state: event.state.into(),
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(text) = event.text {
|
||||
let codepoint = text.chars().next().unwrap();
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CodePoint {
|
||||
codepoint: codepoint as u32,
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::CloseRequested => {
|
||||
self.sender
|
||||
.send(Message::Shutdown(message::ShutdownReason::Normal))
|
||||
.unwrap();
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
self.sender
|
||||
.send(Message::Shutdown(message::ShutdownReason::Terminate))
|
||||
.unwrap();
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let pos: message::Point = position.into();
|
||||
|
||||
if pos != self.last_cursor_pos {
|
||||
self.last_cursor_pos = pos.clone();
|
||||
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorPos {
|
||||
position: pos,
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorButton {
|
||||
button: button,
|
||||
state: state.into(),
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
WindowEvent::Touch(touch) => match touch.phase {
|
||||
winit::event::TouchPhase::Started => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorButton {
|
||||
button: winit::event::MouseButton::Left,
|
||||
state: message::ElementState::Pressed,
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
winit::event::TouchPhase::Ended => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorButton {
|
||||
button: winit::event::MouseButton::Left,
|
||||
state: message::ElementState::Released,
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::ViewPort {
|
||||
event: message::ViewPortEvent::Enter,
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::ViewPort {
|
||||
event: message::ViewPortEvent::Exit,
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
WindowEvent::Resized(size) => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::ViewPort {
|
||||
event: message::ViewPortEvent::Reshape,
|
||||
position: size.into(),
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
renderer.set_size(size.width, size.height);
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
renderer.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
_ => println!("unhandled window event {:?}", event),
|
||||
}
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let window_attributes = Window::default_attributes()
|
||||
.with_title(self.title.clone())
|
||||
.with_resizable(self.resizeable)
|
||||
.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
|
||||
};
|
||||
|
||||
let window = event_loop
|
||||
.create_window(window_attributes)
|
||||
.map(|window| Arc::new(window))
|
||||
.unwrap();
|
||||
|
||||
let context = Context::from_window(window.clone());
|
||||
|
||||
self.window = Some(window);
|
||||
self.renderer = Some(Renderer::new(context));
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
renderer.render_frame();
|
||||
}
|
||||
}
|
||||
}
|
6
native/scenic_renderer/.vscode/settings.json
vendored
Normal file
6
native/scenic_renderer/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"consts",
|
||||
"renderling"
|
||||
]
|
||||
}
|
2758
native/scenic_renderer/Cargo.lock
generated
Normal file
2758
native/scenic_renderer/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
11
native/scenic_renderer/Cargo.toml
Normal file
11
native/scenic_renderer/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
edition = "2021"
|
||||
name = "scenic_renderer"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
hsl = "0.1.1"
|
||||
hsv = "0.1.1"
|
||||
renderling = "0.4.2"
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
wgpu = {version = "0.19"}
|
99
native/scenic_renderer/src/lib.rs
Normal file
99
native/scenic_renderer/src/lib.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
pub mod script;
|
||||
|
||||
use script::{Color, Script};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, f32::consts::PI};
|
||||
|
||||
use renderling::{
|
||||
camera::Camera,
|
||||
math::{Mat4, UVec2, Vec4},
|
||||
slab::Hybrid,
|
||||
stage::Stage,
|
||||
};
|
||||
|
||||
pub use renderling::Context;
|
||||
|
||||
pub struct Renderer {
|
||||
pub stage: Stage,
|
||||
pub ctx: Context,
|
||||
camera: Hybrid<Camera>,
|
||||
scripts: HashMap<String, Script>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Orientation {
|
||||
Normal,
|
||||
Left,
|
||||
Right,
|
||||
UpsideDown,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new(ctx: Context) -> Self {
|
||||
let mut stage = ctx
|
||||
.new_stage()
|
||||
.with_lighting(false)
|
||||
.with_bloom(false)
|
||||
.with_background_color(Vec4::new(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
let size = ctx.get_size();
|
||||
let camera = stage.new_value(Camera::default_ortho2d(size.x as f32, size.y as f32));
|
||||
let scripts = HashMap::default();
|
||||
|
||||
Self {
|
||||
stage,
|
||||
camera,
|
||||
ctx,
|
||||
scripts,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_orientation(&mut self, orientation: Orientation) {
|
||||
match orientation {
|
||||
Orientation::Normal => self
|
||||
.camera
|
||||
.modify(|c| c.set_view(Mat4::from_rotation_z(0.0))),
|
||||
Orientation::Left => self
|
||||
.camera
|
||||
.modify(|c| c.set_view(Mat4::from_rotation_z(PI / -2.0))),
|
||||
Orientation::Right => self
|
||||
.camera
|
||||
.modify(|c| c.set_view(Mat4::from_rotation_z(PI / 2.0))),
|
||||
Orientation::UpsideDown => self
|
||||
.camera
|
||||
.modify(|c| c.set_view(Mat4::from_rotation_z(PI))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_size(&mut self, width: u32, height: u32) {
|
||||
self.ctx.set_size(UVec2::new(width, height));
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self) {
|
||||
self.ctx.get_device().poll(wgpu::Maintain::Wait);
|
||||
}
|
||||
|
||||
pub fn render_frame(&mut self) {
|
||||
let frame = self.ctx.get_next_frame().unwrap();
|
||||
self.stage.render(&frame.view());
|
||||
frame.present();
|
||||
}
|
||||
|
||||
pub fn clear_color(&mut self, color: Color) {
|
||||
self.stage.set_background_color(color);
|
||||
}
|
||||
|
||||
pub fn reset_scene(&mut self) {
|
||||
// ?
|
||||
}
|
||||
|
||||
pub fn update_scene(&mut self, script_id: String, script: Script) {
|
||||
self.scripts.insert(script_id, script);
|
||||
}
|
||||
|
||||
pub fn delete_scripts(&mut self, script_ids: Vec<String>) {
|
||||
for script_id in script_ids {
|
||||
self.scripts.remove(&script_id);
|
||||
}
|
||||
}
|
||||
}
|
327
native/scenic_renderer/src/script.rs
Normal file
327
native/scenic_renderer/src/script.rs
Normal file
|
@ -0,0 +1,327 @@
|
|||
use renderling::math::Vec4;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[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<Color> for Vec4 {
|
||||
fn from(val: Color) -> Self {
|
||||
match val {
|
||||
Color::Gray(g) => {
|
||||
let c = g as f32 / 255_f32;
|
||||
Vec4::new(c, c, c, 1.0)
|
||||
}
|
||||
Color::GrayAlpha(g, a) => {
|
||||
let c = g as f32 / 255_f32;
|
||||
let a = a as f32 / 255_f32;
|
||||
Vec4::new(c, c, c, a)
|
||||
}
|
||||
Color::Rgb(r, g, b) => {
|
||||
let r = r as f32 / 255_f32;
|
||||
let g = g as f32 / 255_f32;
|
||||
let b = b as f32 / 255_f32;
|
||||
Vec4::new(r, g, b, 1.0)
|
||||
}
|
||||
Color::RgbA(r, g, b, a) => {
|
||||
let r = r as f32 / 255_f32;
|
||||
let g = g as f32 / 255_f32;
|
||||
let b = b as f32 / 255_f32;
|
||||
let a = a as f32 / 255_f32;
|
||||
Vec4::new(r, g, b, a)
|
||||
}
|
||||
Color::Hsv(h, s, v) => {
|
||||
let (r, g, b) = hsv::hsv_to_rgb(h as f64, s as f64, v as f64);
|
||||
let r = r as f32 / 255_f32;
|
||||
let g = g as f32 / 255_f32;
|
||||
let b = b as f32 / 255_f32;
|
||||
Vec4::new(r, g, b, 1.0)
|
||||
}
|
||||
Color::Hsl(h, s, l) => {
|
||||
let (r, g, b) = hsl::HSL {
|
||||
h: h as f64,
|
||||
s: s as f64,
|
||||
l: l as f64,
|
||||
}
|
||||
.to_rgb();
|
||||
let r = r as f32 / 255_f32;
|
||||
let g = g as f32 / 255_f32;
|
||||
let b = b as f32 / 255_f32;
|
||||
Vec4::new(r, g, b, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum FillStroke {
|
||||
Fill,
|
||||
Stroke,
|
||||
FillStroke,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SpriteDrawCmd {
|
||||
pub src_x: u32,
|
||||
pub src_y: u32,
|
||||
pub src_w: u32,
|
||||
pub src_h: u32,
|
||||
pub dst_x: u32,
|
||||
pub dst_y: u32,
|
||||
pub dst_w: u32,
|
||||
pub dst_h: u32,
|
||||
}
|
||||
|
||||
#[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),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Script(Vec<ScriptItem>);
|
||||
|
||||
impl From<Vec<ScriptItem>> for Script {
|
||||
fn from(vec: Vec<ScriptItem>) -> Script {
|
||||
Script(vec)
|
||||
}
|
||||
}
|
1
native/scenic_window/.gitignore
vendored
Normal file
1
native/scenic_window/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -4,6 +4,7 @@
|
|||
"centered",
|
||||
"cmds",
|
||||
"Codepoint",
|
||||
"Fullscreen",
|
||||
"Miter",
|
||||
"Rect",
|
||||
"Renderling",
|
|
@ -1847,22 +1847,6 @@ dependencies = [
|
|||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "renderling_window"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"hsl",
|
||||
"hsv",
|
||||
"ipc-channel",
|
||||
"renderling",
|
||||
"rustler",
|
||||
"serde",
|
||||
"wgpu",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
|
@ -1931,6 +1915,30 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scenic_renderer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hsl",
|
||||
"hsv",
|
||||
"renderling",
|
||||
"serde",
|
||||
"wgpu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scenic_window"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"ipc-channel",
|
||||
"rustler",
|
||||
"scenic_renderer",
|
||||
"serde",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
|
@ -1,26 +1,23 @@
|
|||
[package]
|
||||
authors = []
|
||||
edition = "2021"
|
||||
name = "renderling_window"
|
||||
name = "scenic_window"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
name = "renderling_window"
|
||||
name = "scenic_window"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
clap = {version = "4", features = ["derive"]}
|
||||
hsl = "0.1.1"
|
||||
hsv = "0.1.1"
|
||||
ipc-channel = "0.18.0"
|
||||
renderling = "0.4.2"
|
||||
rustler = {version = "0.32.1", features = ["serde", "nif_version_2_16"]}
|
||||
scenic_renderer = {path = "../scenic_renderer"}
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
wgpu = {version = "0.19"}
|
||||
winit = {version = "0.30.0", features = ["serde"]}
|
||||
|
||||
[[bin]]
|
||||
name = "renderling_window_server"
|
||||
name = "scenic_window_server"
|
||||
path = "src/server.rs"
|
|
@ -1,7 +1,8 @@
|
|||
//! Type definitions for anything going into or coming out of the BEAM.
|
||||
|
||||
use crate::message;
|
||||
use rustler::{NifStruct, NifTaggedEnum, NifTuple, NifUnitEnum};
|
||||
|
||||
/// Structures received from BEAM.
|
||||
use scenic_renderer::script;
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum WindowOrientation {
|
||||
|
@ -11,12 +12,25 @@ pub enum WindowOrientation {
|
|||
UpsideDown,
|
||||
}
|
||||
|
||||
impl From<WindowOrientation> for scenic_renderer::Orientation {
|
||||
fn from(orientation: WindowOrientation) -> Self {
|
||||
match orientation {
|
||||
WindowOrientation::Normal => scenic_renderer::Orientation::Normal,
|
||||
WindowOrientation::Left => scenic_renderer::Orientation::Left,
|
||||
WindowOrientation::Right => scenic_renderer::Orientation::Right,
|
||||
WindowOrientation::UpsideDown => scenic_renderer::Orientation::UpsideDown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(NifStruct, Debug, Clone)]
|
||||
#[module = "Scenic.Driver.Renderling.Window.Config.Position"]
|
||||
pub struct WindowPosition {
|
||||
pub scaled: bool,
|
||||
pub centered: bool,
|
||||
pub orientation: WindowOrientation,
|
||||
pub maximised: bool,
|
||||
pub full_screen: bool,
|
||||
}
|
||||
|
||||
#[derive(NifStruct, Debug, Clone)]
|
||||
|
@ -49,6 +63,7 @@ pub struct DriverConfig {
|
|||
pub server_path: String,
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(NifTaggedEnum, Debug, Clone)]
|
||||
pub enum Color {
|
||||
ColorG(u8),
|
||||
|
@ -59,6 +74,19 @@ pub enum Color {
|
|||
ColorHsl((f32, f32, f32)),
|
||||
}
|
||||
|
||||
impl From<Color> for script::Color {
|
||||
fn from(color: Color) -> Self {
|
||||
match color {
|
||||
Color::ColorG(g) => script::Color::Gray(g),
|
||||
Color::ColorGa((g, a)) => script::Color::GrayAlpha(g, a),
|
||||
Color::ColorRgb((r, g, b)) => script::Color::Rgb(r, g, b),
|
||||
Color::ColorRgba((r, g, b, a)) => script::Color::RgbA(r, g, b, a),
|
||||
Color::ColorHsv((h, s, v)) => script::Color::Hsv(h, s, v),
|
||||
Color::ColorHsl((h, s, l)) => script::Color::Hsl(h, s, l),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct Pos(pub u32, pub u32);
|
||||
|
||||
|
@ -77,6 +105,7 @@ pub enum TextBase {
|
|||
Bottom,
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum FillStroke {
|
||||
Fill,
|
||||
|
@ -84,6 +113,16 @@ pub enum FillStroke {
|
|||
FillStroke,
|
||||
}
|
||||
|
||||
impl From<FillStroke> for script::FillStroke {
|
||||
fn from(fill_stroke: FillStroke) -> Self {
|
||||
match fill_stroke {
|
||||
FillStroke::Fill => script::FillStroke::Fill,
|
||||
FillStroke::FillStroke => script::FillStroke::FillStroke,
|
||||
FillStroke::Stroke => script::FillStroke::Stroke,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct DrawLine {
|
||||
pub x0: u32,
|
||||
|
@ -198,6 +237,21 @@ pub struct SpriteDrawCmd {
|
|||
pub dst_wh: Pos,
|
||||
}
|
||||
|
||||
impl From<SpriteDrawCmd> for script::SpriteDrawCmd {
|
||||
fn from(cmd: SpriteDrawCmd) -> Self {
|
||||
script::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(NifTuple, Debug, Clone)]
|
||||
pub struct DrawSprite {
|
||||
pub src_id: String,
|
||||
|
@ -388,6 +442,232 @@ pub enum ScriptItem {
|
|||
TextBase(TextBase),
|
||||
}
|
||||
|
||||
impl From<ScriptItem> for script::ScriptItem {
|
||||
fn from(script_item: ScriptItem) -> Self {
|
||||
match script_item {
|
||||
ScriptItem::PushState => script::ScriptItem::PushState,
|
||||
ScriptItem::PopState => script::ScriptItem::PopState,
|
||||
ScriptItem::PushPopState => script::ScriptItem::PushPopState,
|
||||
ScriptItem::Clear(color) => script::ScriptItem::Clear(color.into()),
|
||||
ScriptItem::DrawLine(dl) => script::ScriptItem::DrawLine {
|
||||
x0: dl.x0,
|
||||
y0: dl.y0,
|
||||
x1: dl.x1,
|
||||
y1: dl.y1,
|
||||
fill_stroke: dl.fill_stroke.into(),
|
||||
},
|
||||
ScriptItem::DrawQuad(dq) => script::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(),
|
||||
},
|
||||
ScriptItem::DrawRect(r) => script::ScriptItem::DrawRect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
fill_stroke: r.fill_stroke.into(),
|
||||
},
|
||||
ScriptItem::DrawRrect(r) => script::ScriptItem::DrawRRect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
radius: r.radius,
|
||||
fill_stroke: r.fill_stroke.into(),
|
||||
},
|
||||
ScriptItem::DrawSector(sector) => script::ScriptItem::DrawSector {
|
||||
radius: sector.radius,
|
||||
radians: sector.radians,
|
||||
fill_stroke: sector.fill_stroke.into(),
|
||||
},
|
||||
ScriptItem::DrawArc(arc) => script::ScriptItem::DrawArc {
|
||||
radius: arc.radius,
|
||||
radians: arc.radians,
|
||||
fill_stroke: arc.fill_stroke.into(),
|
||||
},
|
||||
ScriptItem::DrawCircle(c) => script::ScriptItem::DrawCircle {
|
||||
radius: c.radius,
|
||||
fill_stroke: c.fill_stroke.into(),
|
||||
},
|
||||
ScriptItem::DrawEllipse(e) => script::ScriptItem::DrawEllipse {
|
||||
radius0: e.radius0,
|
||||
radius1: e.radius1,
|
||||
fill_stroke: e.fill_stroke.into(),
|
||||
},
|
||||
ScriptItem::DrawSprites(s) => script::ScriptItem::DrawSprite {
|
||||
src_id: s.src_id,
|
||||
cmds: s
|
||||
.cmds
|
||||
.iter()
|
||||
.map(|x| x.clone().into())
|
||||
.collect::<Vec<script::SpriteDrawCmd>>(),
|
||||
},
|
||||
ScriptItem::DrawText(text) => script::ScriptItem::DrawText(text),
|
||||
ScriptItem::DrawTriangle(t) => script::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(),
|
||||
},
|
||||
ScriptItem::Script(id) => script::ScriptItem::Script(id),
|
||||
ScriptItem::BeginPath => script::ScriptItem::BeginPath,
|
||||
ScriptItem::ClosePath => script::ScriptItem::ClosePath,
|
||||
ScriptItem::FillPath => script::ScriptItem::FillPath,
|
||||
ScriptItem::StrokePath => script::ScriptItem::StrokePath,
|
||||
ScriptItem::MoveTo(p) => script::ScriptItem::MoveTo { x: p.0, y: p.1 },
|
||||
ScriptItem::LineTo(p) => script::ScriptItem::LineTo { x: p.0, y: p.1 },
|
||||
ScriptItem::ArcTo(a) => script::ScriptItem::ArcTo {
|
||||
x0: a.x0,
|
||||
y0: a.y0,
|
||||
x1: a.x1,
|
||||
y1: a.y1,
|
||||
radius: a.radius,
|
||||
},
|
||||
ScriptItem::BezierTo(b) => script::ScriptItem::BezierTo {
|
||||
cp1x: b.cp1x,
|
||||
cp1y: b.cp1y,
|
||||
cp2x: b.cp2x,
|
||||
cp2y: b.cp2y,
|
||||
x: b.x,
|
||||
y: b.y,
|
||||
},
|
||||
ScriptItem::QuadraticTo(q) => script::ScriptItem::QuadraticTo {
|
||||
cpx: q.cpx,
|
||||
cpy: q.cpy,
|
||||
x: q.x,
|
||||
y: q.y,
|
||||
},
|
||||
ScriptItem::Quad(q) => script::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,
|
||||
},
|
||||
ScriptItem::Rect(r) => script::ScriptItem::Rect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
},
|
||||
ScriptItem::Rrect(r) => script::ScriptItem::RRect {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
radius: r.radius,
|
||||
},
|
||||
ScriptItem::Sector(s) => script::ScriptItem::Sector {
|
||||
radius: s.radius,
|
||||
radians: s.radians,
|
||||
},
|
||||
ScriptItem::Circle(c) => script::ScriptItem::Circle { radius: c.radius },
|
||||
ScriptItem::Ellipse(e) => script::ScriptItem::Ellipse {
|
||||
radius0: e.radius0,
|
||||
radius1: e.radius1,
|
||||
},
|
||||
ScriptItem::Triangle(t) => script::ScriptItem::Triangle {
|
||||
x0: t.x0,
|
||||
y0: t.y0,
|
||||
x1: t.x1,
|
||||
y1: t.y1,
|
||||
x2: t.x2,
|
||||
y2: t.y2,
|
||||
},
|
||||
ScriptItem::Scale(s) => script::ScriptItem::Scale { x: s.x, y: s.y },
|
||||
ScriptItem::Rotate(r) => script::ScriptItem::Rotate { radians: r },
|
||||
ScriptItem::Translate(p) => script::ScriptItem::Translate { x: p.0, y: p.1 },
|
||||
ScriptItem::Transform(t) => script::ScriptItem::Transform {
|
||||
a: t.a,
|
||||
b: t.b,
|
||||
c: t.c,
|
||||
d: t.d,
|
||||
e: t.e,
|
||||
f: t.f,
|
||||
},
|
||||
ScriptItem::FillColor(c) => script::ScriptItem::FillColor(c.into()),
|
||||
ScriptItem::FillLinear(l) => script::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(),
|
||||
},
|
||||
ScriptItem::FillRadial(r) => script::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(),
|
||||
},
|
||||
ScriptItem::FillImage(i) => script::ScriptItem::FillImage(i),
|
||||
ScriptItem::StrokeColor(c) => script::ScriptItem::StrokeColor(c.into()),
|
||||
ScriptItem::StrokeLinear(l) => script::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(),
|
||||
},
|
||||
ScriptItem::StrokeRadial(r) => script::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(),
|
||||
},
|
||||
ScriptItem::StrokeImage(s) => script::ScriptItem::StrokeImage(s),
|
||||
ScriptItem::StrokeStream(s) => script::ScriptItem::StrokeStream(s),
|
||||
ScriptItem::StrokeWidth(w) => script::ScriptItem::StrokeWidth(w),
|
||||
ScriptItem::Cap(Cap::Butt) => script::ScriptItem::Cap(script::Cap::Butt),
|
||||
ScriptItem::Cap(Cap::Round) => script::ScriptItem::Cap(script::Cap::Round),
|
||||
ScriptItem::Cap(Cap::Square) => script::ScriptItem::Cap(script::Cap::Square),
|
||||
ScriptItem::Join(Join::Bevel) => script::ScriptItem::Join(script::Join::Bevel),
|
||||
ScriptItem::Join(Join::Round) => script::ScriptItem::Join(script::Join::Round),
|
||||
ScriptItem::Join(Join::Miter) => script::ScriptItem::Join(script::Join::Miter),
|
||||
ScriptItem::MiterLimit(l) => script::ScriptItem::MiterLimit(l),
|
||||
ScriptItem::Scissor(s) => script::ScriptItem::Scissor {
|
||||
width: s.width,
|
||||
height: s.height,
|
||||
},
|
||||
ScriptItem::Font(s) => script::ScriptItem::Font(s),
|
||||
ScriptItem::FontSize(u32) => script::ScriptItem::FontSize(u32),
|
||||
ScriptItem::TextAlign(TextAlign::Left) => {
|
||||
script::ScriptItem::TextAlign(script::TextAlign::Left)
|
||||
}
|
||||
ScriptItem::TextAlign(TextAlign::Center) => {
|
||||
script::ScriptItem::TextAlign(script::TextAlign::Center)
|
||||
}
|
||||
ScriptItem::TextAlign(TextAlign::Right) => {
|
||||
script::ScriptItem::TextAlign(script::TextAlign::Right)
|
||||
}
|
||||
ScriptItem::TextBase(TextBase::Top) => {
|
||||
script::ScriptItem::TextBase(script::TextBase::Top)
|
||||
}
|
||||
ScriptItem::TextBase(TextBase::Middle) => {
|
||||
script::ScriptItem::TextBase(script::TextBase::Middle)
|
||||
}
|
||||
ScriptItem::TextBase(TextBase::Alphabetic) => {
|
||||
script::ScriptItem::TextBase(script::TextBase::Alphabetic)
|
||||
}
|
||||
ScriptItem::TextBase(TextBase::Bottom) => {
|
||||
script::ScriptItem::TextBase(script::TextBase::Bottom)
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum InputClass {
|
||||
CursorButton,
|
||||
|
@ -467,6 +747,7 @@ impl From<message::ElementState> for ElementState {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum Key {
|
||||
KeySpace,
|
||||
|
@ -757,6 +1038,7 @@ impl From<winit::keyboard::KeyCode> for Key {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(NifUnitEnum, Debug, Clone)]
|
||||
pub enum MouseButton {
|
||||
BtnLeft,
|
||||
|
@ -849,16 +1131,7 @@ pub enum ShutdownReason {
|
|||
}
|
||||
|
||||
#[derive(NifTuple, Debug, Clone)]
|
||||
pub struct ShutdownMessage(ShutdownAtom, ShutdownReason);
|
||||
|
||||
impl ShutdownMessage {
|
||||
pub fn normal() -> Self {
|
||||
ShutdownMessage(ShutdownAtom::Shutdown, ShutdownReason::Normal)
|
||||
}
|
||||
pub fn terminate() -> Self {
|
||||
ShutdownMessage(ShutdownAtom::Shutdown, ShutdownReason::Terminate)
|
||||
}
|
||||
}
|
||||
pub struct ShutdownMessage(pub ShutdownAtom, pub ShutdownReason);
|
||||
|
||||
impl From<message::ShutdownReason> for ShutdownMessage {
|
||||
fn from(reason: message::ShutdownReason) -> Self {
|
|
@ -6,6 +6,7 @@ use crate::elixir_types::{
|
|||
};
|
||||
use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
|
||||
use rustler::{Encoder, Env, NifResult, OwnedEnv, ResourceArc, Term};
|
||||
use scenic_renderer::script;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::sync::Mutex;
|
||||
use std::thread::sleep;
|
||||
|
@ -59,7 +60,13 @@ fn init(env: Env<'_>, cfg: DriverConfig) -> ResourceArc<IpcResource> {
|
|||
} else {
|
||||
let mut msg_env = OwnedEnv::new();
|
||||
msg_env
|
||||
.send_and_clear(&pid, |env| ShutdownMessage::terminate().encode(env))
|
||||
.send_and_clear(&pid, |env| {
|
||||
ShutdownMessage(
|
||||
elixir_types::ShutdownAtom::Shutdown,
|
||||
elixir_types::ShutdownReason::Terminate,
|
||||
)
|
||||
.encode(env)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
@ -76,13 +83,13 @@ fn update_scene(script: Term, script_id: String, resource: ResourceArc<IpcResour
|
|||
term.decode::<ScriptItem>()
|
||||
.map(|script_item| script_item.into())
|
||||
})
|
||||
.collect::<NifResult<Vec<message::ScriptItem>>>()
|
||||
.collect::<NifResult<Vec<script::ScriptItem>>>()
|
||||
.unwrap();
|
||||
|
||||
let mutex = resource.0.lock().unwrap();
|
||||
mutex
|
||||
.sender
|
||||
.send(message::Message::UpdateScene(script_id, script))
|
||||
.send(message::Message::UpdateScene(script_id, script.into()))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
@ -140,7 +147,7 @@ fn request_input(classes: Term, resource: ResourceArc<IpcResource>) {
|
|||
fn terminate(resource: ResourceArc<IpcResource>) {
|
||||
println!("Shutting down server.");
|
||||
let mut mutex = resource.0.lock().unwrap();
|
||||
if let None = mutex.handle.try_wait().unwrap() {
|
||||
if mutex.handle.try_wait().unwrap().is_none() {
|
||||
mutex
|
||||
.sender
|
||||
.send(message::Message::Shutdown(message::ShutdownReason::Normal))
|
||||
|
@ -151,7 +158,7 @@ fn terminate(resource: ResourceArc<IpcResource>) {
|
|||
// it.
|
||||
sleep(std::time::Duration::from_micros(100));
|
||||
let mut mutex = resource.0.lock().unwrap();
|
||||
if let None = mutex.handle.try_wait().unwrap() {
|
||||
if mutex.handle.try_wait().unwrap().is_none() {
|
||||
mutex.handle.kill().unwrap();
|
||||
}
|
||||
});
|
180
native/scenic_window/src/message.rs
Normal file
180
native/scenic_window/src/message.rs
Normal file
|
@ -0,0 +1,180 @@
|
|||
use crate::elixir_types;
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use scenic_renderer::script::{Color, Script};
|
||||
use scenic_renderer::Orientation;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
|
||||
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, Eq, PartialEq, Hash)]
|
||||
pub enum ModifierKey {
|
||||
Meta,
|
||||
Alt,
|
||||
Ctrl,
|
||||
Shift,
|
||||
CapsLock,
|
||||
NumLock,
|
||||
ScrollLock,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct Point {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
}
|
||||
|
||||
impl From<winit::dpi::PhysicalPosition<f64>> for Point {
|
||||
fn from(pos: winit::dpi::PhysicalPosition<f64>) -> Self {
|
||||
Point {
|
||||
x: pos.x.round() as u32,
|
||||
y: pos.y.round() as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<winit::dpi::PhysicalSize<u32>> for Point {
|
||||
fn from(pos: winit::dpi::PhysicalSize<u32>) -> Self {
|
||||
Point {
|
||||
x: pos.width,
|
||||
y: pos.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum ViewPortEvent {
|
||||
Enter,
|
||||
Exit,
|
||||
Reshape,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum ElementState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
impl From<winit::event::ElementState> for ElementState {
|
||||
fn from(state: winit::event::ElementState) -> Self {
|
||||
match state {
|
||||
winit::event::ElementState::Pressed => ElementState::Pressed,
|
||||
winit::event::ElementState::Released => ElementState::Released,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum InputEvent {
|
||||
CodePoint {
|
||||
codepoint: u32,
|
||||
modifiers: Vec<ModifierKey>,
|
||||
},
|
||||
Key {
|
||||
key: winit::keyboard::KeyCode,
|
||||
state: ElementState,
|
||||
modifiers: Vec<ModifierKey>,
|
||||
},
|
||||
CursorButton {
|
||||
button: winit::event::MouseButton,
|
||||
state: ElementState,
|
||||
modifiers: Vec<ModifierKey>,
|
||||
position: Point,
|
||||
},
|
||||
CursorScroll {
|
||||
offset: Point,
|
||||
position: Point,
|
||||
},
|
||||
CursorPos {
|
||||
position: Point,
|
||||
},
|
||||
ViewPort {
|
||||
event: ViewPortEvent,
|
||||
position: Point,
|
||||
},
|
||||
Relative {
|
||||
position: Point,
|
||||
},
|
||||
Led {
|
||||
id: String,
|
||||
value: u32,
|
||||
},
|
||||
Switch {
|
||||
id: String,
|
||||
value: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum ShutdownReason {
|
||||
Normal,
|
||||
Terminate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Message {
|
||||
Shutdown(ShutdownReason),
|
||||
ResetScene,
|
||||
Init {
|
||||
centered: bool,
|
||||
full_screen: bool,
|
||||
height: u32,
|
||||
maximised: bool,
|
||||
orientation: Orientation,
|
||||
resizeable: bool,
|
||||
title: String,
|
||||
width: u32,
|
||||
},
|
||||
ClearColor(Color),
|
||||
DeleteScripts(Vec<String>),
|
||||
RequestInput(Vec<InputClass>),
|
||||
UpdateScene(String, Script),
|
||||
InputEvent(InputEvent),
|
||||
}
|
||||
|
||||
impl From<elixir_types::DriverConfig> for Message {
|
||||
fn from(driver_config: elixir_types::DriverConfig) -> Self {
|
||||
Message::Init {
|
||||
centered: driver_config.position.centered,
|
||||
full_screen: driver_config.position.full_screen,
|
||||
height: driver_config.window.height,
|
||||
maximised: driver_config.position.maximised,
|
||||
orientation: driver_config.position.orientation.into(),
|
||||
resizeable: driver_config.window.resizeable,
|
||||
title: driver_config.window.title,
|
||||
width: driver_config.window.width,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct IpcExchange {
|
||||
pub sender: IpcSender<Message>,
|
||||
pub receiver: IpcReceiver<Message>,
|
||||
}
|
464
native/scenic_window/src/server.rs
Normal file
464
native/scenic_window/src/server.rs
Normal file
|
@ -0,0 +1,464 @@
|
|||
//! scenic_window_server
|
||||
//!
|
||||
//! Some platforms require that windows must be opened from a processes "main
|
||||
//! thread" so we can't just start using `winit` from our Erlang NIFs.
|
||||
//!
|
||||
//! This module becomes a separate binary that is spawned by our NIFs and uses
|
||||
//! `ipc_channel` to efficiently send messages between the two. The messages
|
||||
//! are defined in the `messages` module.
|
||||
|
||||
mod elixir_types;
|
||||
mod message;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::message::{IpcExchange, Message};
|
||||
use clap::Parser;
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
|
||||
use message::{InputClass, ModifierKey};
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize};
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey, PhysicalKey};
|
||||
use winit::window::{self, Window, WindowAttributes, WindowId};
|
||||
|
||||
use scenic_renderer::{Context, Orientation, Renderer};
|
||||
|
||||
#[cfg(macos_platform)]
|
||||
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use winit::platform::startup_notify::{
|
||||
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct CmdArgs {
|
||||
#[arg(long)]
|
||||
ipc_channel: String,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = CmdArgs::parse();
|
||||
|
||||
println!("Server: Started.");
|
||||
|
||||
let sender = IpcSender::connect(args.ipc_channel)?;
|
||||
|
||||
let (c2s_send, c2s_recv) = ipc::channel()?;
|
||||
let (s2c_send, s2c_recv) = ipc::channel()?;
|
||||
sender.send(IpcExchange {
|
||||
sender: c2s_send,
|
||||
receiver: s2c_recv,
|
||||
})?;
|
||||
|
||||
let msg = c2s_recv.recv()?;
|
||||
|
||||
if let &Message::Init { .. } = &msg {
|
||||
let event_loop = EventLoop::<Message>::with_user_event().build()?;
|
||||
let event_loop_proxy = event_loop.create_proxy();
|
||||
|
||||
std::thread::spawn(move || loop {
|
||||
let event = c2s_recv.recv().unwrap();
|
||||
|
||||
match event {
|
||||
Message::Init { .. } => unreachable!(),
|
||||
Message::Shutdown(message::ShutdownReason::Normal) => exit(0),
|
||||
Message::Shutdown(message::ShutdownReason::Terminate) => exit(1),
|
||||
_ => event_loop_proxy.send_event(event).unwrap(),
|
||||
};
|
||||
});
|
||||
|
||||
let mut state = Application::new(msg, s2c_send);
|
||||
event_loop.run_app(&mut state).map_err(Into::into)
|
||||
} else {
|
||||
panic!("Server: Invalid message received {:?}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct InitialWindowConfig {
|
||||
centered: bool,
|
||||
full_screen: bool,
|
||||
height: u32,
|
||||
maximised: bool,
|
||||
orientation: Orientation,
|
||||
resizeable: bool,
|
||||
title: String,
|
||||
width: u32,
|
||||
}
|
||||
|
||||
struct Application {
|
||||
initial_config: Option<InitialWindowConfig>,
|
||||
current_modifier_keys: HashSet<message::ModifierKey>,
|
||||
input_classes: HashSet<InputClass>,
|
||||
last_cursor_pos: message::Point,
|
||||
renderer: Option<Renderer>,
|
||||
sender: IpcSender<Message>,
|
||||
window: Option<Arc<Window>>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new(event: Message, sender: IpcSender<Message>) -> Self {
|
||||
match event {
|
||||
Message::Init {
|
||||
centered,
|
||||
full_screen,
|
||||
height,
|
||||
maximised,
|
||||
orientation,
|
||||
resizeable,
|
||||
title,
|
||||
width,
|
||||
} => Application {
|
||||
initial_config: Some(InitialWindowConfig {
|
||||
centered,
|
||||
full_screen,
|
||||
height,
|
||||
maximised,
|
||||
orientation,
|
||||
resizeable,
|
||||
title,
|
||||
width,
|
||||
}),
|
||||
window: None,
|
||||
input_classes: [
|
||||
InputClass::CursorButton,
|
||||
InputClass::CursorScroll,
|
||||
InputClass::CursorPos,
|
||||
InputClass::Codepoint,
|
||||
InputClass::Key,
|
||||
InputClass::Viewport,
|
||||
InputClass::Relative,
|
||||
InputClass::Led,
|
||||
InputClass::Switch,
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashSet<InputClass>>(),
|
||||
sender,
|
||||
current_modifier_keys: HashSet::default(),
|
||||
last_cursor_pos: message::Point { x: 0, y: 0 },
|
||||
renderer: None,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler<Message> for Application {
|
||||
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: Message) {
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
match event {
|
||||
Message::ClearColor(color) => renderer.clear_color(color),
|
||||
Message::ResetScene => renderer.reset_scene(),
|
||||
Message::UpdateScene(script_id, script) => renderer.update_scene(script_id, script),
|
||||
Message::DeleteScripts(script_ids) => renderer.delete_scripts(script_ids),
|
||||
Message::RequestInput(input_classes) => {
|
||||
self.input_classes = input_classes
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashSet<InputClass>>();
|
||||
}
|
||||
_ => {
|
||||
println!("unhandled message event {:?}", event)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
_event_loop: &ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
// Keep a record of which modifier keys are pressed at any given time.
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
if self.input_classes.contains(&InputClass::Key) {
|
||||
match event {
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Meta),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Meta),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Meta),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Meta),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Alt),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Alt),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Alt),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Alt),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Control),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Ctrl),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Control),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Ctrl),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Shift),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::Shift),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Shift),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::Shift),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::CapsLock),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::CapsLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::CapsLock),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::CapsLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::NumLock),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::NumLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::NumLock),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::NumLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::ScrollLock),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => self.current_modifier_keys.insert(ModifierKey::ScrollLock),
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::ScrollLock),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => self.current_modifier_keys.remove(&ModifierKey::ScrollLock),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if let PhysicalKey::Code(keycode) = event.physical_key {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::Key {
|
||||
key: keycode,
|
||||
state: event.state.into(),
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(text) = event.text {
|
||||
let codepoint = text.chars().next().unwrap();
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CodePoint {
|
||||
codepoint: codepoint as u32,
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::CloseRequested => {
|
||||
self.sender
|
||||
.send(Message::Shutdown(message::ShutdownReason::Normal))
|
||||
.unwrap();
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
self.sender
|
||||
.send(Message::Shutdown(message::ShutdownReason::Terminate))
|
||||
.unwrap();
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let pos: message::Point = position.into();
|
||||
|
||||
if pos != self.last_cursor_pos {
|
||||
self.last_cursor_pos = pos.clone();
|
||||
if self.input_classes.contains(&InputClass::CursorPos) {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorPos {
|
||||
position: pos,
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
if self.input_classes.contains(&InputClass::CursorButton) {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorButton {
|
||||
button,
|
||||
state: state.into(),
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
WindowEvent::Touch(touch) => {
|
||||
if self.input_classes.contains(&InputClass::CursorButton) {
|
||||
match touch.phase {
|
||||
winit::event::TouchPhase::Started => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorButton {
|
||||
button: winit::event::MouseButton::Left,
|
||||
state: message::ElementState::Pressed,
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
winit::event::TouchPhase::Ended => {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::CursorButton {
|
||||
button: winit::event::MouseButton::Left,
|
||||
state: message::ElementState::Released,
|
||||
modifiers: self
|
||||
.current_modifier_keys
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<ModifierKey>>(),
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
if self.input_classes.contains(&InputClass::Viewport) {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::ViewPort {
|
||||
event: message::ViewPortEvent::Enter,
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
if self.input_classes.contains(&InputClass::Viewport) {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::ViewPort {
|
||||
event: message::ViewPortEvent::Exit,
|
||||
position: self.last_cursor_pos.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
WindowEvent::Resized(size) => {
|
||||
if self.input_classes.contains(&InputClass::Viewport) {
|
||||
self.sender
|
||||
.send(Message::InputEvent(message::InputEvent::ViewPort {
|
||||
event: message::ViewPortEvent::Reshape,
|
||||
position: size.into(),
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
renderer.set_size(size.width, size.height);
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
renderer.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
_ => println!("unhandled window event {:?}", event),
|
||||
}
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let config = self.initial_config.clone().unwrap();
|
||||
self.initial_config = None;
|
||||
|
||||
let full_screen = if config.full_screen {
|
||||
Some(window::Fullscreen::Borderless(None))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let window_attributes = Window::default_attributes()
|
||||
.with_title(config.title.clone())
|
||||
.with_resizable(config.resizeable)
|
||||
.with_inner_size(LogicalSize::new(config.width as f32, config.height as f32))
|
||||
.with_maximized(config.maximised)
|
||||
.with_fullscreen(full_screen);
|
||||
|
||||
let window_attributes = maybe_center(window_attributes, &config, event_loop);
|
||||
|
||||
let window = event_loop
|
||||
.create_window(window_attributes)
|
||||
.map(Arc::new)
|
||||
.unwrap();
|
||||
|
||||
let context = Context::from_window(window.clone());
|
||||
|
||||
self.window = Some(window);
|
||||
|
||||
let mut renderer = Renderer::new(context);
|
||||
renderer.set_orientation(config.orientation);
|
||||
self.renderer = Some(renderer);
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
if let Some(renderer) = &mut self.renderer {
|
||||
renderer.render_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_center(
|
||||
window_attributes: WindowAttributes,
|
||||
config: &InitialWindowConfig,
|
||||
event_loop: &ActiveEventLoop,
|
||||
) -> WindowAttributes {
|
||||
if config.centered {
|
||||
let display_size = event_loop.primary_monitor().unwrap().size();
|
||||
|
||||
let center_x = (display_size.width as f32 / 2.0) - (config.width as f32 / 2.0);
|
||||
let center_y = (display_size.height as f32 / 2.0) - (config.height as f32 / 2.0);
|
||||
window_attributes.with_position(LogicalPosition::new(center_x, center_y))
|
||||
} else {
|
||||
window_attributes
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue