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!()
}

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,
/// 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?
Copy link
Owner

Choose a reason for hiding this comment

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

There isn't an explicit cache, but you will need to cause the logic in the normal mouse event processing flow to trigger and assess what the cursor should be here, based on its position.

I don't have a great suggestion for this off the top of my head. Most likely will need to add a helper function to figure out if the mouse cursor is over the terminal area and if so, get the shape from the pane identified by pane_id and apply it to the window.

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
}
Comment on lines +1528 to +1533
Copy link
Author

Choose a reason for hiding this comment

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

I copied what's done for Alert::PaletteChanged here, not sure if that's correct.

}

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