You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

962 lines
34 KiB
Rust

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

use crate::context::DrawContext;
use crate::controller::Controller;
use crate::font::FontCache;
use crate::scene::*;
use crate::wayland::*;
use crate::*;
use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle, RegistrationToken};
use smithay_client_toolkit::seat::keyboard::ModifiersState;
use smithay_client_toolkit::shm::AutoMemPool;
use smithay_client_toolkit::WaylandSource;
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use smithay_client_toolkit::reexports::client::{
global_filter,
protocol::wl_buffer::WlBuffer,
protocol::wl_callback,
protocol::wl_compositor::WlCompositor,
protocol::wl_output::{self, WlOutput},
protocol::wl_pointer::{self, WlPointer},
protocol::wl_region::WlRegion,
protocol::wl_seat::{self, Capability, WlSeat},
protocol::wl_shm::WlShm,
protocol::wl_surface::WlSurface,
Attached, Display, GlobalError, GlobalManager, Interface, Main, Proxy,
};
use smithay_client_toolkit::reexports::protocols::wlr::unstable::layer_shell::v1::client::{
zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_surface_v1,
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
};
pub struct Application<M, C>
where
C: Controller<M> + Clone + 'static,
{
display: Display,
globals: Rc<RefCell<Globals>>,
global_manager: GlobalManager,
pub inner: Vec<InnerApplication<M, C>>,
token: RegistrationToken,
}
struct Context {
pending_cb: bool,
time: Option<u32>,
render_node: Option<RenderNode>,
font_cache: FontCache,
}
pub struct CoreApplication<M, C>
where
C: Controller<M> + Clone,
{
pub controller: C,
ctx: Context,
globals: Rc<RefCell<Globals>>,
mempool: AutoMemPool,
widget: Box<dyn Widget<M>>,
surface: Option<Surface>,
}
pub struct InnerApplication<M, C>
where
C: Controller<M> + Clone,
{
core: CoreApplication<M, C>,
cb: Box<dyn FnMut(&mut CoreApplication<M, C>, Event<M>)>,
}
impl Surface {
fn new(
surface: Main<WlSurface>,
shell: Shell,
region: Main<WlRegion>,
previous: Option<Surface>,
) -> Self {
Surface {
alive: true,
surface,
shell,
region,
previous: if let Some(surface) = previous {
Some(Box::new(surface))
} else {
None
},
buffer: None,
}
}
fn commit(&mut self) {
self.surface.commit();
std::mem::drop(&mut self.previous);
self.previous = None;
}
fn destroy(&mut self) {
self.alive = false;
self.surface.destroy();
self.region.destroy();
self.shell.destroy();
if let Some(buffer) = self.buffer.as_ref() {
buffer.destroy();
}
self.buffer = None;
}
fn destroy_previous(&mut self) {
if let Some(surface) = self.previous.as_mut() {
surface.destroy();
}
self.previous = None;
}
fn set_size(&self, width: u32, height: u32) {
self.shell.set_size(width, height);
}
fn damage(&self, report: &[Region]) {
self.surface.attach(self.buffer.as_ref(), 0, 0);
for d in report {
self.surface
.damage(d.x as i32, d.y as i32, d.width as i32, d.height as i32);
}
}
fn attach_buffer(&mut self, buffer: WlBuffer) {
self.buffer = Some(buffer);
}
}
impl Globals {
fn new() -> Self {
Self {
outputs: Vec::new(),
seats: Vec::new(),
shm: None,
compositor: None,
shell: None,
}
}
pub fn create_shell_surface_from<M, C>(
&self,
geometry: &dyn Widget<M>,
config: ShellConfig,
previous: Option<Surface>,
) -> Option<Surface>
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
if self.compositor.is_some() {
match config {
ShellConfig::LayerShell(config) => {
if let Some(layer_shell) = self.shell.as_ref() {
let region = self.compositor.as_ref().unwrap().create_region();
let wl_surface = self.compositor.as_ref().unwrap().create_surface();
let layer_surface = layer_shell.get_layer_surface(
&wl_surface,
config.output.as_ref(),
config.layer,
config.namespace.clone(),
);
if let Some(anchor) = &config.anchor {
layer_surface.set_anchor(*anchor);
}
wl_surface.quick_assign(|_, _, _| {});
layer_surface.set_exclusive_zone(config.exclusive);
layer_surface.set_keyboard_interactivity(config.interactivity);
layer_surface.set_size(geometry.width() as u32, geometry.height() as u32);
layer_surface.set_margin(
config.margin[0],
config.margin[1],
config.margin[2],
config.margin[3],
);
wl_surface.commit();
assign_surface::<M, C>(&layer_surface);
return Some(Surface::new(
wl_surface,
Shell::LayerShell {
surface: layer_surface,
config,
},
region,
previous,
));
}
}
}
}
None
}
pub fn create_mempool(&self) -> AutoMemPool {
let attached = Attached::from(self.shm.clone().unwrap());
AutoMemPool::new(attached).unwrap()
}
pub fn get_outputs(&self) -> Vec<Output> {
self.outputs.clone()
}
pub fn get_seats(&self) -> &[Seat] {
&self.seats
}
}
impl Output {
fn new(output: Main<WlOutput>) -> Self {
Output {
width: 0,
height: 0,
scale: 1,
name: String::new(),
output,
}
}
}
impl<M, C> Application<M, C>
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
pub fn new(pointer: bool) -> (Self, EventLoop<'static, Self>) {
let display = Display::connect_to_env().unwrap();
let event_queue = display.create_event_queue();
let attached_display = (*display).clone().attach(event_queue.token());
let display_handle = display.clone();
let globals = Globals::new();
let global_manager = GlobalManager::new_with_cb(
&attached_display,
global_filter!(
[
ZwlrLayerShellV1,
1,
|layer_shell: Main<ZwlrLayerShellV1>, mut application: DispatchData| {
if let Some(application) = application.get::<Application<M, C>>() {
if let Ok(mut globals) = application.globals.try_borrow_mut() {
globals.shell = Some(layer_shell);
}
}
}
],
[
WlShm,
1,
|shm: Main<WlShm>, mut application: DispatchData| {
shm.quick_assign(|_, _, _| {});
if let Some(application) = application.get::<Application<M, C>>() {
if let Ok(mut globals) = application.globals.try_borrow_mut() {
globals.shm = Some(shm);
}
}
}
],
[
WlCompositor,
4,
|compositor: Main<WlCompositor>, mut application: DispatchData| {
if let Some(application) = application.get::<Application<M, C>>() {
if let Ok(mut globals) = application.globals.try_borrow_mut() {
globals.compositor = Some(compositor);
}
}
}
],
[WlSeat, 7, move |seat: Main<WlSeat>, _: DispatchData| {
seat.quick_assign(move |wl_seat, event, mut application| match event {
wl_seat::Event::Capabilities { capabilities } => {
if let Some(application) = application.get::<Application<M, C>>() {
if pointer
&& capabilities & Capability::Pointer == Capability::Pointer
{
let pointer = wl_seat.get_pointer();
assign_pointer::<M, C>(&pointer);
}
if let Ok(mut globals) = application.globals.try_borrow_mut() {
let mut found = None;
for seat in &mut globals.seats {
if wl_seat.eq(&seat.seat) {
found = Some(());
seat.capabilities = capabilities;
}
}
if found.is_none() {
globals.seats.push(Seat {
capabilities,
seat: wl_seat,
});
}
}
}
}
_ => {}
});
}],
[
WlOutput,
3,
|output: Main<WlOutput>, _application: DispatchData| {
output.quick_assign(move |wl_output, event, mut application| match event {
wl_output::Event::Geometry {
x: _,
y: _,
physical_width: _,
physical_height: _,
subpixel: _,
make,
model: _,
transform: _,
} => {
if let Some(application) = application.get::<Application<M, C>>() {
if let Ok(mut globals) = application.globals.try_borrow_mut() {
let mut found = None;
for output in &mut globals.outputs {
if wl_output.eq(&output.output) {
found = Some(());
output.name = make.clone();
}
}
if found.is_none() {
let mut output = Output::new(wl_output);
output.name = make;
globals.outputs.push(output);
}
}
}
}
wl_output::Event::Mode {
flags: _,
width,
height,
refresh: _,
} => {
if let Some(application) = application.get::<Application<M, C>>() {
if let Ok(mut globals) = application.globals.try_borrow_mut() {
let mut found = None;
for output in &mut globals.outputs {
if wl_output.eq(&output.output) {
found = Some(());
output.width = width;
output.height = height;
}
}
if found.is_none() {
let mut output = Output::new(wl_output);
output.width = width;
output.height = height;
globals.outputs.push(output);
}
}
}
}
wl_output::Event::Scale { factor } => {
if let Some(application) = application.get::<Application<M, C>>() {
if let Ok(mut globals) = application.globals.try_borrow_mut() {
let mut found = None;
for output in &mut globals.outputs {
if wl_output.eq(&output.output) {
found = Some(());
output.scale = factor;
}
}
if found.is_none() {
let mut output = Output::new(wl_output);
output.scale = factor;
globals.outputs.push(output);
}
}
}
}
wl_output::Event::Done => {}
_ => {}
});
}
]
),
);
let event_loop = EventLoop::try_new().expect("Failed to initialize the event loop!");
let token = WaylandSource::new(event_queue)
.quick_insert(event_loop.handle())
.unwrap();
let (mut application, mut event_loop) = (
Application {
display,
globals: Rc::new(RefCell::new(globals)),
global_manager,
inner: Vec::new(),
token,
},
event_loop,
);
for _ in 0..2 {
display_handle.flush().unwrap();
event_loop.dispatch(None, &mut application).unwrap();
}
(application, event_loop)
}
fn get_index(&self, surface: &WlSurface) -> usize {
for i in 0..self.inner.len() {
if self.inner[i].eq(surface) {
return i;
}
}
0
}
fn get_application(&mut self, surface: &WlSurface) -> Option<&mut InnerApplication<M, C>> {
for inner in &mut self.inner {
if inner.eq(surface) {
return Some(inner);
}
}
None
}
pub fn get_global<I>(&self) -> Result<Main<I>, GlobalError>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
{
self.global_manager.instantiate_range::<I>(0, 1 << 8)
}
pub fn create_empty_inner_application<Data: 'static>(
&mut self,
controller: C,
widget: impl Widget<M> + 'static,
handle: LoopHandle<'_, Data>,
cb: impl FnMut(&mut CoreApplication<M, C>, Event<M>) + 'static,
) {
let inner_application =
InnerApplication::empty(controller, widget, self.globals.clone(), cb);
self.inner.push(inner_application);
handle.update(&self.token).unwrap();
}
pub fn create_inner_application_from<Data: 'static>(
&mut self,
controller: C,
config: ShellConfig,
widget: impl Widget<M> + 'static,
handle: LoopHandle<'_, Data>,
cb: impl FnMut(&mut CoreApplication<M, C>, Event<M>) + 'static,
) {
let inner_application =
InnerApplication::new(controller, widget, config, self.globals.clone(), cb);
self.inner.push(inner_application);
handle.update(&self.token).unwrap();
}
pub fn create_inner_application<Data: 'static>(
&mut self,
controller: C,
widget: impl Widget<M> + 'static,
handle: LoopHandle<'_, Data>,
cb: impl FnMut(&mut CoreApplication<M, C>, Event<M>) + 'static,
) {
let inner_application =
InnerApplication::normal(controller, widget, self.globals.clone(), cb);
self.inner.push(inner_application);
handle.update(&self.token).unwrap();
}
pub fn run(mut self, event_loop: &mut EventLoop<'static, Self>) {
loop {
self.display.flush().unwrap();
event_loop.dispatch(None, &mut self).unwrap();
}
}
}
impl<M, C> Deref for InnerApplication<M, C>
where
C: Controller<M> + Clone + 'static,
{
type Target = CoreApplication<M, C>;
fn deref(&self) -> &Self::Target {
&self.core
}
}
impl<M, C> DerefMut for InnerApplication<M, C>
where
C: Controller<M> + Clone + 'static,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.core
}
}
impl<M, C> CoreApplication<M, C>
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
pub fn poll(&mut self, ev: Event<M>) -> C {
let mut ctl = self.controller.clone();
let mut sync_ctx = SyncContext::new(&mut ctl, &mut self.ctx.font_cache);
self.widget.sync(&mut sync_ctx, ev);
ctl
}
pub fn sync(&mut self, ev: Event<M>) -> bool {
let mut sync_ctx = SyncContext::new(&mut self.controller, &mut self.ctx.font_cache);
let mut damage = self.widget.sync(&mut sync_ctx, ev);
while let Ok(msg) = sync_ctx.sync() {
damage = damage.max(self.widget.sync(&mut sync_ctx, Event::Message(&msg)));
}
if damage == Damage::Frame {
if self.ctx.time.is_none() {
self.ctx.time = Some(0);
}
}
damage.is_some() && !self.ctx.pending_cb
}
pub fn destroy(&mut self) {
if let Some(surface) = self.surface.as_mut() {
surface.destroy();
}
}
pub fn get_layer_surface(&self) -> ZwlrLayerSurfaceV1 {
match &self.surface.as_ref().unwrap().shell {
Shell::LayerShell { config: _, surface } => surface.detach(),
}
}
pub fn is_hidden(&self) -> bool {
if let Some(surface) = self.surface.as_ref() {
return !surface.alive;
} else {
true
}
}
pub fn replace_surface(&mut self) {
if let Some(surface) = self.surface.as_mut() {
surface.destroy();
surface.alive = true;
match &surface.shell {
Shell::LayerShell { config, surface: _ } => {
self.surface = self.globals.borrow().create_shell_surface_from::<M, C>(
self.widget.deref(),
ShellConfig::LayerShell(config.clone()),
Some(surface.clone()),
);
}
}
} else {
self.surface = self.globals.borrow().create_shell_surface_from::<M, C>(
self.widget.deref(),
ShellConfig::LayerShell(LayerShellConfig::default()),
None,
);
}
}
pub fn replace_surface_by(&mut self, config: ShellConfig) {
if let Some(surface) = self.surface.as_mut() {
surface.destroy();
surface.alive = true;
self.surface = self.globals.borrow().create_shell_surface_from::<M, C>(
self.widget.deref(),
config,
Some(surface.clone()),
);
} else {
self.surface = self.globals.borrow().create_shell_surface_from::<M, C>(
self.widget.deref(),
config,
None,
);
}
}
}
impl<M, C> Geometry for InnerApplication<M, C>
where
C: Controller<M> + Clone + 'static,
{
fn width(&self) -> f32 {
self.widget.width()
}
fn height(&self) -> f32 {
self.widget.height()
}
fn set_size(&mut self, width: f32, height: f32) -> Result<(), (f32, f32)> {
if let Some(surface) = self.surface.as_ref() {
surface.set_size(width as u32, height as u32);
surface.surface.commit();
}
Ok(())
}
}
impl<M, C> InnerApplication<M, C>
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
pub fn empty(
controller: C,
widget: impl Widget<M> + 'static,
globals: Rc<RefCell<Globals>>,
cb: impl FnMut(&mut CoreApplication<M, C>, Event<M>) + 'static,
) -> Self {
let mempool = globals.borrow().create_mempool();
let mut default = InnerApplication {
core: CoreApplication {
controller,
ctx: Context {
pending_cb: false,
time: None,
font_cache: FontCache::new(),
render_node: None,
},
surface: None,
widget: Box::new(widget),
mempool,
globals,
},
cb: Box::new(cb),
};
default.sync(Event::Prepare);
default
}
pub fn normal(
controller: C,
widget: impl Widget<M> + 'static,
globals: Rc<RefCell<Globals>>,
cb: impl FnMut(&mut CoreApplication<M, C>, Event<M>) + 'static,
) -> Self {
let mempool = globals.borrow().create_mempool();
let mut default = InnerApplication {
core: CoreApplication {
controller,
ctx: Context {
pending_cb: false,
time: None,
font_cache: FontCache::new(),
render_node: None,
},
surface: None,
widget: Box::new(widget),
mempool,
globals,
},
cb: Box::new(cb),
};
default.sync(Event::Prepare);
default.replace_surface();
default
}
pub fn new(
controller: C,
widget: impl Widget<M> + 'static,
config: ShellConfig,
globals: Rc<RefCell<Globals>>,
cb: impl FnMut(&mut CoreApplication<M, C>, Event<M>) + 'static,
) -> Self {
let mempool = globals.borrow().create_mempool();
let mut new = InnerApplication {
core: CoreApplication {
controller,
ctx: Context {
pending_cb: false,
time: None,
font_cache: FontCache::new(),
render_node: None,
},
surface: None,
widget: Box::new(widget),
mempool,
globals,
},
cb: Box::new(cb),
};
new.sync(Event::Prepare);
new.replace_surface_by(config);
new
}
fn eq(&self, wl_surface: &WlSurface) -> bool {
if let Some(surface) = &self.surface {
return surface.surface.detach().eq(wl_surface);
}
false
}
pub fn roundtrip(&mut self, ev: Event<M>) -> Result<RenderNode, ()> {
let width = self.width();
let height = self.height();
// Sending the event to the widget tree
if self.sync(ev) || ev.is_frame() {
// Calling the application´s closure
(self.cb)(&mut self.core, ev);
if !self.is_hidden() {
let current_width = self.width();
let current_height = self.height();
// Resizing the surface in case the widget changed size
if ev.is_frame() {
self.ctx.render_node = None;
} else if width != current_width || height != current_height {
let _ = self.set_size(current_width, current_height);
return Err(());
}
// Creating the render node
let render_node = self.core.widget.create_node(0., 0.);
self.ctx.pending_cb = true;
return Ok(render_node);
}
} else {
// Calling the application´s closure
(self.cb)(&mut self.core, ev);
}
Err(())
}
fn render(&mut self, time: u32, recent_node: RenderNode) {
let width = recent_node.width();
let height = recent_node.height();
if Some(time).ne(&self.core.ctx.time) || time == 0 {
if let Ok((buffer, wl_buffer)) =
Buffer::new(&mut self.core.mempool, width as i32, height as i32)
{
let mut v = Vec::new();
let mut ctx =
DrawContext::new(buffer.backend, &mut self.core.ctx.font_cache, &mut v);
if let Some(render_node) = self.core.ctx.render_node.as_mut() {
if let Err(region) = render_node.draw_merge(
recent_node,
&mut ctx,
&Instruction::empty(0., 0., width, height),
None,
) {
ctx.damage_region(&Background::Transparent, region, false);
}
} else {
ctx.damage_region(
&Background::Transparent,
Region::new(0., 0., width, height),
false,
);
recent_node.render(&mut ctx, None);
self.core.ctx.render_node = Some(recent_node);
}
self.core.ctx.pending_cb = false;
if let Some(surface) = self.core.surface.as_mut() {
surface.attach_buffer(wl_buffer);
surface.damage(&v);
surface.commit();
if let Some(_) = self.core.ctx.time {
self.core.ctx.time = Some(time);
frame_callback::<M, C>(time, surface.surface.clone());
}
}
}
}
}
pub fn callback(&mut self, ev: Event<M>) {
if self.ctx.time.is_none() || ev.is_cb() {
if let Ok(render_node) = self.roundtrip(ev) {
if let Some(surface) = self.surface.as_ref() {
draw_callback::<M, C>(&surface.surface, render_node);
}
}
} else {
let width = self.width();
let height = self.height();
self.sync(ev);
let current_width = self.width();
let current_height = self.height();
// Resizing the surface in case the widget changed size
if width != current_width || height != current_height {
let _ = self.set_size(current_width, current_height);
}
}
}
}
fn frame_callback<M, C>(time: u32, surface: Main<WlSurface>)
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
let h = surface.detach();
surface
.frame()
.quick_assign(move |_, event, mut application| match event {
wl_callback::Event::Done { callback_data } => {
let timeout = (callback_data - time).min(50);
if let Some(application) = application.get::<Application<M, C>>() {
if let Some(inner_application) = application.get_application(&h) {
inner_application.ctx.time = None;
inner_application.callback(Event::Callback(timeout));
}
}
}
_ => {}
});
surface.commit();
}
fn draw_callback<M, C>(surface: &Main<WlSurface>, mut recent_node: RenderNode)
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
let h = surface.detach();
surface
.frame()
.quick_assign(move |_, event, mut application| match event {
wl_callback::Event::Done { callback_data } => {
if let Some(application) = application.get::<Application<M, C>>() {
let inner_application = application.get_application(&h).unwrap();
inner_application.render(callback_data, std::mem::take(&mut recent_node));
}
}
_ => {}
});
surface.commit();
}
impl From<ModifiersState> for Modifiers {
fn from(modifer_state: ModifiersState) -> Modifiers {
Modifiers {
ctrl: modifer_state.ctrl,
alt: modifer_state.alt,
shift: modifer_state.shift,
caps_lock: modifer_state.caps_lock,
logo: modifer_state.logo,
num_lock: modifer_state.num_lock,
}
}
}
fn assign_pointer<M, C>(pointer: &Main<WlPointer>)
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
let mut index = 0;
let mut input = Pointer::Enter;
let (mut x, mut y) = (0., 0.);
pointer.quick_assign(move |_, event, mut inner| match event {
wl_pointer::Event::Leave { serial: _, surface } => {
input = Pointer::Leave;
if let Some(application) = inner.get::<Application<M, C>>() {
if let Some(inner_application) = application.get_application(&surface) {
inner_application.callback(Event::Pointer(x as f32, y as f32, input));
}
}
}
wl_pointer::Event::Button {
serial: _,
time,
button,
state,
} => {
input = Pointer::MouseClick {
time,
button: MouseButton::new(button),
pressed: state == wl_pointer::ButtonState::Pressed,
};
}
wl_pointer::Event::Frame => {
if let Some(application) = inner.get::<Application<M, C>>() {
let inner_application = application.inner.get_mut(index).unwrap();
inner_application.callback(Event::Pointer(x as f32, y as f32, input));
}
}
wl_pointer::Event::Axis {
time: _,
axis,
value,
} => {
input = Pointer::Scroll {
orientation: match axis {
wl_pointer::Axis::VerticalScroll => Orientation::Vertical,
wl_pointer::Axis::HorizontalScroll => Orientation::Horizontal,
_ => Orientation::Vertical,
},
value: value as f32,
}
}
wl_pointer::Event::Enter {
serial: _,
surface,
surface_x,
surface_y,
} => {
if let Some(application) = inner.get::<Application<M, C>>() {
x = surface_x;
y = surface_y;
index = application.get_index(&surface);
}
}
wl_pointer::Event::Motion {
time: _,
surface_x,
surface_y,
} => {
x = surface_x;
y = surface_y;
input = Pointer::Hover;
}
_ => {}
});
}
fn assign_surface<M, C>(shell: &Main<ZwlrLayerSurfaceV1>)
where
M: 'static,
C: Controller<M> + Clone + 'static,
{
shell.quick_assign(move |shell, event, mut inner| match event {
zwlr_layer_surface_v1::Event::Configure {
serial,
width,
height,
} => {
shell.ack_configure(serial);
println!("\nCONFIGURE - {} : {} X {}\n", serial, width, height);
if let Some(application) = inner.get::<Application<M, C>>() {
for inner_application in &mut application.inner {
if let Some(app_surface) = inner_application.surface.as_mut() {
match &app_surface.shell {
Shell::LayerShell { config: _, surface } => {
if shell.eq(surface) {
app_surface.destroy_previous();
let _ = inner_application
.widget
.set_size(width as f32, height as f32);
if inner_application.ctx.pending_cb {
if let Ok(render_node) =
inner_application.roundtrip(Event::Frame)
{
draw_callback::<M, C>(
&inner_application
.surface
.as_ref()
.unwrap()
.surface,
render_node,
);
}
} else {
if let Ok(render_node) =
inner_application.roundtrip(Event::Frame)
{
inner_application.render(0, render_node);
}
}
}
}
}
}
}
}
}
_ => unreachable!(),
});
}