Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce sequencing for 'pane focus' related events #6349

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 4 additions & 36 deletions codec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use config::keyassignment::{PaneDirection, ScrollbackEraseMode};
use mux::client::{ClientId, ClientInfo};
use mux::pane::PaneId;
use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::serial::InputSerial;
use mux::tab::{PaneNode, SerdeUrl, SplitRequest, TabId};
use mux::window::WindowId;
use portable_pty::CommandBuilder;
Expand All @@ -24,7 +25,6 @@ use serde::{Deserialize, Serialize};
use smol::io::AsyncWriteExt;
use smol::prelude::*;
use std::collections::HashMap;
use std::convert::TryInto;
use std::io::Cursor;
use std::ops::Range;
use std::path::PathBuf;
Expand Down Expand Up @@ -594,7 +594,7 @@ impl Pdu {
| Pdu::SetPalette(SetPalette { pane_id, .. })
| Pdu::NotifyAlert(NotifyAlert { pane_id, .. })
| Pdu::SetClipboard(SetClipboard { pane_id, .. })
| Pdu::PaneFocused(PaneFocused { pane_id })
| Pdu::PaneFocused(PaneFocused { pane_id, .. })
| Pdu::PaneRemoved(PaneRemoved { pane_id }) => Some(*pane_id),
_ => None,
}
Expand Down Expand Up @@ -722,40 +722,6 @@ pub struct SendKeyDown {
pub input_serial: InputSerial,
}

/// InputSerial is used to sequence input requests with output events.
/// It started life as a monotonic sequence number but evolved into
/// the number of milliseconds since the unix epoch.
#[derive(Deserialize, Serialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
pub struct InputSerial(u64);

impl InputSerial {
pub const fn empty() -> Self {
Self(0)
}

pub fn now() -> Self {
std::time::SystemTime::now().into()
}

pub fn elapsed_millis(&self) -> u64 {
let now = InputSerial::now();
now.0 - self.0
}
}

impl From<std::time::SystemTime> for InputSerial {
fn from(val: std::time::SystemTime) -> Self {
let duration = val
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("SystemTime before unix epoch?");
let millis: u64 = duration
.as_millis()
.try_into()
.expect("millisecond count to fit in u64");
InputSerial(millis)
}
}

