Fix typo in term_size(), flagged by a Dr Watson log from Temme Rainer.
[u/mdw/putty] / terminal.c
index 281d2d2..f8d92dc 100644 (file)
@@ -177,6 +177,8 @@ static void cc_check(termline *line)
        j += (flags[i] != 0);
 
     assert(j == line->size);
+
+    sfree(flags);
 }
 
 /*
@@ -433,6 +435,9 @@ static void makerle(struct buf *b, termline *ldata,
                hdrpos = b->len;
                hdrsize = 0;
                add(b, 0);
+               /* And ensure this run doesn't interfere with the next. */
+               prevlen = prevpos = 0;
+               prev2 = FALSE;
 
                continue;
            } else {
@@ -634,6 +639,9 @@ static unsigned char *compressline(termline *ldata)
     /*
      * Diagnostics: ensure that the compressed data really does
      * decompress to the right thing.
+     * 
+     * XXX-REMOVE-BEFORE-RELEASE: This is a bit performance-heavy
+     * to be leaving in production code.
      */
 #ifndef CHECK_SB_COMPRESSION
     {
@@ -845,9 +853,12 @@ static termline *decompressline(unsigned char *data, int *bytes_used)
  */
 static void resizeline(Terminal *term, termline *line, int cols)
 {
-    int i, oldlen;
+    int i, oldcols;
 
     if (line->cols != cols) {
+
+       oldcols = line->cols;
+
        /*
         * This line is the wrong length, which probably means it
         * hasn't been accessed since a resize. Resize it now.
@@ -856,24 +867,36 @@ static void resizeline(Terminal *term, termline *line, int cols)
         * out in the resize (if we're shrinking the line) and
         * return their cc lists to the cc free list.
         */
-       for (i = cols; i < line->cols; i++)
+       for (i = cols; i < oldcols; i++)
            clear_cc(line, i);
 
        /*
+        * If we're shrinking the line, we now bodily move the
+        * entire cc section from where it started to where it now
+        * needs to be. (We have to do this before the resize, so
+        * that the data we're copying is still there. However, if
+        * we're expanding, we have to wait until _after_ the
+        * resize so that the space we're copying into is there.)
+        */
+       if (cols < oldcols)
+           memmove(line->chars + cols, line->chars + oldcols,
+                   (line->size - line->cols) * TSIZE);
+
+       /*
         * Now do the actual resize, leaving the _same_ amount of
         * cc space as there was to begin with.
         */
-       oldlen = line->cols;
-       line->size += cols - oldlen;
+       line->size += cols - oldcols;
        line->chars = sresize(line->chars, line->size, TTYPE);
        line->cols = cols;
 
        /*
-        * Bodily move the entire cc section from where it started
-        * to where it now needs to be.
+        * If we're expanding the line, _now_ we move the cc
+        * section.
         */
-       memmove(line->chars + line->cols, line->chars + oldlen,
-               (line->size - line->cols) * TSIZE);
+       if (cols > oldcols)
+           memmove(line->chars + cols, line->chars + oldcols,
+                   (line->size - line->cols) * TSIZE);
 
        /*
         * Go through what's left of the original line, and adjust
@@ -882,18 +905,18 @@ static void resizeline(Terminal *term, termline *line, int cols)
         * relative offsets within the cc block.) Also do the same
         * to the head of the cc_free list.
         */
-       for (i = 0; i < oldlen && i < line->cols; i++)
+       for (i = 0; i < oldcols && i < cols; i++)
            if (line->chars[i].cc_next)
-               line->chars[i].cc_next += cols - oldlen;
+               line->chars[i].cc_next += cols - oldcols;
        if (line->cc_free)
-           line->cc_free += cols - oldlen;
+           line->cc_free += cols - oldcols;
 
        /*
         * And finally fill in the new space with erase chars. (We
         * don't have to worry about cc lists here, because we
         * _know_ the erase char doesn't have one.)
         */
-       for (i = oldlen; i < cols; i++)
+       for (i = oldcols; i < cols; i++)
            line->chars[i] = term->basic_erase_char;
 
        cc_check(line);                /* XXX-REMOVE-BEFORE-RELEASE */
@@ -1119,6 +1142,22 @@ void term_reconfig(Terminal *term, Config *cfg)
        if (term->cfg.wordness[i] != cfg->wordness[i])
            reset_charclass = 1;
 
+    /*
+     * If the bidi or shaping settings have changed, flush the bidi
+     * cache completely.
+     */
+    if (term->cfg.arabicshaping != cfg->arabicshaping ||
+       term->cfg.bidi != cfg->bidi) {
+       for (i = 0; i < term->bidi_cache_size; i++) {
+           sfree(term->pre_bidi_cache[i].chars);
+           sfree(term->post_bidi_cache[i].chars);
+           term->pre_bidi_cache[i].width = -1;
+           term->pre_bidi_cache[i].chars = NULL;
+           term->post_bidi_cache[i].width = -1;
+           term->post_bidi_cache[i].chars = NULL;
+       }
+    }
+
     term->cfg = *cfg;                 /* STRUCTURE COPY */
 
     if (reset_wrap)
@@ -1279,8 +1318,8 @@ void term_free(Terminal *term)
     sfree(term->wcTo);
 
     for (i = 0; i < term->bidi_cache_size; i++) {
-       sfree(term->pre_bidi_cache[i]);
-       sfree(term->post_bidi_cache[i]);
+       sfree(term->pre_bidi_cache[i].chars);
+       sfree(term->post_bidi_cache[i].chars);
     }
     sfree(term->pre_bidi_cache);
     sfree(term->post_bidi_cache);
@@ -1394,7 +1433,7 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
     for (i = 0; i < newrows; i++) {
        newdisp[i] = newline(term, newcols, FALSE);
        for (j = 0; j < newcols; j++)
-           newdisp[i]->chars[i].attr = ATTR_INVALID;
+           newdisp[i]->chars[j].attr = ATTR_INVALID;
     }
     if (term->disptext) {
        for (i = 0; i < oldrows; i++)
@@ -1633,7 +1672,7 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb)
 
                addpos234(term->scrollback, compressline(line), sblen);
 
-               line = newline(term, term->cols, TRUE);
+               /* now `line' itself can be reused as the bottom line */
 
                /*
                 * If the user is currently looking at part of the
@@ -2430,6 +2469,8 @@ void term_out(Terminal *term)
            term->wrapnext = FALSE;
            /* destructive backspace might be disabled */
            if (!term->cfg.no_dbackspace) {
+               check_boundary(term, term->curs.x, term->curs.y);
+               check_boundary(term, term->curs.x+1, term->curs.y);
                copy_termchar(scrlineptr(term->curs.y),
                              term->curs.x, &term->erase_char);
            }
@@ -2640,6 +2681,7 @@ void term_out(Terminal *term)
                            term->curs.y++;
                        term->curs.x = 0;
                        term->wrapnext = FALSE;
+                       cline = scrlineptr(term->curs.y);
                    }
                    if (term->insert && width > 0)
                        insch(term, width);
@@ -2716,7 +2758,26 @@ void term_out(Terminal *term)
 
                        break;
                      case 0:
-                       add_cc(cline, term->curs.x - !term->wrapnext, c);
+                       if (term->curs.x > 0) {
+                           int x = term->curs.x - 1;
+
+                           /* If we're in wrapnext state, the character
+                            * to combine with is _here_, not to our left. */
+                           if (term->wrapnext)
+                               x++;
+
+                           /*
+                            * If the previous character is
+                            * UCSWIDE, back up another one.
+                            */
+                           if (cline->chars[x].chr == UCSWIDE) {
+                               assert(x > 0);
+                               x--;
+                           }
+
+                           add_cc(cline, x, c);
+                           term->seen_disp_event = 1;
+                       }
                        continue;
                      default:
                        continue;
@@ -4126,11 +4187,14 @@ static int term_bidi_cache_hit(Terminal *term, int line,
     if (line >= term->bidi_cache_size)
        return FALSE;                  /* cache doesn't have this many lines */
 
-    if (!term->pre_bidi_cache[line])
+    if (!term->pre_bidi_cache[line].chars)
        return FALSE;                  /* cache doesn't contain _this_ line */
 
+    if (term->pre_bidi_cache[line].width != width)
+       return FALSE;                  /* line is wrong width */
+
     for (i = 0; i < width; i++)
-       if (!termchars_equal(term->pre_bidi_cache[line] + i, lbefore + i))
+       if (!termchars_equal(term->pre_bidi_cache[line].chars+i, lbefore+i))
            return FALSE;              /* line doesn't match cache */
 
     return TRUE;                      /* it didn't match. */
@@ -4144,24 +4208,29 @@ static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore,
        term->bidi_cache_size = line+1;
        term->pre_bidi_cache = sresize(term->pre_bidi_cache,
                                       term->bidi_cache_size,
-                                      termchar *);
+                                      struct bidi_cache_entry);
        term->post_bidi_cache = sresize(term->post_bidi_cache,
                                        term->bidi_cache_size,
-                                       termchar *);
+                                       struct bidi_cache_entry);
        while (j < term->bidi_cache_size) {
-           term->pre_bidi_cache[j] = term->post_bidi_cache[j] = NULL;
+           term->pre_bidi_cache[j].chars =
+               term->post_bidi_cache[j].chars = NULL;
+           term->pre_bidi_cache[j].width =
+               term->post_bidi_cache[j].width = -1;
            j++;
        }
     }
 
-    sfree(term->pre_bidi_cache[line]);
-    sfree(term->post_bidi_cache[line]);
+    sfree(term->pre_bidi_cache[line].chars);
+    sfree(term->post_bidi_cache[line].chars);
 
-    term->pre_bidi_cache[line] = snewn(width, termchar);
-    term->post_bidi_cache[line] = snewn(width, termchar);
+    term->pre_bidi_cache[line].width = width;
+    term->pre_bidi_cache[line].chars = snewn(width, termchar);
+    term->post_bidi_cache[line].width = width;
+    term->post_bidi_cache[line].chars = snewn(width, termchar);
 
-    memcpy(term->pre_bidi_cache[line], lbefore, width * TSIZE);
-    memcpy(term->post_bidi_cache[line], lafter, width * TSIZE);
+    memcpy(term->pre_bidi_cache[line].chars, lbefore, width * TSIZE);
+    memcpy(term->post_bidi_cache[line].chars, lafter, width * TSIZE);
 }
 
 /*
@@ -4364,7 +4433,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
 
                lchars = term->ltemp;
            } else {
-               lchars = term->post_bidi_cache[i];
+               lchars = term->post_bidi_cache[i].chars;
            }
        } else
            lchars = ldata->chars;
@@ -4378,6 +4447,10 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
            tchar = d->chr;
            tattr = d->attr;
 
+            if (!term->cfg.ansi_colour)
+                tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) | 
+                ATTR_DEFFG | ATTR_DEFBG;
+
            switch (tchar & CSET_MASK) {
              case CSET_ASCII:
                tchar = term->ucsdata->unitab_line[tchar & 0xFF];
@@ -4559,6 +4632,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
        if (i == our_curs_y && (term->curstype != cursor || updated_line)) {
            ch[0] = (wchar_t) cursor_background.chr;
            attr = cursor_background.attr | cursor;
+           ccount = 1;
 
            if (cursor_background.cc_next) {
                termchar *dd = ldata->chars + cursor_background.cc_next;
@@ -4594,7 +4668,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
                attr |= TATTR_COMBINING;
            }
 
-           do_cursor(ctx, our_curs_x, i, ch, 1, attr, ldata->lattr);
+           do_cursor(ctx, our_curs_x, i, ch, ccount, attr, ldata->lattr);
            term->curstype = cursor;
        }
 
@@ -4662,7 +4736,7 @@ void term_paint(Terminal *term, Context ctx,
     if (bottom >= term->rows) bottom = term->rows-1;
 
     for (i = top; i <= bottom && i < term->rows; i++) {
-       if (term->disptext[i]->lattr == LATTR_NORM)
+       if ((term->disptext[i]->lattr & LATTR_MODE) == LATTR_NORM)
            for (j = left; j <= right && j < term->cols; j++)
                term->disptext[i]->chars[j].attr = ATTR_INVALID;
        else
@@ -4741,6 +4815,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
         */
        if (!(ldata->lattr & LATTR_WRAPPED)) {
            while (IS_SPACE_CHR(ldata->chars[nlpos.x - 1].chr) &&
+                  !ldata->chars[nlpos.x - 1].cc_next &&
                   poslt(top, nlpos))
                decpos(nlpos);
            if (poslt(nlpos, bottom))
@@ -5013,7 +5088,8 @@ static pos sel_spread_half(Terminal *term, pos p, int dir)
         */
        if (!(ldata->lattr & LATTR_WRAPPED)) {
            termchar *q = ldata->chars + term->cols;
-           while (q > ldata->chars && IS_SPACE_CHR(q[-1].chr))
+           while (q > ldata->chars &&
+                  IS_SPACE_CHR(q[-1].chr) && !q[-1].cc_next)
                q--;
            if (q == ldata->chars + term->cols)
                q--;