Option to support VT100 line drawing in UTF-8 mode.

Thanks to Jiri Kaspar for sending this patch (apart from the new docs
section, which is in my own words), which implements a feature we've
had as a wishlist item ('utf8-plus-vt100') for a long time.

I was actually surprised it was possible to implement it in so few
lines of code! I'd forgotten, or possibly never noticed in the first
place, that even in UTF-8 mode PuTTY not only accepts but still
_processes_ all the ISO 2022 control sequences and shift characters,
and keeps running track of all the same state in term->cset and
term->cset_attrs that it tracks in IS0-2022-enabled modes. It's just
that in UTF-8 mode, at the very last minute when a character+attribute
pair is about to be written into the terminal's character buffer, it
deliberately ignores the contents of those variables.

So all that was needed was a new flag checked at that last moment
which causes it not quite to ignore them after all, and bingo,
utf8-plus-vt100 is supported. And it works no matter which ISO 2022
sequences you're using; whether you're using ESC ( 0 to select the
line drawing set directly into GL and ESC ( B to get back when you're
done, or whether you send a preliminary ESC ( B ESC ) 0 to get GL/GR
to be ASCII and line drawing respectively so you can use SI and SO as
one-byte mode switches thereafter, both work just as well.

This implementation strategy has a couple of consequences, which I
don't think matter very much one way or the other but I document them
just in case they turn out to be important later:

 - if an application expecting this mode has already filled your
   terminal window with lqqqqqqqqk, then enabling this mode in Change
   Settings won't retroactively turn them into the line drawing
   characters you wanted, because no memory is preserved in the screen
   buffer of what the ISO 2022 state was when they were printed. So
   the application still has to do a screen refresh.

 - on the other hand, if you already sent the ESC ( 0 or whatever to
   put the terminal _into_ line drawing mode, and then you turn on
   this mode in Change Settings, you _will_ still be in line drawing
   mode, because the system _does_ remember your current ISO 2022
   state at all times, whether it's currently applying it to output
   printing characters or not.
This commit is contained in:
Simon Tatham 2018-05-12 08:43:52 +01:00
Родитель 383302d70a
Коммит 6afa955a2e
7 изменённых файлов: 43 добавлений и 0 удалений

Просмотреть файл

@ -1939,6 +1939,9 @@ void setup_config_box(struct controlbox *b, int midsession,
ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d', ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
HELPCTX(selection_linedraw), HELPCTX(selection_linedraw),
conf_checkbox_handler, I(CONF_rawcnp)); conf_checkbox_handler, I(CONF_rawcnp));
ctrl_checkbox(s, "Enable VT100 line drawing even in UTF-8 mode",'8',
HELPCTX(translation_utf8linedraw),
conf_checkbox_handler, I(CONF_utf8linedraw));
/* /*
* The Window/Selection panel. * The Window/Selection panel.

Просмотреть файл

@ -1381,6 +1381,36 @@ Note that this option only applies to line-drawing characters which
characters that were received as Unicode code points will paste as characters that were received as Unicode code points will paste as
Unicode always. Unicode always.
\S{config-utf8linedraw} Combining VT100 line-drawing with UTF-8
\cfg{winhelp-topic}{translation.utf8linedraw}
If PuTTY is configured to treat data from the server as encoded in
UTF-8, then by default it disables the older VT100-style system of
control sequences that cause the lower-case letters to be temporarily
replaced by line drawing characters.
The rationale is that in UTF-8 mode you don't need those control
sequences anyway, because all the line-drawing characters they access
are available as Unicode characters already, so there's no need for
applications to put the terminal into a special state to get at them.
Also, it removes a risk of the terminal \e{accidentally} getting into
that state: if you accidentally write uncontrolled binary data to a
non-UTF-8 terminal, it can be surprisingly common to find that your
next shell prompt appears as a sequence of line-drawing characters and
then you have to remember or look up how to get out of that mode. So
by default, UTF-8 mode simply doesn't \e{have} a confusing mode like
that to get into, accidentally or on purpose.
However, not all applications will see it that way. Even UTF-8
terminal users will still sometimes have to run software that tries to
print line-drawing characters in the old-fashioned way. So the
configuration option \q{Enable VT100 line drawing even in UTF-8 mode}
puts PuTTY into a hybrid mode in which it understands the VT100-style
control sequences that change the meaning of the ASCII lower case
letters, \e{and} understands UTF-8.
\H{config-selection} The Selection panel \H{config-selection} The Selection panel
The Selection panel allows you to control the way \i{copy and paste} The Selection panel allows you to control the way \i{copy and paste}

Просмотреть файл

@ -906,6 +906,7 @@ void cleanup_exit(int);
X(INT, NONE, rect_select) \ X(INT, NONE, rect_select) \
X(INT, NONE, paste_controls) \ X(INT, NONE, paste_controls) \
X(INT, NONE, rawcnp) \ X(INT, NONE, rawcnp) \
X(INT, NONE, utf8linedraw) \
X(INT, NONE, rtf_paste) \ X(INT, NONE, rtf_paste) \
X(INT, NONE, mouse_override) \ X(INT, NONE, mouse_override) \
X(INT, INT, wordness) \ X(INT, INT, wordness) \

Просмотреть файл

@ -677,6 +677,7 @@ void save_open_settings(void *sesskey, Conf *conf)
write_setting_s(sesskey, buf, buf2); write_setting_s(sesskey, buf, buf2);
} }
write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp)); write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp));
write_setting_i(sesskey, "UTF8linedraw", conf_get_int(conf, CONF_utf8linedraw));
write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste)); write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste));
write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm)); write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm));
write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select)); write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select));
@ -1102,6 +1103,7 @@ void load_open_settings(void *sesskey, Conf *conf)
sfree(buf2); sfree(buf2);
} }
gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp); gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp);
gppi(sesskey, "UTF8linedraw", 0, conf, CONF_utf8linedraw);
gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste); gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste);
gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm); gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm);
gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select); gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select);

Просмотреть файл

@ -1466,6 +1466,7 @@ void term_copy_stuff_from_conf(Terminal *term)
term->no_remote_wintitle = conf_get_int(term->conf, CONF_no_remote_wintitle); term->no_remote_wintitle = conf_get_int(term->conf, CONF_no_remote_wintitle);
term->no_remote_clearscroll = conf_get_int(term->conf, CONF_no_remote_clearscroll); term->no_remote_clearscroll = conf_get_int(term->conf, CONF_no_remote_clearscroll);
term->rawcnp = conf_get_int(term->conf, CONF_rawcnp); term->rawcnp = conf_get_int(term->conf, CONF_rawcnp);
term->utf8linedraw = conf_get_int(term->conf, CONF_utf8linedraw);
term->rect_select = conf_get_int(term->conf, CONF_rect_select); term->rect_select = conf_get_int(term->conf, CONF_rect_select);
term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action); term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action);
term->rxvt_homeend = conf_get_int(term->conf, CONF_rxvt_homeend); term->rxvt_homeend = conf_get_int(term->conf, CONF_rxvt_homeend);
@ -2913,6 +2914,10 @@ static void term_out(Terminal *term)
/* UTF-8 must be stateless so we ignore iso2022. */ /* UTF-8 must be stateless so we ignore iso2022. */
if (term->ucsdata->unitab_ctrl[c] != 0xFF) if (term->ucsdata->unitab_ctrl[c] != 0xFF)
c = term->ucsdata->unitab_ctrl[c]; c = term->ucsdata->unitab_ctrl[c];
else if ((term->utf8linedraw) &&
(term->cset_attr[term->cset] == CSET_LINEDRW))
/* Linedraw characters are explicitly enabled */
c = ((unsigned char) c) | CSET_LINEDRW;
else c = ((unsigned char)c) | CSET_ASCII; else c = ((unsigned char)c) | CSET_ASCII;
break; break;
} else if ((c & 0xe0) == 0xc0) { } else if ((c & 0xe0) == 0xc0) {

Просмотреть файл

@ -320,6 +320,7 @@ struct terminal_tag {
int no_remote_wintitle; int no_remote_wintitle;
int no_remote_clearscroll; int no_remote_clearscroll;
int rawcnp; int rawcnp;
int utf8linedraw;
int rect_select; int rect_select;
int remote_qtitle_action; int remote_qtitle_action;
int rxvt_homeend; int rxvt_homeend;

Просмотреть файл

@ -138,6 +138,7 @@
#define WINHELP_CTX_translation_cjk_ambig_wide "translation.cjkambigwide:config-cjk-ambig-wide" #define WINHELP_CTX_translation_cjk_ambig_wide "translation.cjkambigwide:config-cjk-ambig-wide"
#define WINHELP_CTX_translation_cyrillic "translation.cyrillic:config-cyr" #define WINHELP_CTX_translation_cyrillic "translation.cyrillic:config-cyr"
#define WINHELP_CTX_translation_linedraw "translation.linedraw:config-linedraw" #define WINHELP_CTX_translation_linedraw "translation.linedraw:config-linedraw"
#define WINHELP_CTX_translation_utf8linedraw "translation.utf8linedraw:config-utf8linedraw"
#define WINHELP_CTX_ssh_tunnels_x11 "ssh.tunnels.x11:config-ssh-x11" #define WINHELP_CTX_ssh_tunnels_x11 "ssh.tunnels.x11:config-ssh-x11"
#define WINHELP_CTX_ssh_tunnels_x11auth "ssh.tunnels.x11auth:config-ssh-x11auth" #define WINHELP_CTX_ssh_tunnels_x11auth "ssh.tunnels.x11auth:config-ssh-x11auth"
#define WINHELP_CTX_ssh_tunnels_xauthority "ssh.tunnels.xauthority:config-ssh-xauthority" #define WINHELP_CTX_ssh_tunnels_xauthority "ssh.tunnels.xauthority:config-ssh-xauthority"