vte: support 7bit and 8bit modes

In 7bit and 8bit modes we treat incoming characters as single-byte
characters and map them directly to the corresponding value instead of
using the UTF8 state-machine.

This allows using old non-utf8 applications with this VT. However, default
value is still UTF8-mode and you must explicitely request a conformance
level to change that. If you selected such a mode once, you can only
return to UTF8-mode by performing a soft/hard-reset.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2012-05-29 12:01:23 +02:00
parent 75d981b7be
commit 64e235bc22

View File

@ -105,9 +105,11 @@ enum parser_action {
#define CSI_ARG_MAX 16
/* terminal flags */
#define FLAG_CURSOR_KEY_MODE 0x01
#define FLAG_KEYPAD_APPLICATION_MODE 0x02 /* TODO: toggle on numlock? */
#define FLAG_LINE_FEED_NEW_LINE_MODE 0x04
#define FLAG_CURSOR_KEY_MODE 0x01 /* DEC cursor key mode */
#define FLAG_KEYPAD_APPLICATION_MODE 0x02 /* DEC keypad application mode; TODO: toggle on numlock? */
#define FLAG_LINE_FEED_NEW_LINE_MODE 0x04 /* DEC line-feed/new-line mode */
#define FLAG_8BIT_MODE 0x08 /* Disable UTF-8 mode and enable 8bit compatible mode */
#define FLAG_7BIT_MODE 0x10 /* Disable 8bit mode and use 7bit compatible mode */
struct kmscon_vte {
unsigned long ref;
@ -191,11 +193,50 @@ void kmscon_vte_unref(struct kmscon_vte *vte)
free(vte);
}
static void vte_write(struct kmscon_vte *vte, const char *u8, size_t len)
/*
* Write raw byte-stream to pty.
* When writing data to the client we must make sure that we send the correct
* encoding. For backwards-compatibility reasons we should always send 7bit
* characters exclusively. However, when FLAG_7BIT_MODE is not set, then we can
* also send raw 8bit characters. For instance, in FLAG_8BIT_MODE we can use the
* GR characters as keyboard input and send them directly or even use the C1
* escape characters. In unicode mode (default) we can send multi-byte utf-8
* characters which are also 8bit. When sending these characters, set the \raw
* flag to true so this function does not perform debug checks on data we send.
* If debugging is disabled, these checks are also disabled and won't affect
* performance.
* For better debugging, we also use the __LINE__ and __FILE__ macros. Use the
* vte_write() and vte_write_raw() macros below for more convenient use.
*
* As a rule of thumb do never send 8bit characters in escape sequences and also
* avoid all 8bit escape codes including the C1 codes. This will guarantee that
* all kind of clients are always compatible to us.
*/
static void vte_write_debug(struct kmscon_vte *vte, const char *u8, size_t len,
bool raw, const char *file, int line)
{
#ifdef KMSCON_ENABLE_DEBUG
/* in debug mode we check that escape sequences are always <0x7f so they
* are correctly parsed by non-unicode and non-8bit-mode clients. */
size_t i;
if (!raw) {
for (i = 0; i < len; ++i) {
if (u8[i] & 0x80)
log_warning("sending 8bit character inline to client in %s:%d",
file, line);
}
}
#endif
vte->write_cb(vte, u8, len, vte->data);
}
#define vte_write(_vte, _u8, _len) \
vte_write_debug((_vte), (_u8), (_len), false, __FILE__, __LINE__)
#define vte_write_raw(_vte, _u8, _len) \
vte_write_debug((_vte), (_u8), (_len), true, __FILE__, __LINE__)
/* execute control character (C0 or C1) */
static void do_execute(struct kmscon_vte *vte, uint32_t ctrl)
{
@ -981,11 +1022,20 @@ void kmscon_vte_input(struct kmscon_vte *vte, const char *u8, size_t len)
return;
for (i = 0; i < len; ++i) {
state = kmscon_utf8_mach_feed(vte->mach, u8[i]);
if (state == KMSCON_UTF8_ACCEPT ||
state == KMSCON_UTF8_REJECT) {
ucs4 = kmscon_utf8_mach_get(vte->mach);
parse_data(vte, ucs4);
if (vte->flags & FLAG_7BIT_MODE) {
if (u8[i] & 0x80)
log_debug("receiving 8bit character U+%d from pty while in 7bit mode",
(int)u8[i]);
parse_data(vte, u8[i] & 0x7f);
} else if (vte->flags & FLAG_8BIT_MODE) {
parse_data(vte, u8[i]);
} else {
state = kmscon_utf8_mach_feed(vte->mach, u8[i]);
if (state == KMSCON_UTF8_ACCEPT ||
state == KMSCON_UTF8_REJECT) {
ucs4 = kmscon_utf8_mach_get(vte->mach);
parse_data(vte, ucs4);
}
}
}
}
@ -994,6 +1044,7 @@ void kmscon_vte_handle_keyboard(struct kmscon_vte *vte,
const struct uterm_input_event *ev)
{
kmscon_symbol_t sym;
char val;
size_t len;
const char *u8;
@ -1403,10 +1454,26 @@ void kmscon_vte_handle_keyboard(struct kmscon_vte *vte,
}
if (ev->unicode != UTERM_INPUT_INVALID) {
sym = kmscon_symbol_make(ev->unicode);
u8 = kmscon_symbol_get_u8(sym, &len);
vte_write(vte, u8, len);
kmscon_symbol_free_u8(u8);
if (vte->flags & FLAG_7BIT_MODE) {
val = ev->unicode;
if (ev->unicode & 0x80) {
log_debug("invalid keyboard input in 7bit mode U+%x; mapping to '?'", ev->unicode);
val = '?';
}
vte_write(vte, &val, 1);
} else if (vte->flags & FLAG_8BIT_MODE) {
val = ev->unicode;
if (ev->unicode > 0xff) {
log_debug("invalid keyboard input in 8bit mode U+%x; mapping to '?'", ev->unicode);
val = '?';
}
vte_write_raw(vte, &val, 1);
} else {
sym = kmscon_symbol_make(ev->unicode);
u8 = kmscon_symbol_get_u8(sym, &len);
vte_write_raw(vte, u8, len);
kmscon_symbol_free_u8(u8);
}
return;
}
}