Skip to content

Commit

Permalink
hterm: implement OSC 1337 file display/transfer
Browse files Browse the repository at this point in the history
This is the escape sequence used by iTerm2 for displaying files inline
and downloading them.

Currently we only support displaying of images.  There is no container
type in HTML for autodetecting formats, and at this point I'd rather
not implement a custom mime type sniffer.

Since things are transferred inline and embedded directly in the DOM,
this does not handle large transfers well (think 5+ MB).  Not clear
whether there's much we could ever do about this, especially since the
base64 encoding is so inefficient.  One possible avenue of research is
the HTML FS, but those too usually have quota limits.

Based on the current parser framework, the escape sequence doesn't
trigger until it's been fully received.  That means we can't display a
progress bar for showing overall transfers.  We might revise that based
on user feedback, or we just discourage people from doing large transfers
here and develop a more efficient solution (e.g. zmodem or sixel).

Two other limitations worth noting:
- If the terminal is resized, the number of rows the image occupies is
  not updated accordingly.  When increasing the character size, this
  can make the padded rows take up more empty space than the image.
- The image is shown only when the last row is visible.  This comes
  up when browsing the history from old->newer -- the image completely
  disappears.  Picking the last row rather than the first row behaves
  better when old content automatically scrolls off (newer->older) which
  I suspect is the more common usage scenario.

Change-Id: Ib1acc8addcf0b94a180b22712039b05d20c4dfc7
Reviewed-on: https://chromium-review.googlesource.com/484519
Tested-by: Mike Frysinger <[email protected]>
Reviewed-by: Brandon Gilmore <[email protected]>
  • Loading branch information
vapier committed Dec 13, 2017
1 parent d069b1a commit 8c5a0a4
Show file tree
Hide file tree
Showing 11 changed files with 809 additions and 15 deletions.
54 changes: 53 additions & 1 deletion hterm/doc/ControlSequences.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,57 @@ For example:
| 118 | Reset Tektronix cursor color | Ignored | ESC ] 118 ; \a |
| 119 | Reset highlight foreground color | Ignored | ESC ] 119 ; \a |
| 777 | rxvt-unicode (urxvt) modules | Only "notify" supported | ESC ] 777 ; notify ; [title] ; [body] \a |
| 1337 | iTerm2 sequences | Ignored | |
|[1337]| iTerm2 sequences | Only "File" supported | ESC ] 1337 ; File = [args] : [base64 data] \a |

### OSC+1337: iTerm2 sequences {#OSC-1337}

The [iTerm2](https://www.iterm2.com/) terminal for macOS provides a lot of
proprietary options via the OSC 1337 command. Many of them duplicate other
standard sequences, so most of them aren't supported.

We support media display and file transfers. This is specified via the `File=`
keyword. None of the options below are required as a reasonable default will
be selected automatically.

***note
There is a [helper script](../etc/hterm-show-file.sh) you can use to handle
the protocol for you.
***

***note
*Warning:* You should avoid transferring larger files as Chrome performance
will suffer. If it's under 2 MB, it probably will be fine, but YMMV.
***

The overall form looks like ESC+] 1337 ; File=name=[base64];inline=1 :
[base64 data] BEL.

* `name`: The base64 encoded name of the file or other human readable text.
* `size`: How many bytes in the base64 data (for transfer progress).
* `width`: The display width specification (see below). Defaults to `auto`.
* `height`: The display height specification (see below). Defaults to `auto`.
* `preserveAspectRatio`: If `0`, scale/stretch the display to fit the space.
If `1` (the default), fill the display as much as possible without stretching.
* `inline`: If `0` (the default), download the file instead of displaying it.
If `1`, display the file in the terminal.
* `align`: Set the display alignment with `left` (the default), `right`, or
`center`.

For the base64 encoded fields, make sure to omit whitespace (e.g. newlines) if
using a tool like `base64`.

For the `width` & `height` fields, a number of forms are accepted. Note that
the terminal will probably restrict the maximum size automatically to the active
terminal dimensions. e.g. If the terminal is 1000 pixels wide, specifying a
width greater than that will automatically be limited to 1000 pixels.

* `N`: How many cells (e.g. rows or columns) to fill.
* `Npx`: How many pixels to fill.
* `N%`: A percentage of the overall terminal screen.
* `auto`: Use the file's dimension.

For inline display, currently only images in formats Chrome itself understands
are supported.

## Control Sequence Introducer (CSI) {#CSI}

Expand Down Expand Up @@ -770,6 +820,8 @@ color selection.
[SGR]: #SGR
[SM]: #SM

[1337]: #OSC-1337

[ECMA-35]: http://www.ecma-international.org/publications/standards/Ecma-035.htm
[ECMA-43]: http://www.ecma-international.org/publications/standards/Ecma-043.htm
[ECMA-48]: http://www.ecma-international.org/publications/standards/Ecma-048.htm
Expand Down
38 changes: 25 additions & 13 deletions hterm/etc/hterm-notify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,49 @@ die() {
exit 1
}

