mirror of
https://github.com/wez/wezterm.git
synced 2024-11-27 12:23:46 +03:00
window: initial wayland render support
This commit is contained in:
parent
a159c28be2
commit
51ada155df
@ -27,6 +27,7 @@ glium = { version = "0.26.0-alpha3", optional=true, default-features = false}
|
||||
[features]
|
||||
async_await = []
|
||||
opengl = ["cgl", "glium", "gl_generator", "libloading"]
|
||||
wayland = ["smithay-client-toolkit", "memmap"]
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
winapi = { version = "0.3", features = [
|
||||
@ -48,6 +49,8 @@ xkbcommon = { version = "0.4", features = ["x11"] }
|
||||
mio = "0.6"
|
||||
mio-extras = "2.0"
|
||||
libc = "0.2"
|
||||
smithay-client-toolkit = {version = "0.6", optional = true}
|
||||
memmap = {version="0.7", optional=true}
|
||||
|
||||
[target.'cfg(target_os="macos")'.dependencies]
|
||||
cocoa = "0.20"
|
||||
|
@ -7,6 +7,7 @@ pub mod input;
|
||||
pub mod os;
|
||||
mod spawn;
|
||||
mod tasks;
|
||||
mod timerlist;
|
||||
|
||||
#[cfg(all(
|
||||
not(target_os = "macos"),
|
||||
|
@ -3,9 +3,12 @@ pub mod windows;
|
||||
#[cfg(windows)]
|
||||
pub use windows::*;
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
pub mod wayland;
|
||||
pub mod x11;
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
pub use self::wayland::*;
|
||||
#[cfg(all(unix, not(feature = "wayland"), not(target_os = "macos")))]
|
||||
pub use self::x11::*;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
229
window/src/os/wayland/connection.rs
Normal file
229
window/src/os/wayland/connection.rs
Normal file
@ -0,0 +1,229 @@
|
||||
#![allow(dead_code)]
|
||||
use super::window::*;
|
||||
use crate::connection::ConnectionOps;
|
||||
use crate::spawn::*;
|
||||
use crate::tasks::{Task, Tasks};
|
||||
use crate::timerlist::{TimerEntry, TimerList};
|
||||
use failure::Fallible;
|
||||
use mio::unix::EventedFd;
|
||||
use mio::{Evented, Events, Poll, PollOpt, Ready, Token};
|
||||
use promise::BasicExecutor;
|
||||
use smithay_client_toolkit as toolkit;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::time::{Duration, Instant};
|
||||
use toolkit::reexports::client::{Display, EventQueue};
|
||||
use toolkit::Environment;
|
||||
|
||||
pub struct Connection {
|
||||
display: RefCell<Display>,
|
||||
event_q: RefCell<EventQueue>,
|
||||
pub(crate) environment: RefCell<Environment>,
|
||||
should_terminate: RefCell<bool>,
|
||||
timers: RefCell<TimerList>,
|
||||
tasks: Tasks,
|
||||
pub(crate) next_window_id: AtomicUsize,
|
||||
pub(crate) windows: RefCell<HashMap<usize, Rc<RefCell<WindowInner>>>>,
|
||||
}
|
||||
|
||||
impl Evented for Connection {
|
||||
fn register(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> std::io::Result<()> {
|
||||
EventedFd(&self.event_q.borrow().get_connection_fd()).register(poll, token, interest, opts)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> std::io::Result<()> {
|
||||
EventedFd(&self.event_q.borrow().get_connection_fd())
|
||||
.reregister(poll, token, interest, opts)
|
||||
}
|
||||
|
||||
fn deregister(&self, poll: &Poll) -> std::io::Result<()> {
|
||||
EventedFd(&self.event_q.borrow().get_connection_fd()).deregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub fn create_new() -> Fallible<Self> {
|
||||
let (display, mut event_q) = Display::connect_to_env()?;
|
||||
let environment = Environment::from_display(&*display, &mut event_q)?;
|
||||
Ok(Self {
|
||||
display: RefCell::new(display),
|
||||
event_q: RefCell::new(event_q),
|
||||
environment: RefCell::new(environment),
|
||||
should_terminate: RefCell::new(false),
|
||||
timers: RefCell::new(TimerList::new()),
|
||||
tasks: Default::default(),
|
||||
next_window_id: AtomicUsize::new(1),
|
||||
windows: RefCell::new(HashMap::new()),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn next_window_id(&self) -> usize {
|
||||
self.next_window_id
|
||||
.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn flush(&self) -> Fallible<()> {
|
||||
if let Err(e) = self.display.borrow_mut().flush() {
|
||||
if e.kind() != ::std::io::ErrorKind::WouldBlock {
|
||||
failure::bail!("Error while flushing display: {}", e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_paint(&self) {}
|
||||
|
||||
fn process_queued_events(&self) -> Fallible<()> {
|
||||
{
|
||||
let mut event_q = self.event_q.borrow_mut();
|
||||
if let Some(guard) = event_q.prepare_read() {
|
||||
if let Err(e) = guard.read_events() {
|
||||
if e.kind() != ::std::io::ErrorKind::WouldBlock {
|
||||
failure::bail!("Error while reading events: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
event_q.dispatch_pending()?;
|
||||
}
|
||||
self.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn window_by_id(&self, window_id: usize) -> Option<Rc<RefCell<WindowInner>>> {
|
||||
self.windows.borrow().get(&window_id).map(Rc::clone)
|
||||
}
|
||||
|
||||
pub(crate) fn with_window_inner<R, F: FnMut(&mut WindowInner) -> Fallible<R> + Send + 'static>(
|
||||
window: usize,
|
||||
mut f: F,
|
||||
) -> promise::Future<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
let mut prom = promise::Promise::new();
|
||||
let future = prom.get_future().unwrap();
|
||||
|
||||
SpawnQueueExecutor {}.execute(Box::new(move || {
|
||||
if let Some(handle) = Connection::get().unwrap().window_by_id(window) {
|
||||
let mut inner = handle.borrow_mut();
|
||||
prom.result(f(&mut inner));
|
||||
}
|
||||
}));
|
||||
|
||||
future
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionOps for Connection {
|
||||
fn spawn_task<F: std::future::Future<Output = ()> + 'static>(&self, future: F) {
|
||||
let id = self.tasks.add_task(Task(Box::pin(future)));
|
||||
Self::wake_task_by_id(id);
|
||||
}
|
||||
|
||||
fn wake_task_by_id(slot: usize) {
|
||||
SpawnQueueExecutor {}.execute(Box::new(move || {
|
||||
let conn = Connection::get().unwrap();
|
||||
conn.tasks.poll_by_slot(slot);
|
||||
}));
|
||||
}
|
||||
|
||||
fn terminate_message_loop(&self) {
|
||||
*self.should_terminate.borrow_mut() = true;
|
||||
}
|
||||
|
||||
fn run_message_loop(&self) -> Fallible<()> {
|
||||
println!("run_message_loop:flush");
|
||||
self.flush()?;
|
||||
|
||||
const TOK_WAYLAND: usize = 0xffff_fffc;
|
||||
const TOK_SPAWN: usize = 0xffff_fffd;
|
||||
let tok_wayland = Token(TOK_WAYLAND);
|
||||
let tok_spawn = Token(TOK_SPAWN);
|
||||
|
||||
let poll = Poll::new()?;
|
||||
let mut events = Events::with_capacity(8);
|
||||
poll.register(self, tok_wayland, Ready::readable(), PollOpt::level())?;
|
||||
poll.register(
|
||||
&*SPAWN_QUEUE,
|
||||
tok_spawn,
|
||||
Ready::readable(),
|
||||
PollOpt::level(),
|
||||
)?;
|
||||
|
||||
let paint_interval = Duration::from_millis(25);
|
||||
let mut last_interval = Instant::now();
|
||||
|
||||
while !*self.should_terminate.borrow() {
|
||||
self.timers.borrow_mut().run_ready();
|
||||
|
||||
let now = Instant::now();
|
||||
let diff = now - last_interval;
|
||||
let period = if diff >= paint_interval {
|
||||
self.do_paint();
|
||||
last_interval = now;
|
||||
paint_interval
|
||||
} else {
|
||||
paint_interval - diff
|
||||
};
|
||||
|
||||
// Process any events that might have accumulated in the local
|
||||
// buffer (eg: due to a flush) before we potentially go to sleep.
|
||||
// The locally queued events won't mark the fd as ready, so we
|
||||
// could potentially sleep when there is work to be done if we
|
||||
// relied solely on that.
|
||||
self.process_queued_events()?;
|
||||
|
||||
// Check the spawn queue before we try to sleep; there may
|
||||
// be work pending and we don't guarantee that there is a
|
||||
// 1:1 wakeup to queued function, so we need to be assertive
|
||||
// in order to avoid missing wakeups
|
||||
let period = if SPAWN_QUEUE.run() {
|
||||
// if we processed one, we don't want to sleep because
|
||||
// there may be others to deal with
|
||||
Duration::new(0, 0)
|
||||
} else {
|
||||
self.timers
|
||||
.borrow()
|
||||
.time_until_due(Instant::now())
|
||||
.map(|duration| duration.min(period))
|
||||
.unwrap_or(period)
|
||||
};
|
||||
|
||||
match poll.poll(&mut events, Some(period)) {
|
||||
Ok(_) => {
|
||||
// We process both event sources unconditionally
|
||||
// in the loop above anyway; we're just using
|
||||
// this to get woken up.
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
failure::bail!("polling for events: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schedule_timer<F: FnMut() + 'static>(&self, interval: std::time::Duration, callback: F) {
|
||||
self.timers.borrow_mut().insert(TimerEntry {
|
||||
callback: Box::new(callback),
|
||||
due: Instant::now(),
|
||||
interval,
|
||||
});
|
||||
}
|
||||
}
|
6
window/src/os/wayland/mod.rs
Normal file
6
window/src/os/wayland/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#![cfg(all(unix, feature="wayland", not(target_os = "macos")))]
|
||||
|
||||
pub mod connection;
|
||||
pub mod window;
|
||||
pub use connection::*;
|
||||
pub use window::*;
|
380
window/src/os/wayland/window.rs
Normal file
380
window/src/os/wayland/window.rs
Normal file
@ -0,0 +1,380 @@
|
||||
use crate::bitmaps::BitmapImage;
|
||||
use crate::color::Color;
|
||||
use crate::connection::ConnectionOps;
|
||||
use crate::{
|
||||
Connection, Dimensions, MouseCursor, Operator, PaintContext, Point, Rect, ScreenPoint,
|
||||
WindowCallbacks, WindowOps, WindowOpsMut,
|
||||
};
|
||||
use failure::Fallible;
|
||||
use promise::Future;
|
||||
use smithay_client_toolkit as toolkit;
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use toolkit::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use toolkit::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use toolkit::reexports::client::NewProxy;
|
||||
use toolkit::utils::DoubleMemPool;
|
||||
use toolkit::window::Event;
|
||||
|
||||
struct MyTheme;
|
||||
use toolkit::window::ButtonState;
|
||||
impl toolkit::window::Theme for MyTheme {
|
||||
fn get_primary_color(&self, _active: bool) -> [u8; 4] {
|
||||
[0xff, 0x80, 0x80, 0x80]
|
||||
}
|
||||
|
||||
fn get_secondary_color(&self, _active: bool) -> [u8; 4] {
|
||||
[0xff, 0x60, 0x60, 0x60]
|
||||
}
|
||||
|
||||
fn get_close_button_color(&self, _status: ButtonState) -> [u8; 4] {
|
||||
[0xff, 0xff, 0xff, 0xff]
|
||||
}
|
||||
fn get_maximize_button_color(&self, _status: ButtonState) -> [u8; 4] {
|
||||
[0xff, 0xff, 0xff, 0xff]
|
||||
}
|
||||
fn get_minimize_button_color(&self, _status: ButtonState) -> [u8; 4] {
|
||||
[0xff, 0xff, 0xff, 0xff]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowInner {
|
||||
window_id: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
surface: WlSurface,
|
||||
seat: WlSeat,
|
||||
window: toolkit::window::Window<toolkit::window::ConceptFrame>,
|
||||
pool: DoubleMemPool,
|
||||
dimensions: (u32, u32),
|
||||
}
|
||||
|
||||
pub struct Window(usize);
|
||||
|
||||
impl Window {
|
||||
pub fn new_window(
|
||||
class_name: &str,
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
) -> Fallible<Window> {
|
||||
let conn = Connection::get().ok_or_else(|| {
|
||||
failure::err_msg(
|
||||
"new_window must be called on the gui thread after Connection::init has succeeded",
|
||||
)
|
||||
})?;
|
||||
|
||||
let window_id = conn.next_window_id();
|
||||
|
||||
let surface = conn
|
||||
.environment
|
||||
.borrow_mut()
|
||||
.compositor
|
||||
.create_surface(NewProxy::implement_dummy)
|
||||
.map_err(|_| failure::err_msg("new_window: failed to create a surface"))?;
|
||||
|
||||
let dimensions = (width as u32, height as u32);
|
||||
let mut window = toolkit::window::Window::<toolkit::window::ConceptFrame>::init_from_env(
|
||||
&*conn.environment.borrow(),
|
||||
surface.clone(),
|
||||
dimensions,
|
||||
move |evt| {
|
||||
Connection::with_window_inner(window_id, move |inner| {
|
||||
inner.handle_event(evt.clone());
|
||||
Ok(())
|
||||
});
|
||||
},
|
||||
)
|
||||
.map_err(|e| failure::format_err!("Failed to create window: {}", e))?;
|
||||
|
||||
window.set_app_id(class_name.to_string());
|
||||
window.set_decorate(true);
|
||||
window.set_resizable(true);
|
||||
window.set_theme(MyTheme {});
|
||||
|
||||
let pool = DoubleMemPool::new(&conn.environment.borrow().shm, || {})?;
|
||||
|
||||
let seat = conn
|
||||
.environment
|
||||
.borrow()
|
||||
.manager
|
||||
.instantiate_range(1, 6, NewProxy::implement_dummy)
|
||||
.map_err(|_| failure::format_err!("Failed to create seat"))?;
|
||||
window.new_seat(&seat);
|
||||
|
||||
let inner = Rc::new(RefCell::new(WindowInner {
|
||||
window_id,
|
||||
callbacks,
|
||||
surface,
|
||||
seat,
|
||||
window,
|
||||
pool,
|
||||
dimensions,
|
||||
}));
|
||||
|
||||
let window_handle = Window(window_id);
|
||||
|
||||
conn.windows.borrow_mut().insert(window_id, inner.clone());
|
||||
|
||||
inner.borrow_mut().callbacks.created(&window_handle);
|
||||
|
||||
Ok(window_handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowInner {
|
||||
fn handle_event(&mut self, evt: Event) {
|
||||
match evt {
|
||||
Event::Close => {
|
||||
if self.callbacks.can_close() {
|
||||
println!("FIXME: I should destroy all refs to the window now");
|
||||
}
|
||||
}
|
||||
Event::Refresh => {
|
||||
self.window.refresh();
|
||||
self.window.surface().commit();
|
||||
}
|
||||
Event::Configure { new_size, states } => {
|
||||
if let Some((w, h)) = new_size {
|
||||
self.window.resize(w, h);
|
||||
self.dimensions = (w, h);
|
||||
}
|
||||
self.window.refresh();
|
||||
self.do_paint().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_paint(&mut self) -> Fallible<()> {
|
||||
let pool = match self.pool.pool() {
|
||||
Some(pool) => pool,
|
||||
None => {
|
||||
// Buffer still in use by server; retry later
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
pool.resize((4 * self.dimensions.0 * self.dimensions.1) as usize)?;
|
||||
|
||||
let mut context = MmapImage {
|
||||
mmap: pool.mmap(),
|
||||
dimensions: (self.dimensions.0 as usize, self.dimensions.1 as usize),
|
||||
};
|
||||
self.callbacks.paint(&mut context);
|
||||
|
||||
let buffer = pool.buffer(
|
||||
0,
|
||||
self.dimensions.0 as i32,
|
||||
self.dimensions.1 as i32,
|
||||
4 * self.dimensions.0 as i32,
|
||||
toolkit::reexports::client::protocol::wl_shm::Format::Argb8888,
|
||||
);
|
||||
|
||||
self.surface.attach(Some(&buffer), 0, 0);
|
||||
self.surface.commit();
|
||||
self.window.refresh();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct MmapImage<'a> {
|
||||
mmap: &'a mut memmap::MmapMut,
|
||||
dimensions: (usize, usize),
|
||||
}
|
||||
|
||||
impl<'a> BitmapImage for MmapImage<'a> {
|
||||
unsafe fn pixel_data(&self) -> *const u8 {
|
||||
self.mmap.as_ptr()
|
||||
}
|
||||
|
||||
unsafe fn pixel_data_mut(&mut self) -> *mut u8 {
|
||||
self.mmap.as_mut_ptr()
|
||||
}
|
||||
|
||||
fn image_dimensions(&self) -> (usize, usize) {
|
||||
self.dimensions
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PaintContext for MmapImage<'a> {
|
||||
fn clear_rect(&mut self, rect: Rect, color: Color) {
|
||||
BitmapImage::clear_rect(self, rect, color)
|
||||
}
|
||||
|
||||
fn clear(&mut self, color: Color) {
|
||||
BitmapImage::clear(self, color);
|
||||
}
|
||||
|
||||
fn get_dimensions(&self) -> Dimensions {
|
||||
let (pixel_width, pixel_height) = self.image_dimensions();
|
||||
Dimensions {
|
||||
pixel_width,
|
||||
pixel_height,
|
||||
dpi: 96,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
dest_top_left: Point,
|
||||
src_rect: Option<Rect>,
|
||||
im: &dyn BitmapImage,
|
||||
operator: Operator,
|
||||
) {
|
||||
BitmapImage::draw_image(self, dest_top_left, src_rect, im, operator)
|
||||
}
|
||||
|
||||
fn draw_line(&mut self, start: Point, end: Point, color: Color, operator: Operator) {
|
||||
BitmapImage::draw_line(self, start, end, color, operator);
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowOps for Window {
|
||||
fn close(&self) -> Future<()> {
|
||||
Connection::with_window_inner(self.0, |inner| {
|
||||
inner.close();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn hide(&self) -> Future<()> {
|
||||
Connection::with_window_inner(self.0, |inner| {
|
||||
inner.hide();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn show(&self) -> Future<()> {
|
||||
Connection::with_window_inner(self.0, |inner| {
|
||||
inner.show();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: Option<MouseCursor>) -> Future<()> {
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
let _ = inner.set_cursor(cursor);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn invalidate(&self) -> Future<()> {
|
||||
Connection::with_window_inner(self.0, |inner| {
|
||||
inner.invalidate();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) -> Future<()> {
|
||||
let title = title.to_owned();
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
inner.set_title(&title);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn set_inner_size(&self, width: usize, height: usize) -> Future<()> {
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
inner.set_inner_size(width, height);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn set_window_position(&self, coords: ScreenPoint) -> Future<()> {
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
inner.set_window_position(coords);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn apply<R, F: Send + 'static + Fn(&mut dyn Any, &dyn WindowOps) -> Fallible<R>>(
|
||||
&self,
|
||||
func: F,
|
||||
) -> promise::Future<R>
|
||||
where
|
||||
Self: Sized,
|
||||
R: Send + 'static,
|
||||
{
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
let window = Window(inner.window_id);
|
||||
func(inner.callbacks.as_any(), &window)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "opengl")]
|
||||
fn enable_opengl<
|
||||
R,
|
||||
F: Send
|
||||
+ 'static
|
||||
+ Fn(
|
||||
&mut dyn Any,
|
||||
&dyn WindowOps,
|
||||
failure::Fallible<std::rc::Rc<glium::backend::Context>>,
|
||||
) -> failure::Fallible<R>,
|
||||
>(
|
||||
&self,
|
||||
func: F,
|
||||
) -> promise::Future<R>
|
||||
where
|
||||
Self: Sized,
|
||||
R: Send + 'static,
|
||||
{
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
let window = Window(inner.window_id);
|
||||
|
||||
let gl_state = crate::egl::GlState::create(
|
||||
Some(inner.conn.display as *const _),
|
||||
inner.window_id as *mut _,
|
||||
)
|
||||
.map(Rc::new)
|
||||
.and_then(|state| unsafe {
|
||||
Ok(glium::backend::Context::new(
|
||||
Rc::clone(&state),
|
||||
true,
|
||||
if cfg!(debug_assertions) {
|
||||
glium::debug::DebugCallbackBehavior::DebugMessageOnError
|
||||
} else {
|
||||
glium::debug::DebugCallbackBehavior::Ignore
|
||||
},
|
||||
)?)
|
||||
});
|
||||
|
||||
inner.gl_state = gl_state.as_ref().map(Rc::clone).ok();
|
||||
|
||||
func(inner.callbacks.as_any(), &window, gl_state)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowOpsMut for WindowInner {
|
||||
fn close(&mut self) {}
|
||||
fn hide(&mut self) {}
|
||||
fn show(&mut self) {
|
||||
let conn = Connection::get().unwrap();
|
||||
|
||||
if !conn.environment.borrow().shell.needs_configure() {
|
||||
self.do_paint().unwrap();
|
||||
} else {
|
||||
self.window.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_cursor(&mut self, cursor: Option<MouseCursor>) {}
|
||||
|
||||
fn invalidate(&mut self) {
|
||||
self.window
|
||||
.surface()
|
||||
.damage(0, 0, self.dimensions.0 as i32, self.dimensions.1 as i32);
|
||||
}
|
||||
|
||||
fn set_inner_size(&self, width: usize, height: usize) {}
|
||||
|
||||
fn set_window_position(&self, coords: ScreenPoint) {}
|
||||
|
||||
/// Change the title for the window manager
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.window.set_title(title.to_string());
|
||||
}
|
||||
}
|
@ -2,83 +2,19 @@ use super::keyboard::Keyboard;
|
||||
use crate::connection::ConnectionOps;
|
||||
use crate::spawn::*;
|
||||
use crate::tasks::{Task, Tasks};
|
||||
use crate::timerlist::{TimerEntry, TimerList};
|
||||
use crate::WindowInner;
|
||||
use failure::Fallible;
|
||||
use mio::unix::EventedFd;
|
||||
use mio::{Evented, Events, Poll, PollOpt, Ready, Token};
|
||||
use promise::BasicExecutor;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
use xcb_util::ffi::keysyms::{xcb_key_symbols_alloc, xcb_key_symbols_free, xcb_key_symbols_t};
|
||||
|
||||
struct TimerEntry {
|
||||
callback: Box<dyn FnMut()>,
|
||||
due: Instant,
|
||||
interval: Duration,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TimerList {
|
||||
timers: VecDeque<TimerEntry>,
|
||||
}
|
||||
|
||||
impl TimerList {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn find_index_after(&self, due: &Instant) -> usize {
|
||||
for (idx, entry) in self.timers.iter().enumerate() {
|
||||
if entry.due.cmp(due) == Ordering::Greater {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
self.timers.len()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, mut entry: TimerEntry) {
|
||||
entry.due = Instant::now() + entry.interval;
|
||||
let idx = self.find_index_after(&entry.due);
|
||||
self.timers.insert(idx, entry);
|
||||
}
|
||||
|
||||
pub fn time_until_due(&self, now: Instant) -> Option<Duration> {
|
||||
self.timers.front().map(|entry| {
|
||||
if entry.due <= now {
|
||||
Duration::from_secs(0)
|
||||
} else {
|
||||
entry.due - now
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn first_is_ready(&self, now: Instant) -> bool {
|
||||
if let Some(first) = self.timers.front() {
|
||||
first.due <= now
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_ready(&mut self) {
|
||||
let now = Instant::now();
|
||||
let mut requeue = vec![];
|
||||
while self.first_is_ready(now) {
|
||||
let mut first = self.timers.pop_front().expect("first_is_ready");
|
||||
(first.callback)();
|
||||
requeue.push(first);
|
||||
}
|
||||
|
||||
for entry in requeue.into_iter() {
|
||||
self.insert(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Connection {
|
||||
pub display: *mut x11::xlib::Display,
|
||||
conn: xcb_util::ewmh::Connection,
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![cfg(all(unix, not(feature="wayland"), not(target_os = "macos")))]
|
||||
pub mod bitmap;
|
||||
pub mod connection;
|
||||
pub mod keyboard;
|
||||
|
67
window/src/timerlist.rs
Normal file
67
window/src/timerlist.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::VecDeque;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct TimerEntry {
|
||||
pub callback: Box<dyn FnMut()>,
|
||||
pub due: Instant,
|
||||
pub interval: Duration,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TimerList {
|
||||
timers: VecDeque<TimerEntry>,
|
||||
}
|
||||
|
||||
impl TimerList {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn find_index_after(&self, due: &Instant) -> usize {
|
||||
for (idx, entry) in self.timers.iter().enumerate() {
|
||||
if entry.due.cmp(due) == Ordering::Greater {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
self.timers.len()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, mut entry: TimerEntry) {
|
||||
entry.due = Instant::now() + entry.interval;
|
||||
let idx = self.find_index_after(&entry.due);
|
||||
self.timers.insert(idx, entry);
|
||||
}
|
||||
|
||||
pub fn time_until_due(&self, now: Instant) -> Option<Duration> {
|
||||
self.timers.front().map(|entry| {
|
||||
if entry.due <= now {
|
||||
Duration::from_secs(0)
|
||||
} else {
|
||||
entry.due - now
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn first_is_ready(&self, now: Instant) -> bool {
|
||||
if let Some(first) = self.timers.front() {
|
||||
first.due <= now
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_ready(&mut self) {
|
||||
let now = Instant::now();
|
||||
let mut requeue = vec![];
|
||||
while self.first_is_ready(now) {
|
||||
let mut first = self.timers.pop_front().expect("first_is_ready");
|
||||
(first.callback)();
|
||||
requeue.push(first);
|
||||
}
|
||||
|
||||
for entry in requeue.into_iter() {
|
||||
self.insert(entry);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user