mirror of
https://github.com/wez/wezterm.git
synced 2024-12-22 12:51:31 +03:00
parent
e10b3cf6db
commit
83da7216c3
@ -33,6 +33,10 @@ use crate::activity::Activity;
|
||||
pub enum MuxNotification {
|
||||
PaneOutput(PaneId),
|
||||
WindowCreated(WindowId),
|
||||
ToastNotification {
|
||||
pane_id: PaneId,
|
||||
notification: wezterm_term::ToastNotification,
|
||||
},
|
||||
}
|
||||
|
||||
static SUB_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
@ -2,7 +2,7 @@ use crate::domain::DomainId;
|
||||
use crate::pane::{Pane, PaneId, Pattern, SearchResult};
|
||||
use crate::renderable::*;
|
||||
use crate::tmux::{TmuxDomain, TmuxDomainState};
|
||||
use crate::{Domain, Mux};
|
||||
use crate::{Domain, Mux, MuxNotification};
|
||||
use anyhow::Error;
|
||||
use async_trait::async_trait;
|
||||
use config::keyassignment::ScrollbackEraseMode;
|
||||
@ -17,7 +17,7 @@ use url::Url;
|
||||
use wezterm_term::color::ColorPalette;
|
||||
use wezterm_term::{
|
||||
CellAttributes, Clipboard, KeyCode, KeyModifiers, MouseEvent, SemanticZone, StableRowIndex,
|
||||
Terminal,
|
||||
Terminal, ToastNotification, ToastNotificationHandler,
|
||||
};
|
||||
|
||||
pub struct LocalPane {
|
||||
@ -367,6 +367,21 @@ impl wezterm_term::DeviceControlHandler for LocalPaneDCSHandler {
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalPaneNotifHandler {
|
||||
pane_id: PaneId,
|
||||
}
|
||||
|
||||
impl ToastNotificationHandler for LocalPaneNotifHandler {
|
||||
fn show_notification(&mut self, notification: ToastNotification) {
|
||||
if let Some(mux) = Mux::get() {
|
||||
mux.notify(MuxNotification::ToastNotification {
|
||||
pane_id: self.pane_id,
|
||||
notification,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalPane {
|
||||
pub fn new(
|
||||
pane_id: PaneId,
|
||||
@ -379,6 +394,7 @@ impl LocalPane {
|
||||
pane_id,
|
||||
tmux_domain: None,
|
||||
}));
|
||||
terminal.set_notification_handler(Box::new(LocalPaneNotifHandler { pane_id }));
|
||||
Self {
|
||||
pane_id,
|
||||
terminal: RefCell::new(terminal),
|
||||
|
@ -35,6 +35,21 @@ pub trait DeviceControlHandler {
|
||||
fn handle_device_control(&mut self, _control: termwiz::escape::DeviceControlMode);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ToastNotification {
|
||||
/// The title text for the notification.
|
||||
pub title: Option<String>,
|
||||
/// The message body
|
||||
pub body: String,
|
||||
/// Whether clicking on the notification should focus the
|
||||
/// window/tab/pane that generated it
|
||||
pub focus: bool,
|
||||
}
|
||||
|
||||
pub trait ToastNotificationHandler {
|
||||
fn show_notification(&mut self, notif: ToastNotification);
|
||||
}
|
||||
|
||||
/// Represents an instance of a terminal emulator.
|
||||
pub struct Terminal {
|
||||
/// The terminal model/state
|
||||
|
@ -289,6 +289,7 @@ pub struct TerminalState {
|
||||
|
||||
clipboard: Option<Arc<dyn Clipboard>>,
|
||||
device_control_handler: Option<Box<dyn DeviceControlHandler>>,
|
||||
notification_handler: Option<Box<dyn ToastNotificationHandler>>,
|
||||
|
||||
current_dir: Option<Url>,
|
||||
|
||||
@ -433,6 +434,7 @@ impl TerminalState {
|
||||
pixel_width: size.pixel_width,
|
||||
clipboard: None,
|
||||
device_control_handler: None,
|
||||
notification_handler: None,
|
||||
current_dir: None,
|
||||
term_program: term_program.to_string(),
|
||||
term_version: term_version.to_string(),
|
||||
@ -449,6 +451,10 @@ impl TerminalState {
|
||||
self.device_control_handler.replace(handler);
|
||||
}
|
||||
|
||||
pub fn set_notification_handler(&mut self, handler: Box<dyn ToastNotificationHandler>) {
|
||||
self.notification_handler.replace(handler);
|
||||
}
|
||||
|
||||
/// Returns the title text associated with the terminal session.
|
||||
/// The title can be changed by the application using a number
|
||||
/// of escape sequences:
|
||||
@ -3176,7 +3182,15 @@ impl<'a> Performer<'a> {
|
||||
}
|
||||
|
||||
OperatingSystemCommand::SystemNotification(message) => {
|
||||
error!("Application sends SystemNotification: {}", message);
|
||||
if let Some(handler) = self.notification_handler.as_mut() {
|
||||
handler.show_notification(ToastNotification {
|
||||
title: None,
|
||||
body: message,
|
||||
focus: true,
|
||||
});
|
||||
} else {
|
||||
log::info!("Application sends SystemNotification: {}", message);
|
||||
}
|
||||
}
|
||||
OperatingSystemCommand::CurrentWorkingDirectory(url) => {
|
||||
self.current_dir = Url::parse(&url).ok();
|
||||
|
@ -4,6 +4,7 @@ pub use config::FrontEndSelection;
|
||||
use mux::{Mux, MuxNotification};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wezterm_toast_notification::*;
|
||||
|
||||
mod glyphcache;
|
||||
mod overlay;
|
||||
@ -63,6 +64,21 @@ impl GuiFrontEnd {
|
||||
}
|
||||
}
|
||||
MuxNotification::PaneOutput(_) => {}
|
||||
MuxNotification::ToastNotification {
|
||||
pane_id: _,
|
||||
notification,
|
||||
} => {
|
||||
let title = notification.title.as_ref().unwrap_or(¬ification.body);
|
||||
let message = if notification.title.is_none() {
|
||||
""
|
||||
} else {
|
||||
¬ification.body
|
||||
};
|
||||
// FIXME: if notification.focus is true, we should do
|
||||
// something here to arrange to focus pane_id when the
|
||||
// notification is clicked
|
||||
persistent_toast_notification(title, message);
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
|
@ -81,6 +81,13 @@ where
|
||||
Ok(Item::Notif(MuxNotification::PaneOutput(pane_id))) => {
|
||||
handler.schedule_pane_push(pane_id);
|
||||
}
|
||||
Ok(Item::Notif(MuxNotification::ToastNotification {
|
||||
pane_id,
|
||||
notification: _,
|
||||
})) => {
|
||||
// FIXME: queue notification to send to client!
|
||||
handler.schedule_pane_push(pane_id);
|
||||
}
|
||||
Ok(Item::Notif(MuxNotification::WindowCreated(_window_id))) => {}
|
||||
Err(err) => {
|
||||
log::error!("process_async Err {}", err);
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![cfg(all(not(target_os = "macos"), not(windows), not(target_os="freebsd")))]
|
||||
#![cfg(all(not(target_os = "macos"), not(windows), not(target_os = "freebsd")))]
|
||||
//! See <https://developer.gnome.org/notification-spec/>
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -91,7 +91,7 @@ pub fn show_notif(title: &str, message: &str, url: Option<&str>) -> Result<(), z
|
||||
let proxy = NotificationsProxy::new(&connection)?;
|
||||
let caps = proxy.get_capabilities()?;
|
||||
|
||||
if !caps.iter().any(|cap| cap == "actions") {
|
||||
if url.is_some() && !caps.iter().any(|cap| cap == "actions") {
|
||||
// Server doesn't support actions, so skip showing this notification
|
||||
// because it might have text that says "click to see more"
|
||||
// and that just wouldn't work.
|
||||
@ -99,8 +99,7 @@ pub fn show_notif(title: &str, message: &str, url: Option<&str>) -> Result<(), z
|
||||
}
|
||||
|
||||
let mut hints = HashMap::new();
|
||||
let resident = Value::Bool(true);
|
||||
hints.insert("resident", resident);
|
||||
hints.insert("urgency", Value::U8(2 /* Critical */));
|
||||
let notification = proxy.notify(
|
||||
"wezterm",
|
||||
0,
|
||||
@ -116,60 +115,56 @@ pub fn show_notif(title: &str, message: &str, url: Option<&str>) -> Result<(), z
|
||||
0, // Never timeout
|
||||
)?;
|
||||
|
||||
// If we have a URL, we need to listen for signals to know when/if
|
||||
// the user clicks on it. The thread will stick around until an
|
||||
// error is encountered or the user clicks/dismisses the notification.
|
||||
if let Some(url) = &url {
|
||||
let url = url.to_string();
|
||||
let url = url.map(|s| s.to_string());
|
||||
|
||||
struct State {
|
||||
notification: u32,
|
||||
done: bool,
|
||||
url: String,
|
||||
}
|
||||
|
||||
let state = Arc::new(Mutex::new(State {
|
||||
notification,
|
||||
done: false,
|
||||
url,
|
||||
}));
|
||||
|
||||
proxy.connect_action_invoked({
|
||||
let state = Arc::clone(&state);
|
||||
move |nid, _action_name| {
|
||||
let mut state = state.lock().unwrap();
|
||||
if nid == state.notification {
|
||||
let _ = open::that(&state.url);
|
||||
state.done = true;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
proxy.connect_notification_closed({
|
||||
let state = Arc::clone(&state);
|
||||
move |nid, reason| {
|
||||
let _reason = Reason::new(reason);
|
||||
let mut state = state.lock().unwrap();
|
||||
if nid == state.notification {
|
||||
state.done = true;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
while !state.lock().unwrap().done {
|
||||
match proxy.next_signal() {
|
||||
Err(err) => {
|
||||
log::error!("next_signal: {:#}", err);
|
||||
break;
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
struct State {
|
||||
notification: u32,
|
||||
done: bool,
|
||||
url: Option<String>,
|
||||
}
|
||||
|
||||
let state = Arc::new(Mutex::new(State {
|
||||
notification,
|
||||
done: false,
|
||||
url,
|
||||
}));
|
||||
|
||||
proxy.connect_action_invoked({
|
||||
let state = Arc::clone(&state);
|
||||
move |nid, _action_name| {
|
||||
let state = state.lock().unwrap();
|
||||
if nid == state.notification {
|
||||
if let Some(url) = state.url.as_ref() {
|
||||
let _ = open::that(url);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
proxy.connect_notification_closed({
|
||||
let state = Arc::clone(&state);
|
||||
move |nid, reason| {
|
||||
let _reason = Reason::new(reason);
|
||||
let mut state = state.lock().unwrap();
|
||||
if nid == state.notification {
|
||||
state.done = true;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
while !state.lock().unwrap().done {
|
||||
match proxy.next_signal() {
|
||||
Err(err) => {
|
||||
log::error!("next_signal: {:#}", err);
|
||||
break;
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub fn persistent_toast_notification_with_click_to_open_url(title: &str, message
|
||||
macos::show_notif(title, message, Some(url));
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_os = "macos"), not(windows), not(target_os="freebsd")))]
|
||||
#[cfg(all(not(target_os = "macos"), not(windows), not(target_os = "freebsd")))]
|
||||
{
|
||||
if let Err(err) = dbus::show_notif(title, message, Some(url)) {
|
||||
log::error!("Failed to show notification: {}", err);
|
||||
@ -24,7 +24,7 @@ pub fn persistent_toast_notification(title: &str, message: &str) {
|
||||
macos::show_notif(title, message, None);
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_os = "macos"), not(windows), not(target_os="freebsd")))]
|
||||
#[cfg(all(not(target_os = "macos"), not(windows), not(target_os = "freebsd")))]
|
||||
{
|
||||
if let Err(err) = dbus::show_notif(title, message, None) {
|
||||
log::error!("Failed to show notification: {}", err);
|
||||
|
Loading…
Reference in New Issue
Block a user