mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 15:04:36 +03:00
window: separate gui window state from app state
Removes the callbacks type and replaces event dispatch with an async capable channel. This makes it a bit simpler to model some of the window internals, and to prepare for a wgpu enabled future. This changes have been tested only on linux so far.
This commit is contained in:
parent
f9a6e265e9
commit
d56bfd0b7f
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4658,7 +4658,9 @@ name = "window"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
"bitflags",
|
||||
"cgl",
|
||||
"clipboard",
|
||||
|
@ -15,7 +15,9 @@ pretty_env_logger = "0.4"
|
||||
gl_generator = "0.14"
|
||||
|
||||
[dependencies]
|
||||
async-channel = "1.6"
|
||||
async-task = "4.0"
|
||||
async-trait = "0.1"
|
||||
anyhow = "1.0"
|
||||
config = { path = "../config" }
|
||||
thiserror = "1.0"
|
||||
|
@ -1,10 +1,11 @@
|
||||
use ::window::*;
|
||||
use promise::spawn::spawn;
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
|
||||
struct MyWindow {
|
||||
allow_close: bool,
|
||||
cursor_pos: Point,
|
||||
dims: Dimensions,
|
||||
}
|
||||
|
||||
impl Drop for MyWindow {
|
||||
@ -13,76 +14,81 @@ impl Drop for MyWindow {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowCallbacks for MyWindow {
|
||||
fn can_close(&mut self) -> bool {
|
||||
eprintln!("can I close?");
|
||||
if self.allow_close {
|
||||
true
|
||||
} else {
|
||||
self.allow_close = true;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&mut self) {
|
||||
eprintln!("destroy was called!");
|
||||
Connection::get().unwrap().terminate_message_loop();
|
||||
}
|
||||
|
||||
fn resize(&mut self, dims: Dimensions, is_full_screen: bool) {
|
||||
eprintln!("resize {:?} is_full_screen={}", dims, is_full_screen);
|
||||
}
|
||||
|
||||
fn key_event(&mut self, key: &KeyEvent, ctx: &dyn WindowOps) -> bool {
|
||||
eprintln!("{:?}", key);
|
||||
ctx.set_cursor(Some(MouseCursor::Text));
|
||||
false
|
||||
}
|
||||
|
||||
fn mouse_event(&mut self, event: &MouseEvent, ctx: &dyn WindowOps) {
|
||||
self.cursor_pos = event.coords;
|
||||
ctx.invalidate();
|
||||
ctx.set_cursor(Some(MouseCursor::Arrow));
|
||||
|
||||
if event.kind == MouseEventKind::Press(MousePress::Left) {
|
||||
eprintln!("{:?}", event);
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
async fn spawn_window() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let win = Window::new_window(
|
||||
"myclass",
|
||||
"the title",
|
||||
800,
|
||||
600,
|
||||
Box::new(MyWindow {
|
||||
allow_close: false,
|
||||
cursor_pos: Point::new(100, 200),
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let (win, events) = Window::new_window("myclass", "the title", 800, 600, None).await?;
|
||||
|
||||
let mut state = MyWindow {
|
||||
allow_close: false,
|
||||
cursor_pos: Point::new(100, 200),
|
||||
dims: Dimensions {
|
||||
pixel_width: 800,
|
||||
pixel_height: 600,
|
||||
dpi: 0,
|
||||
},
|
||||
};
|
||||
|
||||
eprintln!("before show");
|
||||
win.show().await?;
|
||||
eprintln!("after show");
|
||||
win.apply(|myself, _win| {
|
||||
eprintln!("doing apply");
|
||||
if let Some(myself) = myself.downcast_ref::<MyWindow>() {
|
||||
eprintln!(
|
||||
"got myself; allow_close={}, cursor_pos:{:?}",
|
||||
myself.allow_close, myself.cursor_pos
|
||||
);
|
||||
let gl = win.enable_opengl().await?;
|
||||
eprintln!("window is visible, do loop");
|
||||
|
||||
while let Ok(event) = events.recv().await {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
eprintln!("can I close?");
|
||||
if state.allow_close {
|
||||
win.close();
|
||||
} else {
|
||||
state.allow_close = true;
|
||||
}
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
eprintln!("destroy was called!");
|
||||
Connection::get().unwrap().terminate_message_loop();
|
||||
}
|
||||
WindowEvent::Resized {
|
||||
dimensions,
|
||||
is_full_screen,
|
||||
} => {
|
||||
eprintln!("resize {:?} is_full_screen={}", dimensions, is_full_screen);
|
||||
state.dims = dimensions;
|
||||
}
|
||||
WindowEvent::MouseEvent(event) => {
|
||||
state.cursor_pos = event.coords;
|
||||
win.invalidate();
|
||||
win.set_cursor(Some(MouseCursor::Arrow));
|
||||
|
||||
if event.kind == MouseEventKind::Press(MousePress::Left) {
|
||||
eprintln!("{:?}", event);
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyEvent(key) => {
|
||||
eprintln!("{:?}", key);
|
||||
win.set_cursor(Some(MouseCursor::Text));
|
||||
win.default_key_processing(key);
|
||||
}
|
||||
WindowEvent::NeedRepaint => {
|
||||
if gl.is_context_lost() {
|
||||
eprintln!("opengl context was lost; should reinit");
|
||||
break;
|
||||
}
|
||||
|
||||
let mut frame = glium::Frame::new(
|
||||
Rc::clone(&gl),
|
||||
(
|
||||
state.dims.pixel_width as u32,
|
||||
state.dims.pixel_height as u32,
|
||||
),
|
||||
);
|
||||
|
||||
use glium::Surface;
|
||||
frame.clear_color_srgb(0.25, 0.125, 0.375, 1.0);
|
||||
win.finish_frame(frame)?;
|
||||
}
|
||||
WindowEvent::FocusChanged(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
eprintln!("done with spawn_window");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -90,7 +96,7 @@ fn main() -> anyhow::Result<()> {
|
||||
let conn = Connection::init()?;
|
||||
spawn(async {
|
||||
eprintln!("running this async block");
|
||||
spawn_window().await.ok();
|
||||
dbg!(spawn_window().await).ok();
|
||||
eprintln!("end of async block");
|
||||
})
|
||||
.detach();
|
||||
|
@ -1,5 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use promise::Future;
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
pub mod bitmaps;
|
||||
pub mod color;
|
||||
mod configuration;
|
||||
@ -59,70 +61,61 @@ pub enum MouseCursor {
|
||||
SizeLeftRight,
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait WindowCallbacks: Any {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum WindowEvent {
|
||||
/// Called when the window close button is clicked.
|
||||
/// Return true to allow the close to continue, false to
|
||||
/// prevent it from closing.
|
||||
fn can_close(&mut self) -> bool {
|
||||
true
|
||||
}
|
||||
/// The window closure is deferred and this event is
|
||||
/// sent to your application to decide whether it will
|
||||
/// really close the window.
|
||||
CloseRequested,
|
||||
|
||||
/// Called when the window is being destroyed by the gui system
|
||||
fn destroy(&mut self) {}
|
||||
/// Called when the window is being destroyed by the window system
|
||||
Destroyed,
|
||||
|
||||
/// Called when the window is resized, or when the dpi has changed
|
||||
fn resize(&mut self, dimensions: Dimensions, is_full_screen: bool) {}
|
||||
/// Called when the window has been resized
|
||||
Resized {
|
||||
dimensions: Dimensions,
|
||||
is_full_screen: bool,
|
||||
},
|
||||
|
||||
/// Called when window gains/loses focus
|
||||
fn focus_change(&mut self, focused: bool) {}
|
||||
/// Called when the window has been invalidated and needs to
|
||||
/// be repainted
|
||||
NeedRepaint,
|
||||
|
||||
/// Called when the window has opengl mode enabled and the window
|
||||
/// contents need painting.
|
||||
fn paint(&mut self, frame: &mut glium::Frame) {
|
||||
use glium::Surface;
|
||||
frame.clear_color_srgb(0.25, 0.125, 0.375, 1.0);
|
||||
}
|
||||
|
||||
/// Called if the opengl context is lost
|
||||
fn opengl_context_lost(&mut self, _window: &dyn WindowOps) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
/// Called when the window gains/loses focus
|
||||
FocusChanged(bool),
|
||||
|
||||
/// Called to handle a key event.
|
||||
/// If your window didn't handle the event, you must return false.
|
||||
/// This is particularly important for eg: ALT keys on windows,
|
||||
/// otherwise standard key assignments may not function in your window.
|
||||
fn key_event(&mut self, key: &KeyEvent, context: &dyn WindowOps) -> bool {
|
||||
false
|
||||
}
|
||||
/// If you didn't handle this event, then you must call
|
||||
/// window.default_key_processing(key) to allow the system to perform
|
||||
/// the default key handling.
|
||||
/// This is important on Windows for ALT keys to continue working
|
||||
/// correctly.
|
||||
KeyEvent(KeyEvent),
|
||||
|
||||
fn mouse_event(&mut self, event: &MouseEvent, context: &dyn WindowOps) {
|
||||
context.set_cursor(Some(MouseCursor::Arrow));
|
||||
}
|
||||
|
||||
/// Called when the window is created and allows the embedding
|
||||
/// app to reference the window and operate upon it.
|
||||
fn created(
|
||||
&mut self,
|
||||
_window: &Window,
|
||||
_context: std::rc::Rc<glium::backend::Context>,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An unfortunate bit of boilerplate; you need to provie an impl
|
||||
/// of this method that returns `self` in order for the downcast_ref
|
||||
/// method of the Any trait to be usable on WindowCallbacks.
|
||||
/// https://stackoverflow.com/q/46045298/149111 and others have
|
||||
/// some rationale on why Rust works this way.
|
||||
fn as_any(&mut self) -> &mut dyn Any;
|
||||
MouseEvent(MouseEvent),
|
||||
}
|
||||
|
||||
pub type WindowEventSender = async_channel::Sender<WindowEvent>;
|
||||
pub type WindowEventReceiver = async_channel::Receiver<WindowEvent>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Graphics drivers lost context")]
|
||||
pub struct GraphicsDriversLostContext {}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
pub trait WindowOps {
|
||||
/// Show a hidden window
|
||||
fn show(&self) -> Future<()>;
|
||||
|
||||
/// Setup opengl for rendering
|
||||
async fn enable_opengl(&self) -> anyhow::Result<Rc<glium::backend::Context>>;
|
||||
/// Advise the window that a frame is finished
|
||||
fn finish_frame(&self, frame: glium::Frame) -> anyhow::Result<()> {
|
||||
frame.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hide a visible window
|
||||
fn hide(&self) -> Future<()>;
|
||||
|
||||
@ -139,6 +132,8 @@ pub trait WindowOps {
|
||||
/// Change the titlebar text for the window
|
||||
fn set_title(&self, title: &str) -> Future<()>;
|
||||
|
||||
fn default_key_processing(&self, _key: KeyEvent) {}
|
||||
|
||||
/// Resize the inner or client area of the window
|
||||
fn set_inner_size(&self, width: usize, height: usize) -> Future<Dimensions>;
|
||||
|
||||
@ -156,18 +151,6 @@ pub trait WindowOps {
|
||||
Future::ok(())
|
||||
}
|
||||
|
||||
/// Schedule a callback on the data associated with the window.
|
||||
/// The `Any` that is passed in corresponds to the WindowCallbacks
|
||||
/// impl you passed to `new_window`, pre-converted to Any so that
|
||||
/// you can `downcast_ref` or `downcast_mut` it and operate on it.
|
||||
fn apply<R, F: Send + 'static + FnMut(&mut dyn Any, &dyn WindowOps) -> anyhow::Result<R>>(
|
||||
&self,
|
||||
func: F,
|
||||
) -> promise::Future<R>
|
||||
where
|
||||
Self: Sized,
|
||||
R: Send + 'static;
|
||||
|
||||
/// Initiate textual transfer from the clipboard
|
||||
fn get_clipboard(&self, clipboard: Clipboard) -> Future<String>;
|
||||
|
||||
|
@ -5,15 +5,15 @@ use crate::connection::ConnectionOps;
|
||||
use crate::os::wayland::connection::WaylandConnection;
|
||||
use crate::os::xkeysyms::keysym_to_keycode;
|
||||
use crate::{
|
||||
Clipboard, Connection, Dimensions, MouseCursor, Point, ScreenPoint, Window, WindowCallbacks,
|
||||
WindowOps,
|
||||
Clipboard, Connection, Dimensions, MouseCursor, Point, ScreenPoint, Window, WindowEvent,
|
||||
WindowEventReceiver, WindowEventSender, WindowOps,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use async_trait::async_trait;
|
||||
use config::ConfigHandle;
|
||||
use filedescriptor::FileDescriptor;
|
||||
use promise::{Future, Promise};
|
||||
use smithay_client_toolkit as toolkit;
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::io::{Read, Write};
|
||||
@ -79,19 +79,18 @@ fn frame_config() -> ConceptConfig {
|
||||
}
|
||||
|
||||
pub struct WaylandWindowInner {
|
||||
window_id: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
events: WindowEventSender,
|
||||
surface: WlSurface,
|
||||
copy_and_paste: Arc<Mutex<CopyAndPaste>>,
|
||||
window: Option<toolkit::window::Window<ConceptFrame>>,
|
||||
dimensions: Dimensions,
|
||||
need_paint: bool,
|
||||
full_screen: bool,
|
||||
last_mouse_coords: Point,
|
||||
mouse_buttons: MouseButtons,
|
||||
modifiers: Modifiers,
|
||||
pending_event: Arc<Mutex<PendingEvent>>,
|
||||
pending_mouse: Arc<Mutex<PendingMouse>>,
|
||||
pending_first_configure: Option<async_channel::Sender<()>>,
|
||||
// wegl_surface is listed before gl_state because it
|
||||
// must be dropped before gl_state otherwise the underlying
|
||||
// libraries will segfault on shutdown
|
||||
@ -166,9 +165,8 @@ impl WaylandWindow {
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
_config: Option<&ConfigHandle>,
|
||||
) -> anyhow::Result<Window> {
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
let conn = WaylandConnection::get()
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
@ -179,6 +177,9 @@ impl WaylandWindow {
|
||||
|
||||
let window_id = conn.next_window_id();
|
||||
let pending_event = Arc::new(Mutex::new(PendingEvent::default()));
|
||||
let (events, receiver) = async_channel::unbounded();
|
||||
|
||||
let (pending_first_configure, wait_configure) = async_channel::bounded(1);
|
||||
|
||||
let surface = conn
|
||||
.environment
|
||||
@ -247,18 +248,17 @@ impl WaylandWindow {
|
||||
|
||||
let inner = Rc::new(RefCell::new(WaylandWindowInner {
|
||||
copy_and_paste,
|
||||
window_id,
|
||||
callbacks,
|
||||
events,
|
||||
surface: surface.detach(),
|
||||
window: Some(window),
|
||||
dimensions,
|
||||
need_paint: true,
|
||||
full_screen: false,
|
||||
last_mouse_coords: Point::new(0, 0),
|
||||
mouse_buttons: MouseButtons::NONE,
|
||||
modifiers: Modifiers::NONE,
|
||||
pending_event,
|
||||
pending_mouse,
|
||||
pending_first_configure: Some(pending_first_configure),
|
||||
gl_state: None,
|
||||
wegl_surface: None,
|
||||
}));
|
||||
@ -267,7 +267,9 @@ impl WaylandWindow {
|
||||
|
||||
conn.windows.borrow_mut().insert(window_id, inner.clone());
|
||||
|
||||
Ok(window_handle)
|
||||
wait_configure.recv().await?;
|
||||
|
||||
Ok((window_handle, receiver))
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,8 +322,7 @@ impl WaylandWindowInner {
|
||||
repeat_count: 1,
|
||||
}
|
||||
.normalize_shift();
|
||||
self.callbacks
|
||||
.key_event(&key_event, &Window::Wayland(WaylandWindow(self.window_id)));
|
||||
self.events.try_send(WindowEvent::KeyEvent(key_event)).ok();
|
||||
}
|
||||
KeyboardEvent::Modifiers { modifiers } => self.modifiers = modifiers,
|
||||
// Clear the modifiers when we change focus, otherwise weird
|
||||
@ -331,11 +332,11 @@ impl WaylandWindowInner {
|
||||
// be left in a broken state.
|
||||
KeyboardEvent::Enter { .. } => {
|
||||
self.modifiers = Modifiers::NONE;
|
||||
self.callbacks.focus_change(true)
|
||||
self.events.try_send(WindowEvent::FocusChanged(true)).ok();
|
||||
}
|
||||
KeyboardEvent::Leave { .. } => {
|
||||
self.modifiers = Modifiers::NONE;
|
||||
self.callbacks.focus_change(false)
|
||||
self.events.try_send(WindowEvent::FocusChanged(false)).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -360,8 +361,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.callbacks
|
||||
.mouse_event(&event, &Window::Wayland(WaylandWindow(self.window_id)));
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
self.refresh_frame();
|
||||
}
|
||||
|
||||
@ -391,8 +391,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.callbacks
|
||||
.mouse_event(&event, &Window::Wayland(WaylandWindow(self.window_id)));
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
}
|
||||
|
||||
if let Some((value_x, value_y)) = PendingMouse::scroll(&pending_mouse) {
|
||||
@ -409,8 +408,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.callbacks
|
||||
.mouse_event(&event, &Window::Wayland(WaylandWindow(self.window_id)));
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
}
|
||||
|
||||
let discrete_y = value_y.trunc() * factor;
|
||||
@ -425,8 +423,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.callbacks
|
||||
.mouse_event(&event, &Window::Wayland(WaylandWindow(self.window_id)));
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -453,9 +450,10 @@ impl WaylandWindowInner {
|
||||
pending = pending_events.clone();
|
||||
*pending_events = PendingEvent::default();
|
||||
}
|
||||
if pending.close && self.callbacks.can_close() {
|
||||
self.callbacks.destroy();
|
||||
self.window.take();
|
||||
if pending.close {
|
||||
if self.events.try_send(WindowEvent::CloseRequested).is_err() {
|
||||
self.window.take();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(full_screen) = pending.full_screen.take() {
|
||||
@ -503,22 +501,29 @@ impl WaylandWindowInner {
|
||||
if new_dimensions != self.dimensions {
|
||||
self.dimensions = new_dimensions;
|
||||
|
||||
self.callbacks.resize(self.dimensions, self.full_screen);
|
||||
self.events
|
||||
.try_send(WindowEvent::Resized {
|
||||
dimensions: self.dimensions,
|
||||
is_full_screen: self.full_screen,
|
||||
})
|
||||
.ok();
|
||||
if let Some(wegl_surface) = self.wegl_surface.as_mut() {
|
||||
wegl_surface.resize(pixel_width, pixel_height, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
self.refresh_frame();
|
||||
self.need_paint = true;
|
||||
self.do_paint().unwrap();
|
||||
}
|
||||
}
|
||||
if pending.refresh_decorations && self.window.is_some() {
|
||||
self.refresh_frame();
|
||||
}
|
||||
if pending.had_configure_event && self.window.is_some() && self.wegl_surface.is_none() {
|
||||
self.enable_opengl().unwrap();
|
||||
if pending.had_configure_event && self.window.is_some() {
|
||||
if let Some(notify) = self.pending_first_configure.take() {
|
||||
// Allow window creation to complete
|
||||
notify.try_send(()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -529,8 +534,7 @@ impl WaylandWindowInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_opengl(&mut self) -> anyhow::Result<()> {
|
||||
let window = Window::Wayland(WaylandWindow(self.window_id));
|
||||
fn enable_opengl(&mut self) -> anyhow::Result<Rc<glium::backend::Context>> {
|
||||
let wayland_conn = Connection::get().unwrap().wayland();
|
||||
let mut wegl_surface = None;
|
||||
|
||||
@ -573,38 +577,39 @@ impl WaylandWindowInner {
|
||||
self.gl_state.replace(gl_state.clone());
|
||||
self.wegl_surface = wegl_surface;
|
||||
|
||||
self.callbacks.created(&window, gl_state)
|
||||
Ok(gl_state)
|
||||
}
|
||||
|
||||
fn do_paint(&mut self) -> anyhow::Result<()> {
|
||||
if let Some(gl_context) = self.gl_state.as_ref() {
|
||||
if gl_context.is_context_lost() {
|
||||
log::error!("opengl context was lost; should reinit");
|
||||
drop(self.gl_state.take());
|
||||
self.enable_opengl()?;
|
||||
return self.do_paint();
|
||||
}
|
||||
|
||||
let mut frame = glium::Frame::new(
|
||||
Rc::clone(&gl_context),
|
||||
(
|
||||
self.dimensions.pixel_width as u32,
|
||||
self.dimensions.pixel_height as u32,
|
||||
),
|
||||
);
|
||||
|
||||
self.callbacks.paint(&mut frame);
|
||||
frame.finish()?;
|
||||
// self.damage();
|
||||
self.refresh_frame();
|
||||
self.need_paint = false;
|
||||
}
|
||||
|
||||
self.events.try_send(WindowEvent::NeedRepaint)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl WindowOps for WaylandWindow {
|
||||
async fn enable_opengl(&self) -> anyhow::Result<Rc<glium::backend::Context>> {
|
||||
let window = self.0;
|
||||
promise::spawn::spawn(async move {
|
||||
if let Some(handle) = Connection::get().unwrap().wayland().window_by_id(window) {
|
||||
let mut inner = handle.borrow_mut();
|
||||
inner.enable_opengl()
|
||||
} else {
|
||||
anyhow::bail!("invalid window");
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn finish_frame(&self, frame: glium::Frame) -> anyhow::Result<()> {
|
||||
frame.finish()?;
|
||||
WaylandConnection::with_window_inner(self.0, |inner| {
|
||||
inner.refresh_frame();
|
||||
Ok(())
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close(&self) -> Future<()> {
|
||||
WaylandConnection::with_window_inner(self.0, |inner| {
|
||||
inner.close();
|
||||
@ -668,20 +673,6 @@ impl WindowOps for WaylandWindow {
|
||||
})
|
||||
}
|
||||
|
||||
fn apply<R, F: Send + 'static + FnMut(&mut dyn Any, &dyn WindowOps) -> anyhow::Result<R>>(
|
||||
&self,
|
||||
mut func: F,
|
||||
) -> promise::Future<R>
|
||||
where
|
||||
Self: Sized,
|
||||
R: Send + 'static,
|
||||
{
|
||||
WaylandConnection::with_window_inner(self.0, move |inner| {
|
||||
let window = Window::Wayland(WaylandWindow(inner.window_id));
|
||||
func(inner.callbacks.as_any(), &window)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_clipboard(&self, _clipboard: Clipboard) -> Future<String> {
|
||||
let mut promise = Promise::new();
|
||||
let future = promise.get_future().unwrap();
|
||||
@ -797,7 +788,7 @@ fn read_pipe_with_timeout(mut file: FileDescriptor) -> anyhow::Result<String> {
|
||||
|
||||
impl WaylandWindowInner {
|
||||
fn close(&mut self) {
|
||||
self.callbacks.destroy();
|
||||
self.events.try_send(WindowEvent::Destroyed).ok();
|
||||
self.window.take();
|
||||
}
|
||||
|
||||
@ -821,17 +812,9 @@ impl WaylandWindowInner {
|
||||
if self.window.is_none() {
|
||||
return;
|
||||
}
|
||||
let conn = Connection::get().unwrap().wayland();
|
||||
|
||||
if !conn
|
||||
.environment
|
||||
.borrow()
|
||||
.get_shell()
|
||||
.unwrap()
|
||||
.needs_configure()
|
||||
{
|
||||
self.do_paint().unwrap();
|
||||
}
|
||||
// The window won't be visible until we've done our first paint,
|
||||
// so we unconditionally queue a NeedRepaint event
|
||||
self.do_paint().unwrap();
|
||||
}
|
||||
|
||||
fn set_cursor(&mut self, cursor: Option<MouseCursor>) {
|
||||
@ -848,7 +831,6 @@ impl WaylandWindowInner {
|
||||
}
|
||||
|
||||
fn invalidate(&mut self) {
|
||||
self.need_paint = true;
|
||||
self.do_paint().unwrap();
|
||||
}
|
||||
|
||||
|
@ -292,7 +292,10 @@ impl XConnection {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn window_by_id(&self, window_id: xcb::xproto::Window) -> Option<Arc<Mutex<XWindowInner>>> {
|
||||
pub(crate) fn window_by_id(
|
||||
&self,
|
||||
window_id: xcb::xproto::Window,
|
||||
) -> Option<Arc<Mutex<XWindowInner>>> {
|
||||
self.windows.borrow().get(&window_id).map(Arc::clone)
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,13 @@ use crate::os::xkeysyms;
|
||||
use crate::os::{Connection, Window};
|
||||
use crate::{
|
||||
Clipboard, Dimensions, MouseButtons, MouseCursor, MouseEvent, MouseEventKind, MousePress,
|
||||
Point, Rect, ScreenPoint, Size, WindowCallbacks, WindowDecorations, WindowOps,
|
||||
Point, Rect, ScreenPoint, Size, WindowDecorations, WindowEvent, WindowEventReceiver,
|
||||
WindowEventSender, WindowOps,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use async_trait::async_trait;
|
||||
use config::ConfigHandle;
|
||||
use promise::{Future, Promise};
|
||||
use std::any::Any;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryInto;
|
||||
use std::rc::{Rc, Weak};
|
||||
@ -51,7 +52,7 @@ impl CopyAndPaste {
|
||||
pub(crate) struct XWindowInner {
|
||||
window_id: xcb::xproto::Window,
|
||||
conn: Weak<XConnection>,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
events: WindowEventSender,
|
||||
width: u16,
|
||||
height: u16,
|
||||
dpi: f64,
|
||||
@ -83,7 +84,7 @@ impl Drop for XWindowInner {
|
||||
}
|
||||
|
||||
impl XWindowInner {
|
||||
fn enable_opengl(&mut self) -> anyhow::Result<()> {
|
||||
fn enable_opengl(&mut self) -> anyhow::Result<Rc<glium::backend::Context>> {
|
||||
let conn = self.conn();
|
||||
|
||||
let gl_state = match conn.gl_connection.borrow().as_ref() {
|
||||
@ -114,8 +115,7 @@ impl XWindowInner {
|
||||
})?;
|
||||
|
||||
self.gl_state.replace(gl_state.clone());
|
||||
let window_handle = Window::X11(XWindow::from_id(self.window_id));
|
||||
self.callbacks.created(&window_handle, gl_state)
|
||||
Ok(gl_state)
|
||||
}
|
||||
|
||||
pub fn paint(&mut self) -> anyhow::Result<()> {
|
||||
@ -124,24 +124,7 @@ impl XWindowInner {
|
||||
}
|
||||
self.paint_all = false;
|
||||
self.expose.clear();
|
||||
|
||||
if let Some(gl_context) = self.gl_state.as_ref() {
|
||||
if gl_context.is_context_lost() {
|
||||
log::error!("opengl context was lost; should reinit");
|
||||
drop(self.gl_state.take());
|
||||
self.enable_opengl()?;
|
||||
return self.paint();
|
||||
}
|
||||
|
||||
let mut frame = glium::Frame::new(
|
||||
Rc::clone(&gl_context),
|
||||
(u32::from(self.width), u32::from(self.height)),
|
||||
);
|
||||
|
||||
self.callbacks.paint(&mut frame);
|
||||
frame.finish()?;
|
||||
}
|
||||
|
||||
self.events.try_send(WindowEvent::NeedRepaint)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -164,9 +147,8 @@ impl XWindowInner {
|
||||
self.expose.push_back(expose);
|
||||
}
|
||||
|
||||
fn do_mouse_event(&mut self, event: &MouseEvent) -> anyhow::Result<()> {
|
||||
self.callbacks
|
||||
.mouse_event(&event, &XWindow::from_id(self.window_id));
|
||||
fn do_mouse_event(&mut self, event: MouseEvent) -> anyhow::Result<()> {
|
||||
self.events.try_send(WindowEvent::MouseEvent(event))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -185,14 +167,14 @@ impl XWindowInner {
|
||||
self.dpi
|
||||
);
|
||||
self.dpi = dpi;
|
||||
self.callbacks.resize(
|
||||
Dimensions {
|
||||
let _ = self.events.try_send(WindowEvent::Resized {
|
||||
dimensions: Dimensions {
|
||||
pixel_width: self.width as usize,
|
||||
pixel_height: self.height as usize,
|
||||
dpi: self.dpi as usize,
|
||||
},
|
||||
self.is_fullscreen().unwrap_or(false),
|
||||
);
|
||||
is_full_screen: self.is_fullscreen().unwrap_or(false),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,16 +200,17 @@ impl XWindowInner {
|
||||
self.resize_promises.remove(0).ok(dimensions);
|
||||
}
|
||||
|
||||
self.callbacks
|
||||
.resize(dimensions, self.is_fullscreen().unwrap_or(false))
|
||||
self.events.try_send(WindowEvent::Resized {
|
||||
dimensions,
|
||||
is_full_screen: self.is_fullscreen().unwrap_or(false),
|
||||
})?;
|
||||
}
|
||||
xcb::KEY_PRESS | xcb::KEY_RELEASE => {
|
||||
let key_press: &xcb::KeyPressEvent = unsafe { xcb::cast_event(event) };
|
||||
self.copy_and_paste.time = key_press.time();
|
||||
if let Some(key) = conn.keyboard.process_key_event(key_press) {
|
||||
let key = key.normalize_shift();
|
||||
self.callbacks
|
||||
.key_event(&key, &XWindow::from_id(self.window_id));
|
||||
self.events.try_send(WindowEvent::KeyEvent(key))?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,7 +230,7 @@ impl XWindowInner {
|
||||
modifiers: xkeysyms::modifiers_from_state(motion.state()),
|
||||
mouse_buttons: MouseButtons::default(),
|
||||
};
|
||||
self.do_mouse_event(&event)?;
|
||||
self.do_mouse_event(event)?;
|
||||
}
|
||||
xcb::BUTTON_PRESS | xcb::BUTTON_RELEASE => {
|
||||
let button_press: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(event) };
|
||||
@ -302,16 +285,19 @@ impl XWindowInner {
|
||||
modifiers: xkeysyms::modifiers_from_state(button_press.state()),
|
||||
mouse_buttons: MouseButtons::default(),
|
||||
};
|
||||
self.do_mouse_event(&event)?;
|
||||
self.do_mouse_event(event)?;
|
||||
}
|
||||
xcb::CLIENT_MESSAGE => {
|
||||
let msg: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(event) };
|
||||
if msg.data().data32()[0] == conn.atom_delete() && self.callbacks.can_close() {
|
||||
xcb::destroy_window(conn.conn(), self.window_id);
|
||||
|
||||
if msg.data().data32()[0] == conn.atom_delete() {
|
||||
if self.events.try_send(WindowEvent::CloseRequested).is_err() {
|
||||
xcb::destroy_window(conn.conn(), self.window_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
xcb::DESTROY_NOTIFY => {
|
||||
self.callbacks.destroy();
|
||||
self.events.try_send(WindowEvent::Destroyed)?;
|
||||
conn.windows.borrow_mut().remove(&self.window_id);
|
||||
}
|
||||
xcb::SELECTION_CLEAR => {
|
||||
@ -354,11 +340,11 @@ impl XWindowInner {
|
||||
}
|
||||
xcb::FOCUS_IN => {
|
||||
log::trace!("Calling focus_change(true)");
|
||||
self.callbacks.focus_change(true);
|
||||
self.events.try_send(WindowEvent::FocusChanged(true))?;
|
||||
}
|
||||
xcb::FOCUS_OUT => {
|
||||
log::trace!("Calling focus_change(false)");
|
||||
self.callbacks.focus_change(false);
|
||||
self.events.try_send(WindowEvent::FocusChanged(false))?;
|
||||
}
|
||||
_ => {
|
||||
eprintln!("unhandled: {:x}", r);
|
||||
@ -683,9 +669,8 @@ impl XWindow {
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
config: Option<&ConfigHandle>,
|
||||
) -> anyhow::Result<Window> {
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
let config = match config {
|
||||
Some(c) => c.clone(),
|
||||
None => config::configuration(),
|
||||
@ -698,6 +683,8 @@ impl XWindow {
|
||||
})?
|
||||
.x11();
|
||||
|
||||
let (events, receiver) = async_channel::unbounded();
|
||||
|
||||
let window_id;
|
||||
let window = {
|
||||
let setup = conn.conn().get_setup();
|
||||
@ -761,7 +748,7 @@ impl XWindow {
|
||||
Arc::new(Mutex::new(XWindowInner {
|
||||
window_id,
|
||||
conn: Rc::downgrade(&conn),
|
||||
callbacks,
|
||||
events,
|
||||
width: width.try_into()?,
|
||||
height: height.try_into()?,
|
||||
dpi: conn.default_dpi(),
|
||||
@ -797,14 +784,12 @@ impl XWindow {
|
||||
|
||||
let window_handle = Window::X11(XWindow::from_id(window_id));
|
||||
|
||||
window.lock().unwrap().enable_opengl()?;
|
||||
|
||||
conn.windows.borrow_mut().insert(window_id, window);
|
||||
|
||||
window_handle.set_title(name);
|
||||
window_handle.show();
|
||||
|
||||
Ok(window_handle)
|
||||
Ok((window_handle, receiver))
|
||||
}
|
||||
}
|
||||
|
||||
@ -900,7 +885,21 @@ impl XWindowInner {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl WindowOps for XWindow {
|
||||
async fn enable_opengl(&self) -> anyhow::Result<Rc<glium::backend::Context>> {
|
||||
let window = self.0;
|
||||
promise::spawn::spawn(async move {
|
||||
if let Some(handle) = Connection::get().unwrap().x11().window_by_id(window) {
|
||||
let mut inner = handle.lock().unwrap();
|
||||
inner.enable_opengl()
|
||||
} else {
|
||||
anyhow::bail!("invalid window");
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn close(&self) -> Future<()> {
|
||||
XConnection::with_window_inner(self.0, |inner| {
|
||||
inner.close();
|
||||
@ -997,20 +996,6 @@ impl WindowOps for XWindow {
|
||||
})
|
||||
}
|
||||
|
||||
fn apply<R, F: Send + 'static + FnMut(&mut dyn Any, &dyn WindowOps) -> anyhow::Result<R>>(
|
||||
&self,
|
||||
mut func: F,
|
||||
) -> promise::Future<R>
|
||||
where
|
||||
Self: Sized,
|
||||
R: Send + 'static,
|
||||
{
|
||||
XConnection::with_window_inner(self.0, move |inner| {
|
||||
let window = XWindow(inner.window_id);
|
||||
func(inner.callbacks.as_any(), &window)
|
||||
})
|
||||
}
|
||||
|
||||
/// Initiate textual transfer from the clipboard
|
||||
fn get_clipboard(&self, clipboard: Clipboard) -> Future<String> {
|
||||
let mut promise = Promise::new();
|
||||
|
@ -7,10 +7,10 @@ use crate::os::wayland::connection::WaylandConnection;
|
||||
use crate::os::wayland::window::WaylandWindow;
|
||||
use crate::os::x11::connection::XConnection;
|
||||
use crate::os::x11::window::XWindow;
|
||||
use crate::{Clipboard, Dimensions, MouseCursor, ScreenPoint, WindowCallbacks, WindowOps};
|
||||
use crate::{Clipboard, Dimensions, MouseCursor, ScreenPoint, WindowEventReceiver, WindowOps};
|
||||
use async_trait::async_trait;
|
||||
use config::ConfigHandle;
|
||||
use promise::*;
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum Connection {
|
||||
@ -51,16 +51,13 @@ impl Connection {
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
config: Option<&ConfigHandle>,
|
||||
) -> anyhow::Result<Window> {
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
match self {
|
||||
Self::X11(_) => {
|
||||
XWindow::new_window(class_name, name, width, height, callbacks, config).await
|
||||
}
|
||||
Self::X11(_) => XWindow::new_window(class_name, name, width, height, config).await,
|
||||
#[cfg(feature = "wayland")]
|
||||
Self::Wayland(_) => {
|
||||
WaylandWindow::new_window(class_name, name, width, height, callbacks, config).await
|
||||
WaylandWindow::new_window(class_name, name, width, height, config).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,17 +110,33 @@ impl Window {
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
config: Option<&ConfigHandle>,
|
||||
) -> anyhow::Result<Window> {
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
Connection::get()
|
||||
.unwrap()
|
||||
.new_window(class_name, name, width, height, callbacks, config)
|
||||
.new_window(class_name, name, width, height, config)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl WindowOps for Window {
|
||||
async fn enable_opengl(&self) -> anyhow::Result<Rc<glium::backend::Context>> {
|
||||
match self {
|
||||
Self::X11(x) => x.enable_opengl().await,
|
||||
#[cfg(feature = "wayland")]
|
||||
Self::Wayland(w) => w.enable_opengl().await,
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_frame(&self, frame: glium::Frame) -> anyhow::Result<()> {
|
||||
match self {
|
||||
Self::X11(x) => x.finish_frame(frame),
|
||||
#[cfg(feature = "wayland")]
|
||||
Self::Wayland(w) => w.finish_frame(frame),
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&self) -> Future<()> {
|
||||
match self {
|
||||
Self::X11(x) => x.close(),
|
||||
@ -212,21 +225,6 @@ impl WindowOps for Window {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply<R, F: Send + 'static + FnMut(&mut dyn Any, &dyn WindowOps) -> anyhow::Result<R>>(
|
||||
&self,
|
||||
func: F,
|
||||
) -> promise::Future<R>
|
||||
where
|
||||
Self: Sized,
|
||||
R: Send + 'static,
|
||||
{
|
||||
match self {
|
||||
Self::X11(x) => x.apply(func),
|
||||
#[cfg(feature = "wayland")]
|
||||
Self::Wayland(w) => w.apply(func),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clipboard(&self, clipboard: Clipboard) -> Future<String> {
|
||||
match self {
|
||||
Self::X11(x) => x.get_clipboard(clipboard),
|
||||
|
Loading…
Reference in New Issue
Block a user