1
1
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:
Wez Furlong 2020-12-13 00:17:10 -08:00
parent 3c3ba6a30a
commit 6b414bebc9
8 changed files with 153 additions and 53 deletions

11
Cargo.lock generated
View File

@ -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"

View File

@ -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)]

View File

@ -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"]

View File

@ -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>,

View File

@ -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)]

View File

@ -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

View 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"

View 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();
});
}
}