Experimental wgpu WebGL backend support
- Added missing `draw_cache_align_4x4` call for `brush_glyph` on wasm32 target - Added WebGL support to `integratio_wgpu` example - Fixed test.yml CI workflow - Removed spir-v shader in `integration_wgpu`; Fixed formatting - Removed redundant `BoxStream` typedef
This commit is contained in:
parent
c75ed37148
commit
bdca20fc4a
21 changed files with 414 additions and 86 deletions
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
|
@ -37,3 +37,5 @@ jobs:
|
||||||
run: cargo build --package tour --target wasm32-unknown-unknown
|
run: cargo build --package tour --target wasm32-unknown-unknown
|
||||||
- name: Check compilation of `todos` example
|
- name: Check compilation of `todos` example
|
||||||
run: cargo build --package todos --target wasm32-unknown-unknown
|
run: cargo build --package todos --target wasm32-unknown-unknown
|
||||||
|
- name: Check compilation of `integration_wgpu` example
|
||||||
|
run: cargo build --package integration_wgpu --target wasm32-unknown-unknown
|
||||||
|
|
|
||||||
2
examples/integration_wgpu/.gitignore
vendored
Normal file
2
examples/integration_wgpu/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*.wasm
|
||||||
|
*.js
|
||||||
|
|
@ -7,5 +7,16 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_winit = { path = "../../winit" }
|
iced_winit = { path = "../../winit" }
|
||||||
iced_wgpu = { path = "../../wgpu", features = ["spirv"] }
|
iced_wgpu = { path = "../../wgpu", features = ["webgl"] }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
console_error_panic_hook = "0.1.7"
|
||||||
|
console_log = "0.2.0"
|
||||||
|
log = "0.4"
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
web-sys = { version = "0.3", features = ["Element", "HtmlCanvasElement", "Window", "Document"] }
|
||||||
|
# This dependency a little bit quirky, it is deep in the tree and without `js` feature it
|
||||||
|
# refuses to work with `wasm32-unknown-unknown target`. Unfortunately, we need this patch
|
||||||
|
# to make it work
|
||||||
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,22 @@ You can run it with `cargo run`:
|
||||||
cargo run --package integration
|
cargo run --package integration
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### How to run this example with WebGL backend
|
||||||
|
NOTE: Currently, WebGL backend is is still experimental, so expect bugs.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 0. Install prerequisites
|
||||||
|
cargo install wasm-bindgen-cli https
|
||||||
|
# 1. cd to the current folder
|
||||||
|
# 2. Compile wasm module
|
||||||
|
cargo build -p integration_wgpu --target wasm32-unknown-unknown
|
||||||
|
# 3. Invoke wasm-bindgen
|
||||||
|
wasm-bindgen ../../target/wasm32-unknown-unknown/debug/integration_wgpu.wasm --out-dir . --target web --no-typescript
|
||||||
|
# 4. run http server
|
||||||
|
http
|
||||||
|
# 5. Open 127.0.0.1:8000 in browser
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
[`main`]: src/main.rs
|
[`main`]: src/main.rs
|
||||||
[`wgpu`]: https://github.com/gfx-rs/wgpu
|
[`wgpu`]: https://github.com/gfx-rs/wgpu
|
||||||
|
|
|
||||||
21
examples/integration_wgpu/index.html
Normal file
21
examples/integration_wgpu/index.html
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
||||||
|
<title>Iced - wgpu + WebGL integration</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>integration_wgpu</h1>
|
||||||
|
<canvas id="iced_canvas"></canvas>
|
||||||
|
<script type="module">
|
||||||
|
import init from "./integration_wgpu.js";
|
||||||
|
init('./integration_wgpu_bg.wasm');
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1,23 +1,29 @@
|
||||||
use iced_wgpu::Renderer;
|
use iced_wgpu::Renderer;
|
||||||
use iced_winit::widget::slider::{self, Slider};
|
use iced_winit::widget::slider::{self, Slider};
|
||||||
|
use iced_winit::widget::text_input::{self, TextInput};
|
||||||
use iced_winit::widget::{Column, Row, Text};
|
use iced_winit::widget::{Column, Row, Text};
|
||||||
use iced_winit::{Alignment, Color, Command, Element, Length, Program};
|
use iced_winit::{Alignment, Color, Command, Element, Length, Program};
|
||||||
|
|
||||||
pub struct Controls {
|
pub struct Controls {
|
||||||
background_color: Color,
|
background_color: Color,
|
||||||
|
text: String,
|
||||||
sliders: [slider::State; 3],
|
sliders: [slider::State; 3],
|
||||||
|
text_input: text_input::State,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
BackgroundColorChanged(Color),
|
BackgroundColorChanged(Color),
|
||||||
|
TextChanged(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Controls {
|
impl Controls {
|
||||||
pub fn new() -> Controls {
|
pub fn new() -> Controls {
|
||||||
Controls {
|
Controls {
|
||||||
background_color: Color::BLACK,
|
background_color: Color::BLACK,
|
||||||
|
text: Default::default(),
|
||||||
sliders: Default::default(),
|
sliders: Default::default(),
|
||||||
|
text_input: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,6 +41,9 @@ impl Program for Controls {
|
||||||
Message::BackgroundColorChanged(color) => {
|
Message::BackgroundColorChanged(color) => {
|
||||||
self.background_color = color;
|
self.background_color = color;
|
||||||
}
|
}
|
||||||
|
Message::TextChanged(text) => {
|
||||||
|
self.text = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
|
|
@ -42,7 +51,9 @@ impl Program for Controls {
|
||||||
|
|
||||||
fn view(&mut self) -> Element<Message, Renderer> {
|
fn view(&mut self) -> Element<Message, Renderer> {
|
||||||
let [r, g, b] = &mut self.sliders;
|
let [r, g, b] = &mut self.sliders;
|
||||||
|
let t = &mut self.text_input;
|
||||||
let background_color = self.background_color;
|
let background_color = self.background_color;
|
||||||
|
let text = &self.text;
|
||||||
|
|
||||||
let sliders = Row::new()
|
let sliders = Row::new()
|
||||||
.width(Length::Units(500))
|
.width(Length::Units(500))
|
||||||
|
|
@ -96,7 +107,13 @@ impl Program for Controls {
|
||||||
Text::new(format!("{:?}", background_color))
|
Text::new(format!("{:?}", background_color))
|
||||||
.size(14)
|
.size(14)
|
||||||
.color(Color::WHITE),
|
.color(Color::WHITE),
|
||||||
),
|
)
|
||||||
|
.push(TextInput::new(
|
||||||
|
t,
|
||||||
|
"Placeholder",
|
||||||
|
text,
|
||||||
|
move |text| Message::TextChanged(text),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,39 @@ use winit::{
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use web_sys::HtmlCanvasElement;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use winit::platform::web::WindowBuilderExtWebSys;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let canvas_element = {
|
||||||
|
console_log::init_with_level(log::Level::Debug)
|
||||||
|
.expect("could not initialize logger");
|
||||||
|
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
|
|
||||||
|
web_sys::window()
|
||||||
|
.and_then(|win| win.document())
|
||||||
|
.and_then(|doc| doc.get_element_by_id("iced_canvas"))
|
||||||
|
.and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok())
|
||||||
|
.expect("Canvas with id `iced_canvas` is missing")
|
||||||
|
};
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
// Initialize winit
|
// Initialize winit
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let window = winit::window::WindowBuilder::new()
|
||||||
|
.with_canvas(Some(canvas_element))
|
||||||
|
.build(&event_loop)
|
||||||
|
.expect("Failed to build winit window");
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
let window = winit::window::Window::new(&event_loop).unwrap();
|
let window = winit::window::Window::new(&event_loop).unwrap();
|
||||||
|
|
||||||
let physical_size = window.inner_size();
|
let physical_size = window.inner_size();
|
||||||
|
|
@ -31,18 +59,35 @@ pub fn main() {
|
||||||
let mut clipboard = Clipboard::connect(&window);
|
let mut clipboard = Clipboard::connect(&window);
|
||||||
|
|
||||||
// Initialize wgpu
|
// Initialize wgpu
|
||||||
let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let default_backend = wgpu::Backends::GL;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let default_backend = wgpu::Backends::PRIMARY;
|
||||||
|
|
||||||
|
let backend =
|
||||||
|
wgpu::util::backend_bits_from_env().unwrap_or(default_backend);
|
||||||
|
|
||||||
|
let instance = wgpu::Instance::new(backend);
|
||||||
let surface = unsafe { instance.create_surface(&window) };
|
let surface = unsafe { instance.create_surface(&window) };
|
||||||
|
|
||||||
let (format, (mut device, queue)) = futures::executor::block_on(async {
|
let (format, (mut device, queue)) = futures::executor::block_on(async {
|
||||||
let adapter = instance
|
let adapter = wgpu::util::initialize_adapter_from_env_or_default(
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
&instance,
|
||||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
backend,
|
||||||
compatible_surface: Some(&surface),
|
Some(&surface),
|
||||||
force_fallback_adapter: false,
|
)
|
||||||
})
|
.await
|
||||||
.await
|
.expect("No suitable GPU adapters found on the system!");
|
||||||
.expect("Request adapter");
|
|
||||||
|
let adapter_features = adapter.features();
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let needed_limits = wgpu::Limits::downlevel_webgl2_defaults()
|
||||||
|
.using_resolution(adapter.limits());
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let needed_limits = wgpu::Limits::default();
|
||||||
|
|
||||||
(
|
(
|
||||||
surface
|
surface
|
||||||
|
|
@ -52,8 +97,8 @@ pub fn main() {
|
||||||
.request_device(
|
.request_device(
|
||||||
&wgpu::DeviceDescriptor {
|
&wgpu::DeviceDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
features: wgpu::Features::empty(),
|
features: adapter_features & wgpu::Features::default(),
|
||||||
limits: wgpu::Limits::default(),
|
limits: needed_limits,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
@ -62,20 +107,17 @@ pub fn main() {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
surface.configure(
|
||||||
let size = window.inner_size();
|
&device,
|
||||||
|
&wgpu::SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
format,
|
||||||
|
width: physical_size.width,
|
||||||
|
height: physical_size.height,
|
||||||
|
present_mode: wgpu::PresentMode::Mailbox,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
surface.configure(
|
|
||||||
&device,
|
|
||||||
&wgpu::SurfaceConfiguration {
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
format,
|
|
||||||
width: size.width,
|
|
||||||
height: size.height,
|
|
||||||
present_mode: wgpu::PresentMode::Mailbox,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let mut resized = false;
|
let mut resized = false;
|
||||||
|
|
||||||
// Initialize staging belt and local pool
|
// Initialize staging belt and local pool
|
||||||
|
|
@ -83,7 +125,7 @@ pub fn main() {
|
||||||
let mut local_pool = futures::executor::LocalPool::new();
|
let mut local_pool = futures::executor::LocalPool::new();
|
||||||
|
|
||||||
// Initialize scene and GUI controls
|
// Initialize scene and GUI controls
|
||||||
let scene = Scene::new(&mut device);
|
let scene = Scene::new(&mut device, format);
|
||||||
let controls = Controls::new();
|
let controls = Controls::new();
|
||||||
|
|
||||||
// Initialize iced
|
// Initialize iced
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@ pub struct Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
pub fn new(device: &wgpu::Device) -> Scene {
|
pub fn new(
|
||||||
let pipeline = build_pipeline(device);
|
device: &wgpu::Device,
|
||||||
|
texture_format: wgpu::TextureFormat,
|
||||||
|
) -> Scene {
|
||||||
|
let pipeline = build_pipeline(device, texture_format);
|
||||||
|
|
||||||
Scene { pipeline }
|
Scene { pipeline }
|
||||||
}
|
}
|
||||||
|
|
@ -47,12 +50,14 @@ impl Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
|
fn build_pipeline(
|
||||||
let vs_module =
|
device: &wgpu::Device,
|
||||||
device.create_shader_module(&wgpu::include_spirv!("shader/vert.spv"));
|
texture_format: wgpu::TextureFormat,
|
||||||
|
) -> wgpu::RenderPipeline {
|
||||||
let fs_module =
|
let (vs_module, fs_module) = (
|
||||||
device.create_shader_module(&wgpu::include_spirv!("shader/frag.spv"));
|
device.create_shader_module(&wgpu::include_wgsl!("shader/vert.wgsl")),
|
||||||
|
device.create_shader_module(&wgpu::include_wgsl!("shader/frag.wgsl")),
|
||||||
|
);
|
||||||
|
|
||||||
let pipeline_layout =
|
let pipeline_layout =
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
|
@ -74,7 +79,7 @@ fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
|
||||||
module: &fs_module,
|
module: &fs_module,
|
||||||
entry_point: "main",
|
entry_point: "main",
|
||||||
targets: &[wgpu::ColorTargetState {
|
targets: &[wgpu::ColorTargetState {
|
||||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
format: texture_format,
|
||||||
blend: Some(wgpu::BlendState {
|
blend: Some(wgpu::BlendState {
|
||||||
color: wgpu::BlendComponent::REPLACE,
|
color: wgpu::BlendComponent::REPLACE,
|
||||||
alpha: wgpu::BlendComponent::REPLACE,
|
alpha: wgpu::BlendComponent::REPLACE,
|
||||||
|
|
|
||||||
Binary file not shown.
4
examples/integration_wgpu/src/shader/frag.wgsl
Normal file
4
examples/integration_wgpu/src/shader/frag.wgsl
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[[stage(fragment)]]
|
||||||
|
fn main() -> [[location(0)]] vec4<f32> {
|
||||||
|
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
Binary file not shown.
6
examples/integration_wgpu/src/shader/vert.wgsl
Normal file
6
examples/integration_wgpu/src/shader/vert.wgsl
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[[stage(vertex)]]
|
||||||
|
fn main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {
|
||||||
|
let x = f32(1 - i32(in_vertex_index)) * 0.5;
|
||||||
|
let y = f32(1 - i32(in_vertex_index & 1u) * 2) * 0.5;
|
||||||
|
return vec4<f32>(x, y, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,50 @@ use crate::{subscription, Executor, Subscription};
|
||||||
use futures::{channel::mpsc, Sink};
|
use futures::{channel::mpsc, Sink};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod trait_aliases {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait RuntimeMessage: Send + 'static {}
|
||||||
|
|
||||||
|
impl<T> RuntimeMessage for T where T: Send + 'static {}
|
||||||
|
|
||||||
|
pub trait RuntimeMessageSender<Message: RuntimeMessage>:
|
||||||
|
Sink<Message, Error = mpsc::SendError> + Unpin + Send + Clone + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message: RuntimeMessage, T> RuntimeMessageSender<Message> for T where
|
||||||
|
T: Sink<Message, Error = mpsc::SendError>
|
||||||
|
+ Unpin
|
||||||
|
+ Send
|
||||||
|
+ Clone
|
||||||
|
+ 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod trait_aliases {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait RuntimeMessage: 'static {}
|
||||||
|
|
||||||
|
impl<T> RuntimeMessage for T where T: 'static {}
|
||||||
|
|
||||||
|
pub trait RuntimeMessageSender<Message: RuntimeMessage>:
|
||||||
|
Sink<Message, Error = mpsc::SendError> + Unpin + Clone + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message: RuntimeMessage, T> RuntimeMessageSender<Message> for T where
|
||||||
|
T: Sink<Message, Error = mpsc::SendError> + Unpin + Clone + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use trait_aliases::{RuntimeMessage, RuntimeMessageSender};
|
||||||
|
|
||||||
/// A batteries-included runtime of commands and subscriptions.
|
/// A batteries-included runtime of commands and subscriptions.
|
||||||
///
|
///
|
||||||
/// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any
|
/// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any
|
||||||
|
|
@ -23,9 +67,8 @@ where
|
||||||
Hasher: std::hash::Hasher + Default,
|
Hasher: std::hash::Hasher + Default,
|
||||||
Event: Send + Clone + 'static,
|
Event: Send + Clone + 'static,
|
||||||
Executor: self::Executor,
|
Executor: self::Executor,
|
||||||
Sender:
|
Sender: RuntimeMessageSender<Message>,
|
||||||
Sink<Message, Error = mpsc::SendError> + Unpin + Send + Clone + 'static,
|
Message: RuntimeMessage,
|
||||||
Message: Send + 'static,
|
|
||||||
{
|
{
|
||||||
/// Creates a new empty [`Runtime`].
|
/// Creates a new empty [`Runtime`].
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,50 @@ use crate::{BoxFuture, Subscription};
|
||||||
use futures::{channel::mpsc, sink::Sink};
|
use futures::{channel::mpsc, sink::Sink};
|
||||||
use std::{collections::HashMap, marker::PhantomData};
|
use std::{collections::HashMap, marker::PhantomData};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod trait_aliases {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait TrackerMessage: Send + 'static {}
|
||||||
|
|
||||||
|
impl<T> TrackerMessage for T where T: Send + 'static {}
|
||||||
|
|
||||||
|
pub trait TrackerMessageReceiver<Message: TrackerMessage>:
|
||||||
|
Sink<Message, Error = mpsc::SendError> + Unpin + Send + Clone + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message: TrackerMessage, T> TrackerMessageReceiver<Message> for T where
|
||||||
|
T: Sink<Message, Error = mpsc::SendError>
|
||||||
|
+ Unpin
|
||||||
|
+ Send
|
||||||
|
+ Clone
|
||||||
|
+ 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod trait_aliases {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait TrackerMessage: 'static {}
|
||||||
|
|
||||||
|
impl<T> TrackerMessage for T where T: 'static {}
|
||||||
|
|
||||||
|
pub trait TrackerMessageReceiver<Message: TrackerMessage>:
|
||||||
|
Sink<Message, Error = mpsc::SendError> + Unpin + Clone + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message: TrackerMessage, T> TrackerMessageReceiver<Message> for T where
|
||||||
|
T: Sink<Message, Error = mpsc::SendError> + Unpin + Clone + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use trait_aliases::{TrackerMessage, TrackerMessageReceiver};
|
||||||
|
|
||||||
/// A registry of subscription streams.
|
/// A registry of subscription streams.
|
||||||
///
|
///
|
||||||
/// If you have an application that continuously returns a [`Subscription`],
|
/// If you have an application that continuously returns a [`Subscription`],
|
||||||
|
|
@ -57,12 +101,8 @@ where
|
||||||
receiver: Receiver,
|
receiver: Receiver,
|
||||||
) -> Vec<BoxFuture<()>>
|
) -> Vec<BoxFuture<()>>
|
||||||
where
|
where
|
||||||
Message: 'static + Send,
|
Message: TrackerMessage,
|
||||||
Receiver: 'static
|
Receiver: TrackerMessageReceiver<Message>,
|
||||||
+ Sink<Message, Error = mpsc::SendError>
|
|
||||||
+ Unpin
|
|
||||||
+ Send
|
|
||||||
+ Clone,
|
|
||||||
{
|
{
|
||||||
use futures::{future::FutureExt, stream::StreamExt};
|
use futures::{future::FutureExt, stream::StreamExt};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,15 @@ impl Pipeline {
|
||||||
.expect("Load fallback font")
|
.expect("Load fallback font")
|
||||||
});
|
});
|
||||||
|
|
||||||
let draw_brush =
|
let draw_brush_builder =
|
||||||
glow_glyph::GlyphBrushBuilder::using_font(font.clone())
|
wgpu_glyph::GlyphBrushBuilder::using_font(font.clone())
|
||||||
.initial_cache_size((2048, 2048))
|
.initial_cache_size((2048, 2048))
|
||||||
.draw_cache_multithread(multithreading)
|
.draw_cache_multithread(multithreading);
|
||||||
.build(&gl);
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let draw_brush_builder = draw_brush_builder.draw_cache_align_4x4(true);
|
||||||
|
|
||||||
|
let draw_brush = draw_brush_builder.build(&gl);
|
||||||
|
|
||||||
let measure_brush =
|
let measure_brush =
|
||||||
glyph_brush::GlyphBrushBuilder::using_font(font).build();
|
glyph_brush::GlyphBrushBuilder::using_font(font).build();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,37 @@ use iced_futures::BoxStream;
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod trait_aliases {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Wrapper type
|
||||||
|
pub trait RunnerStream<Message>:
|
||||||
|
Stream<Item = Message> + Send + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Message> RunnerStream<Message> for T where
|
||||||
|
T: Stream<Item = Message> + Send + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod trait_aliases {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Wrapper type
|
||||||
|
pub trait RunnerStream<Message>: Stream<Item = Message> + 'static {}
|
||||||
|
|
||||||
|
impl<T, Message> RunnerStream<Message> for T where
|
||||||
|
T: Stream<Item = Message> + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use trait_aliases::RunnerStream;
|
||||||
|
|
||||||
/// A request to listen to external events.
|
/// A request to listen to external events.
|
||||||
///
|
///
|
||||||
/// Besides performing async actions on demand with [`Command`], most
|
/// Besides performing async actions on demand with [`Command`], most
|
||||||
|
|
@ -191,7 +222,7 @@ impl<I, S, F, Message> Recipe<Hasher, (Event, event::Status)>
|
||||||
where
|
where
|
||||||
I: Hash + 'static,
|
I: Hash + 'static,
|
||||||
F: FnOnce(EventStream) -> S,
|
F: FnOnce(EventStream) -> S,
|
||||||
S: Stream<Item = Message> + Send + 'static,
|
S: RunnerStream<Message>,
|
||||||
{
|
{
|
||||||
type Output = Message;
|
type Output = Message;
|
||||||
|
|
||||||
|
|
@ -203,6 +234,13 @@ where
|
||||||
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
|
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
(self.spawn)(input).boxed()
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
(self.spawn)(input).boxed_local()
|
||||||
|
}
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
(self.spawn)(input).boxed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ canvas = ["iced_graphics/canvas"]
|
||||||
qr_code = ["iced_graphics/qr_code"]
|
qr_code = ["iced_graphics/qr_code"]
|
||||||
default_system_font = ["iced_graphics/font-source"]
|
default_system_font = ["iced_graphics/font-source"]
|
||||||
spirv = ["wgpu/spirv"]
|
spirv = ["wgpu/spirv"]
|
||||||
|
webgl = ["wgpu/webgl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wgpu = "0.12"
|
wgpu = "0.12"
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,15 @@ impl Pipeline {
|
||||||
.expect("Load fallback font")
|
.expect("Load fallback font")
|
||||||
});
|
});
|
||||||
|
|
||||||
let draw_brush =
|
let draw_brush_builder =
|
||||||
wgpu_glyph::GlyphBrushBuilder::using_font(font.clone())
|
wgpu_glyph::GlyphBrushBuilder::using_font(font.clone())
|
||||||
.initial_cache_size((2048, 2048))
|
.initial_cache_size((2048, 2048))
|
||||||
.draw_cache_multithread(multithreading)
|
.draw_cache_multithread(multithreading);
|
||||||
.build(device, format);
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let draw_brush_builder = draw_brush_builder.draw_cache_align_4x4(true);
|
||||||
|
|
||||||
|
let draw_brush = draw_brush_builder.build(device, format);
|
||||||
|
|
||||||
let measure_brush =
|
let measure_brush =
|
||||||
glyph_brush::GlyphBrushBuilder::using_font(font).build();
|
glyph_brush::GlyphBrushBuilder::using_font(font).build();
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ categories = ["gui"]
|
||||||
debug = ["iced_native/debug"]
|
debug = ["iced_native/debug"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
window_clipboard = "0.2"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
|
||||||
|
|
@ -37,3 +36,6 @@ path = "../futures"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.window_clipboard]
|
||||||
|
version = "0.2"
|
||||||
|
|
|
||||||
|
|
@ -115,12 +115,16 @@ where
|
||||||
use futures::task;
|
use futures::task;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use winit::platform::run_return::EventLoopExtRunReturn;
|
use winit::platform::run_return::EventLoopExtRunReturn;
|
||||||
|
|
||||||
let mut debug = Debug::new();
|
let mut debug = Debug::new();
|
||||||
debug.startup_started();
|
debug.startup_started();
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
let mut event_loop = EventLoop::with_user_event();
|
let mut event_loop = EventLoop::with_user_event();
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let event_loop = EventLoop::with_user_event();
|
||||||
let mut proxy = event_loop.create_proxy();
|
let mut proxy = event_loop.create_proxy();
|
||||||
|
|
||||||
let mut runtime = {
|
let mut runtime = {
|
||||||
|
|
@ -179,41 +183,81 @@ where
|
||||||
|
|
||||||
let mut context = task::Context::from_waker(task::noop_waker_ref());
|
let mut context = task::Context::from_waker(task::noop_waker_ref());
|
||||||
|
|
||||||
event_loop.run_return(move |event, _, control_flow| {
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use winit::event_loop::ControlFlow;
|
{
|
||||||
|
event_loop.run_return(move |event, _, control_flow| {
|
||||||
|
use winit::event_loop::ControlFlow;
|
||||||
|
|
||||||
if let ControlFlow::Exit = control_flow {
|
if let ControlFlow::Exit = control_flow {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = match event {
|
let event = match event {
|
||||||
winit::event::Event::WindowEvent {
|
winit::event::Event::WindowEvent {
|
||||||
event:
|
event:
|
||||||
winit::event::WindowEvent::ScaleFactorChanged {
|
winit::event::WindowEvent::ScaleFactorChanged {
|
||||||
new_inner_size,
|
new_inner_size,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
window_id,
|
window_id,
|
||||||
} => Some(winit::event::Event::WindowEvent {
|
} => Some(winit::event::Event::WindowEvent {
|
||||||
event: winit::event::WindowEvent::Resized(*new_inner_size),
|
event: winit::event::WindowEvent::Resized(*new_inner_size),
|
||||||
window_id,
|
window_id,
|
||||||
}),
|
}),
|
||||||
_ => event.to_static(),
|
_ => event.to_static(),
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(event) = event {
|
|
||||||
sender.start_send(event).expect("Send event");
|
|
||||||
|
|
||||||
let poll = instance.as_mut().poll(&mut context);
|
|
||||||
|
|
||||||
*control_flow = match poll {
|
|
||||||
task::Poll::Pending => ControlFlow::Wait,
|
|
||||||
task::Poll::Ready(_) => ControlFlow::Exit,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
if let Some(event) = event {
|
||||||
|
sender.start_send(event).expect("Send event");
|
||||||
|
|
||||||
|
let poll = instance.as_mut().poll(&mut context);
|
||||||
|
|
||||||
|
*control_flow = match poll {
|
||||||
|
task::Poll::Pending => ControlFlow::Wait,
|
||||||
|
task::Poll::Ready(_) => ControlFlow::Exit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
use winit::event_loop::ControlFlow;
|
||||||
|
|
||||||
|
if let ControlFlow::Exit = control_flow {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let event = match event {
|
||||||
|
winit::event::Event::WindowEvent {
|
||||||
|
event:
|
||||||
|
winit::event::WindowEvent::ScaleFactorChanged {
|
||||||
|
new_inner_size,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
} => Some(winit::event::Event::WindowEvent {
|
||||||
|
event: winit::event::WindowEvent::Resized(*new_inner_size),
|
||||||
|
window_id,
|
||||||
|
}),
|
||||||
|
_ => event.to_static(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(event) = event {
|
||||||
|
sender.start_send(event).expect("Send event");
|
||||||
|
|
||||||
|
let poll = instance.as_mut().poll(&mut context);
|
||||||
|
|
||||||
|
*control_flow = match poll {
|
||||||
|
task::Poll::Pending => ControlFlow::Wait,
|
||||||
|
task::Poll::Ready(_) => ControlFlow::Exit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_instance<A, E, C>(
|
async fn run_instance<A, E, C>(
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,40 @@ use crate::command::{self, Command};
|
||||||
/// A buffer for short-term storage and transfer within and between
|
/// A buffer for short-term storage and transfer within and between
|
||||||
/// applications.
|
/// applications.
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub struct Clipboard;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
impl Clipboard {
|
||||||
|
/// Creates a new [`Clipboard`] for the given window.
|
||||||
|
pub fn connect(_window: &winit::window::Window) -> Clipboard {
|
||||||
|
Clipboard
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the current content of the [`Clipboard`] as text.
|
||||||
|
pub fn read(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the given text contents to the [`Clipboard`].
|
||||||
|
pub fn write(&mut self, _contents: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A buffer for short-term storage and transfer within and between
|
||||||
|
/// applications.
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub struct Clipboard {
|
pub struct Clipboard {
|
||||||
state: State,
|
state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
enum State {
|
enum State {
|
||||||
Connected(window_clipboard::Clipboard),
|
Connected(window_clipboard::Clipboard),
|
||||||
Unavailable,
|
Unavailable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
impl Clipboard {
|
impl Clipboard {
|
||||||
/// Creates a new [`Clipboard`] for the given window.
|
/// Creates a new [`Clipboard`] for the given window.
|
||||||
pub fn connect(window: &winit::window::Window) -> Clipboard {
|
pub fn connect(window: &winit::window::Window) -> Clipboard {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue