wayland: upgrade wayland-window (#339)

* wayland: upgrade wayland-window

This new version of wayland window considerably simplifies the
window handling for winit, meaning much of the previous juggling
is no longer needed, and the windows will appear even if nothing is
drawn.

* wayland: cleanup unused stuff
This commit is contained in:
Victor Berger 2017-11-03 17:35:29 +01:00 committed by GitHub
parent 37a10e6741
commit 61d25be3e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 67 additions and 158 deletions

View file

@ -37,6 +37,5 @@ dwmapi-sys = "0.1"
wayland-client = { version = "0.12.0", features = ["dlopen"] } wayland-client = { version = "0.12.0", features = ["dlopen"] }
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols"] } wayland-protocols = { version = "0.12.0", features = ["unstable_protocols"] }
wayland-kbd = "0.13.0" wayland-kbd = "0.13.0"
wayland-window = "0.12.0" wayland-window = "0.13.0"
tempfile = "2.1"
x11-dl = "2.8" x11-dl = "2.8"

View file

@ -11,12 +11,6 @@ fn main() {
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::NoneCursor, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize]; let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::NoneCursor, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
let mut cursor_idx = 0; let mut cursor_idx = 0;
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
match event { match event {
Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => { Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => {

View file

@ -31,12 +31,6 @@ fn main() {
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
println!("{:?}", event); println!("{:?}", event);

View file

@ -10,12 +10,6 @@ fn main() {
let mut grabbed = false; let mut grabbed = false;
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
println!("{:?}", event); println!("{:?}", event);

View file

@ -9,12 +9,6 @@ fn main() {
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
println!("{:?}", event); println!("{:?}", event);

View file

@ -9,12 +9,6 @@ fn main() {
let mut num_windows = 3; let mut num_windows = 3;
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
match event { match event {
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, window_id } => { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, window_id } => {

View file

@ -10,12 +10,6 @@ fn main() {
let proxy = events_loop.create_proxy(); let proxy = events_loop.create_proxy();
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
std::thread::spawn(move || { std::thread::spawn(move || {
// Wake up the `events_loop` once every second. // Wake up the `events_loop` once every second.
loop { loop {

View file

@ -9,12 +9,6 @@ fn main() {
window.set_title("A fantastic window!"); window.set_title("A fantastic window!");
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
println!("{:?}", event); println!("{:?}", event);

View file

@ -8,12 +8,6 @@ fn main() {
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
if cfg!(target_os = "linux") {
println!("Running this example under wayland may not display a window at all.\n\
This is normal and because this example does not actually draw anything in the window,\
thus the compositor does not display it.");
}
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
println!("{:?}", event); println!("{:?}", event);

View file

@ -121,13 +121,11 @@ pub trait WindowExt {
/// Check if the window is ready for drawing /// Check if the window is ready for drawing
/// ///
/// On wayland, drawing on a surface before the server has configured /// It is a remnant of a previous implementation detail for the
/// it using a special event is illegal. As a result, you should wait /// wayland backend, and is no longer relevant.
/// until this method returns `true`.
/// ///
/// Once it starts returning `true`, it can never return `false` again. /// Always return true.
/// #[deprecated]
/// If the window is X11-based, this will just always return `true`.
fn is_ready(&self) -> bool; fn is_ready(&self) -> bool;
} }
@ -195,10 +193,7 @@ impl WindowExt for Window {
#[inline] #[inline]
fn is_ready(&self) -> bool { fn is_ready(&self) -> bool {
match self.window { true
LinuxWindow::Wayland(ref w) => w.is_ready(),
LinuxWindow::X(_) => true
}
} }
} }

View file

@ -1,8 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fs::File;
use std::io::Write;
use std::os::unix::io::AsRawFd;
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -14,14 +11,12 @@ use super::keyboard::init_keyboard;
use wayland_client::{EnvHandler, EnvNotify, default_connect, EventQueue, EventQueueHandle, Proxy, StateToken}; use wayland_client::{EnvHandler, EnvNotify, default_connect, EventQueue, EventQueueHandle, Proxy, StateToken};
use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor, use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor,
wl_display, wl_registry, wl_output, wl_surface, wl_buffer, wl_display, wl_registry, wl_output, wl_surface,
wl_pointer, wl_keyboard}; wl_pointer, wl_keyboard};
use super::wayland_window::{DecoratedSurface, Shell, init_decorated_surface, DecoratedSurfaceImplementation}; use super::wayland_window::{Frame, Shell, create_frame, FrameImplementation};
use super::wayland_protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6; use super::wayland_protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6;
use super::tempfile;
pub struct EventsLoopSink { pub struct EventsLoopSink {
buffer: VecDeque<::Event> buffer: VecDeque<::Event>
} }
@ -345,10 +340,15 @@ impl EventsLoop {
} }
// process pending resize/refresh // process pending resize/refresh
evq.state().get_mut(&self.store).for_each( evq.state().get_mut(&self.store).for_each(
|newsize, refresh, closed, wid, decorated| { |newsize, refresh, frame_refresh, closed, wid, frame| {
if let (Some((w, h)), Some(decorated)) = (newsize, decorated) { if let Some(frame) = frame {
decorated.resize(w as i32, h as i32); if let Some((w, h)) = newsize {
frame.resize(w as i32, h as i32);
frame.refresh();
sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid); sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid);
} else if frame_refresh {
frame.refresh();
}
} }
if refresh { if refresh {
sink.send_event(::WindowEvent::Refresh, wid); sink.send_event(::WindowEvent::Refresh, wid);
@ -360,50 +360,24 @@ impl EventsLoop {
) )
} }
/// Creates a buffer of given size and assign it to the surface
///
/// This buffer only contains white pixels, and is needed when using wl_shell
/// to make sure the window actually exists and can receive events before the
/// use starts its event loop
fn blank_surface(&self, surface: &wl_surface::WlSurface, width: i32, height: i32) {
let mut tmp = tempfile::tempfile().expect("Failed to create a tmpfile buffer.");
for _ in 0..(width*height) {
tmp.write_all(&[0xff,0xff,0xff,0xff]).unwrap();
}
tmp.flush().unwrap();
let mut evq = self.evq.borrow_mut();
let pool = evq.state()
.get(&self.env_token)
.shm
.create_pool(tmp.as_raw_fd(), width*height*4);
let buffer = pool.create_buffer(0, width, height, width, wl_shm::Format::Argb8888)
.expect("Pool cannot be already dead");
surface.attach(Some(&buffer), 0, 0);
surface.commit();
// the buffer will keep the contents alive as needed
pool.destroy();
// register the buffer for freeing
evq.register(&buffer, free_buffer(), Some(tmp));
}
/// Create a new window with given dimensions /// Create a new window with given dimensions
/// ///
/// Grabs a lock on the event queue in the process /// Grabs a lock on the event queue in the process
pub fn create_window<ID: 'static, F>(&self, width: u32, height: u32, decorated: bool, implem: DecoratedSurfaceImplementation<ID>, idata: F) pub fn create_window<ID: 'static, F>(&self, width: u32, height: u32, implem: FrameImplementation<ID>, idata: F)
-> (wl_surface::WlSurface, DecoratedSurface, bool) -> (wl_surface::WlSurface, Frame)
where F: FnOnce(&wl_surface::WlSurface) -> ID where F: FnOnce(&wl_surface::WlSurface) -> ID
{ {
let (surface, decorated, xdg) = { let (surface, frame) = {
let mut guard = self.evq.borrow_mut(); let mut guard = self.evq.borrow_mut();
let env = guard.state().get(&self.env_token).clone_inner().unwrap(); let env = guard.state().get(&self.env_token).clone_inner().unwrap();
let (shell, xdg) = match guard.state().get(&self.ctxt_token).shell { let shell = match guard.state().get(&self.ctxt_token).shell {
Some(Shell::Wl(ref wl_shell)) => (Shell::Wl(wl_shell.clone().unwrap()), false), Some(Shell::Wl(ref wl_shell)) => Shell::Wl(wl_shell.clone().unwrap()),
Some(Shell::Xdg(ref xdg_shell)) => (Shell::Xdg(xdg_shell.clone().unwrap()), true), Some(Shell::Xdg(ref xdg_shell)) => Shell::Xdg(xdg_shell.clone().unwrap()),
None => unreachable!() None => unreachable!()
}; };
let seat = guard.state().get(&self.ctxt_token).seat.as_ref().and_then(|s| s.clone()); let seat = guard.state().get(&self.ctxt_token).seat.as_ref().and_then(|s| s.clone());
let surface = env.compositor.create_surface(); let surface = env.compositor.create_surface();
let decorated = init_decorated_surface( let frame = create_frame(
&mut guard, &mut guard,
implem, implem,
idata(&surface), idata(&surface),
@ -412,21 +386,12 @@ impl EventsLoop {
&env.subcompositor, &env.subcompositor,
&env.shm, &env.shm,
&shell, &shell,
seat, seat
decorated
).expect("Failed to create a tmpfile buffer."); ).expect("Failed to create a tmpfile buffer.");
(surface, decorated, xdg) (surface, frame)
}; };
if !xdg { (surface, frame)
// if using wl_shell, we need to draw something in order to kickstart
// the event loop
// if using xdg_shell, it is an error to do it now, and the events loop will not
// be stuck. We cannot draw anything before having received an appropriate event
// from the compositor
self.blank_surface(&surface, width as i32, height as i32);
}
(surface, decorated, xdg)
} }
} }
@ -470,15 +435,6 @@ fn xdg_ping_implementation() -> zxdg_shell_v6::Implementation<()> {
} }
} }
fn free_buffer() -> wl_buffer::Implementation<Option<File>> {
wl_buffer::Implementation {
release: |_, data, buffer| {
buffer.destroy();
*data = None;
}
}
}
struct SeatIData { struct SeatIData {
sink: Arc<Mutex<EventsLoopSink>>, sink: Arc<Mutex<EventsLoopSink>>,
pointer: Option<wl_pointer::WlPointer>, pointer: Option<wl_pointer::WlPointer>,

View file

@ -6,7 +6,6 @@ pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorI
extern crate wayland_kbd; extern crate wayland_kbd;
extern crate wayland_window; extern crate wayland_window;
extern crate wayland_protocols; extern crate wayland_protocols;
extern crate tempfile;
use wayland_client::protocol::wl_surface; use wayland_client::protocol::wl_surface;
use wayland_client::Proxy; use wayland_client::Proxy;

View file

@ -8,14 +8,13 @@ use platform::MonitorId as PlatformMonitorId;
use window::MonitorId as RootMonitorId; use window::MonitorId as RootMonitorId;
use super::{EventsLoop, WindowId, make_wid, MonitorId}; use super::{EventsLoop, WindowId, make_wid, MonitorId};
use super::wayland_window::{DecoratedSurface, DecoratedSurfaceImplementation}; use super::wayland_window::{Frame, FrameImplementation, State as FrameState};
use super::event_loop::StateContext; use super::event_loop::StateContext;
pub struct Window { pub struct Window {
surface: wl_surface::WlSurface, surface: wl_surface::WlSurface,
decorated: Arc<Mutex<DecoratedSurface>>, frame: Arc<Mutex<Frame>>,
monitors: Arc<Mutex<MonitorList>>, monitors: Arc<Mutex<MonitorList>>,
ready: Arc<Mutex<bool>>,
size: Arc<Mutex<(u32, u32)>>, size: Arc<Mutex<(u32, u32)>>,
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>), kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
display: Arc<wl_display::WlDisplay>, display: Arc<wl_display::WlDisplay>,
@ -27,24 +26,30 @@ impl Window {
let (width, height) = attributes.dimensions.unwrap_or((800,600)); let (width, height) = attributes.dimensions.unwrap_or((800,600));
// Create the decorated surface // Create the decorated surface
let ready = Arc::new(Mutex::new(false));
let size = Arc::new(Mutex::new((width, height))); let size = Arc::new(Mutex::new((width, height)));
let store_token = evlp.store.clone(); let store_token = evlp.store.clone();
let (surface, mut decorated, xdg) = evlp.create_window( let (surface, mut frame) = evlp.create_window(
width, height, attributes.decorations, decorated_impl(), width, height, decorated_impl(),
|surface| DecoratedIData { |surface| FrameIData {
ready: ready.clone(),
surface: surface.clone().unwrap(), surface: surface.clone().unwrap(),
store_token: store_token.clone() store_token: store_token.clone()
} }
); );
// If we are using xdg, we are not ready yet
{ *ready.lock().unwrap() = !xdg; }
// Check for fullscreen requirements // Check for fullscreen requirements
if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen { if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen {
let info = monitor_id.info.lock().unwrap(); let info = monitor_id.info.lock().unwrap();
decorated.set_fullscreen(Some(&info.output)); frame.set_state(FrameState::Fullscreen(Some(&info.output)));
} else if attributes.maximized {
frame.set_state(FrameState::Maximized);
} }
// set decorations
frame.set_decorate(attributes.decorations);
// min-max dimensions
frame.set_min_size(attributes.min_dimensions.map(|(w, h)| (w as i32, h as i32)));
frame.set_max_size(attributes.max_dimensions.map(|(w, h)| (w as i32, h as i32)));
// setup the monitor tracking // setup the monitor tracking
let monitor_list = Arc::new(Mutex::new(MonitorList::default())); let monitor_list = Arc::new(Mutex::new(MonitorList::default()));
{ {
@ -52,12 +57,9 @@ impl Window {
let idata = (evlp.ctxt_token.clone(), monitor_list.clone()); let idata = (evlp.ctxt_token.clone(), monitor_list.clone());
evq.register(&surface, surface_impl(), idata); evq.register(&surface, surface_impl(), idata);
} }
// a surface commit with no buffer so that the compositor don't
// forget to configure us
surface.commit();
let kill_switch = Arc::new(Mutex::new(false)); let kill_switch = Arc::new(Mutex::new(false));
let decorated = Arc::new(Mutex::new(decorated)); let frame = Arc::new(Mutex::new(frame));
{ {
let mut evq = evlp.evq.borrow_mut(); let mut evq = evlp.evq.borrow_mut();
@ -65,9 +67,10 @@ impl Window {
closed: false, closed: false,
newsize: None, newsize: None,
need_refresh: false, need_refresh: false,
need_frame_refresh: true,
surface: surface.clone().unwrap(), surface: surface.clone().unwrap(),
kill_switch: kill_switch.clone(), kill_switch: kill_switch.clone(),
decorated: Arc::downgrade(&decorated) frame: Arc::downgrade(&frame)
}); });
evq.sync_roundtrip().unwrap(); evq.sync_roundtrip().unwrap();
} }
@ -75,9 +78,8 @@ impl Window {
Ok(Window { Ok(Window {
display: evlp.display.clone(), display: evlp.display.clone(),
surface: surface, surface: surface,
decorated: decorated, frame: frame,
monitors: monitor_list, monitors: monitor_list,
ready: ready,
size: size, size: size,
kill_switch: (kill_switch, evlp.cleanup_needed.clone()) kill_switch: (kill_switch, evlp.cleanup_needed.clone())
}) })
@ -89,7 +91,7 @@ impl Window {
} }
pub fn set_title(&self, title: &str) { pub fn set_title(&self, title: &str) {
self.decorated.lock().unwrap().set_title(title.into()); self.frame.lock().unwrap().set_title(title.into());
} }
#[inline] #[inline]
@ -127,7 +129,7 @@ impl Window {
#[inline] #[inline]
// NOTE: This will only resize the borders, the contents must be updated by the user // NOTE: This will only resize the borders, the contents must be updated by the user
pub fn set_inner_size(&self, x: u32, y: u32) { pub fn set_inner_size(&self, x: u32, y: u32) {
self.decorated.lock().unwrap().resize(x as i32, y as i32); self.frame.lock().unwrap().resize(x as i32, y as i32);
*(self.size.lock().unwrap()) = (x, y); *(self.size.lock().unwrap()) = (x, y);
} }
@ -178,10 +180,6 @@ impl Window {
let guard = self.monitors.lock().unwrap(); let guard = self.monitors.lock().unwrap();
guard.monitors.last().unwrap().clone() guard.monitors.last().unwrap().clone()
} }
pub fn is_ready(&self) -> bool {
*self.ready.lock().unwrap()
}
} }
impl Drop for Window { impl Drop for Window {
@ -199,9 +197,10 @@ struct InternalWindow {
surface: wl_surface::WlSurface, surface: wl_surface::WlSurface,
newsize: Option<(i32, i32)>, newsize: Option<(i32, i32)>,
need_refresh: bool, need_refresh: bool,
need_frame_refresh: bool,
closed: bool, closed: bool,
kill_switch: Arc<Mutex<bool>>, kill_switch: Arc<Mutex<bool>>,
decorated: Weak<Mutex<DecoratedSurface>> frame: Weak<Mutex<Frame>>
} }
pub struct WindowStore { pub struct WindowStore {
@ -235,14 +234,15 @@ impl WindowStore {
} }
pub fn for_each<F>(&mut self, mut f: F) pub fn for_each<F>(&mut self, mut f: F)
where F: FnMut(Option<(i32, i32)>, bool, bool, WindowId, Option<&mut DecoratedSurface>) where F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut Frame>)
{ {
for window in &mut self.windows { for window in &mut self.windows {
let opt_arc = window.decorated.upgrade(); let opt_arc = window.frame.upgrade();
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap()); let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
f( f(
window.newsize.take(), window.newsize.take(),
window.need_refresh, window.need_refresh,
window.need_frame_refresh,
window.closed, window.closed,
make_wid(&window.surface), make_wid(&window.surface),
opt_mutex_lock.as_mut().map(|m| &mut **m) opt_mutex_lock.as_mut().map(|m| &mut **m)
@ -258,21 +258,20 @@ impl WindowStore {
* Protocol implementation * Protocol implementation
*/ */
struct DecoratedIData { struct FrameIData {
ready: Arc<Mutex<bool>>,
store_token: StateToken<WindowStore>, store_token: StateToken<WindowStore>,
surface: wl_surface::WlSurface surface: wl_surface::WlSurface
} }
fn decorated_impl() -> DecoratedSurfaceImplementation<DecoratedIData> { fn decorated_impl() -> FrameImplementation<FrameIData> {
DecoratedSurfaceImplementation { FrameImplementation {
configure: |evqh, idata, _, newsize| { configure: |evqh, idata, _, newsize| {
*idata.ready.lock().unwrap() = true;
let store = evqh.state().get_mut(&idata.store_token); let store = evqh.state().get_mut(&idata.store_token);
for window in &mut store.windows { for window in &mut store.windows {
if window.surface.equals(&idata.surface) { if window.surface.equals(&idata.surface) {
window.newsize = newsize; window.newsize = newsize;
window.need_refresh = true; window.need_refresh = true;
window.need_frame_refresh = true;
return; return;
} }
} }
@ -285,6 +284,15 @@ fn decorated_impl() -> DecoratedSurfaceImplementation<DecoratedIData> {
return; return;
} }
} }
},
refresh: |evqh, idata| {
let store = evqh.state().get_mut(&idata.store_token);
for window in &mut store.windows {
if window.surface.equals(&idata.surface) {
window.need_frame_refresh = true;
return;
}
}
} }
} }
} }