diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc index ca2ae8655..b30183c73 100644 --- a/src/terminal/terminaldisplay.cc +++ b/src/terminal/terminaldisplay.cc @@ -102,6 +102,17 @@ std::string Display::new_frame( bool initialized, const Framebuffer& last, const } } + /* has cwd changed? OSC 7 current directory support */ + if ( f.is_current_directory_initialized() + && ( !initialized || f.get_current_directory() != frame.last_frame.get_current_directory() ) ) { + frame.append( "\033]7;" ); + for ( const auto c : f.get_current_directory() ) { + frame.append( c ); + } + frame.append( '\007' ); + /* ST is more correct, but BEL more widely supported */ + } + /* has clipboard changed? */ if ( f.get_clipboard() != frame.last_frame.get_clipboard() ) { frame.append( "\033]52;c;" ); diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc index ae6b83268..a13743306 100644 --- a/src/terminal/terminalframebuffer.cc +++ b/src/terminal/terminalframebuffer.cc @@ -73,8 +73,8 @@ DrawState::DrawState( int s_width, int s_height ) } Framebuffer::Framebuffer( int s_width, int s_height ) - : rows(), icon_name(), window_title(), clipboard(), bell_count( 0 ), title_initialized( false ), - ds( s_width, s_height ) + : rows(), icon_name(), window_title(), current_directory(), clipboard(), bell_count( 0 ), + title_initialized( false ), cwd_initialized( false ), ds( s_width, s_height ) { assert( s_height > 0 ); assert( s_width > 0 ); @@ -85,8 +85,8 @@ Framebuffer::Framebuffer( int s_width, int s_height ) Framebuffer::Framebuffer( const Framebuffer& other ) : rows( other.rows ), icon_name( other.icon_name ), window_title( other.window_title ), - clipboard( other.clipboard ), bell_count( other.bell_count ), title_initialized( other.title_initialized ), - ds( other.ds ) + current_directory( other.current_directory ), clipboard( other.clipboard ), bell_count( other.bell_count ), + title_initialized( other.title_initialized ), cwd_initialized( other.cwd_initialized ), ds( other.ds ) {} Framebuffer& Framebuffer::operator=( const Framebuffer& other ) @@ -95,9 +95,11 @@ Framebuffer& Framebuffer::operator=( const Framebuffer& other ) rows = other.rows; icon_name = other.icon_name; window_title = other.window_title; + current_directory = other.current_directory; clipboard = other.clipboard; bell_count = other.bell_count; title_initialized = other.title_initialized; + cwd_initialized = other.cwd_initialized; ds = other.ds; } return *this; @@ -387,6 +389,7 @@ void Framebuffer::reset( void ) ds = DrawState( width, height ); rows = rows_type( height, newrow() ); window_title.clear(); + current_directory.clear(); clipboard.clear(); /* do not reset bell_count */ } diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h index ef87ea84b..c94906c76 100644 --- a/src/terminal/terminalframebuffer.h +++ b/src/terminal/terminalframebuffer.h @@ -416,9 +416,11 @@ class Framebuffer rows_type rows; title_type icon_name; title_type window_title; + title_type current_directory; title_type clipboard; unsigned int bell_count; bool title_initialized; /* true if the window title has been set via an OSC */ + bool cwd_initialized; /* true if the cwd has been set via an OSC */ row_pointer newrow( void ) { @@ -495,11 +497,15 @@ class Framebuffer void set_title_initialized( void ) { title_initialized = true; } bool is_title_initialized( void ) const { return title_initialized; } + void set_current_directory_initialized( void ) { cwd_initialized = true; } + bool is_current_directory_initialized( void ) const { return cwd_initialized; } void set_icon_name( const title_type& s ) { icon_name = s; } void set_window_title( const title_type& s ) { window_title = s; } + void set_current_directory( const title_type& s ) { current_directory = s; } void set_clipboard( const title_type& s ) { clipboard = s; } const title_type& get_icon_name( void ) const { return icon_name; } const title_type& get_window_title( void ) const { return window_title; } + const title_type& get_current_directory( void ) const { return current_directory; } const title_type& get_clipboard( void ) const { return clipboard; } void prefix_window_title( const title_type& s ); @@ -514,8 +520,8 @@ class Framebuffer bool operator==( const Framebuffer& x ) const { - return ( rows == x.rows ) && ( window_title == x.window_title ) && ( clipboard == x.clipboard ) - && ( bell_count == x.bell_count ) && ( ds == x.ds ); + return ( rows == x.rows ) && ( window_title == x.window_title ) && ( current_directory == x.current_directory ) + && ( clipboard == x.clipboard ) && ( bell_count == x.bell_count ) && ( ds == x.ds ); } }; } diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc index a823fab76..f6b5c650b 100644 --- a/src/terminal/terminalfunctions.cc +++ b/src/terminal/terminalfunctions.cc @@ -657,6 +657,13 @@ void Dispatcher::OSC_dispatch( const Parser::OSC_End* act __attribute( ( unused OSC_8( osc_8_str, fb ); return; } + if ( cmd_num == 7 ) { + fb->set_current_directory_initialized(); + int cwd_length = std::min( OSC_string.size(), (size_t)1024 ); + Terminal::Framebuffer::title_type new_cwd( OSC_string.begin() + offset, OSC_string.begin() + cwd_length ); + fb->set_current_directory( new_cwd ); + return; + } bool set_icon = cmd_num == 0 || cmd_num == 1; bool set_title = cmd_num == 0 || cmd_num == 2; if ( set_icon || set_title ) { diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index c7d124e0e..128297fdf 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -25,6 +25,7 @@ displaytests = \ emulation-back-tab.test \ emulation-cursor-motion.test \ emulation-multiline-scroll.test \ + emulation-osc7.test \ emulation-scroll.test \ emulation-wrap-across-frames.test \ network-no-diff.test \ diff --git a/src/tests/emulation-osc7.test b/src/tests/emulation-osc7.test new file mode 100755 index 000000000..69c5162d0 --- /dev/null +++ b/src/tests/emulation-osc7.test @@ -0,0 +1,41 @@ +#!/bin/sh + +# +# This test is for OSC7 support. +# + +# shellcheck source=e2e-test-subrs +. "$(dirname "$0")/e2e-test-subrs" +PATH=$PATH:.:$srcdir + +if ! tmux_check 3 3; then + printf "tmux does not support OSC7\n" >&2 + exit 77 +fi + +# Top-level wrapper. +if [ $# -eq 0 ]; then + e2e-test "$0" baseline post + exit +fi + +# OK, we have arguments, we're one of the test hooks. +if [ $# -ne 1 ]; then + fail "bad arguments %s\n" "$@" +fi + +baseline() +{ + printf '\033]7;file://hostname/directory/\007' + echo 0 + printf '\033]7;\007' +} + +case $1 in + baseline) + baseline;; + post) + ;; + *) + fail "unknown test argument %s\n" "$1";; +esac