mirror of
https://github.com/wez/wezterm.git
synced 2024-12-22 12:51:31 +03:00
tweak updating checking
* Allow injecting some initial output to new panes * Have the update checker set this new-pane-banner to a short upsell to let the user know there is an update. * Refactor toast notifications into their own crate * Have the update checker call a new stub function that triggers a toast notification with an URL... but it does nothing because the rust ecosystem doesn't support this on macos yet and I'm writing this code there
This commit is contained in:
parent
3c3ba6a30a
commit
6b414bebc9
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -4494,7 +4494,6 @@ dependencies = [
|
||||
"metrics",
|
||||
"mlua",
|
||||
"mux",
|
||||
"notify-rust",
|
||||
"open",
|
||||
"openssl",
|
||||
"ordered-float",
|
||||
@ -4527,9 +4526,9 @@ dependencies = [
|
||||
"wezterm-font",
|
||||
"wezterm-gui-subcommands",
|
||||
"wezterm-term",
|
||||
"wezterm-toast-notification",
|
||||
"winapi 0.3.9",
|
||||
"window",
|
||||
"winrt-notification",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4602,6 +4601,14 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wezterm-toast-notification"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"notify-rust",
|
||||
"winrt-notification",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
|
@ -45,6 +45,7 @@ pub struct Mux {
|
||||
domains: RefCell<HashMap<DomainId, Arc<dyn Domain>>>,
|
||||
domains_by_name: RefCell<HashMap<String, Arc<dyn Domain>>>,
|
||||
subscribers: RefCell<HashMap<usize, Box<dyn Fn(MuxNotification) -> bool>>>,
|
||||
banner: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
/// This function bounces the data over to the main thread to feed to
|
||||
@ -121,7 +122,7 @@ fn accumulator(pane_id: PaneId, dead: &Arc<AtomicBool>, rx: Receiver<Vec<u8>>) {
|
||||
/// blocking reads from the pty (non-blocking reads are not portable to
|
||||
/// all platforms and pty/tty types) and relay the data to the `accumulator`
|
||||
/// function above that this function spawns a new thread.
|
||||
fn read_from_pane_pty(pane_id: PaneId, mut reader: Box<dyn std::io::Read>) {
|
||||
fn read_from_pane_pty(pane_id: PaneId, banner: Option<String>, mut reader: Box<dyn std::io::Read>) {
|
||||
const BUFSIZE: usize = 4 * 1024;
|
||||
let mut buf = [0; BUFSIZE];
|
||||
|
||||
@ -138,6 +139,10 @@ fn read_from_pane_pty(pane_id: PaneId, mut reader: Box<dyn std::io::Read>) {
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(banner) = banner {
|
||||
tx.send(banner.into_bytes()).ok();
|
||||
}
|
||||
|
||||
while !dead.load(Ordering::Relaxed) {
|
||||
match reader.read(&mut buf) {
|
||||
Ok(size) if size == 0 => {
|
||||
@ -232,6 +237,7 @@ impl Mux {
|
||||
domains_by_name: RefCell::new(domains_by_name),
|
||||
domains: RefCell::new(domains),
|
||||
subscribers: RefCell::new(HashMap::new()),
|
||||
banner: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,7 +322,8 @@ impl Mux {
|
||||
.insert(pane.pane_id(), Rc::clone(pane));
|
||||
let reader = pane.reader()?;
|
||||
let pane_id = pane.pane_id();
|
||||
thread::spawn(move || read_from_pane_pty(pane_id, reader));
|
||||
let banner = self.banner.borrow().clone();
|
||||
thread::spawn(move || read_from_pane_pty(pane_id, banner, reader));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -526,6 +533,10 @@ impl Mux {
|
||||
|
||||
self.prune_dead_windows();
|
||||
}
|
||||
|
||||
pub fn set_banner(&self, banner: Option<String>) {
|
||||
*self.banner.borrow_mut() = banner;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -64,12 +64,9 @@ wezterm-client = { path = "../wezterm-client" }
|
||||
wezterm-font = { path = "../wezterm-font" }
|
||||
wezterm-gui-subcommands = { path = "../wezterm-gui-subcommands" }
|
||||
wezterm-term = { path = "../term", features=["use_serde"] }
|
||||
wezterm-toast-notification = { path = "../wezterm-toast-notification" }
|
||||
window = { path = "../window", features=["opengl", "wayland"]}
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
# show a notification
|
||||
notify-rust = "4"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
shared_library = "0.1"
|
||||
uds_windows = "0.1"
|
||||
@ -82,7 +79,6 @@ winapi = { version = "0.3", features = [
|
||||
"synchapi",
|
||||
"winsock2",
|
||||
]}
|
||||
winrt-notification = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["vendor_openssl"]
|
||||
|
@ -19,6 +19,7 @@ mod utilsprites;
|
||||
pub use selection::SelectionMode;
|
||||
pub use termwindow::set_window_class;
|
||||
pub use termwindow::TermWindow;
|
||||
pub use termwindow::ICON_DATA;
|
||||
|
||||
pub struct GuiFrontEnd {
|
||||
connection: Rc<Connection>,
|
||||
|
@ -13,6 +13,7 @@ use std::sync::Arc;
|
||||
use structopt::StructOpt;
|
||||
use wezterm_client::domain::{ClientDomain, ClientDomainConfig};
|
||||
use wezterm_gui_subcommands::*;
|
||||
use wezterm_toast_notification::*;
|
||||
|
||||
mod gui;
|
||||
mod markdown;
|
||||
@ -295,49 +296,8 @@ fn run_terminal_gui(config: config::ConfigHandle, opts: StartCommand) -> anyhow:
|
||||
gui.run_forever()
|
||||
}
|
||||
|
||||
fn toast_notification(title: &str, message: &str) {
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
let mut notif = notify_rust::Notification::new();
|
||||
notif.summary(title).body(message);
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
// Stay on the screen until dismissed
|
||||
notif.hint(notify_rust::Hint::Resident(true));
|
||||
}
|
||||
|
||||
notif
|
||||
// timeout isn't respected on macos
|
||||
.timeout(0)
|
||||
.show()
|
||||
.ok();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let title = title.to_owned();
|
||||
let message = message.to_owned();
|
||||
|
||||
// We need to be in a different thread from the caller
|
||||
// in case we get called in the guts of a windows message
|
||||
// loop dispatch and are unable to pump messages
|
||||
std::thread::spawn(move || {
|
||||
use winrt_notification::Toast;
|
||||
|
||||
Toast::new(Toast::POWERSHELL_APP_ID)
|
||||
.title(&title)
|
||||
.text1(&message)
|
||||
.duration(winrt_notification::Duration::Long)
|
||||
.show()
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn fatal_toast_notification(title: &str, message: &str) {
|
||||
toast_notification(title, message);
|
||||
persistent_toast_notification(title, message);
|
||||
// We need a short delay otherwise the notification
|
||||
// will not show
|
||||
#[cfg(windows)]
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::gui::ICON_DATA;
|
||||
use anyhow::anyhow;
|
||||
use config::configuration;
|
||||
use config::wezterm_version;
|
||||
@ -13,7 +14,11 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use termwiz::cell::{AttributeChange, Hyperlink, Underline};
|
||||
use termwiz::escape::csi::{Cursor, Sgr};
|
||||
use termwiz::escape::osc::{ITermDimension, ITermFileData, ITermProprietary};
|
||||
use termwiz::escape::{OneBased, OperatingSystemCommand, CSI};
|
||||
use termwiz::surface::{Change, CursorVisibility};
|
||||
use wezterm_toast_notification::*;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Release {
|
||||
@ -198,7 +203,7 @@ fn show_update_available(release: Release) {
|
||||
Change::Attribute(AttributeChange::Hyperlink(Some(Arc::new(Hyperlink::new(
|
||||
install,
|
||||
))))),
|
||||
format!("Version {} is now available!\r\n", release.tag_name).into(),
|
||||
format!("\r\nVersion {} is now available!\r\n", release.tag_name).into(),
|
||||
Change::Attribute(AttributeChange::Hyperlink(None)),
|
||||
Change::Attribute(AttributeChange::Underline(Underline::None)),
|
||||
format!("(this is version {})\r\n", wezterm_version()).into(),
|
||||
@ -276,7 +281,61 @@ fn update_checker() {
|
||||
if let Ok(latest) = get_latest_release_info() {
|
||||
let current = wezterm_version();
|
||||
if latest.tag_name.as_str() > current || force_ui {
|
||||
log::trace!(
|
||||
let url = format!(
|
||||
"https://wezfurlong.org/wezterm/changelog.html#{}",
|
||||
latest.tag_name
|
||||
);
|
||||
|
||||
promise::spawn::spawn_into_main_thread({
|
||||
let url = url.clone();
|
||||
async move {
|
||||
let mux = crate::Mux::get().unwrap();
|
||||
let icon = ITermFileData {
|
||||
name: None,
|
||||
size: Some(ICON_DATA.len()),
|
||||
width: ITermDimension::Automatic,
|
||||
height: ITermDimension::Cells(2),
|
||||
preserve_aspect_ratio: true,
|
||||
inline: true,
|
||||
data: ICON_DATA.to_vec(),
|
||||
};
|
||||
let icon = OperatingSystemCommand::ITermProprietary(
|
||||
ITermProprietary::File(Box::new(icon)),
|
||||
);
|
||||
let top_line_pos = CSI::Cursor(Cursor::CharacterAndLinePosition {
|
||||
line: OneBased::new(1),
|
||||
col: OneBased::new(6),
|
||||
});
|
||||
let second_line_pos = CSI::Cursor(Cursor::CharacterAndLinePosition {
|
||||
line: OneBased::new(2),
|
||||
col: OneBased::new(6),
|
||||
});
|
||||
let link_on =
|
||||
OperatingSystemCommand::SetHyperlink(Some(Hyperlink::new(url)));
|
||||
let underline_on = CSI::Sgr(Sgr::Underline(Underline::Single));
|
||||
let underline_off = CSI::Sgr(Sgr::Underline(Underline::None));
|
||||
let link_off = OperatingSystemCommand::SetHyperlink(None);
|
||||
mux.set_banner(Some(format!(
|
||||
"{}{}WezTerm Update Available\r\n{}{}{}Click to see what's new{}{}\r\n",
|
||||
icon,
|
||||
top_line_pos,
|
||||
second_line_pos,
|
||||
link_on,
|
||||
underline_on,
|
||||
underline_off,
|
||||
link_off,
|
||||
)));
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
persistent_toast_notification_with_click_to_open_url(
|
||||
"WezTerm Update Available",
|
||||
"Click to see what's new",
|
||||
&url,
|
||||
);
|
||||
|
||||
log::info!(
|
||||
"latest release {} is newer than current build {}",
|
||||
latest.tag_name,
|
||||
current
|
||||
|
16
wezterm-toast-notification/Cargo.toml
Normal file
16
wezterm-toast-notification/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "wezterm-toast-notification"
|
||||
version = "0.1.0"
|
||||
authors = ["Wez Furlong <wez@wezfurlong.org>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
# show a notification
|
||||
notify-rust = "4"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winrt-notification = "0.2"
|
50
wezterm-toast-notification/src/lib.rs
Normal file
50
wezterm-toast-notification/src/lib.rs
Normal file
@ -0,0 +1,50 @@
|
||||
pub fn persistent_toast_notification_with_click_to_open_url(
|
||||
_title: &str,
|
||||
_message: &str,
|
||||
_url: &str,
|
||||
) {
|
||||
// Do nothing for now; none of the rust-accesible toast
|
||||
// notifications let us manage clicking on the notification
|
||||
// persistent_toast_notification(title, message);
|
||||
}
|
||||
|
||||
pub fn persistent_toast_notification(title: &str, message: &str) {
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
let mut notif = notify_rust::Notification::new();
|
||||
notif.summary(title).body(message);
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
// Stay on the screen until dismissed
|
||||
notif.hint(notify_rust::Hint::Resident(true));
|
||||
}
|
||||
|
||||
notif
|
||||
// timeout isn't respected on macos
|
||||
.timeout(0)
|
||||
.show()
|
||||
.ok();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let title = title.to_owned();
|
||||
let message = message.to_owned();
|
||||
|
||||
// We need to be in a different thread from the caller
|
||||
// in case we get called in the guts of a windows message
|
||||
// loop dispatch and are unable to pump messages
|
||||
std::thread::spawn(move || {
|
||||
use winrt_notification::Toast;
|
||||
|
||||
Toast::new(Toast::POWERSHELL_APP_ID)
|
||||
.title(&title)
|
||||
.text1(&message)
|
||||
.duration(winrt_notification::Duration::Long)
|
||||
.show()
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user