# Send a notification running under tmux.
# Usage: [title] [body]
notify_tmux() {
local title="${1-}" body="${2-}"
printf '\033Ptmux;\033\033]777;notify;%s;%s\a\033\\' "${title}" "${body}"
# Send a DCS sequence through tmux.
# Usage: <sequence>
tmux_dcs() {
printf '\033Ptmux;\033%s\033\\' "$1"
}

# Send a notification.
# Usage: [title] [body]
notify() {
local title="${1-}" body="${2-}"
# Send a DCS sequence through screen.
# Usage: <sequence>
screen_dcs() {
printf '\033P\033%s\033\\' "$1"
}

# Send an escape sequence to hterm.
# Usage: <sequence>
print_seq() {
local seq="$1"

case ${TERM-} in
screen*)
# Since tmux defaults to setting TERM=screen (ugh), we need to detect
# it here specially.
if [ -n "${TMUX-}" ]; then
notify_tmux "${title}" "${body}"
tmux_dcs "${seq}"
else
printf '\033P\033\033]777;notify;%s;%s\a\033\\' "${title}" "${body}"
screen_dcs "${seq}"
fi
;;
tmux*)
notify_tmux "${title}" "${body}"
tmux_dcs "${seq}"
;;
*)
printf '\033]777;notify;%s;%s\a' "${title}" "${body}"
echo "${seq}"
;;
esac
}

# Send a notification.
# Usage: [title] [body]
notify() {
local title="${1-}" body="${2-}"
print_seq "$(printf '\033]777;notify;%s;%s\a' "${title}" "${body}")"
}

# Write tool usage and exit.
# Usage: [error message]
usage() {
Expand Down
120 changes: 120 additions & 0 deletions hterm/etc/hterm-show-file.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/bin/sh
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Write an error message and exit.
# Usage: <message>
die() {
echo "ERROR: $*"
exit 1
}

# Send a DCS sequence through tmux.
# Usage: <sequence>
tmux_dcs() {
printf '\033Ptmux;\033%s\033\\' "$1"
}

# Send a DCS sequence through screen.
# Usage: <sequence>
screen_dcs() {
printf '\033P\033%s\033\\' "$1"
}

# Send an escape sequence to hterm.
# Usage: <sequence>
print_seq() {
local seq="$1"

case ${TERM-} in
screen*)
# Since tmux defaults to setting TERM=screen (ugh), we need to detect
# it here specially.
if [ -n "${TMUX-}" ]; then
tmux_dcs "${seq}"
else
screen_dcs "${seq}"
fi
;;
tmux*)
tmux_dcs "${seq}"
;;
*)
echo "${seq}"
;;
esac
}

# Base64 encode stdin.
b64enc() {
base64 | tr -d '\n'
}

# Get the image height/width via imagemagick if possible.
# Usage: <file>
dimensions() {
identify -format 'width=%wpx;height=%hpx;' "$1" 2>/dev/null
}

# Send the 1337 OSC sequence to display the file.
# Usage: <file>
show() {
local name="$1"
local opts="inline=1;$2"

print_seq "$(printf '\033]1337;File=name=%s;%s%s:%s\a' \
"$(echo "$(basename "${name}")" | b64enc)" \
"$(dimensions "${name}")" \
"${opts}" \
"$(b64enc <"${name}")")"
}

# Write tool usage and exit.
# Usage: [error message]
usage() {
if [ $# -gt 0 ]; then
exec 1>&2
fi
cat <<EOF
Usage: hterm-show-file [options] <file> [options]
Send a file to hterm. It can be shown inline or downloaded.
This can also be used for small file transfers.
EOF

if [ $# -gt 0 ]; then
echo
die "$@"
else
exit 0
fi
}

main() {
set -e

while [ $# -gt 0 ]; do
case $1 in
-h|--help)
usage
;;
-*)
usage "Unknown option: $1"
;;
*)
break
;;
esac
done

if [ $# -eq 0 ]; then
die "Missing file to send"
fi
if [ $# -gt 2 ]; then
usage "Too many arguments"
fi

show "$@"
}
main "$@"
9 changes: 9 additions & 0 deletions hterm/js/hterm_preference_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ hterm.PreferenceManager.categories.CopyPaste = 'CopyPaste';
hterm.PreferenceManager.categories.Sounds = 'Sounds';
hterm.PreferenceManager.categories.Scrolling = 'Scrolling';
hterm.PreferenceManager.categories.Encoding = 'Encoding';
hterm.PreferenceManager.categories.Extensions = 'Extensions';
hterm.PreferenceManager.categories.Miscellaneous = 'Miscellaneous';

/**
Expand All @@ -70,6 +71,8 @@ hterm.PreferenceManager.categoryDefinitions = [
text: 'Scrolling'},
{ id: hterm.PreferenceManager.categories.Sounds,
text: 'Sounds'},
{ id: hterm.PreferenceManager.categories.Extensions,
text: 'Extensions'},
{ id: hterm.PreferenceManager.categories.Miscellaneous,
text: 'Misc.'}
];
Expand Down Expand Up @@ -496,6 +499,12 @@ hterm.PreferenceManager.defaultPreferences = {
'user-css-text':
[hterm.PreferenceManager.categories.Appearance, '', 'multiline-string',
'Custom CSS text for styling the terminal.'],

'allow-images-inline':
[hterm.PreferenceManager.categories.Extensions, null, 'tristate',
'Whether to allow the remote side to display images in the terminal.\n' +
'\n' +
'By default, we prompt until a choice is made.'],
};

hterm.PreferenceManager.prototype =
Expand Down
Loading

0 comments on commit 8c5a0a4

Please sign in to comment.