#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SendMouseEvent {
pub pane_id: PaneId,
Expand Down Expand Up @@ -822,6 +788,7 @@ pub struct WindowTitleChanged {
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct PaneFocused {
pub pane_id: PaneId,
pub pane_focus_serial: Option<InputSerial>,
}

#[derive(Deserialize, Serialize, PartialEq, Debug)]
Expand All @@ -839,6 +806,7 @@ pub struct SetClientId {
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SetFocusedPane {
pub pane_id: PaneId,
pub pane_focus_serial: Option<InputSerial>,
}

#[derive(Deserialize, Serialize, PartialEq, Debug)]
Expand Down
19 changes: 18 additions & 1 deletion mux/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::client::{ClientId, ClientInfo};
use crate::pane::{CachePolicy, Pane, PaneId};
use crate::serial::InputSerial;
use crate::ssh_agent::AgentProxy;
use crate::tab::{SplitRequest, Tab, TabId};
use crate::window::{Window, WindowId};
Expand Down Expand Up @@ -38,6 +39,7 @@ pub mod domain;
pub mod localpane;
pub mod pane;
pub mod renderable;
pub mod serial;
pub mod ssh;
pub mod ssh_agent;
pub mod tab;
Expand Down Expand Up @@ -79,7 +81,10 @@ pub enum MuxNotification {
tab_id: TabId,
window_id: WindowId,
},
PaneFocused(PaneId),
PaneFocused {
pane_id: PaneId,
pane_focus_serial: Option<InputSerial>,
},
TabResized(TabId),
TabTitleChanged {
tab_id: TabId,
Expand Down Expand Up @@ -111,6 +116,7 @@ pub struct Mux {
num_panes_by_workspace: RwLock<HashMap<String, usize>>,
main_thread_id: std::thread::ThreadId,
agent: Option<AgentProxy>,
pane_focus_serial: RwLock<InputSerial>,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better place to put this?

}

const BUFSIZE: usize = 1024 * 1024;
Expand Down Expand Up @@ -441,6 +447,7 @@ impl Mux {
num_panes_by_workspace: RwLock::new(HashMap::new()),
main_thread_id: std::thread::current().id(),
agent,
pane_focus_serial: RwLock::new(InputSerial::empty()),
}
}

Expand Down Expand Up @@ -1382,6 +1389,16 @@ impl Mux {

Ok((tab, pane, window_id))
}

pub fn current_pane_focus_serial(&self) -> InputSerial {
*self.pane_focus_serial.read()
}

pub fn increment_pane_focus_serial(&self) -> InputSerial {
let mut pane_focus_serial = self.pane_focus_serial.write();
*pane_focus_serial = InputSerial::now();
*pane_focus_serial
}
}

pub struct IdentityHolder {
Expand Down
5 changes: 5 additions & 0 deletions mux/src/localpane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,11 @@ impl Pane for LocalPane {
}

fn focus_changed(&self, focused: bool) {
if focused {
let mux = Mux::get();
mux.increment_pane_focus_serial();
}

self.terminal.lock().focus_changed(focused);
}

Expand Down
36 changes: 36 additions & 0 deletions mux/src/serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use serde::{Deserialize, Serialize};
use std::convert::TryInto;

/// InputSerial is used to sequence input requests with output events.
/// It started life as a monotonic sequence number but evolved into
/// the number of milliseconds since the unix epoch.
#[derive(Deserialize, Serialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
pub struct InputSerial(u64);

impl InputSerial {
pub const fn empty() -> Self {
Self(0)
}

pub fn now() -> Self {
std::time::SystemTime::now().into()
}

pub fn elapsed_millis(&self) -> u64 {
let now = InputSerial::now();
now.0 - self.0
}
}

impl From<std::time::SystemTime> for InputSerial {
fn from(val: std::time::SystemTime) -> Self {
let duration = val
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("SystemTime before unix epoch?");
let millis: u64 = duration
.as_millis()
.try_into()
.expect("millisecond count to fit in u64");
InputSerial(millis)
}
}
10 changes: 8 additions & 2 deletions mux/src/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1779,11 +1779,17 @@ impl TabInner {
(Some(prior), Some(current)) if prior.pane_id() != current.pane_id() => {
prior.focus_changed(false);
current.focus_changed(true);
mux.notify(MuxNotification::PaneFocused(current.pane_id()));
mux.notify(MuxNotification::PaneFocused {
pane_id: current.pane_id(),
pane_focus_serial: None,
});
}
(None, Some(current)) => {
current.focus_changed(true);
mux.notify(MuxNotification::PaneFocused(current.pane_id()));
mux.notify(MuxNotification::PaneFocused {
pane_id: current.pane_id(),
pane_focus_serial: None,
});
}
(Some(prior), None) => {
prior.focus_changed(false);
Expand Down
1 change: 1 addition & 0 deletions wezterm-client/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use config::{SshDomain, TlsDomainClient, UnixDomain};
use mux::connui::{ConnectionUI, ConnectionUIParams};
use mux::domain::{alloc_domain_id, Domain, DomainId, DomainState, SplitSource};
use mux::pane::{Pane, PaneId};
use mux::serial::InputSerial;
use mux::tab::{SplitRequest, Tab, TabId};
use mux::window::WindowId;
use mux::{Mux, MuxNotification};
Expand Down
19 changes: 16 additions & 3 deletions wezterm-client/src/pane/clientpane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use mux::pane::{
Pattern, SearchResult, WithPaneLines,
};
use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::serial::InputSerial;
use mux::tab::TabId;
use mux::{Mux, MuxNotification};
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
Expand Down Expand Up @@ -205,7 +206,10 @@ impl ClientPane {

self.client.expire_stale_mappings();
}
Pdu::PaneFocused(PaneFocused { pane_id }) => {
Pdu::PaneFocused(PaneFocused {
pane_id,
pane_focus_serial,
}) => {
// We get here whenever the pane focus is changed on the
// server. That might be due to the user here in the GUI
// doing things, or it may be due to a "remote"
Expand All @@ -218,8 +222,13 @@ impl ClientPane {
log::trace!("advised of remote pane focus: {pane_id}");

let mux = Mux::get();
if let Err(err) = mux.focus_pane_and_containing_tab(self.local_pane_id) {
log::error!("Error reconciling remote PaneFocused notification: {err:#}");
let client_pane_focus_serial = mux.current_pane_focus_serial();
if pane_focus_serial.is_none_or(|server_pane_focus_serial| {
server_pane_focus_serial >= client_pane_focus_serial
}) {
if let Err(err) = mux.focus_pane_and_containing_tab(self.local_pane_id) {
log::error!("Error reconciling remote PaneFocused notification: {err:#}");
}
}
}
_ => bail!("unhandled unilateral pdu: {:?}", pdu),
Expand Down Expand Up @@ -567,13 +576,17 @@ impl Pane for ClientPane {
let mut focused_pane = self.client.focused_remote_pane_id.lock().unwrap();
if *focused_pane != Some(self.remote_pane_id) {
focused_pane.replace(self.remote_pane_id);
let mux = Mux::get();
let client = Arc::clone(&self.client);
let remote_pane_id = self.remote_pane_id;
let pane_focus_serial = Some(mux.increment_pane_focus_serial());

promise::spawn::spawn(async move {
client
.client
.set_focused_pane_id(SetFocusedPane {
pane_id: remote_pane_id,
pane_focus_serial,
})
.await
})
Expand Down
1 change: 1 addition & 0 deletions wezterm-client/src/pane/renderable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use config::{configuration, ConfigHandle};
use lru::LruCache;
use mux::pane::PaneId;
use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::serial::InputSerial;
use mux::Mux;
use promise::BrokenPromise;
use rangeset::*;
Expand Down
2 changes: 1 addition & 1 deletion wezterm-gui/src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl GuiFrontEnd {
})
.detach();
}
MuxNotification::PaneFocused(pane_id) => {
MuxNotification::PaneFocused { pane_id, .. } => {
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get();
if let Err(err) = mux.focus_pane_and_containing_tab(pane_id) {
Expand Down
4 changes: 2 additions & 2 deletions wezterm-gui/src/termwindow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1292,7 +1292,7 @@ impl TermWindow {
MuxNotification::SaveToDownloads { .. } => {
// Handled by frontend
}
MuxNotification::PaneFocused(_) => {
MuxNotification::PaneFocused { .. } => {
// Also handled by clientpane
self.update_title_post_status();
}
Expand Down Expand Up @@ -1458,7 +1458,7 @@ impl TermWindow {
| Alert::SetUserVar { .. }
| Alert::Bell,
}
| MuxNotification::PaneFocused(pane_id)
| MuxNotification::PaneFocused { pane_id, .. }
| MuxNotification::PaneRemoved(pane_id)
| MuxNotification::PaneOutput(pane_id) => {
// Ideally we'd check to see if pane_id is part of this window,
Expand Down
14 changes: 10 additions & 4 deletions wezterm-mux-server-impl/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,16 @@ where
stream.flush().await.context("flushing PDU to client")?;
}
}
Ok(Item::Notif(MuxNotification::PaneFocused(pane_id))) => {
Pdu::PaneFocused(codec::PaneFocused { pane_id })
.encode_async(&mut stream, 0)
.await?;
Ok(Item::Notif(MuxNotification::PaneFocused {
pane_id,
pane_focus_serial,
})) => {
Pdu::PaneFocused(codec::PaneFocused {
pane_id,
pane_focus_serial,
})
.encode_async(&mut stream, 0)
.await?;
stream.flush().await.context("flushing PDU to client")?;
}
Ok(Item::Notif(MuxNotification::TabResized(tab_id))) => {
Expand Down
11 changes: 9 additions & 2 deletions wezterm-mux-server-impl/src/sessionhandler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use mux::client::ClientId;
use mux::domain::SplitSource;
use mux::pane::{CachePolicy, Pane, PaneId};
use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::serial::InputSerial;
use mux::tab::TabId;
use mux::{Mux, MuxNotification};
use promise::spawn::spawn_into_main_thread;
Expand Down Expand Up @@ -328,7 +329,10 @@ impl SessionHandler {
}
send_response(Ok(Pdu::UnitResponse(UnitResponse {})))
}
Pdu::SetFocusedPane(SetFocusedPane { pane_id }) => {
Pdu::SetFocusedPane(SetFocusedPane {
pane_id,
pane_focus_serial,
}) => {
let client_id = self.client_id.clone();
spawn_into_main_thread(async move {
catch(
Expand Down Expand Up @@ -361,7 +365,10 @@ impl SessionHandler {
tab.set_active_pane(&pane);

mux.record_focus_for_current_identity(pane_id);
mux.notify(mux::MuxNotification::PaneFocused(pane_id));
mux.notify(mux::MuxNotification::PaneFocused {
pane_id,
pane_focus_serial,
});

Ok(Pdu::UnitResponse(UnitResponse {}))
},
Expand Down
5 changes: 4 additions & 1 deletion wezterm/src/cli/activate_pane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ impl ActivatePane {
pub async fn run(&self, client: Client) -> anyhow::Result<()> {
let pane_id = client.resolve_pane_id(self.pane_id).await?;
client
.set_focused_pane_id(codec::SetFocusedPane { pane_id })
.set_focused_pane_id(codec::SetFocusedPane {
pane_id,
pane_focus_serial: None,
})
.await?;
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions wezterm/src/cli/activate_tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ impl ActivateTab {
client
.set_focused_pane_id(codec::SetFocusedPane {
pane_id: target_pane,
pane_focus_serial: None,
})
.await?;
Ok(())
Expand Down