X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/979f69871f000309e798393dc8964ba9782b9906..a9c0245444b7b67e077826089b026d46297fab64:/terminal.c diff --git a/terminal.c b/terminal.c index f0dc9b11..ab97e3c4 100644 --- a/terminal.c +++ b/terminal.c @@ -201,6 +201,7 @@ static void deselect(void); /* log session to file stuff ... */ static FILE *lgfp = NULL; static void logtraffic(unsigned char c, int logmode); +static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm); /* * Resize a line to make it `cols' columns wide. @@ -496,6 +497,7 @@ void term_size(int newrows, int newcols, int newsavelines) update_sbar(); term_update(); + back->size(); } /* @@ -586,7 +588,7 @@ static void check_selection(pos from, pos to) static void scroll(int topline, int botline, int lines, int sb) { unsigned long *line, *line2; - int i; + int i, seltop; if (topline != 0 || alt_which != 0) sb = FALSE; @@ -636,6 +638,23 @@ static void scroll(int topline, int botline, int lines, int sb) } addpos234(scrollback, line, sblen); line = line2; + + /* + * If the user is currently looking at part of the + * scrollback, and they haven't enabled any options + * that are going to reset the scrollback as a + * result of this movement, then the chances are + * they'd like to keep looking at the same line. So + * we move their viewpoint at the same rate as the + * scroll, at least until their viewpoint hits the + * top end of the scrollback buffer, at which point + * we don't have the choice any more. + * + * Thanks to Jan Holmen Holsten for the idea and + * initial implementation. + */ + if (disptop > -savelines && disptop < 0) + disptop--; } line = resizeline(line, cols); for (i = 0; i < cols; i++) @@ -643,17 +662,26 @@ static void scroll(int topline, int botline, int lines, int sb) line[cols + 1] = 0; addpos234(screen, line, botline); - if (selstart.y >= topline && selstart.y <= botline) { + /* + * If the selection endpoints move into the scrollback, + * we keep them moving until they hit the top. However, + * of course, if the line _hasn't_ moved into the + * scrollback then we don't do this, and cut them off + * at the top of the scroll region. + */ + seltop = sb ? -savelines : 0; + + if (selstart.y >= seltop && selstart.y <= botline) { selstart.y--; - if (selstart.y < topline) { - selstart.y = topline; + if (selstart.y < seltop) { + selstart.y = seltop; selstart.x = 0; } } - if (selend.y >= topline && selend.y <= botline) { + if (selend.y >= seltop && selend.y <= botline) { selend.y--; - if (selend.y < topline) { - selend.y = topline; + if (selend.y < seltop) { + selend.y = seltop; selend.x = 0; } } @@ -824,7 +852,7 @@ static void toggle_mode(int mode, int query, int state) break; case 3: /* 80/132 columns */ deselect(); - request_resize(state ? 132 : 80, rows, 1); + request_resize(state ? 132 : 80, rows); reset_132 = state; break; case 5: /* reverse video */ @@ -934,15 +962,18 @@ void term_out(void) { int c, inbuf_reap; + /* + * Optionally log the session traffic to a file. Useful for + * debugging and possibly also useful for actual logging. + */ + if (cfg.logtype == LGTYP_DEBUG) + for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) { + logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG); + } + for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) { c = inbuf[inbuf_reap]; - /* - * Optionally log the session traffic to a file. Useful for - * debugging and possibly also useful for actual logging. - */ - logtraffic((unsigned char) c, LGTYP_DEBUG); - /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even * be able to display 8-bit characters, but I'll let that go 'cause * of i18n. @@ -985,9 +1016,9 @@ void term_out(void) case 4: case 5: if ((c & 0xC0) != 0x80) { - inbuf_reap--; /* This causes the faulting character */ - c = UCSERR; /* to be logged twice - not really a */ - utf_state = 0; /* serious problem. */ + inbuf_reap--; + c = UCSERR; + utf_state = 0; break; } utf_char = (utf_char << 6) | (c & 0x3f); @@ -1309,12 +1340,18 @@ void term_out(void) width = wcwidth((wchar_t) c); switch (width) { case 2: - if (curs.x + 1 != cols) { - *cpos++ = c | ATTR_WIDE | curr_attr; - *cpos++ = UCSWIDE | curr_attr; - curs.x++; - break; + *cpos++ = c | curr_attr; + if (++curs.x == cols) { + *cpos |= LATTR_WRAPPED; + if (curs.y == marg_b) + scroll(marg_t, marg_b, 1, TRUE); + else if (curs.y < rows - 1) + curs.y++; + curs.x = 0; + fix_cpos; } + *cpos++ = UCSWIDE | curr_attr; + break; case 1: *cpos++ = c | curr_attr; break; @@ -1431,7 +1468,7 @@ void term_out(void) compatibility(VT100); power_on(); if (reset_132) { - request_resize(80, rows, 1); + request_resize(80, rows); reset_132 = 0; } fix_cpos; @@ -1876,7 +1913,7 @@ void term_out(void) compatibility(VT340TEXT); if (esc_nargs <= 1 && (esc_args[0] < 1 || esc_args[0] >= 24)) { - request_resize(cols, def(esc_args[0], 24), 0); + request_resize(cols, def(esc_args[0], 24)); deselect(); } break; @@ -1902,9 +1939,7 @@ void term_out(void) */ compatibility(VT420); if (esc_nargs == 1 && esc_args[0] > 0) { - request_resize(cols, - def(esc_args[0], cfg.height), - 0); + request_resize(cols, def(esc_args[0], cfg.height)); deselect(); } break; @@ -1915,8 +1950,7 @@ void term_out(void) */ compatibility(VT340TEXT); if (esc_nargs <= 1) { - request_resize(def(esc_args[0], cfg.width), - rows, 0); + request_resize(def(esc_args[0], cfg.width), rows); deselect(); } break; @@ -2044,9 +2078,9 @@ void term_out(void) */ if (!has_compat(VT420) && has_compat(VT100)) { if (reset_132) - request_resize(132, 24, 1); + request_resize(132, 24); else - request_resize(80, 24, 1); + request_resize(80, 24); } #endif break; @@ -2501,9 +2535,10 @@ static void do_paint(Context ctx, int may_optimise) if (dispcurs && (curstype != cursor || dispcurs != disptext + our_curs_y * (cols + 1) + curs.x)) { - if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE)) + if (dispcurs > disptext && + (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE) dispcurs[-1] |= ATTR_INVALID; - if ((*dispcurs & ATTR_WIDE)) + if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE) dispcurs[1] |= ATTR_INVALID; *dispcurs |= ATTR_INVALID; curstype = 0; @@ -2550,6 +2585,8 @@ static void do_paint(Context ctx, int may_optimise) } tattr |= (tchar & CSET_MASK); tchar &= CHAR_MASK; + if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE) + tattr |= ATTR_WIDE; /* Video reversing things */ tattr = (tattr ^ rv @@ -2566,6 +2603,17 @@ static void do_paint(Context ctx, int may_optimise) tattr &= ~ATTR_BLINK; } + /* + * Check the font we'll _probably_ be using to see if + * the character is wide when we don't want it to be. + */ + if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) { + if ((tattr & ATTR_WIDE) == 0 && + CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2) + tattr |= ATTR_NARROW; + } else if (disptext[idx]&ATTR_NARROW) + tattr |= ATTR_NARROW; + /* Cursor here ? Save the 'background' */ if (i == our_curs_y && j == curs.x) { cursor_background = tattr | tchar; @@ -2686,14 +2734,14 @@ void term_invalidate(void) /* * Paint the window in response to a WM_PAINT message. */ -void term_paint(Context ctx, int l, int t, int r, int b) +void term_paint(Context ctx, int left, int top, int right, int bottom) { - int i, j, left, top, right, bottom; + int i, j; + if (left < 0) left = 0; + if (top < 0) top = 0; + if (right >= cols) right = cols-1; + if (bottom >= rows) bottom = rows-1; - left = l / font_width; - right = (r - 1) / font_width; - top = t / font_height; - bottom = (b - 1) / font_height; for (i = top; i <= bottom && i < rows; i++) { if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM) for (j = left; j <= right && j < cols; j++) @@ -2706,8 +2754,9 @@ void term_paint(Context ctx, int l, int t, int r, int b) /* This should happen soon enough, also for some reason it sometimes * fails to actually do anything when re-sizing ... painting the wrong * window perhaps ? - do_paint (ctx, FALSE); */ + if (alt_pressed) + do_paint (ctx, FALSE); } /* @@ -2957,6 +3006,12 @@ static int wordtype(int uc) break; } + /* For DBCS font's I can't do anything usefull. Even this will sometimes + * fail as there's such a thing as a double width space. :-( + */ + if (dbcs_screenfont && font_codepage == line_codepage) + return (uc != ' '); + if (uc < 0x80) return wordness[uc]; @@ -3079,11 +3134,18 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, { pos selpoint; unsigned long *ldata; + int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift); - if (y < 0) + if (y < 0) { y = 0; - if (y >= rows) + if (a == MA_DRAG && !raw_mouse) + term_scroll(0, -1); + } + if (y >= rows) { y = rows - 1; + if (a == MA_DRAG && !raw_mouse) + term_scroll(0, +1); + } if (x < 0) { if (y > 0) { x = cols - 1; @@ -3100,7 +3162,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) selpoint.x /= 2; - if (xterm_mouse) { + if (raw_mouse) { int encstate = 0, r, c; char abuf[16]; static int is_down = 0; @@ -3281,13 +3343,33 @@ int term_ldisc(int option) /* * from_backend(), to get data from the backend for the terminal. */ -void from_backend(int is_stderr, char *data, int len) +int from_backend(int is_stderr, char *data, int len) { while (len--) { if (inbuf_head >= INBUF_SIZE) term_out(); inbuf[inbuf_head++] = *data++; } + + /* + * We process all stdout/stderr data immediately we receive it, + * and don't return until it's all gone. Therefore, there's no + * reason at all to return anything other than zero from this + * function. + * + * This is a slightly suboptimal way to deal with SSH2 - in + * principle, the window mechanism would allow us to continue + * to accept data on forwarded ports and X connections even + * while the terminal processing was going slowly - but we + * can't do the 100% right thing without moving the terminal + * processing into a separate thread, and that might hurt + * portability. So we manage stdout buffering the old SSH1 way: + * if the terminal processing goes slowly, the whole SSH + * connection stops accepting data until it's ready. + * + * In practice, I can't imagine this causing serious trouble. + */ + return 0; } /* @@ -3306,22 +3388,35 @@ void logtraffic(unsigned char c, int logmode) } } +void settimstr(char *ta, int no_sec); +char *subslfcode(char *dest, char *src, char *dstrt); +char *stpncpy(char *dst, const char *src, size_t maxlen); +char timdatbuf[20]; +char currlogfilename[FILENAME_MAX]; + /* open log file append/overwrite mode */ void logfopen(void) { char buf[256]; time_t t; - struct tm *tm; + struct tm tm; char writemod[4]; if (!cfg.logtype) return; sprintf(writemod, "wb"); /* default to rewrite */ - lgfp = fopen(cfg.logfilename, "r"); /* file already present? */ + + time(&t); + tm = *localtime(&t); + + /* substitute special codes in file name */ + xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm); + + lgfp = fopen(currlogfilename, "r"); /* file already present? */ if (lgfp) { int i; fclose(lgfp); - i = askappend(cfg.logfilename); + i = askappend(currlogfilename); if (i == 1) writemod[0] = 'a'; /* set append mode */ else if (i == 0) { /* cancelled */ @@ -3331,22 +3426,20 @@ void logfopen(void) } } - lgfp = fopen(cfg.logfilename, writemod); + lgfp = fopen(currlogfilename, writemod); if (lgfp) { /* enter into event log */ sprintf(buf, "%s session log (%s mode) to file : ", (writemod[0] == 'a') ? "Appending" : "Writing new", (cfg.logtype == LGTYP_ASCII ? "ASCII" : cfg.logtype == LGTYP_DEBUG ? "raw" : "")); /* Make sure we do not exceed the output buffer size */ - strncat(buf, cfg.logfilename, 128); + strncat(buf, currlogfilename, 128); buf[strlen(buf)] = '\0'; logevent(buf); - /* --- write header line iinto log file */ + /* --- write header line into log file */ fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp); - time(&t); - tm = localtime(&t); - strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm); + strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm); fputs(buf, lgfp); fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp); } @@ -3359,3 +3452,57 @@ void logfclose(void) lgfp = NULL; } } + +/* + * translate format codes into time/date strings + * and insert them into log file name + * + * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h": "&&":& + */ +static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) { + char buf[10], *bufp; + int size; + char *ds = d; /* save start pos. */ + int len = FILENAME_MAX-1; + + while (*s) { + /* Let (bufp, len) be the string to append. */ + bufp = buf; /* don't usually override this */ + if (*s == '&') { + char c; + s++; + if (*s) switch (c = *s++, tolower(c)) { + case 'y': + size = strftime(buf, sizeof(buf), "%Y", tm); + break; + case 'm': + size = strftime(buf, sizeof(buf), "%m", tm); + break; + case 'd': + size = strftime(buf, sizeof(buf), "%d", tm); + break; + case 't': + size = strftime(buf, sizeof(buf), "%H%M%S", tm); + break; + case 'h': + bufp = hostname; + size = strlen(bufp); + break; + default: + buf[0] = '&'; + size = 1; + if (c != '&') + buf[size++] = c; + } + } else { + buf[0] = *s++; + size = 1; + } + if (size > len) + size = len; + memcpy(d, bufp, size); + d += size; + len -= size; + } + *d = '\0'; +}