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

Add support for OSC 22 control sequence to change mouse cursor shape #6292

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions mux/src/localpane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ use url::Url;
use wezterm_dynamic::Value;
use wezterm_term::color::ColorPalette;
use wezterm_term::{
Alert, AlertHandler, Clipboard, DownloadHandler, KeyCode, KeyModifiers, MouseEvent,
SemanticZone, StableRowIndex, Terminal, TerminalConfiguration, TerminalSize,
Alert, AlertHandler, Clipboard, DownloadHandler, KeyCode, KeyModifiers, MouseCursor,
MouseEvent, SemanticZone, StableRowIndex, Terminal, TerminalConfiguration, TerminalSize,
};

const PROC_INFO_CACHE_TTL: Duration = Duration::from_millis(300);
Expand Down Expand Up @@ -137,6 +137,10 @@ pub struct LocalPane {

#[async_trait(?Send)]
impl Pane for LocalPane {
fn get_mouse_cursor_shape(&self) -> MouseCursor {
self.terminal.lock().get_mouse_cursor_shape()
}

fn pane_id(&self) -> PaneId {
self.pane_id
}
Expand Down
7 changes: 7 additions & 0 deletions mux/src/pane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use termwiz::surface::{Line, SequenceNo};
use url::Url;
use wezterm_dynamic::Value;
use wezterm_term::color::ColorPalette;
pub use wezterm_term::MouseCursor;
use wezterm_term::{
Clipboard, DownloadHandler, KeyCode, KeyModifiers, MouseEvent, SemanticZone, StableRowIndex,
TerminalConfiguration, TerminalSize,
Expand Down Expand Up @@ -231,6 +232,8 @@ pub trait Pane: Downcast + Send + Sync {
/// Returns render related dimensions
fn get_dimensions(&self) -> RenderableDimensions;

fn get_mouse_cursor_shape(&self) -> MouseCursor;
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

fn get_title(&self) -> String;
fn send_paste(&self, text: &str) -> anyhow::Result<()>;
fn reader(&self) -> anyhow::Result<Option<Box<dyn std::io::Read + Send>>>;
Expand Down Expand Up @@ -550,6 +553,10 @@ mod test {
}

impl Pane for FakePane {
fn get_mouse_cursor_shape(&self) -> MouseCursor {
unimplemented!()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
unimplemented!()
None

}

fn pane_id(&self) -> PaneId {
unimplemented!()
}
Expand Down
4 changes: 4 additions & 0 deletions mux/src/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,10 @@ mod test {
}

impl Pane for FakePane {
fn get_mouse_cursor_shape(&self) -> MouseCursor {
unimplemented!();
}

fn pane_id(&self) -> PaneId {
self.id
}
Expand Down
7 changes: 6 additions & 1 deletion mux/src/termwiztermtab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ use termwiz::Context;
use url::Url;
use wezterm_term::color::ColorPalette;
use wezterm_term::{
KeyCode, KeyModifiers, MouseEvent, StableRowIndex, TerminalConfiguration, TerminalSize,
KeyCode, KeyModifiers, MouseCursor, MouseEvent, StableRowIndex, TerminalConfiguration,
TerminalSize,
};

struct TermWizTerminalDomain {
Expand Down Expand Up @@ -126,6 +127,10 @@ impl TermWizTerminalPane {
}

impl Pane for TermWizTerminalPane {
fn get_mouse_cursor_shape(&self) -> MouseCursor {
MouseCursor::Arrow
}

fn pane_id(&self) -> PaneId {
self.pane_id
}
Expand Down
2 changes: 2 additions & 0 deletions term/src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum Alert {
TabTitleChanged(Option<String>),
/// When the color palette has been updated
PaletteChanged,
/// When the mouse cursor shape has been updated
MouseCursorShapeChanged,
Copy link
Member

Choose a reason for hiding this comment

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

Let's pass the name along with this, like with the title changed event above.
That will make it easier to do a half-baked mux client implementation (see 44866cc)

Suggested change
MouseCursorShapeChanged,
MouseCursorShapeChanged(Option<String>)

Copy link
Member

Choose a reason for hiding this comment

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

Note that by changing the Alert enum, you're changing the binary signature of types on which the mux protocol depends. You'll need to bump the CODEC_VERSION:

wezterm/codec/src/lib.rs

Lines 441 to 444 in c06cc87

/// The overall version of the codec.
/// This must be bumped when backwards incompatible changes
/// are made to the types and protocol.
pub const CODEC_VERSION: usize = 44;

/// A UserVar has changed value
SetUserVar {
name: String,
Expand Down
39 changes: 39 additions & 0 deletions term/src/terminalstate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ pub(crate) enum MouseEncoding {
SgrPixels,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MouseCursor {
Arrow,
Hand,
Text,
SizeUpDown,
SizeLeftRight,
}
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

impl TabStop {
fn new(screen_width: usize, tab_width: usize) -> Self {
let mut tabs = Vec::with_capacity(screen_width);
Expand Down Expand Up @@ -342,6 +351,9 @@ pub struct TerminalState {

palette: Option<ColorPalette>,

/// The mouse cursor shape (OSC 22)
mouse_cursor_shape: String,
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

pixel_width: usize,
pixel_height: usize,
dpi: u32,
Expand Down Expand Up @@ -547,6 +559,7 @@ impl TerminalState {
any_event_mouse: false,
button_event_mouse: false,
mouse_tracking: false,
mouse_cursor_shape: String::from(""),
last_mouse_move: None,
cursor_visible: true,
g0_charset: CharSet::Ascii,
Expand Down Expand Up @@ -665,6 +678,26 @@ impl TerminalState {
.unwrap_or_else(|| self.config.color_palette())
}

/// Returns a copy of the current mouse cursor shape.
pub fn get_mouse_cursor_shape(&self) -> MouseCursor {
reykjalin marked this conversation as resolved.
Show resolved Hide resolved
// FIXME: The log here is clearing a cache somewhere causing the mouse shape
// to be updated correctly. Not sure what I need to change.
log::info!("Mouse cursor shape: {}", self.mouse_cursor_shape);
reykjalin marked this conversation as resolved.
Show resolved Hide resolved
if self.mouse_cursor_shape == "pointer" {
MouseCursor::Hand
} else if self.mouse_cursor_shape == "text" {
MouseCursor::Text
} else if self.mouse_cursor_shape == "row-resize" || self.mouse_cursor_shape == "ns-resize"
{
MouseCursor::SizeUpDown
} else if self.mouse_cursor_shape == "col-resize" || self.mouse_cursor_shape == "ew-resize"
{
MouseCursor::SizeLeftRight
} else {
MouseCursor::Arrow
}
}
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

/// Called in response to dynamic color scheme escape sequences.
/// Will make a copy of the palette from the config file if this
/// is the first of these escapes we've seen.
Expand Down Expand Up @@ -927,6 +960,12 @@ impl TerminalState {
}
}

fn mouse_cursor_shape_did_change(&mut self) {
if let Some(handler) = self.alert_handler.as_mut() {
handler.alert(Alert::MouseCursorShapeChanged);
}
}

/// When dealing with selection, mark a range of lines as dirty
pub fn make_all_lines_dirty(&mut self) {
let seqno = self.seqno;
Expand Down
6 changes: 6 additions & 0 deletions term/src/terminalstate/performer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,7 @@ impl<'a> Performer<'a> {
self.focus_tracking = false;
self.mouse_tracking = false;
self.mouse_encoding = MouseEncoding::X10;
self.mouse_cursor_shape = String::from("");
reykjalin marked this conversation as resolved.
Show resolved Hide resolved
self.keyboard_encoding = KeyboardEncoding::Xterm;
self.sixel_scrolls_right = false;
self.any_event_mouse = false;
Expand All @@ -704,6 +705,7 @@ impl<'a> Performer<'a> {
self.erase_in_display(EraseInDisplay::EraseScrollback);
self.erase_in_display(EraseInDisplay::EraseDisplay);
self.palette_did_change();
self.mouse_cursor_shape_did_change();
}

_ => {
Expand All @@ -718,6 +720,10 @@ impl<'a> Performer<'a> {
self.pop_tmux_title_state();
self.flush_print();
match osc {
OperatingSystemCommand::SetMouseCursorShape(mouse_shape) => {
self.mouse_cursor_shape = mouse_shape;
self.mouse_cursor_shape_did_change();
}
OperatingSystemCommand::SetIconNameSun(title)
| OperatingSystemCommand::SetIconName(title) => {
if title.is_empty() {
Expand Down
4 changes: 4 additions & 0 deletions termwiz/src/escape/osc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub enum OperatingSystemCommand {
QuerySelection(Selection),
SetSelection(Selection, String),
SystemNotification(String),
SetMouseCursorShape(String),
ITermProprietary(ITermProprietary),
FinalTermSemanticPrompt(FinalTermSemanticPrompt),
ChangeColorNumber(Vec<ChangeColorPair>),
Expand Down Expand Up @@ -312,6 +313,7 @@ impl OperatingSystemCommand {
SetHyperlink => Ok(OperatingSystemCommand::SetHyperlink(Hyperlink::parse(osc)?)),
ManipulateSelectionData => Self::parse_selection(osc),
SystemNotification => single_string!(SystemNotification),
SetMouseCursorShape => single_string!(SetMouseCursorShape),
SetCurrentWorkingDirectory => single_string!(CurrentWorkingDirectory),
ITermProprietary => {
self::ITermProprietary::parse(osc).map(OperatingSystemCommand::ITermProprietary)
Expand Down Expand Up @@ -419,6 +421,7 @@ osc_entries!(
SetHighlightBackgroundColor = "17",
SetTektronixCursorColor = "18",
SetHighlightForegroundColor = "19",
SetMouseCursorShape = "22",
SetLogFileName = "46",
SetFont = "50",
EmacsShell = "51",
Expand Down Expand Up @@ -509,6 +512,7 @@ impl Display for OperatingSystemCommand {
QuerySelection(s) => write!(f, "52;{};?", s)?,
SetSelection(s, val) => write!(f, "52;{};{}", s, base64_encode(val))?,
SystemNotification(s) => write!(f, "9;{}", s)?,
SetMouseCursorShape(s) => write!(f, "22;{}", s)?,
ITermProprietary(i) => i.fmt(f)?,
FinalTermSemanticPrompt(i) => i.fmt(f)?,
ResetColors(colors) => {
Expand Down
6 changes: 5 additions & 1 deletion wezterm-client/src/pane/clientpane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use url::Url;
use wezterm_dynamic::Value;
use wezterm_term::color::ColorPalette;
use wezterm_term::{
Alert, Clipboard, KeyCode, KeyModifiers, Line, MouseEvent, StableRowIndex,
Alert, Clipboard, KeyCode, KeyModifiers, Line, MouseCursor, MouseEvent, StableRowIndex,
TerminalConfiguration, TerminalSize,
};

Expand Down Expand Up @@ -245,6 +245,10 @@ impl ClientPane {

#[async_trait(?Send)]
impl Pane for ClientPane {
fn get_mouse_cursor_shape(&self) -> MouseCursor {
MouseCursor::Arrow
}
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

fn pane_id(&self) -> PaneId {
self.local_pane_id
}
Expand Down
1 change: 1 addition & 0 deletions wezterm-gui/src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ impl GuiFrontEnd {
alert:
Alert::OutputSinceFocusLost
| Alert::PaletteChanged
| Alert::MouseCursorShapeChanged
| Alert::CurrentWorkingDirectoryChanged
| Alert::WindowTitleChanged(_)
| Alert::TabTitleChanged(_)
Expand Down
8 changes: 6 additions & 2 deletions wezterm-gui/src/overlay/copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use unicode_segmentation::*;
use url::Url;
use wezterm_term::color::ColorPalette;
use wezterm_term::{
unicode_column_width, Clipboard, KeyCode, KeyModifiers, Line, MouseEvent, SemanticType,
StableRowIndex, TerminalSize,
unicode_column_width, Clipboard, KeyCode, KeyModifiers, Line, MouseCursor, MouseEvent,
SemanticType, StableRowIndex, TerminalSize,
};
use window::{KeyCode as WKeyCode, Modifiers, WindowOps};

Expand Down Expand Up @@ -1101,6 +1101,10 @@ impl CopyRenderable {
}

impl Pane for CopyOverlay {
fn get_mouse_cursor_shape(&self) -> MouseCursor {
MouseCursor::Arrow
}
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

fn pane_id(&self) -> PaneId {
self.delegate.pane_id()
}
Expand Down
6 changes: 5 additions & 1 deletion wezterm-gui/src/overlay/quickselect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use termwiz::surface::{SequenceNo, SEQ_ZERO};
use url::Url;
use wezterm_term::color::ColorPalette;
use wezterm_term::{
Clipboard, KeyCode, KeyModifiers, Line, MouseEvent, StableRowIndex, TerminalSize,
Clipboard, KeyCode, KeyModifiers, Line, MouseCursor, MouseEvent, StableRowIndex, TerminalSize,
};
use window::WindowOps;

Expand Down Expand Up @@ -317,6 +317,10 @@ impl QuickSelectOverlay {
}

impl Pane for QuickSelectOverlay {
fn get_mouse_cursor_shape(&self) -> MouseCursor {
MouseCursor::Arrow
}
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

fn pane_id(&self) -> PaneId {
self.delegate.pane_id()
}
Expand Down
13 changes: 13 additions & 0 deletions wezterm-gui/src/termwindow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,13 @@ impl TermWindow {
} => {
self.update_title();
}
MuxNotification::Alert {
alert: Alert::MouseCursorShapeChanged,
pane_id,
} => {
// FIXME: Is there a cache that needs to be invalidated like for PaletteChanged?
reykjalin marked this conversation as resolved.
Show resolved Hide resolved
self.mux_pane_output_event(pane_id);
}
MuxNotification::Alert {
alert: Alert::PaletteChanged,
pane_id,
Expand Down Expand Up @@ -1519,6 +1526,12 @@ impl TermWindow {
} => {
// fall through
}
MuxNotification::Alert {
alert: Alert::MouseCursorShapeChanged { .. },
..
} => {
// fall through
}
wez marked this conversation as resolved.
Show resolved Hide resolved
}

window.notify(TermWindowNotif::MuxNotification(n));
Expand Down
10 changes: 7 additions & 3 deletions wezterm-gui/src/termwindow/mouseevent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::termwindow::{
GuiWin, MouseCapture, PositionedSplit, ScrollHit, TermWindowNotif, UIItem, UIItemType, TMB,
};
use ::window::{
MouseButtons as WMB, MouseCursor, MouseEvent, MouseEventKind as WMEK, MousePress,
WindowDecorations, WindowOps, WindowState,
MouseButtons as WMB, MouseEvent, MouseEventKind as WMEK, MousePress, WindowDecorations,
WindowOps, WindowState,
};
use config::keyassignment::{KeyAssignment, MouseEventTrigger, SpawnTabDomain};
use config::MouseEventAltScreen;
Expand All @@ -21,7 +21,7 @@ use termwiz::hyperlink::Hyperlink;
use termwiz::surface::Line;
use wezterm_dynamic::ToDynamic;
use wezterm_term::input::{MouseButton, MouseEventKind as TMEK};
use wezterm_term::{ClickPosition, LastMouseClick, StableRowIndex};
use wezterm_term::{ClickPosition, LastMouseClick, MouseCursor, StableRowIndex};

impl super::TermWindow {
fn resolve_ui_item(&self, event: &MouseEvent) -> Option<UIItem> {
Expand Down Expand Up @@ -843,6 +843,10 @@ impl super::TermWindow {
MouseCursor::Text
}));

if let Some(pane) = self.get_active_pane_or_overlay() {
context.set_cursor(Some(pane.get_mouse_cursor_shape()));
}
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

let event_trigger_type = match &event.kind {
WMEK::Press(press) => {
let press = mouse_press_to_tmb(press);
Expand Down
1 change: 1 addition & 0 deletions window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ wezterm-bidi = { path = "../bidi" }
wezterm-color-types = { path = "../color-types" }
wezterm-font = { path = "../wezterm-font" }
wezterm-input-types = { path = "../wezterm-input-types" }
wezterm-term = { path = "../term", features=["use_serde"] }
reykjalin marked this conversation as resolved.
Show resolved Hide resolved

[target."cfg(windows)".dependencies]
clipboard-win = "2.2"
Expand Down
1 change: 1 addition & 0 deletions window/examples/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use promise::spawn::spawn;
use std::cell::RefCell;
use std::rc::Rc;
use wezterm_font::FontConfiguration;
use wezterm_term::MouseCursor;

struct MyWindow {
allow_close: bool,
Expand Down
Loading