mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 15:04:36 +03:00
window: move away from async event queue
I added this originally thinking that it would make it easier to resolve https://github.com/wez/wezterm/issues/695 and to integrate wgpu support, but it's the cause of https://github.com/wez/wezterm/issues/922 so let's take it out and more directly connect the window events to those in the terminal. This commit likely breaks mac and windows; pushing it so that I can check it out and verify on those systems.
This commit is contained in:
parent
ad19b4f57b
commit
db6da81272
@ -142,6 +142,7 @@ pub fn designate_this_as_the_main_thread() {
|
||||
});
|
||||
}
|
||||
|
||||
#[must_use = "Cancels the subscription when dropped"]
|
||||
pub struct ConfigSubscription(usize);
|
||||
|
||||
impl Drop for ConfigSubscription {
|
||||
@ -150,11 +151,11 @@ impl Drop for ConfigSubscription {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe_to_config_reload<F>(subscriber: F)
|
||||
pub fn subscribe_to_config_reload<F>(subscriber: F) -> ConfigSubscription
|
||||
where
|
||||
F: Fn() -> bool + 'static + Send,
|
||||
{
|
||||
CONFIG.subscribe(subscriber);
|
||||
ConfigSubscription(CONFIG.subscribe(subscriber))
|
||||
}
|
||||
|
||||
/// Spawn a future that will run with an optional Lua state from the most
|
||||
@ -561,12 +562,12 @@ impl Configuration {
|
||||
}
|
||||
|
||||
/// Subscribe to config reload events
|
||||
fn subscribe<F>(&self, subscriber: F)
|
||||
fn subscribe<F>(&self, subscriber: F) -> usize
|
||||
where
|
||||
F: Fn() -> bool + 'static + Send,
|
||||
{
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.subscribe(subscriber);
|
||||
inner.subscribe(subscriber)
|
||||
}
|
||||
|
||||
fn unsub(&self, sub_id: usize) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::termwindow::TermWindowNotif;
|
||||
use crate::TermWindow;
|
||||
use config::keyassignment::{ClipboardCopyDestination, ClipboardPasteSource};
|
||||
use mux::pane::Pane;
|
||||
@ -88,11 +89,7 @@ impl TermWindow {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn paste_from_clipboard(
|
||||
&mut self,
|
||||
pane: &Rc<dyn Pane>,
|
||||
clipboard: ClipboardPasteSource,
|
||||
) {
|
||||
pub fn paste_from_clipboard(&mut self, pane: &Rc<dyn Pane>, clipboard: ClipboardPasteSource) {
|
||||
let pane_id = pane.pane_id();
|
||||
let window = self.window.as_ref().unwrap().clone();
|
||||
let clipboard = match clipboard {
|
||||
@ -100,14 +97,18 @@ impl TermWindow {
|
||||
ClipboardPasteSource::PrimarySelection => Clipboard::PrimarySelection,
|
||||
};
|
||||
let future = window.get_clipboard(clipboard);
|
||||
|
||||
if let Ok(clip) = future.await {
|
||||
if let Some(pane) = self.pane_state(pane_id).overlay.clone().or_else(|| {
|
||||
let mux = Mux::get().unwrap();
|
||||
mux.get_pane(pane_id)
|
||||
}) {
|
||||
pane.trickle_paste(clip).ok();
|
||||
promise::spawn::spawn(async move {
|
||||
if let Ok(clip) = future.await {
|
||||
window.notify(TermWindowNotif::Apply(Box::new(move |myself| {
|
||||
if let Some(pane) = myself.pane_state(pane_id).overlay.clone().or_else(|| {
|
||||
let mux = Mux::get().unwrap();
|
||||
mux.get_pane(pane_id)
|
||||
}) {
|
||||
pane.trickle_paste(clip).ok();
|
||||
}
|
||||
})));
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ pub enum Key {
|
||||
}
|
||||
|
||||
impl super::TermWindow {
|
||||
pub async fn key_event_impl(&mut self, window_key: KeyEvent, context: &dyn WindowOps) -> bool {
|
||||
pub fn key_event_impl(&mut self, window_key: KeyEvent, context: &dyn WindowOps) -> bool {
|
||||
if !window_key.key_is_down {
|
||||
return false;
|
||||
}
|
||||
@ -95,7 +95,7 @@ impl super::TermWindow {
|
||||
.input_map
|
||||
.lookup_key(&raw_code_key, window_key.raw_modifiers | leader_mod)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &assignment).await.ok();
|
||||
self.perform_key_assignment(&pane, &assignment).ok();
|
||||
context.invalidate();
|
||||
|
||||
if leader_active {
|
||||
@ -124,7 +124,7 @@ impl super::TermWindow {
|
||||
.input_map
|
||||
.lookup_key(key, window_key.raw_modifiers | leader_mod)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &assignment).await.ok();
|
||||
self.perform_key_assignment(&pane, &assignment).ok();
|
||||
context.invalidate();
|
||||
|
||||
if leader_active {
|
||||
@ -189,7 +189,7 @@ impl super::TermWindow {
|
||||
.input_map
|
||||
.lookup_key(&window_key.key, window_key.modifiers | leader_mod)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &assignment).await.ok();
|
||||
self.perform_key_assignment(&pane, &assignment).ok();
|
||||
context.invalidate();
|
||||
if leader_active {
|
||||
// A successful leader key-lookup cancels the leader
|
||||
|
@ -34,7 +34,6 @@ use mux::{Mux, MuxNotification};
|
||||
use portable_pty::PtySize;
|
||||
use serde::*;
|
||||
use smol::channel::Sender;
|
||||
use smol::future::FutureExt;
|
||||
use smol::Timer;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
@ -220,13 +219,17 @@ pub struct TermWindow {
|
||||
shape_cache:
|
||||
RefCell<LruCache<ShapeCacheKey, anyhow::Result<Rc<Vec<ShapedInfo<SrgbTexture2d>>>>>>,
|
||||
|
||||
last_blink_paint: Instant,
|
||||
next_blink_paint: RefCell<Instant>,
|
||||
last_status_call: Instant,
|
||||
|
||||
palette: Option<ColorPalette>,
|
||||
|
||||
event_states: HashMap<String, EventState>,
|
||||
has_animation: RefCell<Option<Instant>>,
|
||||
scheduled_animation: RefCell<Option<Instant>>,
|
||||
|
||||
gl: Option<Rc<glium::backend::Context>>,
|
||||
config_subscription: Option<config::ConfigSubscription>,
|
||||
}
|
||||
|
||||
impl TermWindow {
|
||||
@ -271,7 +274,7 @@ impl TermWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn focus_changed(&mut self, focused: bool) {
|
||||
fn focus_changed(&mut self, focused: bool, window: &Window) {
|
||||
log::trace!("Setting focus to {:?}", focused);
|
||||
self.focused = if focused { Some(Instant::now()) } else { None };
|
||||
|
||||
@ -284,7 +287,7 @@ impl TermWindow {
|
||||
self.prev_cursor.bump();
|
||||
|
||||
// force cursor to be repainted
|
||||
self.window.as_ref().unwrap().invalidate();
|
||||
window.invalidate();
|
||||
|
||||
if let Some(pane) = self.get_active_pane_or_overlay() {
|
||||
pane.focus_changed(focused);
|
||||
@ -445,7 +448,9 @@ impl TermWindow {
|
||||
|
||||
let clipboard_contents = Arc::new(Mutex::new(None));
|
||||
|
||||
let mut myself = Self {
|
||||
let myself = Self {
|
||||
config_subscription: None,
|
||||
gl: None,
|
||||
window: None,
|
||||
window_background,
|
||||
config: config.clone(),
|
||||
@ -484,19 +489,29 @@ impl TermWindow {
|
||||
"shape_cache.miss.rate",
|
||||
65536,
|
||||
)),
|
||||
last_blink_paint: Instant::now(),
|
||||
next_blink_paint: RefCell::new(Instant::now()),
|
||||
last_status_call: Instant::now(),
|
||||
event_states: HashMap::new(),
|
||||
has_animation: RefCell::new(None),
|
||||
scheduled_animation: RefCell::new(None),
|
||||
};
|
||||
|
||||
let (window, events) = Window::new_window(
|
||||
let tw = Rc::new(RefCell::new(myself));
|
||||
let tw_event = Rc::clone(&tw);
|
||||
|
||||
let window = Window::new_window(
|
||||
&*WINDOW_CLASS.lock().unwrap(),
|
||||
"wezterm",
|
||||
dimensions.pixel_width,
|
||||
dimensions.pixel_height,
|
||||
Some(&config),
|
||||
Rc::clone(&fontconfig),
|
||||
move |event, window| {
|
||||
let mut tw = tw_event.borrow_mut();
|
||||
if let Err(err) = tw.dispatch_window_event(event, window) {
|
||||
log::error!("dispatch_window_event: {:#}", err);
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -513,144 +528,37 @@ impl TermWindow {
|
||||
}
|
||||
});
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
let gl = window.enable_opengl().await?;
|
||||
let gl = window.enable_opengl().await?;
|
||||
{
|
||||
let mut myself = tw.borrow_mut();
|
||||
myself.config_subscription.replace(config_subscription);
|
||||
myself.gl.replace(Rc::clone(&gl));
|
||||
myself.created(&window, Rc::clone(&gl))?;
|
||||
myself.subscribe_to_pane_updates();
|
||||
myself.emit_status_event();
|
||||
|
||||
loop {
|
||||
let mut need_invalidate = false;
|
||||
let mut sleep_interval = None;
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
for f in &[
|
||||
Self::maintain_status,
|
||||
Self::maintain_animation,
|
||||
Self::maintain_blink,
|
||||
] {
|
||||
let (invalidate, next) = f(&mut myself, now);
|
||||
if invalidate {
|
||||
need_invalidate = true;
|
||||
}
|
||||
if let Some(next) = next {
|
||||
let next = sleep_interval.unwrap_or(next).min(next);
|
||||
sleep_interval.replace(next);
|
||||
}
|
||||
}
|
||||
|
||||
if need_invalidate {
|
||||
if !myself.do_paint(&gl, &window) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let recv = async {
|
||||
let event = events.recv().await?;
|
||||
anyhow::Result::<Option<WindowEvent>>::Ok(Some(event))
|
||||
};
|
||||
|
||||
let wakeup = async {
|
||||
Timer::at(sleep_interval.unwrap_or(now + Duration::from_secs(86400))).await;
|
||||
anyhow::Result::<Option<WindowEvent>>::Ok(None)
|
||||
};
|
||||
|
||||
match recv.or(wakeup).await {
|
||||
Err(_) => break,
|
||||
Ok(None) => {}
|
||||
Ok(Some(event)) => {
|
||||
let (event, peeked) = Self::coalesce_window_events(event, &events);
|
||||
|
||||
match myself.dispatch_window_event(event, &window, &gl).await {
|
||||
Ok(true) => {}
|
||||
Ok(false) => break,
|
||||
Err(err) => {
|
||||
log::error!("{:#}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(event) = peeked {
|
||||
match myself.dispatch_window_event(event, &window, &gl).await {
|
||||
Ok(true) => {}
|
||||
Ok(false) => break,
|
||||
Err(err) => {
|
||||
log::error!("{:#}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(config_subscription);
|
||||
anyhow::Result::<()>::Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
crate::update::start_update_checker();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Collapse a series of Resized and NeedRepaint events into a single
|
||||
/// Resized event, or a series of NeedRepaint into a single NeedRepaint
|
||||
/// event.
|
||||
/// Returns the coalesced event, and possibly a subsequent event of
|
||||
/// some other type.
|
||||
fn coalesce_window_events(
|
||||
event: WindowEvent,
|
||||
events: &WindowEventReceiver,
|
||||
) -> (WindowEvent, Option<WindowEvent>) {
|
||||
if matches!(
|
||||
&event,
|
||||
WindowEvent::Resized { .. } | WindowEvent::NeedRepaint
|
||||
) {
|
||||
let mut resize = if matches!(&event, WindowEvent::Resized { .. }) {
|
||||
Some(event)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut peek = None;
|
||||
|
||||
while let Ok(next) = events.try_recv() {
|
||||
match next {
|
||||
WindowEvent::NeedRepaint => {}
|
||||
e @ WindowEvent::Resized { .. } => {
|
||||
resize.replace(e);
|
||||
}
|
||||
other => {
|
||||
peek.replace(other);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(resize.unwrap_or(WindowEvent::NeedRepaint), peek)
|
||||
} else {
|
||||
(event, None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn dispatch_window_event(
|
||||
fn dispatch_window_event(
|
||||
&mut self,
|
||||
event: WindowEvent,
|
||||
window: &Window,
|
||||
gl: &Rc<glium::backend::Context>,
|
||||
) -> anyhow::Result<bool> {
|
||||
match event {
|
||||
WindowEvent::Destroyed => Ok(false),
|
||||
WindowEvent::CloseRequested => {
|
||||
self.close_requested(&window);
|
||||
self.close_requested(window);
|
||||
Ok(true)
|
||||
}
|
||||
WindowEvent::FocusChanged(focused) => {
|
||||
self.focus_changed(focused);
|
||||
self.focus_changed(focused, window);
|
||||
Ok(true)
|
||||
}
|
||||
WindowEvent::MouseEvent(event) => {
|
||||
self.mouse_event_impl(event, window).await;
|
||||
self.mouse_event_impl(event, window);
|
||||
Ok(true)
|
||||
}
|
||||
WindowEvent::Resized {
|
||||
@ -658,17 +566,16 @@ impl TermWindow {
|
||||
is_full_screen,
|
||||
} => {
|
||||
self.resize(dimensions, is_full_screen);
|
||||
Ok(self.do_paint(&gl, window))
|
||||
}
|
||||
WindowEvent::KeyEvent(event) => {
|
||||
self.key_event_impl(event, window).await;
|
||||
Ok(true)
|
||||
}
|
||||
WindowEvent::NeedRepaint => Ok(self.do_paint(&gl, window)),
|
||||
WindowEvent::KeyEvent(event) => {
|
||||
self.key_event_impl(event, window);
|
||||
Ok(true)
|
||||
}
|
||||
WindowEvent::NeedRepaint => Ok(self.do_paint(window)),
|
||||
WindowEvent::Notification(item) => {
|
||||
if let Ok(notif) = item.downcast::<TermWindowNotif>() {
|
||||
self.dispatch_notif(*notif, window)
|
||||
.await
|
||||
.context("dispatch_notif")?;
|
||||
}
|
||||
Ok(true)
|
||||
@ -676,7 +583,12 @@ impl TermWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn do_paint(&mut self, gl: &Rc<glium::backend::Context>, window: &Window) -> bool {
|
||||
fn do_paint(&mut self, window: &Window) -> bool {
|
||||
let gl = match self.gl.as_ref() {
|
||||
Some(gl) => gl,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if gl.is_context_lost() {
|
||||
log::error!("opengl context was lost; should reinit");
|
||||
window.close();
|
||||
@ -695,12 +607,8 @@ impl TermWindow {
|
||||
window.finish_frame(frame).is_ok()
|
||||
}
|
||||
|
||||
async fn dispatch_notif(
|
||||
&mut self,
|
||||
notif: TermWindowNotif,
|
||||
window: &Window,
|
||||
) -> anyhow::Result<()> {
|
||||
fn chan_err<T>(e: smol::channel::SendError<T>) -> anyhow::Error {
|
||||
fn dispatch_notif(&mut self, notif: TermWindowNotif, window: &Window) -> anyhow::Result<()> {
|
||||
fn chan_err<T>(e: smol::channel::TrySendError<T>) -> anyhow::Error {
|
||||
anyhow::anyhow!("{}", e)
|
||||
}
|
||||
|
||||
@ -718,24 +626,23 @@ impl TermWindow {
|
||||
.get_pane(pane_id)
|
||||
.ok_or_else(|| anyhow!("pane id {} is not valid", pane_id))?;
|
||||
self.perform_key_assignment(&pane, &assignment)
|
||||
.await
|
||||
.context("perform_key_assignment")?;
|
||||
}
|
||||
TermWindowNotif::SetRightStatus(status) => {
|
||||
if status != self.right_status {
|
||||
self.right_status = status;
|
||||
self.update_title_post_status();
|
||||
} else {
|
||||
self.schedule_next_status_update();
|
||||
}
|
||||
}
|
||||
TermWindowNotif::GetDimensions(tx) => {
|
||||
tx.send((self.dimensions, self.is_full_screen))
|
||||
.await
|
||||
tx.try_send((self.dimensions, self.is_full_screen))
|
||||
.map_err(chan_err)
|
||||
.context("send GetDimensions response")?;
|
||||
}
|
||||
TermWindowNotif::GetEffectiveConfig(tx) => {
|
||||
tx.send(self.config.clone())
|
||||
.await
|
||||
tx.try_send(self.config.clone())
|
||||
.map_err(chan_err)
|
||||
.context("send GetEffectiveConfig response")?;
|
||||
}
|
||||
@ -743,8 +650,7 @@ impl TermWindow {
|
||||
self.finish_window_event(&name, again);
|
||||
}
|
||||
TermWindowNotif::GetConfigOverrides(tx) => {
|
||||
tx.send(self.config_overrides.clone())
|
||||
.await
|
||||
tx.try_send(self.config_overrides.clone())
|
||||
.map_err(chan_err)
|
||||
.context("send GetConfigOverrides response")?;
|
||||
}
|
||||
@ -793,8 +699,7 @@ impl TermWindow {
|
||||
.get_pane(pane_id)
|
||||
.ok_or_else(|| anyhow!("pane id {} is not valid", pane_id))?;
|
||||
|
||||
tx.send(self.selection_text(&pane))
|
||||
.await
|
||||
tx.try_send(self.selection_text(&pane))
|
||||
.map_err(chan_err)
|
||||
.context("send GetSelectionForPane response")?;
|
||||
}
|
||||
@ -1031,66 +936,6 @@ impl TermWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn maintain_status(&mut self, now: Instant) -> (bool, Option<Instant>) {
|
||||
let interval = Duration::from_millis(self.config.status_update_interval);
|
||||
if now.duration_since(self.last_status_call) > interval {
|
||||
self.last_status_call = now;
|
||||
self.schedule_status_update();
|
||||
}
|
||||
|
||||
(false, Some(self.last_status_call + interval))
|
||||
}
|
||||
|
||||
/// If self.has_animation is some, then the last render detected
|
||||
/// image attachments with multiple frames, so we also need to
|
||||
/// invalidate the viewport when the next frame is due
|
||||
fn maintain_animation(&mut self, now: Instant) -> (bool, Option<Instant>) {
|
||||
if self.focused.is_some() {
|
||||
if let Some(next_due) = *self.has_animation.borrow() {
|
||||
return (now >= next_due, Some(next_due));
|
||||
}
|
||||
}
|
||||
(false, None)
|
||||
}
|
||||
|
||||
/// If blinking is permitted, and the cursor shape is set
|
||||
/// to a blinking variant, and it's been longer than the
|
||||
/// blink rate interval, then invalidate and redraw
|
||||
/// so that we will re-evaluate the cursor visibility.
|
||||
/// This is pretty heavyweight: it would be nice to only invalidate
|
||||
/// the line on which the cursor resides, and then only if the cursor
|
||||
/// is within the viewport.
|
||||
fn maintain_blink(&mut self, now: Instant) -> (bool, Option<Instant>) {
|
||||
if self.focused.is_none() || self.config.cursor_blink_rate == 0 {
|
||||
return (false, None);
|
||||
}
|
||||
|
||||
let panes = self.get_panes_to_render();
|
||||
if panes.is_empty() {
|
||||
self.window.as_ref().unwrap().close();
|
||||
return (false, None);
|
||||
}
|
||||
|
||||
for pos in panes {
|
||||
if pos.is_active {
|
||||
let shape = self
|
||||
.config
|
||||
.default_cursor_style
|
||||
.effective_shape(pos.pane.get_cursor_position().shape);
|
||||
if shape.is_blinking() {
|
||||
let interval = Duration::from_millis(self.config.cursor_blink_rate);
|
||||
if now.duration_since(self.last_blink_paint) > interval {
|
||||
self.last_blink_paint = now;
|
||||
return (true, Some(self.last_blink_paint + interval));
|
||||
}
|
||||
return (false, Some(self.last_blink_paint + interval));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(false, None)
|
||||
}
|
||||
|
||||
fn check_for_dirty_lines_and_invalidate_selection(&mut self, pane: &Rc<dyn Pane>) -> bool {
|
||||
let dims = pane.get_dimensions();
|
||||
let viewport = self
|
||||
@ -1347,6 +1192,25 @@ impl TermWindow {
|
||||
self.config_was_reloaded();
|
||||
}
|
||||
}
|
||||
self.schedule_next_status_update();
|
||||
}
|
||||
|
||||
fn schedule_next_status_update(&mut self) {
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
let now = Instant::now();
|
||||
if self.last_status_call <= now {
|
||||
let interval = Duration::from_millis(self.config.status_update_interval);
|
||||
let target = now + interval;
|
||||
self.last_status_call = target;
|
||||
|
||||
let window = window.clone();
|
||||
promise::spawn::spawn(async move {
|
||||
Timer::at(target).await;
|
||||
window.notify(TermWindowNotif::EmitStatusUpdate);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_text_cursor(&mut self, pane: &Rc<dyn Pane>) {
|
||||
@ -1652,7 +1516,7 @@ impl TermWindow {
|
||||
self.move_tab(tab)
|
||||
}
|
||||
|
||||
pub async fn perform_key_assignment(
|
||||
pub fn perform_key_assignment(
|
||||
&mut self,
|
||||
pane: &Rc<dyn Pane>,
|
||||
assignment: &KeyAssignment,
|
||||
@ -1694,15 +1558,13 @@ impl TermWindow {
|
||||
self.copy_to_clipboard(*dest, text);
|
||||
}
|
||||
Paste => {
|
||||
self.paste_from_clipboard(pane, ClipboardPasteSource::Clipboard)
|
||||
.await;
|
||||
self.paste_from_clipboard(pane, ClipboardPasteSource::Clipboard);
|
||||
}
|
||||
PastePrimarySelection => {
|
||||
self.paste_from_clipboard(pane, ClipboardPasteSource::PrimarySelection)
|
||||
.await;
|
||||
self.paste_from_clipboard(pane, ClipboardPasteSource::PrimarySelection);
|
||||
}
|
||||
PasteFrom(source) => {
|
||||
self.paste_from_clipboard(pane, *source).await;
|
||||
self.paste_from_clipboard(pane, *source);
|
||||
}
|
||||
ActivateTabRelative(n) => {
|
||||
self.activate_tab_relative(*n)?;
|
||||
|
@ -18,7 +18,7 @@ use wezterm_term::input::MouseEventKind as TMEK;
|
||||
use wezterm_term::{LastMouseClick, StableRowIndex};
|
||||
|
||||
impl super::TermWindow {
|
||||
pub async fn mouse_event_impl(&mut self, event: MouseEvent, context: &dyn WindowOps) {
|
||||
pub fn mouse_event_impl(&mut self, event: MouseEvent, context: &dyn WindowOps) {
|
||||
let pane = match self.get_active_pane_or_overlay() {
|
||||
Some(pane) => pane,
|
||||
None => return,
|
||||
@ -202,8 +202,7 @@ impl super::TermWindow {
|
||||
} else if in_scroll_bar {
|
||||
self.mouse_event_scroll_bar(pane, event, context);
|
||||
} else {
|
||||
self.mouse_event_terminal(pane, x, term_y, event, context)
|
||||
.await;
|
||||
self.mouse_event_terminal(pane, x, term_y, event, context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +295,7 @@ impl super::TermWindow {
|
||||
context.set_cursor(Some(MouseCursor::Arrow));
|
||||
}
|
||||
|
||||
pub async fn mouse_event_terminal(
|
||||
pub fn mouse_event_terminal(
|
||||
&mut self,
|
||||
mut pane: Rc<dyn Pane>,
|
||||
mut x: usize,
|
||||
@ -503,7 +502,7 @@ impl super::TermWindow {
|
||||
.input_map
|
||||
.lookup_mouse(event_trigger_type.clone(), modifiers)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &action).await.ok();
|
||||
self.perform_key_assignment(&pane, &action).ok();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,10 @@ use config::{ConfigHandle, HsbTransform, TextStyle};
|
||||
use mux::pane::Pane;
|
||||
use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
||||
use mux::tab::{PositionedPane, PositionedSplit, SplitDirection};
|
||||
use smol::Timer;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use termwiz::cellcluster::CellCluster;
|
||||
use termwiz::surface::{CursorShape, CursorVisibility};
|
||||
@ -138,6 +140,23 @@ impl super::TermWindow {
|
||||
metrics::histogram!("gui.paint.opengl", start.elapsed());
|
||||
metrics::histogram!("gui.paint.opengl.rate", 1.);
|
||||
self.update_title_post_status();
|
||||
|
||||
// If self.has_animation is some, then the last render detected
|
||||
// image attachments with multiple frames, so we also need to
|
||||
// invalidate the viewport when the next frame is due
|
||||
if self.focused.is_some() {
|
||||
if let Some(next_due) = *self.has_animation.borrow() {
|
||||
if Some(next_due) != *self.scheduled_animation.borrow() {
|
||||
self.scheduled_animation.borrow_mut().replace(next_due);
|
||||
let window = self.window.clone().take().unwrap();
|
||||
promise::spawn::spawn(async move {
|
||||
Timer::at(next_due).await;
|
||||
window.invalidate();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_next_frame_time(&self, next_due: Option<Instant>) {
|
||||
@ -1133,10 +1152,34 @@ impl super::TermWindow {
|
||||
&& params.config.cursor_blink_rate != 0
|
||||
&& self.focused.is_some();
|
||||
if blinking {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
// schedule an invalidation so that we can paint the next
|
||||
// cycle at the right time.
|
||||
if let Some(window) = self.window.clone() {
|
||||
let interval = Duration::from_millis(params.config.cursor_blink_rate);
|
||||
let next = *self.next_blink_paint.borrow();
|
||||
if next < now {
|
||||
let target = next + interval;
|
||||
let target = if target <= now {
|
||||
now + interval
|
||||
} else {
|
||||
target
|
||||
};
|
||||
|
||||
*self.next_blink_paint.borrow_mut() = target;
|
||||
promise::spawn::spawn(async move {
|
||||
Timer::at(target).await;
|
||||
window.invalidate();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
// Divide the time since we last moved by the blink rate.
|
||||
// If the result is even then the cursor is "on", else it
|
||||
// is "off"
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
let milli_uptime = now
|
||||
.duration_since(self.prev_cursor.last_cursor_movement())
|
||||
.as_millis();
|
||||
|
@ -1,5 +1,6 @@
|
||||
use ::window::*;
|
||||
use promise::spawn::spawn;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wezterm_font::FontConfiguration;
|
||||
|
||||
@ -7,6 +8,8 @@ struct MyWindow {
|
||||
allow_close: bool,
|
||||
cursor_pos: Point,
|
||||
dims: Dimensions,
|
||||
win: Option<Window>,
|
||||
gl: Option<Rc<glium::backend::Context>>,
|
||||
}
|
||||
|
||||
impl Drop for MyWindow {
|
||||
@ -15,37 +18,20 @@ impl Drop for MyWindow {
|
||||
}
|
||||
}
|
||||
|
||||
async fn spawn_window() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let fontconfig = Rc::new(FontConfiguration::new(
|
||||
None,
|
||||
::window::default_dpi() as usize,
|
||||
)?);
|
||||
let (win, events) =
|
||||
Window::new_window("myclass", "the title", 800, 600, None, fontconfig).await?;
|
||||
impl MyWindow {
|
||||
fn dispatch(&mut self, event: WindowEvent) {
|
||||
let win = match self.win.as_ref() {
|
||||
Some(win) => win,
|
||||
None => return,
|
||||
};
|
||||
|
||||
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?;
|
||||
let gl = win.enable_opengl().await?;
|
||||
eprintln!("window is visible, do loop");
|
||||
|
||||
while let Ok(event) = events.recv().await {
|
||||
match dbg!(event) {
|
||||
WindowEvent::CloseRequested => {
|
||||
eprintln!("can I close?");
|
||||
if state.allow_close {
|
||||
if self.allow_close {
|
||||
win.close();
|
||||
} else {
|
||||
state.allow_close = true;
|
||||
self.allow_close = true;
|
||||
}
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
@ -57,11 +43,11 @@ async fn spawn_window() -> Result<(), Box<dyn std::error::Error>> {
|
||||
is_full_screen,
|
||||
} => {
|
||||
eprintln!("resize {:?} is_full_screen={}", dimensions, is_full_screen);
|
||||
state.dims = dimensions;
|
||||
self.dims = dimensions;
|
||||
}
|
||||
WindowEvent::MouseEvent(event) => {
|
||||
state.cursor_pos = event.coords;
|
||||
win.invalidate();
|
||||
self.cursor_pos = event.coords;
|
||||
// win.invalidate();
|
||||
win.set_cursor(Some(MouseCursor::Arrow));
|
||||
|
||||
if event.kind == MouseEventKind::Press(MousePress::Left) {
|
||||
@ -74,27 +60,68 @@ async fn spawn_window() -> Result<(), Box<dyn std::error::Error>> {
|
||||
win.default_key_processing(key);
|
||||
}
|
||||
WindowEvent::NeedRepaint => {
|
||||
if gl.is_context_lost() {
|
||||
eprintln!("opengl context was lost; should reinit");
|
||||
break;
|
||||
if let Some(gl) = self.gl.as_mut() {
|
||||
if gl.is_context_lost() {
|
||||
eprintln!("opengl context was lost; should reinit");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut frame = glium::Frame::new(
|
||||
Rc::clone(&gl),
|
||||
(self.dims.pixel_width as u32, self.dims.pixel_height as u32),
|
||||
);
|
||||
|
||||
use glium::Surface;
|
||||
frame.clear_color_srgb(0.25, 0.125, 0.375, 1.0);
|
||||
win.finish_frame(frame).unwrap();
|
||||
}
|
||||
|
||||
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::Notification(_) | WindowEvent::FocusChanged(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn spawn_window() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let fontconfig = Rc::new(FontConfiguration::new(
|
||||
None,
|
||||
::window::default_dpi() as usize,
|
||||
)?);
|
||||
|
||||
let state = Rc::new(RefCell::new(MyWindow {
|
||||
allow_close: false,
|
||||
cursor_pos: Point::new(100, 200),
|
||||
dims: Dimensions {
|
||||
pixel_width: 800,
|
||||
pixel_height: 600,
|
||||
dpi: 0,
|
||||
},
|
||||
win: None,
|
||||
gl: None,
|
||||
}));
|
||||
|
||||
let cb_state = Rc::clone(&state);
|
||||
let win = Window::new_window(
|
||||
"myclass",
|
||||
"the title",
|
||||
800,
|
||||
600,
|
||||
None,
|
||||
fontconfig,
|
||||
move |event| {
|
||||
let mut state = cb_state.borrow_mut();
|
||||
state.dispatch(event)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
state.borrow_mut().win.replace(win.clone());
|
||||
|
||||
eprintln!("before show");
|
||||
win.show();
|
||||
let gl = win.enable_opengl().await?;
|
||||
|
||||
state.borrow_mut().gl.replace(gl);
|
||||
win.invalidate();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -98,8 +98,27 @@ pub enum WindowEvent {
|
||||
Notification(Box<dyn Any + Send + Sync>),
|
||||
}
|
||||
|
||||
pub type WindowEventSender = async_channel::Sender<WindowEvent>;
|
||||
pub type WindowEventReceiver = async_channel::Receiver<WindowEvent>;
|
||||
pub struct WindowEventSender {
|
||||
handler: Box<dyn FnMut(WindowEvent, &Window)>,
|
||||
window: Option<Window>,
|
||||
}
|
||||
|
||||
impl WindowEventSender {
|
||||
pub fn new<F: 'static + FnMut(WindowEvent, &Window)>(handler: F) -> Self {
|
||||
Self {
|
||||
handler: Box::new(handler),
|
||||
window: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn assign_window(&mut self, window: Window) {
|
||||
self.window.replace(window);
|
||||
}
|
||||
|
||||
pub fn dispatch(&mut self, event: WindowEvent) {
|
||||
(self.handler)(event, self.window.as_ref().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Graphics drivers lost context")]
|
||||
|
@ -531,13 +531,7 @@ impl WindowOps for Window {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// If we're already on the correct thread, just queue it up
|
||||
if let Some(conn) = Connection::get() {
|
||||
let handle = match conn.window_by_id(self.0) {
|
||||
Some(h) => h,
|
||||
None => return,
|
||||
};
|
||||
let inner = handle.borrow();
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
if let Some(window_view) = WindowView::get_this(unsafe { &**inner.view }) {
|
||||
window_view
|
||||
.inner
|
||||
@ -546,20 +540,8 @@ impl WindowOps for Window {
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
}
|
||||
} else {
|
||||
// Otherwise, get into that thread and write to the queue
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
if let Some(window_view) = WindowView::get_this(unsafe { &**inner.view }) {
|
||||
window_view
|
||||
.inner
|
||||
.borrow()
|
||||
.events
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
|
@ -6,7 +6,7 @@ use crate::os::wayland::connection::WaylandConnection;
|
||||
use crate::os::x11::keyboard::Keyboard;
|
||||
use crate::{
|
||||
Clipboard, Connection, Dimensions, MouseCursor, Point, ScreenPoint, Window, WindowEvent,
|
||||
WindowEventReceiver, WindowEventSender, WindowOps,
|
||||
WindowEventSender, WindowOps,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use async_io::Timer;
|
||||
@ -65,7 +65,7 @@ impl KeyRepeatState {
|
||||
}
|
||||
};
|
||||
|
||||
let inner = handle.borrow();
|
||||
let mut inner = handle.borrow_mut();
|
||||
|
||||
if inner.key_repeat.as_ref().map(|k| Arc::as_ptr(k))
|
||||
!= Some(Arc::as_ptr(&state))
|
||||
@ -93,7 +93,7 @@ impl KeyRepeatState {
|
||||
event.repeat_count += 1;
|
||||
elapsed -= gap;
|
||||
}
|
||||
inner.events.try_send(WindowEvent::KeyEvent(event)).ok();
|
||||
inner.events.dispatch(WindowEvent::KeyEvent(event));
|
||||
|
||||
st.when = Instant::now();
|
||||
}
|
||||
@ -189,14 +189,18 @@ impl PendingEvent {
|
||||
pub struct WaylandWindow(usize);
|
||||
|
||||
impl WaylandWindow {
|
||||
pub async fn new_window(
|
||||
pub async fn new_window<F>(
|
||||
class_name: &str,
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
config: Option<&ConfigHandle>,
|
||||
font_config: Rc<FontConfiguration>,
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
event_handler: F,
|
||||
) -> anyhow::Result<Window>
|
||||
where
|
||||
F: 'static + FnMut(WindowEvent, &Window),
|
||||
{
|
||||
let conn = WaylandConnection::get()
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
@ -207,7 +211,6 @@ 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);
|
||||
|
||||
@ -285,7 +288,7 @@ impl WaylandWindow {
|
||||
window_id,
|
||||
key_repeat: None,
|
||||
copy_and_paste,
|
||||
events,
|
||||
events: WindowEventSender::new(event_handler),
|
||||
surface: surface.detach(),
|
||||
window: Some(window),
|
||||
dimensions,
|
||||
@ -301,12 +304,16 @@ impl WaylandWindow {
|
||||
}));
|
||||
|
||||
let window_handle = Window::Wayland(WaylandWindow(window_id));
|
||||
inner
|
||||
.borrow_mut()
|
||||
.events
|
||||
.assign_window(window_handle.clone());
|
||||
|
||||
conn.windows.borrow_mut().insert(window_id, inner.clone());
|
||||
|
||||
wait_configure.recv().await?;
|
||||
|
||||
Ok((window_handle, receiver))
|
||||
Ok(window_handle)
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,7 +362,7 @@ impl WaylandWindowInner {
|
||||
} else {
|
||||
self.key_repeat.take();
|
||||
}
|
||||
self.events.try_send(WindowEvent::KeyEvent(event)).ok();
|
||||
self.events.dispatch(WindowEvent::KeyEvent(event));
|
||||
} else {
|
||||
self.key_repeat.take();
|
||||
}
|
||||
@ -383,9 +390,7 @@ impl WaylandWindowInner {
|
||||
self.modifiers = Modifiers::NONE;
|
||||
mapper.update_modifier_state(0, 0, 0, 0);
|
||||
self.key_repeat.take();
|
||||
self.events
|
||||
.try_send(WindowEvent::FocusChanged(focused))
|
||||
.ok();
|
||||
self.events.dispatch(WindowEvent::FocusChanged(focused));
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_pending_mouse(&mut self) {
|
||||
@ -408,7 +413,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
self.events.dispatch(WindowEvent::MouseEvent(event));
|
||||
self.refresh_frame();
|
||||
}
|
||||
|
||||
@ -439,7 +444,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
self.events.dispatch(WindowEvent::MouseEvent(event));
|
||||
}
|
||||
|
||||
if let Some((value_x, value_y)) = PendingMouse::scroll(&pending_mouse) {
|
||||
@ -456,7 +461,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
self.events.dispatch(WindowEvent::MouseEvent(event));
|
||||
}
|
||||
|
||||
let discrete_y = value_y.trunc() * factor;
|
||||
@ -471,7 +476,7 @@ impl WaylandWindowInner {
|
||||
mouse_buttons: self.mouse_buttons,
|
||||
modifiers: self.modifiers,
|
||||
};
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
self.events.dispatch(WindowEvent::MouseEvent(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -499,9 +504,7 @@ impl WaylandWindowInner {
|
||||
*pending_events = PendingEvent::default();
|
||||
}
|
||||
if pending.close {
|
||||
if self.events.try_send(WindowEvent::CloseRequested).is_err() {
|
||||
self.window.take();
|
||||
}
|
||||
self.events.dispatch(WindowEvent::CloseRequested);
|
||||
}
|
||||
|
||||
if let Some(full_screen) = pending.full_screen.take() {
|
||||
@ -549,12 +552,10 @@ impl WaylandWindowInner {
|
||||
if new_dimensions != self.dimensions {
|
||||
self.dimensions = new_dimensions;
|
||||
|
||||
self.events
|
||||
.try_send(WindowEvent::Resized {
|
||||
dimensions: self.dimensions,
|
||||
is_full_screen: self.full_screen,
|
||||
})
|
||||
.ok();
|
||||
self.events.dispatch(WindowEvent::Resized {
|
||||
dimensions: self.dimensions,
|
||||
is_full_screen: self.full_screen,
|
||||
});
|
||||
if let Some(wegl_surface) = self.wegl_surface.as_mut() {
|
||||
wegl_surface.resize(pixel_width, pixel_height, 0, 0);
|
||||
}
|
||||
@ -629,7 +630,7 @@ impl WaylandWindowInner {
|
||||
}
|
||||
|
||||
fn do_paint(&mut self) -> anyhow::Result<()> {
|
||||
self.events.try_send(WindowEvent::NeedRepaint).ok();
|
||||
self.events.dispatch(WindowEvent::NeedRepaint);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -675,27 +676,12 @@ impl WindowOps for WaylandWindow {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// If we're already on the correct thread, just queue it up
|
||||
if let Some(conn) = Connection::get() {
|
||||
let handle = match conn.wayland().window_by_id(self.0) {
|
||||
Some(h) => h,
|
||||
None => return,
|
||||
};
|
||||
let inner = handle.borrow();
|
||||
WaylandConnection::with_window_inner(self.0, move |inner| {
|
||||
inner
|
||||
.events
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
} else {
|
||||
// Otherwise, get into that thread and write to the queue
|
||||
WaylandConnection::with_window_inner(self.0, move |inner| {
|
||||
inner
|
||||
.events
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
.dispatch(WindowEvent::Notification(Box::new(t)));
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
@ -876,7 +862,7 @@ fn read_pipe_with_timeout(mut file: FileDescriptor) -> anyhow::Result<String> {
|
||||
|
||||
impl WaylandWindowInner {
|
||||
fn close(&mut self) {
|
||||
self.events.try_send(WindowEvent::Destroyed).ok();
|
||||
self.events.dispatch(WindowEvent::Destroyed);
|
||||
self.window.take();
|
||||
}
|
||||
|
||||
|
@ -380,7 +380,11 @@ impl Window {
|
||||
height: usize,
|
||||
config: Option<&ConfigHandle>,
|
||||
_font_config: Rc<FontConfiguration>,
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
event_handler: F,
|
||||
) -> anyhow::Result<Window>
|
||||
where
|
||||
F: 'static + FnMut(WindowEvent),
|
||||
{
|
||||
let (events, receiver) = async_channel::unbounded();
|
||||
let config = match config {
|
||||
Some(c) => c.clone(),
|
||||
@ -580,27 +584,13 @@ impl WindowOps for Window {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// If we're already on the correct thread, just queue it up
|
||||
if let Some(conn) = Connection::get() {
|
||||
let handle = match conn.get_window(self.0) {
|
||||
Some(h) => h,
|
||||
None => return,
|
||||
};
|
||||
let inner = handle.borrow_mut();
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
inner
|
||||
.events
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
} else {
|
||||
// Otherwise, get into that thread and write to the queue
|
||||
Connection::with_window_inner(self.0, move |inner| {
|
||||
inner
|
||||
.events
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
|
@ -5,8 +5,7 @@ use crate::os::xkeysyms;
|
||||
use crate::os::{Connection, Window};
|
||||
use crate::{
|
||||
Clipboard, Dimensions, MouseButtons, MouseCursor, MouseEvent, MouseEventKind, MousePress,
|
||||
Point, ScreenPoint, WindowDecorations, WindowEvent, WindowEventReceiver, WindowEventSender,
|
||||
WindowOps,
|
||||
Point, ScreenPoint, WindowDecorations, WindowEvent, WindowEventSender, WindowOps,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use async_trait::async_trait;
|
||||
@ -62,7 +61,6 @@ pub(crate) struct XWindowInner {
|
||||
cursors: CursorInfo,
|
||||
copy_and_paste: CopyAndPaste,
|
||||
config: ConfigHandle,
|
||||
resize_promises: Vec<Promise<Dimensions>>,
|
||||
}
|
||||
|
||||
impl Drop for XWindowInner {
|
||||
@ -123,11 +121,11 @@ impl XWindowInner {
|
||||
/// it to encompass both. This avoids bloating the list with a series
|
||||
/// of increasing rectangles when resizing larger or smaller.
|
||||
fn expose(&mut self, _x: u16, _y: u16, _width: u16, _height: u16) {
|
||||
self.events.try_send(WindowEvent::NeedRepaint).ok();
|
||||
self.events.dispatch(WindowEvent::NeedRepaint);
|
||||
}
|
||||
|
||||
fn do_mouse_event(&mut self, event: MouseEvent) -> anyhow::Result<()> {
|
||||
self.events.try_send(WindowEvent::MouseEvent(event)).ok();
|
||||
self.events.dispatch(WindowEvent::MouseEvent(event));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -146,7 +144,7 @@ impl XWindowInner {
|
||||
self.dpi
|
||||
);
|
||||
self.dpi = dpi;
|
||||
let _ = self.events.try_send(WindowEvent::Resized {
|
||||
self.events.dispatch(WindowEvent::Resized {
|
||||
dimensions: Dimensions {
|
||||
pixel_width: self.width as usize,
|
||||
pixel_height: self.height as usize,
|
||||
@ -167,31 +165,37 @@ impl XWindowInner {
|
||||
}
|
||||
xcb::CONFIGURE_NOTIFY => {
|
||||
let cfg: &xcb::ConfigureNotifyEvent = unsafe { xcb::cast_event(event) };
|
||||
self.width = cfg.width();
|
||||
self.height = cfg.height();
|
||||
self.dpi = conn.default_dpi();
|
||||
let width = cfg.width();
|
||||
let height = cfg.height();
|
||||
let dpi = conn.default_dpi();
|
||||
|
||||
if width == self.width && height == self.height && dpi == self.dpi {
|
||||
// Effectively unchanged; perhaps it was simply moved?
|
||||
// Do nothing!
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
self.dpi = dpi;
|
||||
|
||||
let dimensions = Dimensions {
|
||||
pixel_width: self.width as usize,
|
||||
pixel_height: self.height as usize,
|
||||
dpi: self.dpi as usize,
|
||||
};
|
||||
if !self.resize_promises.is_empty() {
|
||||
self.resize_promises.remove(0).ok(dimensions);
|
||||
}
|
||||
|
||||
self.events
|
||||
.try_send(WindowEvent::Resized {
|
||||
dimensions,
|
||||
is_full_screen: self.is_fullscreen().unwrap_or(false),
|
||||
})
|
||||
.ok();
|
||||
self.events.dispatch(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.events.try_send(WindowEvent::KeyEvent(key)).ok();
|
||||
self.events.dispatch(WindowEvent::KeyEvent(key));
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,13 +276,11 @@ impl XWindowInner {
|
||||
let msg: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(event) };
|
||||
|
||||
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);
|
||||
}
|
||||
self.events.dispatch(WindowEvent::CloseRequested);
|
||||
}
|
||||
}
|
||||
xcb::DESTROY_NOTIFY => {
|
||||
self.events.try_send(WindowEvent::Destroyed).ok();
|
||||
self.events.dispatch(WindowEvent::Destroyed);
|
||||
conn.windows.borrow_mut().remove(&self.window_id);
|
||||
}
|
||||
xcb::SELECTION_CLEAR => {
|
||||
@ -321,11 +323,11 @@ impl XWindowInner {
|
||||
}
|
||||
xcb::FOCUS_IN => {
|
||||
log::trace!("Calling focus_change(true)");
|
||||
self.events.try_send(WindowEvent::FocusChanged(true)).ok();
|
||||
self.events.dispatch(WindowEvent::FocusChanged(true));
|
||||
}
|
||||
xcb::FOCUS_OUT => {
|
||||
log::trace!("Calling focus_change(false)");
|
||||
self.events.try_send(WindowEvent::FocusChanged(false)).ok();
|
||||
self.events.dispatch(WindowEvent::FocusChanged(false));
|
||||
}
|
||||
_ => {
|
||||
eprintln!("unhandled: {:x}", r);
|
||||
@ -645,14 +647,18 @@ impl XWindow {
|
||||
|
||||
/// Create a new window on the specified screen with the specified
|
||||
/// dimensions
|
||||
pub async fn new_window(
|
||||
pub async fn new_window<F>(
|
||||
class_name: &str,
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
config: Option<&ConfigHandle>,
|
||||
_font_config: Rc<FontConfiguration>,
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
event_handler: F,
|
||||
) -> anyhow::Result<Window>
|
||||
where
|
||||
F: 'static + FnMut(WindowEvent, &Window),
|
||||
{
|
||||
let config = match config {
|
||||
Some(c) => c.clone(),
|
||||
None => config::configuration(),
|
||||
@ -665,7 +671,7 @@ impl XWindow {
|
||||
})?
|
||||
.x11();
|
||||
|
||||
let (events, receiver) = async_channel::unbounded();
|
||||
let mut events = WindowEventSender::new(event_handler);
|
||||
|
||||
let window_id;
|
||||
let window = {
|
||||
@ -727,6 +733,8 @@ impl XWindow {
|
||||
.request_check()
|
||||
.context("xcb::create_window_checked")?;
|
||||
|
||||
events.assign_window(Window::X11(XWindow::from_id(window_id)));
|
||||
|
||||
Arc::new(Mutex::new(XWindowInner {
|
||||
window_id,
|
||||
conn: Rc::downgrade(&conn),
|
||||
@ -737,7 +745,6 @@ impl XWindow {
|
||||
copy_and_paste: CopyAndPaste::default(),
|
||||
cursors: CursorInfo::new(&conn),
|
||||
config: config.clone(),
|
||||
resize_promises: vec![],
|
||||
}))
|
||||
};
|
||||
|
||||
@ -768,7 +775,7 @@ impl XWindow {
|
||||
window_handle.set_title(name);
|
||||
window_handle.show();
|
||||
|
||||
Ok((window_handle, receiver))
|
||||
Ok(window_handle)
|
||||
}
|
||||
}
|
||||
|
||||
@ -781,7 +788,7 @@ impl XWindowInner {
|
||||
xcb::map_window(self.conn().conn(), self.window_id);
|
||||
}
|
||||
fn invalidate(&mut self) {
|
||||
self.events.try_send(WindowEvent::NeedRepaint).ok();
|
||||
self.events.dispatch(WindowEvent::NeedRepaint);
|
||||
}
|
||||
|
||||
fn toggle_fullscreen(&mut self) {
|
||||
@ -896,27 +903,12 @@ impl WindowOps for XWindow {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// If we're already on the correct thread, just queue it up
|
||||
if let Some(conn) = Connection::get() {
|
||||
let handle = match conn.x11().window_by_id(self.0) {
|
||||
Some(h) => h,
|
||||
None => return,
|
||||
};
|
||||
let inner = handle.lock().unwrap();
|
||||
XConnection::with_window_inner(self.0, move |inner| {
|
||||
inner
|
||||
.events
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
} else {
|
||||
// Otherwise, get into that thread and write to the queue
|
||||
XConnection::with_window_inner(self.0, move |inner| {
|
||||
inner
|
||||
.events
|
||||
.try_send(WindowEvent::Notification(Box::new(t)))
|
||||
.ok();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
.dispatch(WindowEvent::Notification(Box::new(t)));
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
|
@ -7,7 +7,7 @@ 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, MouseCursor, ScreenPoint, WindowEventReceiver, WindowOps};
|
||||
use crate::{Clipboard, MouseCursor, ScreenPoint, WindowEvent, WindowOps};
|
||||
use async_trait::async_trait;
|
||||
use config::ConfigHandle;
|
||||
use promise::*;
|
||||
@ -46,7 +46,7 @@ impl Connection {
|
||||
Ok(Connection::X11(Rc::new(XConnection::create_new()?)))
|
||||
}
|
||||
|
||||
pub async fn new_window(
|
||||
pub async fn new_window<F>(
|
||||
&self,
|
||||
class_name: &str,
|
||||
name: &str,
|
||||
@ -54,15 +54,36 @@ impl Connection {
|
||||
height: usize,
|
||||
config: Option<&ConfigHandle>,
|
||||
font_config: Rc<FontConfiguration>,
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
event_handler: F,
|
||||
) -> anyhow::Result<Window>
|
||||
where
|
||||
F: 'static + FnMut(WindowEvent, &Window),
|
||||
{
|
||||
match self {
|
||||
Self::X11(_) => {
|
||||
XWindow::new_window(class_name, name, width, height, config, font_config).await
|
||||
XWindow::new_window(
|
||||
class_name,
|
||||
name,
|
||||
width,
|
||||
height,
|
||||
config,
|
||||
font_config,
|
||||
event_handler,
|
||||
)
|
||||
.await
|
||||
}
|
||||
#[cfg(feature = "wayland")]
|
||||
Self::Wayland(_) => {
|
||||
WaylandWindow::new_window(class_name, name, width, height, config, font_config)
|
||||
.await
|
||||
WaylandWindow::new_window(
|
||||
class_name,
|
||||
name,
|
||||
width,
|
||||
height,
|
||||
config,
|
||||
font_config,
|
||||
event_handler,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,17 +124,29 @@ impl ConnectionOps for Connection {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub async fn new_window(
|
||||
pub async fn new_window<F>(
|
||||
class_name: &str,
|
||||
name: &str,
|
||||
width: usize,
|
||||
height: usize,
|
||||
config: Option<&ConfigHandle>,
|
||||
font_config: Rc<FontConfiguration>,
|
||||
) -> anyhow::Result<(Window, WindowEventReceiver)> {
|
||||
event_handler: F,
|
||||
) -> anyhow::Result<Window>
|
||||
where
|
||||
F: 'static + FnMut(WindowEvent, &Window),
|
||||
{
|
||||
Connection::get()
|
||||
.unwrap()
|
||||
.new_window(class_name, name, width, height, config, font_config)
|
||||
.new_window(
|
||||
class_name,
|
||||
name,
|
||||
width,
|
||||
height,
|
||||
config,
|
||||
font_config,
|
||||
event_handler,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user