diff --git a/Recipe b/Recipe index 94d4a566..77eb2aef 100644 --- a/Recipe +++ b/Recipe @@ -239,7 +239,7 @@ CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 CONF = conf marshal # Terminal emulator and its (platform-independent) dependencies. -TERMINAL = terminal stripctrl wcwidth ldiscucs logging tree234 minibidi +TERMINAL = terminal stripctrl wcwidth logging tree234 minibidi + config dialog CONF # GUI front end and terminal emulator (putty, puttytel). diff --git a/ldiscucs.c b/ldiscucs.c deleted file mode 100644 index d7a0e269..00000000 --- a/ldiscucs.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * ldisc.c: PuTTY line discipline. Sits between the input coming - * from keypresses in the window, and the output channel leading to - * the back end. Implements echo and/or local line editing, - * depending on what's currently configured. - */ - -#include -#include - -#include "putty.h" -#include "terminal.h" -#include "ldisc.h" - -void lpage_send(Ldisc *ldisc, - int codepage, const char *buf, int len, bool interactive) -{ - wchar_t *widebuffer = 0; - int widesize = 0; - int wclen; - - if (codepage < 0) { - ldisc_send(ldisc, buf, len, interactive); - return; - } - - widesize = len * 2; - widebuffer = snewn(widesize, wchar_t); - - wclen = mb_to_wc(codepage, 0, buf, len, widebuffer, widesize); - luni_send(ldisc, widebuffer, wclen, interactive); - - sfree(widebuffer); -} - -void luni_send(Ldisc *ldisc, const wchar_t *widebuf, int len, bool interactive) -{ - int ratio = (in_utf(ldisc->term))?3:1; - char *linebuffer; - int linesize; - int i; - char *p; - - linesize = len * ratio * 2; - linebuffer = snewn(linesize, char); - - if (in_utf(ldisc->term)) { - /* UTF is a simple algorithm */ - for (p = linebuffer, i = 0; i < len; i++) { - unsigned long ch = widebuf[i]; - - if (IS_SURROGATE(ch)) { -#ifdef PLATFORM_IS_UTF16 - if (i+1 < len) { - unsigned long ch2 = widebuf[i+1]; - if (IS_SURROGATE_PAIR(ch, ch2)) { - ch = FROM_SURROGATES(ch, ch2); - i++; - } - } else -#endif - { - /* Unrecognised UTF-16 sequence */ - ch = '.'; - } - } - - p += encode_utf8(p, ch); - } - } else { - int rv; - rv = wc_to_mb(ldisc->term->ucsdata->line_codepage, 0, widebuf, len, - linebuffer, linesize, NULL, ldisc->term->ucsdata); - if (rv >= 0) - p = linebuffer + rv; - else - p = linebuffer; - } - if (p > linebuffer) - ldisc_send(ldisc, linebuffer, p - linebuffer, interactive); - - sfree(linebuffer); -} diff --git a/putty.h b/putty.h index ff2e6a1c..89ead30f 100644 --- a/putty.h +++ b/putty.h @@ -1643,6 +1643,8 @@ void term_set_focus(Terminal *term, bool has_focus); char *term_get_ttymode(Terminal *term, const char *mode); int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input); void term_set_trust_status(Terminal *term, bool trusted); +void term_keyinput(Terminal *, int codepage, const void *buf, int len); +void term_keyinputw(Terminal *, const wchar_t * widebuf, int len); typedef enum SmallKeypadKey { SKK_HOME, SKK_END, SKK_INSERT, SKK_DELETE, SKK_PGUP, SKK_PGDN, @@ -1779,13 +1781,6 @@ void ldisc_free(Ldisc *); void ldisc_send(Ldisc *, const void *buf, int len, bool interactive); void ldisc_echoedit_update(Ldisc *); -/* - * Exports from ldiscucs.c. - */ -void lpage_send(Ldisc *, int codepage, const char *buf, int len, - bool interactive); -void luni_send(Ldisc *, const wchar_t * widebuf, int len, bool interactive); - /* * Exports from sshrand.c. */ diff --git a/terminal.c b/terminal.c index d64c15bd..3f102bcd 100644 --- a/terminal.c +++ b/terminal.c @@ -114,6 +114,7 @@ static void deselect(Terminal *); static void term_print_finish(Terminal *); static void scroll(Terminal *, int, int, int, bool); static void parse_optionalrgb(optionalrgb *out, unsigned *values); +static void term_added_data(Terminal *term); static termline *newtermline(Terminal *term, int cols, bool bce) { @@ -2955,6 +2956,84 @@ static void term_display_graphic_char(Terminal *term, unsigned long c) seen_disp_event(term); } +static strbuf *term_input_data_from_unicode( + Terminal *term, const wchar_t *widebuf, int len) +{ + strbuf *buf = strbuf_new(); + + if (in_utf(term)) { + /* + * Translate input wide characters into UTF-8 to go in the + * terminal's input data queue. + */ + for (int i = 0; i < len; i++) { + unsigned long ch = widebuf[i]; + + if (IS_SURROGATE(ch)) { +#ifdef PLATFORM_IS_UTF16 + if (i+1 < len) { + unsigned long ch2 = widebuf[i+1]; + if (IS_SURROGATE_PAIR(ch, ch2)) { + ch = FROM_SURROGATES(ch, ch2); + i++; + } + } else +#endif + { + /* Unrecognised UTF-16 sequence */ + ch = '.'; + } + } + + char utf8_chr[6]; + put_data(buf, utf8_chr, encode_utf8(utf8_chr, ch)); + } + } else { + /* + * Call to the character-set subsystem to translate into + * whatever charset the terminal is currently configured in. + * + * Since the terminal doesn't currently support any multibyte + * character set other than UTF-8, we can assume here that + * there will be at most one output byte per input wchar_t. + */ + char *bufptr = strbuf_append(buf, len); + int rv; + rv = wc_to_mb(term->ucsdata->line_codepage, 0, widebuf, len, + bufptr, len, NULL, term->ucsdata); + buf->len = rv < 0 ? 0 : rv; + } + + return buf; +} + +static strbuf *term_input_data_from_charset( + Terminal *term, int codepage, const char *str, int len) +{ + strbuf *buf; + + if (codepage < 0) { + buf = strbuf_new(); + put_data(buf, str, len); + } else { + int widesize = len * 2; /* allow for UTF-16 surrogates */ + wchar_t *widebuf = snewn(widesize, wchar_t); + int widelen = mb_to_wc(codepage, 0, str, len, widebuf, widesize); + buf = term_input_data_from_unicode(term, widebuf, widelen); + sfree(widebuf); + } + + return buf; +} + +static inline void term_keyinput_internal( + Terminal *term, const void *buf, int len, bool interactive) +{ + if (term->ldisc) + ldisc_send(term->ldisc, buf, len, interactive); + term_seen_key_event(term); +} + unsigned long term_translate( Terminal *term, struct term_utf8_decode *utf8, unsigned char c) { @@ -3227,8 +3306,11 @@ static void term_out(Terminal *term) */ compatibility(ANSIMIN); if (term->ldisc) { - lpage_send(term->ldisc, DEFAULT_CODEPAGE, - term->answerback, term->answerbacklen, false); + strbuf *buf = term_input_data_from_charset( + term, DEFAULT_CODEPAGE, + term->answerback, term->answerbacklen); + ldisc_send(term->ldisc, buf->s, buf->len, false); + strbuf_free(buf); } break; case '\007': /* BEL: Bell */ @@ -6231,9 +6313,12 @@ static void term_paste_callback(void *vterm) if (term->paste_buffer[term->paste_pos + n++] == '\015') break; } - if (term->ldisc) - luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, - false); + if (term->ldisc) { + strbuf *buf = term_input_data_from_unicode( + term, term->paste_buffer + term->paste_pos, n); + term_keyinput_internal(term, buf->s, buf->len, false); + strbuf_free(buf); + } term->paste_pos += n; if (term->paste_pos < term->paste_len) { @@ -6336,8 +6421,12 @@ void term_do_paste(Terminal *term, const wchar_t *data, int len) /* Assume a small paste will be OK in one go. */ if (term->paste_len < 256) { - if (term->ldisc) - luni_send(term->ldisc, term->paste_buffer, term->paste_len, false); + if (term->ldisc) { + strbuf *buf = term_input_data_from_unicode( + term, term->paste_buffer, term->paste_len); + term_keyinput_internal(term, buf->s, buf->len, false); + strbuf_free(buf); + } if (term->paste_buffer) sfree(term->paste_buffer); term->paste_buffer = 0; @@ -6840,6 +6929,33 @@ int format_numeric_keypad_key(char *buf, Terminal *term, char key, return p - buf; } +void term_keyinputw(Terminal *term, const wchar_t *widebuf, int len) +{ + strbuf *buf = term_input_data_from_unicode(term, widebuf, len); + if (buf->len) + term_keyinput_internal(term, buf->s, buf->len, true); + strbuf_free(buf); +} + +void term_keyinput(Terminal *term, int codepage, const void *str, int len) +{ + if (codepage < 0 || codepage == term->ucsdata->line_codepage) { + /* + * This text needs no translation, either because it's already + * in the right character set, or because we got the special + * codepage value -1 from our caller which means 'this data + * should be charset-agnostic, just send it raw' (for really + * simple things like control characters). + */ + term_keyinput_internal(term, str, len, true); + } else { + strbuf *buf = term_input_data_from_charset(term, codepage, str, len); + if (buf->len) + term_keyinput_internal(term, buf->s, buf->len, true); + strbuf_free(buf); + } +} + void term_nopaste(Terminal *term) { if (term->paste_len == 0) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 15598bf0..ef781c44 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -1932,8 +1932,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) */ output[end] = '\0'; /* NUL-terminate */ generated_something = true; - if (inst->ldisc) - ldisc_send(inst->ldisc, output+start, -2, true); + term_keyinput(inst->term, -1, output+start, -2); } else if (!inst->direct_to_font) { if (!use_ucsoutput) { #ifdef KEY_EVENT_DIAGNOSTICS @@ -1952,9 +1951,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) sfree(string_string); #endif generated_something = true; - if (inst->ldisc) - lpage_send(inst->ldisc, output_charset, output+start, - end-start, true); + term_keyinput(inst->term, output_charset, + output+start, end-start); } else { #ifdef KEY_EVENT_DIAGNOSTICS char *string_string = dupstr(""); @@ -1977,8 +1975,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * keysym, so use that instead. */ generated_something = true; - if (inst->ldisc) - luni_send(inst->ldisc, ucsoutput+start, end-start, true); + term_keyinputw(inst->term, ucsoutput+start, end-start); } } else { /* @@ -2001,12 +1998,10 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) sfree(string_string); #endif generated_something = true; - if (inst->ldisc) - ldisc_send(inst->ldisc, output+start, end-start, true); + term_keyinput(inst->term, -1, output+start, end-start); } show_mouseptr(inst, false); - term_seen_key_event(inst->term); } if (generated_something) @@ -2034,10 +2029,8 @@ void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data) sfree(string_string); #endif - if (inst->ldisc) - lpage_send(inst->ldisc, CS_UTF8, str, strlen(str), true); + term_keyinput(inst->term, CS_UTF8, str, strlen(str)); show_mouseptr(inst, false); - term_seen_key_event(inst->term); key_pressed(inst); } #endif diff --git a/windows/window.c b/windows/window.c index 2c387098..26ef3bbf 100644 --- a/windows/window.c +++ b/windows/window.c @@ -3245,9 +3245,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * messages. We _have_ to buffer everything * we're sent. */ - term_seen_key_event(term); - if (ldisc) - ldisc_send(ldisc, buf, len, true); + term_keyinput(term, -1, buf, len); show_mouseptr(false); } } @@ -3289,10 +3287,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, /* * Jaeyoun Chung reports that Korean character * input doesn't work correctly if we do a single - * luni_send() covering the whole of buff. So - * instead we luni_send the characters one by one. + * term_keyinputw covering the whole of buff. So + * instead we send the characters one by one. */ - term_seen_key_event(term); /* don't divide SURROGATE PAIR */ if (ldisc) { for (i = 0; i < n; i += 2) { @@ -3300,13 +3297,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (IS_HIGH_SURROGATE(hs) && i+2 < n) { WCHAR ls = *(unsigned short *)(buff+i+2); if (IS_LOW_SURROGATE(ls)) { - luni_send(ldisc, (unsigned short *)(buff+i), - 2, true); + term_keyinputw( + term, (unsigned short *)(buff+i), 2); i += 2; continue; } } - luni_send(ldisc, (unsigned short *)(buff+i), 1, true); + term_keyinputw( + term, (unsigned short *)(buff+i), 1); } } free(buff); @@ -3321,14 +3319,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, buf[1] = wParam; buf[0] = wParam >> 8; - term_seen_key_event(term); - if (ldisc) - lpage_send(ldisc, kbd_codepage, buf, 2, true); + term_keyinput(term, kbd_codepage, buf, 2); } else { char c = (unsigned char) wParam; term_seen_key_event(term); - if (ldisc) - lpage_send(ldisc, kbd_codepage, &c, 1, true); + term_keyinput(term, kbd_codepage, &c, 1); } return (0); case WM_CHAR: @@ -3349,11 +3344,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, wchar_t pair[2]; pair[0] = pending_surrogate; pair[1] = c; - term_seen_key_event(term); - luni_send(ldisc, pair, 2, true); + term_keyinputw(term, pair, 2); } else if (!IS_SURROGATE(c)) { - term_seen_key_event(term); - luni_send(ldisc, &c, 1, true); + term_keyinputw(term, &c, 1); } } return 0; @@ -4691,9 +4684,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return 0; } keybuf = nc; - term_seen_key_event(term); - if (ldisc) - luni_send(ldisc, &keybuf, 1, true); + term_keyinputw(term, &keybuf, 1); continue; } @@ -4703,9 +4694,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (alt_sum) { if (in_utf(term) || ucsdata.dbcs_screenfont) { keybuf = alt_sum; - term_seen_key_event(term); - if (ldisc) - luni_send(ldisc, &keybuf, 1, true); + term_keyinputw(term, &keybuf, 1); } else { char ch = (char) alt_sum; /* @@ -4717,33 +4706,23 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * messages. We _have_ to buffer * everything we're sent. */ - term_seen_key_event(term); - if (ldisc) - ldisc_send(ldisc, &ch, 1, true); + term_keyinput(term, -1, &ch, 1); } alt_sum = 0; } else { - term_seen_key_event(term); - if (ldisc) - luni_send(ldisc, &wch, 1, true); + term_keyinputw(term, &wch, 1); } } else { if(capsOn && wch < 0x80) { WCHAR cbuf[2]; cbuf[0] = 27; cbuf[1] = xlat_uskbd2cyrllic(wch); - term_seen_key_event(term); - if (ldisc) - luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, - true); + term_keyinputw(term, cbuf+!left_alt, 1+!!left_alt); } else { WCHAR cbuf[2]; cbuf[0] = '\033'; cbuf[1] = wch; - term_seen_key_event(term); - if (ldisc) - luni_send(ldisc, cbuf +!left_alt, 1+!!left_alt, - true); + term_keyinputw(term, cbuf +!left_alt, 1+!!left_alt); } } show_mouseptr(false);