Comment explaining `need_save' argument to cmdline_process_param().
[u/mdw/putty] / terminal.c
index db024ad..bf531ee 100644 (file)
@@ -201,7 +201,7 @@ static void power_on(Terminal *term)
     term->in_vbell = FALSE;
     term->cursor_on = 1;
     term->big_cursor = 0;
-    term->save_attr = term->curr_attr = ATTR_DEFAULT;
+    term->default_attr = term->save_attr = term->curr_attr = ATTR_DEFAULT;
     term->term_editing = term->term_echoing = FALSE;
     term->app_cursor_keys = term->cfg.app_cursor;
     term->app_keypad_keys = term->cfg.app_keypad;
@@ -237,6 +237,15 @@ void term_update(Terminal *term)
            term->seen_disp_event = 0;
            need_sbar_update = TRUE;
        }
+
+       /* Allocate temporary buffers for Arabic shaping and bidi. */
+       if (!term->cfg.arabicshaping || !term->cfg.bidi)
+       {
+           term->wcFrom = sresize(term->wcFrom, term->cols, bidi_char);
+           term->ltemp = sresize(term->ltemp, term->cols+1, unsigned long);
+           term->wcTo = sresize(term->wcTo, term->cols, bidi_char);
+       }
+
        if (need_sbar_update)
            update_sbar(term);
        do_paint(term, ctx, TRUE);
@@ -428,6 +437,12 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
     term->resize_fn = NULL;
     term->resize_ctx = NULL;
     term->in_term_out = FALSE;
+    term->ltemp = NULL;
+    term->wcFrom = NULL;
+    term->wcTo = NULL;
+
+    term->bidi_cache_size = 0;
+    term->pre_bidi_cache = term->post_bidi_cache = NULL;
 
     return term;
 }
@@ -436,6 +451,7 @@ void term_free(Terminal *term)
 {
     unsigned long *line;
     struct beeptime *beep;
+    int i;
 
     while ((line = delpos234(term->scrollback, 0)) != NULL)
        sfree(line);
@@ -457,6 +473,17 @@ void term_free(Terminal *term)
        printer_finish_job(term->print_job);
     bufchain_clear(&term->printer_buf);
     sfree(term->paste_buffer);
+    sfree(term->ltemp);
+    sfree(term->wcFrom);
+    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);
+    sfree(term->post_bidi_cache);
+
     sfree(term);
 }
 
@@ -850,26 +877,29 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb)
             */
            seltop = sb ? -term->savelines : topline;
 
-           if (term->selstart.y >= seltop &&
-               term->selstart.y <= botline) {
-               term->selstart.y--;
-               if (term->selstart.y < seltop) {
-                   term->selstart.y = seltop;
-                   term->selstart.x = 0;
+           if (term->selstate != NO_SELECTION) {
+               if (term->selstart.y >= seltop &&
+                   term->selstart.y <= botline) {
+                   term->selstart.y--;
+                   if (term->selstart.y < seltop) {
+                       term->selstart.y = seltop;
+                       term->selstart.x = 0;
+                   }
                }
-           }
-           if (term->selend.y >= seltop && term->selend.y <= botline) {
-               term->selend.y--;
-               if (term->selend.y < seltop) {
-                   term->selend.y = seltop;
-                   term->selend.x = 0;
+               if (term->selend.y >= seltop && term->selend.y <= botline) {
+                   term->selend.y--;
+                   if (term->selend.y < seltop) {
+                       term->selend.y = seltop;
+                       term->selend.x = 0;
+                   }
                }
-           }
-           if (term->selanchor.y >= seltop && term->selanchor.y <= botline) {
-               term->selanchor.y--;
-               if (term->selanchor.y < seltop) {
-                   term->selanchor.y = seltop;
-                   term->selanchor.x = 0;
+               if (term->selanchor.y >= seltop &&
+                   term->selanchor.y <= botline) {
+                   term->selanchor.y--;
+                   if (term->selanchor.y < seltop) {
+                       term->selanchor.y = seltop;
+                       term->selanchor.x = 0;
+                   }
                }
            }
 
@@ -1697,7 +1727,7 @@ void term_out(Terminal *term)
                            term_update(term);
                        }
                    }
-                   term->disptop = 0;
+                   term->seen_disp_event = TRUE;
                }
                break;
              case '\b':              /* BS: Back space */
@@ -2376,7 +2406,7 @@ void term_out(Terminal *term)
                            for (i = 0; i < term->esc_nargs; i++) {
                                switch (def(term->esc_args[i], 0)) {
                                  case 0:       /* restore defaults */
-                                   term->curr_attr = ATTR_DEFAULT;
+                                   term->curr_attr = term->default_attr;
                                    break;
                                  case 1:       /* enable bold */
                                    compatibility(VT100AVO);
@@ -2782,21 +2812,27 @@ void term_out(Terminal *term)
                      case ANSI('F', '='):      /* set normal foreground */
                        compatibility(SCOANSI);
                        if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
-                           term->curr_attr &= ~ATTR_FGMASK;
-                           term->curr_attr |=
-                               (sco2ansicolour[term->esc_args[0] & 0x7] |
+                           long colour =
+                               (sco2ansicolour[term->esc_args[0] & 0x7] |
                                 ((term->esc_args[0] & 0x8) << 1)) <<
                                ATTR_FGSHIFT;
+                           term->curr_attr &= ~ATTR_FGMASK;
+                           term->curr_attr |= colour;
+                           term->default_attr &= ~ATTR_FGMASK;
+                           term->default_attr |= colour;
                        }
                        break;
                      case ANSI('G', '='):      /* set normal background */
                        compatibility(SCOANSI);
                        if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
-                           term->curr_attr &= ~ATTR_BGMASK;
-                           term->curr_attr |=
-                               (sco2ansicolour[term->esc_args[0] & 0x7] |
+                           long colour =
+                               (sco2ansicolour[term->esc_args[0] & 0x7] |
                                 ((term->esc_args[0] & 0x8) << 1)) <<
                                ATTR_BGSHIFT;
+                           term->curr_attr &= ~ATTR_BGMASK;
+                           term->curr_attr |= colour;
+                           term->default_attr &= ~ATTR_BGMASK;
+                           term->default_attr |= colour;
                        }
                        break;
                      case ANSI('L', '='):
@@ -3294,12 +3330,65 @@ static int linecmp(Terminal *term, unsigned long *a, unsigned long *b)
 #endif
 
 /*
+ * To prevent having to run the reasonably tricky bidi algorithm
+ * too many times, we maintain a cache of the last lineful of data
+ * fed to the algorithm on each line of the display.
+ */
+static int term_bidi_cache_hit(Terminal *term, int line,
+                              unsigned long *lbefore, int width)
+{
+    if (!term->pre_bidi_cache)
+       return FALSE;                  /* cache doesn't even exist yet! */
+
+    if (line >= term->bidi_cache_size)
+       return FALSE;                  /* cache doesn't have this many lines */
+
+    if (!term->pre_bidi_cache[line])
+       return FALSE;                  /* cache doesn't contain _this_ line */
+
+    if (!memcmp(term->pre_bidi_cache[line], lbefore,
+               width * sizeof(unsigned long)))
+       return TRUE;                   /* aha! the line matches the cache */
+
+    return FALSE;                     /* it didn't match. */
+}
+
+static void term_bidi_cache_store(Terminal *term, int line,
+                                 unsigned long *lbefore,
+                                 unsigned long *lafter, int width)
+{
+    if (!term->pre_bidi_cache || term->bidi_cache_size <= line) {
+       int j = term->bidi_cache_size;
+       term->bidi_cache_size = line+1;
+       term->pre_bidi_cache = sresize(term->pre_bidi_cache,
+                                      term->bidi_cache_size,
+                                      unsigned long *);
+       term->post_bidi_cache = sresize(term->post_bidi_cache,
+                                       term->bidi_cache_size,
+                                       unsigned long *);
+       while (j < term->bidi_cache_size) {
+           term->pre_bidi_cache[j] = term->post_bidi_cache[j] = NULL;
+           j++;
+       }
+    }
+
+    sfree(term->pre_bidi_cache[line]);
+    sfree(term->post_bidi_cache[line]);
+
+    term->pre_bidi_cache[line] = snewn(width, unsigned long);
+    term->post_bidi_cache[line] = snewn(width, unsigned long);
+
+    memcpy(term->pre_bidi_cache[line], lbefore, width * sizeof(unsigned long));
+    memcpy(term->post_bidi_cache[line], lafter, width * sizeof(unsigned long));
+}
+
+/*
  * Given a context, update the window. Out of paranoia, we don't
  * allow WM_PAINT responses to do scrolling optimisations.
  */
 static void do_paint(Terminal *term, Context ctx, int may_optimise)
 {
-    int i, j, our_curs_y, our_curs_x;
+    int i, it, j, our_curs_y, our_curs_x;
     unsigned long rv, cursor;
     pos scrpos;
     char ch[1024];
@@ -3315,8 +3404,8 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
     if (term->in_vbell) {
        ticks = GETTICKCOUNT();
        if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
-           term->in_vbell = FALSE; 
-   }
+           term->in_vbell = FALSE;
+    }
 
     rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
 
@@ -3402,6 +3491,67 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
                                  term->disptext[idx + term->cols]);
        term->disptext[idx + term->cols] = ldata[term->cols];
 
+       /* Do Arabic shaping and bidi. */
+       if(!term->cfg.bidi || !term->cfg.arabicshaping) {
+
+           if (!term_bidi_cache_hit(term, i, ldata, term->cols)) {
+
+               for(it=0; it<term->cols ; it++)
+               {
+                   int uc = (ldata[it] & 0xFFFF);
+
+                   switch (uc & CSET_MASK) {
+                     case ATTR_LINEDRW:
+                       if (!term->cfg.rawcnp) {
+                           uc = term->ucsdata->unitab_xterm[uc & 0xFF];
+                           break;
+                       }
+                     case ATTR_ASCII:
+                       uc = term->ucsdata->unitab_line[uc & 0xFF];
+                       break;
+                     case ATTR_SCOACS:
+                       uc = term->ucsdata->unitab_scoacs[uc&0xFF];
+                       break;
+                   }
+                   switch (uc & CSET_MASK) {
+                     case ATTR_ACP:
+                       uc = term->ucsdata->unitab_font[uc & 0xFF];
+                       break;
+                     case ATTR_OEMCP:
+                       uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
+                       break;
+                   }
+
+                   term->wcFrom[it].origwc = term->wcFrom[it].wc = uc;
+                   term->wcFrom[it].index = it;
+               }
+
+               if(!term->cfg.bidi)
+                   do_bidi(term->wcFrom, term->cols);
+
+               /* this is saved iff done from inside the shaping */
+               if(!term->cfg.bidi && term->cfg.arabicshaping)
+                   for(it=0; it<term->cols; it++)
+                       term->wcTo[it] = term->wcFrom[it];
+
+               if(!term->cfg.arabicshaping)
+                   do_shape(term->wcFrom, term->wcTo, term->cols);
+
+               for(it=0; it<term->cols ; it++)
+               {
+                   term->ltemp[it] = ldata[term->wcTo[it].index];
+
+                   if (term->wcTo[it].origwc != term->wcTo[it].wc)
+                       term->ltemp[it] = ((term->ltemp[it] & 0xFFFF0000) |
+                                          term->wcTo[it].wc);
+               }
+               term_bidi_cache_store(term, i, ldata, term->ltemp, term->cols);
+               ldata = term->ltemp;
+           } else {
+               ldata = term->post_bidi_cache[i];
+           }
+       }
+
        for (j = 0; j < term->cols; j++, idx++) {
            unsigned long tattr, tchar;
            unsigned long *d = ldata + j;
@@ -3423,8 +3573,9 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
            }
            tattr |= (tchar & CSET_MASK);
            tchar &= CHAR_MASK;
-           if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
-                   tattr |= ATTR_WIDE;
+           if (j < term->cols-1 &&
+               (d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+               tattr |= ATTR_WIDE;
 
            /* Video reversing things */
            if (term->selstate == DRAGGING || term->selstate == SELECTED) {
@@ -3790,9 +3941,13 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
 void term_copyall(Terminal *term)
 {
     pos top;
+    pos bottom;
+    tree234 *screen = term->screen;
     top.y = -sblines(term);
     top.x = 0;
-    clipme(term, top, term->curs, 0, TRUE);
+    bottom.y = find_last_nonempty_line(term, screen);
+    bottom.x = term->cols;
+    clipme(term, top, bottom, 0, TRUE);
 }
 
 /*
@@ -4786,8 +4941,6 @@ int term_ldisc(Terminal *term, int option)
 
 int term_data(Terminal *term, int is_stderr, const char *data, int len)
 {
-    assert(len > 0);
-
     bufchain_add(&term->inbuf, data, len);
 
     if (!term->in_term_out) {