From 0965bee0865fd8ea129b2de62a3c50e09c59a184 Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 24 Jan 2001 14:08:20 +0000 Subject: [PATCH] Rethink the whole line discipline architecture. Instead of having multiple switchable line disciplines, we now have a single unified one which changes its behaviour based on option settings. Each option setting can be suggested by the back end and/or the terminal handler, and can be forcibly overridden by the configuration. Local echo and local line editing are separate, independently switchable, options. git-svn-id: svn://svn.tartarus.org/sgt/putty@895 cda61777-01e9-0310-a592-d414129be87e --- ldisc.c | 242 +++++++++++++++++++++++++++++++++---------------------------- plink.c | 32 +++++--- putty.h | 28 +++++-- raw.c | 12 +-- rlogin.c | 7 +- scp.c | 1 - settings.c | 6 +- ssh.c | 20 ++++- telnet.c | 30 ++++---- terminal.c | 44 ++++++----- windlg.c | 97 +++++++++++++++++++------ window.c | 25 ++----- 12 files changed, 328 insertions(+), 216 deletions(-) diff --git a/ldisc.c b/ldisc.c index 80659cca..1ab832dd 100644 --- a/ldisc.c +++ b/ldisc.c @@ -1,21 +1,27 @@ +/* + * 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 #include "putty.h" -/* - * ldisc.c: PuTTY line disciplines - */ +#define ECHOING (cfg.localecho == LD_YES || \ + (cfg.localecho == LD_BACKEND && \ + (back->ldisc(LD_ECHO) || term_ldisc(LD_ECHO)))) +#define EDITING (cfg.localedit == LD_YES || \ + (cfg.localedit == LD_BACKEND && \ + (back->ldisc(LD_EDIT) || term_ldisc(LD_EDIT)))) static void c_write (char *buf, int len) { from_backend(0, buf, len); } -static void c_write1 (char c) { - c_write(&c, 1); -} - static char *term_buf = NULL; static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0; @@ -32,7 +38,7 @@ static int plen(unsigned char c) { static void pwrite(unsigned char c) { if ((c >= 32 && c <= 126) || (c >= 160)) { - c_write1(c); + c_write(&c, 1); } else if (c < 128) { char cc[2]; cc[1] = (c == 127 ? '?' : c + 0x40); @@ -52,108 +58,124 @@ static void bsb(int n) { #define CTRL(x) (x^'@') -static void term_send(char *buf, int len) { - while (len--) { - char c; - c = *buf++; - switch (term_quotenext ? ' ' : c) { - /* - * ^h/^?: delete one char and output one BSB - * ^w: delete, and output BSBs, to return to last space/nonspace - * boundary - * ^u: delete, and output BSBs, to return to BOL - * ^c: Do a ^u then send a telnet IP - * ^z: Do a ^u then send a telnet SUSP - * ^\: Do a ^u then send a telnet ABORT - * ^r: echo "^R\n" and redraw line - * ^v: quote next char - * ^d: if at BOL, end of file and close connection, else send line - * and reset to BOL - * ^m: send line-plus-\r\n and reset to BOL - */ - case CTRL('H'): case CTRL('?'): /* backspace/delete */ - if (term_buflen > 0) { - bsb(plen(term_buf[term_buflen-1])); - term_buflen--; - } - break; - case CTRL('W'): /* delete word */ - while (term_buflen > 0) { - bsb(plen(term_buf[term_buflen-1])); - term_buflen--; - if (term_buflen > 0 && - isspace(term_buf[term_buflen-1]) && - !isspace(term_buf[term_buflen])) - break; - } - break; - case CTRL('U'): /* delete line */ - case CTRL('C'): /* Send IP */ - case CTRL('\\'): /* Quit */ - case CTRL('Z'): /* Suspend */ - while (term_buflen > 0) { - bsb(plen(term_buf[term_buflen-1])); - term_buflen--; - } - back->special (TS_EL); - if( c == CTRL('C') ) back->special (TS_IP); - if( c == CTRL('Z') ) back->special (TS_SUSP); - if( c == CTRL('\\') ) back->special (TS_ABORT); - break; - case CTRL('R'): /* redraw line */ - c_write("^R\r\n", 4); - { - int i; - for (i = 0; i < term_buflen; i++) - pwrite(term_buf[i]); - } - break; - case CTRL('V'): /* quote next char */ - term_quotenext = TRUE; - break; - case CTRL('D'): /* logout or send */ - if (term_buflen == 0) { - back->special (TS_EOF); - } else { - back->send(term_buf, term_buflen); - term_buflen = 0; - } - break; - case CTRL('M'): /* send with newline */ - if (term_buflen > 0) - back->send(term_buf, term_buflen); - if (cfg.protocol == PROT_RAW) - back->send("\r\n", 2); - else - back->send("\r", 1); - c_write("\r\n", 2); - term_buflen = 0; - break; - default: /* get to this label from ^V handler */ - if (term_buflen >= term_bufsiz) { - term_bufsiz = term_buflen + 256; - term_buf = saferealloc(term_buf, term_bufsiz); - } - term_buf[term_buflen++] = c; - pwrite(c); - term_quotenext = FALSE; - break; - } +void ldisc_send(char *buf, int len) { + /* + * Called with len=0 when the options change. We must inform + * the front end in case it needs to know. + */ + if (len == 0) { + void ldisc_update(int echo, int edit); + ldisc_update(ECHOING, EDITING); } -} - -static void simple_send(char *buf, int len) { - if( term_buflen != 0 ) - { - back->send(term_buf, term_buflen); - while (term_buflen > 0) { - bsb(plen(term_buf[term_buflen-1])); - term_buflen--; - } + /* + * Either perform local editing, or just send characters. + */ + if (EDITING) { + while (len--) { + char c; + c = *buf++; + switch (term_quotenext ? ' ' : c) { + /* + * ^h/^?: delete one char and output one BSB + * ^w: delete, and output BSBs, to return to last + * space/nonspace boundary + * ^u: delete, and output BSBs, to return to BOL + * ^c: Do a ^u then send a telnet IP + * ^z: Do a ^u then send a telnet SUSP + * ^\: Do a ^u then send a telnet ABORT + * ^r: echo "^R\n" and redraw line + * ^v: quote next char + * ^d: if at BOL, end of file and close connection, + * else send line and reset to BOL + * ^m: send line-plus-\r\n and reset to BOL + */ + case CTRL('H'): case CTRL('?'): /* backspace/delete */ + if (term_buflen > 0) { + if (ECHOING) + bsb(plen(term_buf[term_buflen-1])); + term_buflen--; + } + break; + case CTRL('W'): /* delete word */ + while (term_buflen > 0) { + if (ECHOING) + bsb(plen(term_buf[term_buflen-1])); + term_buflen--; + if (term_buflen > 0 && + isspace(term_buf[term_buflen-1]) && + !isspace(term_buf[term_buflen])) + break; + } + break; + case CTRL('U'): /* delete line */ + case CTRL('C'): /* Send IP */ + case CTRL('\\'): /* Quit */ + case CTRL('Z'): /* Suspend */ + while (term_buflen > 0) { + if (ECHOING) + bsb(plen(term_buf[term_buflen-1])); + term_buflen--; + } + back->special (TS_EL); + if( c == CTRL('C') ) back->special (TS_IP); + if( c == CTRL('Z') ) back->special (TS_SUSP); + if( c == CTRL('\\') ) back->special (TS_ABORT); + break; + case CTRL('R'): /* redraw line */ + if (ECHOING) { + int i; + c_write("^R\r\n", 4); + for (i = 0; i < term_buflen; i++) + pwrite(term_buf[i]); + } + break; + case CTRL('V'): /* quote next char */ + term_quotenext = TRUE; + break; + case CTRL('D'): /* logout or send */ + if (term_buflen == 0) { + back->special (TS_EOF); + } else { + back->send(term_buf, term_buflen); + term_buflen = 0; + } + break; + case CTRL('M'): /* send with newline */ + if (term_buflen > 0) + back->send(term_buf, term_buflen); + if (cfg.protocol == PROT_RAW) + back->send("\r\n", 2); + else + back->send("\r", 1); + if (ECHOING) + c_write("\r\n", 2); + term_buflen = 0; + break; + default: /* get to this label from ^V handler */ + if (term_buflen >= term_bufsiz) { + term_bufsiz = term_buflen + 256; + term_buf = saferealloc(term_buf, term_bufsiz); + } + term_buf[term_buflen++] = c; + if (ECHOING) + pwrite(c); + term_quotenext = FALSE; + break; + } + } + } else { + if( term_buflen != 0 ) + { + back->send(term_buf, term_buflen); + while (term_buflen > 0) { + bsb(plen(term_buf[term_buflen-1])); + term_buflen--; + } + } + if (len > 0) { + if (ECHOING) + c_write(buf, len); + back->send(buf, len); + } } - if (len > 0) - back->send(buf, len); } - -Ldisc ldisc_term = { term_send }; -Ldisc ldisc_simple = { simple_send }; diff --git a/plink.c b/plink.c index 61552fac..6cbc2f6d 100644 --- a/plink.c +++ b/plink.c @@ -119,18 +119,11 @@ void verify_ssh_host_key(char *host, int port, char *keytype, } } -HANDLE outhandle, errhandle; +HANDLE inhandle, outhandle, errhandle; DWORD orig_console_mode; WSAEVENT netevent; -void begin_session(void) { - if (!cfg.ldisc_term) - SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); - else - SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), orig_console_mode); -} - void from_backend(int is_stderr, char *data, int len) { int pos; DWORD ret; @@ -144,6 +137,23 @@ void from_backend(int is_stderr, char *data, int len) { } } +int term_ldisc(int mode) { return FALSE; } +void ldisc_update(int echo, int edit) { + /* Update stdin read mode to reflect changes in line discipline. */ + DWORD mode; + + mode = ENABLE_PROCESSED_INPUT; + if (echo) + mode = mode | ENABLE_ECHO_INPUT; + else + mode = mode &~ ENABLE_ECHO_INPUT; + if (edit) + mode = mode | ENABLE_LINE_INPUT; + else + mode = mode &~ ENABLE_LINE_INPUT; + SetConsoleMode(inhandle, mode); +} + struct input_data { DWORD len; char buffer[4096]; @@ -403,7 +413,6 @@ int main(int argc, char **argv) { len2 = strlen(cp); len -= len2; cp += len2; } cfg.nopty = TRUE; /* command => no terminal */ - cfg.ldisc_term = TRUE; /* use stdin like a line buffer */ break; /* done with cmdline */ } } @@ -475,10 +484,11 @@ int main(int argc, char **argv) { stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL); - GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &orig_console_mode); - SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); + inhandle = GetStdHandle(STD_INPUT_HANDLE); outhandle = GetStdHandle(STD_OUTPUT_HANDLE); errhandle = GetStdHandle(STD_ERROR_HANDLE); + GetConsoleMode(inhandle, &orig_console_mode); + SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT); /* * Turn off ECHO and LINE input modes. We don't care if this diff --git a/putty.h b/putty.h index 20eea8b3..5423ae91 100644 --- a/putty.h +++ b/putty.h @@ -115,6 +115,21 @@ typedef enum { VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN } VT_Mode; +enum { + /* + * Line discipline option states: off, on, up to the backend. + */ + LD_YES, LD_NO, LD_BACKEND +}; + +enum { + /* + * Line discipline options which the backend might try to control. + */ + LD_EDIT, /* local line editing */ + LD_ECHO /* local echo */ +}; + typedef struct { char *(*init) (char *host, int port, char **realhost); void (*send) (char *buf, int len); @@ -122,6 +137,7 @@ typedef struct { void (*special) (Telnet_Special code); Socket (*socket) (void); int (*sendok) (void); + int (*ldisc) (int); int default_port; } Backend; @@ -134,12 +150,6 @@ extern struct backend_list { } backends[]; typedef struct { - void (*send) (char *buf, int len); -} Ldisc; - -GLOBAL Ldisc *ldisc; - -typedef struct { /* Basic options */ char host[512]; int port; @@ -176,7 +186,8 @@ typedef struct { int alt_f4; /* is it special? */ int alt_space; /* is it special? */ int alt_only; /* is it special? */ - int ldisc_term; + int localecho; + int localedit; int alwaysontop; int scroll_on_key; int scroll_on_disp; @@ -333,6 +344,7 @@ void term_invalidate(void); void term_blink(int set_cursor); void term_paste(void); void term_nopaste(void); +int telnet_ldisc(int option); void from_backend(int is_stderr, char *data, int len); void logfopen (void); void logfclose (void); @@ -367,7 +379,7 @@ extern Backend ssh_backend; * Exports from ldisc.c. */ -extern Ldisc ldisc_term, ldisc_simple; +extern void ldisc_send(char *buf, int len); /* * Exports from sshrand.c. diff --git a/raw.c b/raw.c index c3545803..2abc0a8d 100644 --- a/raw.c +++ b/raw.c @@ -70,11 +70,6 @@ static char *raw_init (char *host, int port, char **realhost) { sk_addr_free(addr); - /* - * We have no pre-session phase. - */ - begin_session(); - return NULL; } @@ -109,6 +104,12 @@ static Socket raw_socket(void) { return s; } static int raw_sendok(void) { return 1; } +static int raw_ldisc(int option) { + if (option == LD_EDIT || option == LD_ECHO) + return 1; + return 0; +} + Backend raw_backend = { raw_init, raw_send, @@ -116,5 +117,6 @@ Backend raw_backend = { raw_special, raw_socket, raw_sendok, + raw_ldisc, 1 }; diff --git a/rlogin.c b/rlogin.c index c77f381f..20296a0b 100644 --- a/rlogin.c +++ b/rlogin.c @@ -102,8 +102,6 @@ static char *rlogin_init (char *host, int port, char **realhost) { sk_write(s, &z, 1); } - begin_session(); - return NULL; } @@ -142,6 +140,10 @@ static Socket rlogin_socket(void) { return s; } static int rlogin_sendok(void) { return 1; } +static int rlogin_ldisc(int option) { + return 0; +} + Backend rlogin_backend = { rlogin_init, rlogin_send, @@ -149,5 +151,6 @@ Backend rlogin_backend = { rlogin_special, rlogin_socket, rlogin_sendok, + rlogin_ldisc, 1 }; diff --git a/scp.c b/scp.c index 086c6be9..c13be5ec 100644 --- a/scp.c +++ b/scp.c @@ -74,7 +74,6 @@ static void send_str_msg(unsigned int msg_id, char *str); static void gui_update_stats(char *name, unsigned long size, int percentage, unsigned long elapsed); -void begin_session(void) { } void logevent(char *string) { } void verify_ssh_host_key(char *host, int port, char *keytype, diff --git a/settings.c b/settings.c index 0a7f2117..b8a7f79f 100644 --- a/settings.c +++ b/settings.c @@ -93,7 +93,8 @@ void save_settings (char *section, int do_host, Config *cfg) { write_setting_i (sesskey, "AltSpace", cfg->alt_space); write_setting_i (sesskey, "AltOnly", cfg->alt_only); write_setting_i (sesskey, "ComposeKey", cfg->compose_key); - write_setting_i (sesskey, "LdiscTerm", cfg->ldisc_term); + write_setting_i (sesskey, "LocalEcho", cfg->localecho); + write_setting_i (sesskey, "LocalEdit", cfg->localedit); write_setting_i (sesskey, "AlwaysOnTop", cfg->alwaysontop); write_setting_i (sesskey, "HideMousePtr", cfg->hide_mouseptr); write_setting_i (sesskey, "CurType", cfg->cursor_type); @@ -237,7 +238,8 @@ void load_settings (char *section, int do_host, Config *cfg) { gppi (sesskey, "AltSpace", 0, &cfg->alt_space); gppi (sesskey, "AltOnly", 0, &cfg->alt_only); gppi (sesskey, "ComposeKey", 0, &cfg->compose_key); - gppi (sesskey, "LdiscTerm", 0, &cfg->ldisc_term); + gppi (sesskey, "LocalEcho", LD_BACKEND, &cfg->localecho); + gppi (sesskey, "LocalEdit", LD_BACKEND, &cfg->localedit); gppi (sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop); gppi (sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr); gppi (sesskey, "CurType", 0, &cfg->cursor_type); diff --git a/ssh.c b/ssh.c index 3845264c..44075a7e 100644 --- a/ssh.c +++ b/ssh.c @@ -272,6 +272,7 @@ int (*ssh_get_password)(const char *prompt, char *str, int maxlen) = NULL; static char *savedhost; static int savedport; static int ssh_send_ok; +static int ssh_echoing, ssh_editing; static tree234 *ssh_channels; /* indexed by local id */ static struct ssh_channel *mainchan; /* primary session channel */ @@ -1840,8 +1841,11 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { crReturnV; } else if (pktin.type == SSH1_SMSG_FAILURE) { c_write("Server refused to allocate pty\r\n", 32); + ssh_editing = ssh_echoing = 1; } logevent("Allocated pty"); + } else { + ssh_editing = ssh_echoing = 1; } if (cfg.compression) { @@ -1871,9 +1875,9 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { if (eof_needed) ssh_special(TS_EOF); + ldisc_send(NULL, 0); /* cause ldisc to notice changes */ ssh_send_ok = 1; ssh_channels = newtree234(ssh_channelcmp); - begin_session(); while (1) { crReturnV; if (ispkt) { @@ -2743,9 +2747,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) crReturnV; } c_write("Server refused to allocate pty\r\n", 32); + ssh_editing = ssh_echoing = 1; } else { logevent("Allocated pty"); } + } else { + ssh_editing = ssh_echoing = 1; } /* @@ -2793,8 +2800,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) /* * Transfer data! */ + ldisc_send(NULL, 0); /* cause ldisc to notice changes */ ssh_send_ok = 1; - begin_session(); while (1) { static int try_send; crReturnV; @@ -3005,6 +3012,8 @@ static char *ssh_init (char *host, int port, char **realhost) { #endif ssh_send_ok = 0; + ssh_editing = 0; + ssh_echoing = 0; p = connect_to_host(host, port, realhost); if (p != NULL) @@ -3100,6 +3109,12 @@ static Socket ssh_socket(void) { return s; } static int ssh_sendok(void) { return ssh_send_ok; } +static int ssh_ldisc(int option) { + if (option == LD_ECHO) return ssh_echoing; + if (option == LD_EDIT) return ssh_editing; + return FALSE; +} + Backend ssh_backend = { ssh_init, ssh_send, @@ -3107,5 +3122,6 @@ Backend ssh_backend = { ssh_special, ssh_socket, ssh_sendok, + ssh_ldisc, 22 }; diff --git a/telnet.c b/telnet.c index f30902d2..3e65cd41 100644 --- a/telnet.c +++ b/telnet.c @@ -132,6 +132,8 @@ static struct Opt *opts[] = { &o_we_sga, &o_they_sga, NULL }; +static int echoing = TRUE, editing = TRUE; + static int in_synch; static int sb_opt, sb_len; static char *sb_buf = NULL; @@ -170,8 +172,11 @@ static void deactivate_option (struct Opt *o) { * Generate side effects of enabling or disabling an option. */ static void option_side_effects(struct Opt *o, int enabled) { - if (o->option == TELOPT_ECHO && cfg.ldisc_term) - ldisc = enabled ? &ldisc_simple : &ldisc_term; + if (o->option == TELOPT_ECHO && o->send == DO) + echoing = !enabled; + else if (o->option = TELOPT_SGA && o->send == DO) + editing = !enabled; + ldisc_send(NULL, 0); /* cause ldisc to notice the change */ } static void activate_option (struct Opt *o) { @@ -509,15 +514,6 @@ static char *telnet_init (char *host, int port, char **realhost) { /* * Initialise option states. */ - if( cfg.ldisc_term ) - { - struct Opt **o; - - for (o = opts; *o; o++) - if ((*o)->state == REQUESTED) - (*o)->state = INACTIVE; - } - else { struct Opt **o; @@ -531,11 +527,6 @@ static char *telnet_init (char *host, int port, char **realhost) { */ in_synch = FALSE; - /* - * We have no pre-session phase. - */ - begin_session(); - return NULL; } @@ -638,6 +629,12 @@ static Socket telnet_socket(void) { return s; } static int telnet_sendok(void) { return 1; } +static int telnet_ldisc(int option) { + if (option == LD_ECHO) return echoing; + if (option == LD_EDIT) return editing; + return FALSE; +} + Backend telnet_backend = { telnet_init, telnet_send, @@ -645,5 +642,6 @@ Backend telnet_backend = { telnet_special, telnet_socket, telnet_sendok, + telnet_ldisc, 23 }; diff --git a/terminal.c b/terminal.c index 49c0ddf9..0f4504ac 100644 --- a/terminal.c +++ b/terminal.c @@ -78,6 +78,8 @@ static int use_bce; /* Use Background coloured erase */ static int blinker; /* When blinking is the cursor on ? */ static int tblinker; /* When the blinking text is on */ static int blink_is_real; /* Actually blink blinking text */ +static int term_echoing; /* Does terminal want local echo? */ +static int term_editing; /* Does terminal want local edit? */ static unsigned long cset_attr[2]; @@ -183,6 +185,8 @@ static void power_on(void) { rvideo = 0; cursor_on = 1; save_attr = curr_attr = ATTR_DEFAULT; + term_editing = term_echoing = FALSE; + ldisc_send(NULL, 0); /* cause ldisc to notice changes */ app_cursor_keys = cfg.app_cursor; app_keypad_keys = cfg.app_keypad; use_bce = cfg.bce; @@ -707,6 +711,10 @@ static void toggle_mode (int mode, int query, int state) { case 8: /* auto key repeat */ repeat_off = !state; break; + case 10: /* set local edit mode */ + term_editing = state; + ldisc_send(NULL, 0); /* cause ldisc to notice changes */ + break; case 25: /* enable/disable cursor */ compatibility2(OTHER,VT220); cursor_on = state; @@ -724,16 +732,8 @@ static void toggle_mode (int mode, int query, int state) { insert = state; break; case 12: /* set echo mode */ - /* - * This may be very good in smcup and rmcup (or smkx & rmkx) if you - * have a long RTT and the telnet client/daemon doesn't understand - * linemode. - * - * DONT send TS_RECHO/TS_LECHO; the telnet daemon tries to fix the - * tty and _really_ confuses some programs. - */ - compatibility2(OTHER,VT220); - ldisc = (state? &ldisc_simple : &ldisc_term); + term_echoing = !state; + ldisc_send(NULL, 0); /* cause ldisc to notice changes */ break; case 20: /* Return sends ... */ cr_lf_return = state; @@ -804,7 +804,7 @@ static int beep_overload = 0; * An xterm returns "xterm" (5 characters) */ compatibility(OTHER); - ldisc->send ("PuTTY", 5); + ldisc_send ("PuTTY", 5); break; case '\007': beep_count++; @@ -1056,7 +1056,7 @@ static int beep_overload = 0; break; case 'Z': /* terminal type query */ compatibility(VT100); - ldisc->send (id_string, strlen(id_string)); + ldisc_send (id_string, strlen(id_string)); break; case 'c': /* restore power-on settings */ compatibility(VT100); @@ -1199,16 +1199,16 @@ static int beep_overload = 0; case 'c': /* terminal type query */ compatibility(VT100); /* This is the response for a VT102 */ - ldisc->send (id_string, strlen(id_string)); + ldisc_send (id_string, strlen(id_string)); break; case 'n': /* cursor position query */ if (esc_args[0] == 6) { char buf[32]; sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1); - ldisc->send (buf, strlen(buf)); + ldisc_send (buf, strlen(buf)); } else if (esc_args[0] == 5) { - ldisc->send ("\033[0n", 4); + ldisc_send ("\033[0n", 4); } break; case 'h': /* toggle modes to high */ @@ -1420,7 +1420,7 @@ static int beep_overload = 0; if (i == 0 || i == 1) { strcpy (buf, "\033[2;1;1;112;112;1;0x"); buf[2] += i; - ldisc->send (buf, 20); + ldisc_send (buf, 20); } } break; @@ -1692,7 +1692,7 @@ static int beep_overload = 0; termstate = VT52_Y1; break; case 'Z': - ldisc->send ("\033/Z", 3); + ldisc_send ("\033/Z", 3); break; case '=': app_keypad_keys = TRUE; @@ -2162,7 +2162,7 @@ void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) { /* Assume a small paste will be OK in one go. */ if (paste_len<256) { - ldisc->send (paste_buffer, paste_len); + ldisc_send (paste_buffer, paste_len); if (paste_buffer) sfree(paste_buffer); paste_buffer = 0; paste_pos = paste_hold = paste_len = 0; @@ -2203,7 +2203,7 @@ void term_paste() { if (paste_buffer[paste_pos + n++] == '\r') break; } - ldisc->send (paste_buffer+paste_pos, n); + ldisc_send (paste_buffer+paste_pos, n); paste_pos += n; if (paste_pos < paste_len) { @@ -2226,6 +2226,12 @@ void term_deselect (void) { term_update(); } +int term_ldisc(int option) { + if (option == LD_ECHO) return term_echoing; + if (option == LD_EDIT) return term_editing; + return FALSE; +} + /* * from_backend(), to get data from the backend for the terminal. */ diff --git a/windlg.c b/windlg.c index 629e6892..12bff5d9 100644 --- a/windlg.c +++ b/windlg.c @@ -200,6 +200,17 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue, IDC_CLOSEEXIT, sessionpanelend, + loggingpanelstart, + IDC_BOX_LOGGING1, + IDC_LSTATSTATIC, + IDC_LSTATOFF, + IDC_LSTATASCII, + IDC_LSTATRAW, + IDC_LGFSTATIC, + IDC_LGFEDIT, + IDC_LGFBUTTON, + loggingpanelend, + keyboardpanelstart, IDC_TITLE_KEYBOARD, IDC_BOX_KEYBOARD1, @@ -238,14 +249,14 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue, IDC_BEEP, IDC_BCE, IDC_BLINKTEXT, - IDC_LDISCTERM, - IDC_LSTATSTATIC, - IDC_LSTATOFF, - IDC_LSTATASCII, - IDC_LSTATRAW, - IDC_LGFSTATIC, - IDC_LGFEDIT, - IDC_LGFBUTTON, + IDC_ECHOSTATIC, + IDC_ECHOBACKEND, + IDC_ECHOYES, + IDC_ECHONO, + IDC_EDITSTATIC, + IDC_EDITBACKEND, + IDC_EDITYES, + IDC_EDITNO, terminalpanelend, windowpanelstart, @@ -489,7 +500,12 @@ static void init_dlg_ctrls(HWND hwnd) { CheckDlgButton (hwnd, IDC_ALTSPACE, cfg.alt_space); CheckDlgButton (hwnd, IDC_ALTONLY, cfg.alt_only); CheckDlgButton (hwnd, IDC_COMPOSEKEY, cfg.compose_key); - CheckDlgButton (hwnd, IDC_LDISCTERM, cfg.ldisc_term); + CheckRadioButton (hwnd, IDC_ECHOBACKEND, IDC_ECHONO, + cfg.localecho == LD_BACKEND ? IDC_ECHOBACKEND: + cfg.localecho == LD_YES ? IDC_ECHOYES : IDC_ECHONO); + CheckRadioButton (hwnd, IDC_EDITBACKEND, IDC_EDITNO, + cfg.localedit == LD_BACKEND ? IDC_EDITBACKEND: + cfg.localedit == LD_YES ? IDC_EDITYES : IDC_EDITNO); CheckDlgButton (hwnd, IDC_ALWAYSONTOP, cfg.alwaysontop); CheckDlgButton (hwnd, IDC_SCROLLKEY, cfg.scroll_on_key); CheckDlgButton (hwnd, IDC_SCROLLDISP, cfg.scroll_on_disp); @@ -681,8 +697,26 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) { endbox(&cp); } + if (panel == loggingpanelstart) { + /* The Logging panel. Accelerators used: [acgo] lpt */ + struct ctlpos cp; + ctlposinit(&cp, hwnd, 80, 3, 13); + bartitle(&cp, "Options controlling session logging", + IDC_TITLE_TERMINAL); + beginbox(&cp, NULL, IDC_BOX_LOGGING1); + radiobig(&cp, + "Session logging:", IDC_LSTATSTATIC, + "Logging &turned off completely", IDC_LSTATOFF, + "Log &printable output only", IDC_LSTATASCII, + "&Log all session output", IDC_LSTATRAW, NULL); + editbutton(&cp, "Log &file name:", + IDC_LGFSTATIC, IDC_LGFEDIT, "Bro&wse...", + IDC_LGFBUTTON); + endbox(&cp); + } + if (panel == terminalpanelstart) { - /* The Terminal panel. Accelerators used: [acgo] &dflbenuw */ + /* The Terminal panel. Accelerators used: [acgo] &dflbentuw */ struct ctlpos cp; ctlposinit(&cp, hwnd, 80, 3, 13); bartitle(&cp, "Options controlling the terminal emulation", @@ -695,19 +729,18 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) { checkbox(&cp, "&Beep enabled", IDC_BEEP); checkbox(&cp, "Use background colour to &erase screen", IDC_BCE); checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT); - checkbox(&cp, "&Use local terminal line discipline", IDC_LDISCTERM); endbox(&cp); - beginbox(&cp, "Control session logging", + beginbox(&cp, "Line discipline options", IDC_BOX_TERMINAL2); - radiobig(&cp, - "Session logging:", IDC_LSTATSTATIC, - "Logging turned &off completely", IDC_LSTATOFF, - "Log printable output only", IDC_LSTATASCII, - "Log all session output", IDC_LSTATRAW, NULL); - editbutton(&cp, "Log &file name:", - IDC_LGFSTATIC, IDC_LGFEDIT, "Bro&wse...", - IDC_LGFBUTTON); + radioline(&cp, "Local echo:", IDC_ECHOSTATIC, 3, + "A&uto", IDC_ECHOBACKEND, + "Force on", IDC_ECHOYES, + "Force off", IDC_ECHONO, NULL); + radioline(&cp, "Local line editing:", IDC_EDITSTATIC, 3, + "Au&to", IDC_EDITBACKEND, + "Force on", IDC_EDITYES, + "Force off", IDC_EDITNO, NULL); endbox(&cp); } @@ -1081,6 +1114,7 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, * Set up the tree view contents. */ hsession = treeview_insert(&tvfaff, 0, "Session"); + treeview_insert(&tvfaff, 1, "Logging"); treeview_insert(&tvfaff, 0, "Terminal"); treeview_insert(&tvfaff, 1, "Keyboard"); treeview_insert(&tvfaff, 0, "Window"); @@ -1144,6 +1178,8 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, } if (!strcmp(buffer, "Session")) create_controls(hwnd, dlgtype, sessionpanelstart); + if (!strcmp(buffer, "Logging")) + create_controls(hwnd, dlgtype, loggingpanelstart); if (!strcmp(buffer, "Keyboard")) create_controls(hwnd, dlgtype, keyboardpanelstart); if (!strcmp(buffer, "Terminal")) @@ -1398,10 +1434,25 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, HIWORD(wParam) == BN_DOUBLECLICKED) cfg.alt_only = IsDlgButtonChecked (hwnd, IDC_ALTONLY); break; - case IDC_LDISCTERM: + case IDC_ECHOBACKEND: + case IDC_ECHOYES: + case IDC_ECHONO: if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) - cfg.ldisc_term = IsDlgButtonChecked (hwnd, IDC_LDISCTERM); + HIWORD(wParam) == BN_DOUBLECLICKED) { + if (LOWORD(wParam)==IDC_ECHOBACKEND) cfg.localecho=LD_BACKEND; + if (LOWORD(wParam)==IDC_ECHOYES) cfg.localecho=LD_YES; + if (LOWORD(wParam)==IDC_ECHONO) cfg.localecho=LD_NO; + } + break; + case IDC_EDITBACKEND: + case IDC_EDITYES: + case IDC_EDITNO: + if (HIWORD(wParam) == BN_CLICKED || + HIWORD(wParam) == BN_DOUBLECLICKED) { + if (LOWORD(wParam)==IDC_EDITBACKEND) cfg.localedit=LD_BACKEND; + if (LOWORD(wParam)==IDC_EDITYES) cfg.localedit=LD_YES; + if (LOWORD(wParam)==IDC_EDITNO) cfg.localedit=LD_NO; + } break; case IDC_ALWAYSONTOP: if (HIWORD(wParam) == BN_CLICKED || diff --git a/window.c b/window.c index cd4d5391..cef6825f 100644 --- a/window.c +++ b/window.c @@ -101,13 +101,10 @@ static Mouse_Button lastbtn; static char *window_name, *icon_name; -static Ldisc *real_ldisc; - static int compose_state = 0; -void begin_session(void) { - ldisc = real_ldisc; -} +/* Dummy routine, only required in plink. */ +void ldisc_update(int echo, int edit) {} int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { static char appname[] = "PuTTY"; @@ -318,11 +315,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { return 1; } - real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple); - /* To start with, we use the simple line discipline, so we can - * type passwords etc without fear of them being echoed... */ - ldisc = &ldisc_simple; - if (!prev) { wndclass.style = 0; wndclass.lpfnWndProc = WndProc; @@ -1220,11 +1212,10 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, init_fonts(0); sfree(logpal); /* - * Telnet will change local echo -> remote if the - * remote asks. + * Flush the line discipline's edit buffer in the + * case where local editing has just been disabled. */ - if (cfg.protocol != PROT_TELNET) - ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple); + ldisc_send(NULL, 0); if (pal) DeleteObject(pal); logpal = NULL; @@ -1615,7 +1606,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, len = TranslateKey (message, wParam, lParam, buf); if (len == -1) return DefWindowProc (hwnd, message, wParam, lParam); - ldisc->send (buf, len); + ldisc_send (buf, len); if (len > 0) show_mouseptr(0); @@ -1628,7 +1619,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, buf[1] = wParam; buf[0] = wParam >> 8; - ldisc->send (buf, 2); + ldisc_send (buf, 2); } case WM_CHAR: case WM_SYSCHAR: @@ -1640,7 +1631,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, */ { char c = xlat_kbd2tty((unsigned char)wParam); - ldisc->send (&c, 1); + ldisc_send (&c, 1); } return 0; } -- 2.11.0