/* SLang Screen management routines */ #include #include #include "slang.h" typedef struct Screen_Type { int n; /* number of chars written last time */ int flags; /* line untouched, etc... */ unsigned short *old, *neew; #ifndef pc_system unsigned long old_hash, new_hash; #endif } Screen_Type; #define TOUCHED 0x1 #define TRASHED 0x2 #ifndef pc_system #define MAX_SCREEN_SIZE 120 #else #define MAX_SCREEN_SIZE 75 #endif Screen_Type SL_Screen[MAX_SCREEN_SIZE]; static int Screen_Cols, Screen_Rows; static int This_Row, This_Col; static int This_Color; static int Start_Column; static void blank_line (unsigned short *p, int n) { register unsigned short *pmax = p + n; while (p < pmax) { *p = (This_Color << 8) | (unsigned short) ' '; p++; } } static void clear_region (int row, int n) { int i; int imax = row + n; if (imax > Screen_Rows) imax = Screen_Rows; for (i = row; i < imax; i++) { blank_line (SL_Screen[i].neew, Screen_Cols); SL_Screen[i].flags |= TOUCHED; } } void SLsmg_erase_eol (void) { int c = This_Col; if (c < 0) c = 0; else if (c >= Screen_Cols) return; blank_line (SL_Screen[This_Row].neew + c , Screen_Cols - c); SL_Screen[This_Row].flags |= TOUCHED; } void SLsmg_gotorc (int r , int c) { if (r < 0) r = 0; if (r >= Screen_Rows) r = Screen_Rows - 1; if (c < 0) c = 0; if (c >= Screen_Cols) c = Screen_Cols - 1; This_Row = r; This_Col = c - Start_Column; } void SLsmg_erase_eos (void) { SLsmg_erase_eol (); clear_region (This_Row + 1, Screen_Rows); } void SLsmg_set_color (int color) { if (color >= 0) This_Color = color; } void SLsmg_reverse_video (void) { This_Color = 1; } void SLsmg_normal_video (void) { This_Color = 0; } void SLsmg_printf (char *fmt, ...) { char p[1000]; va_list ap; va_start(ap, fmt); (void) vsprintf(p, fmt, ap); va_end(ap); SLsmg_write_string (p); } void SLsmg_write_string (char *str) { SLsmg_write_nchars (str, strlen (str)); } int SLsmg_Tab_Width = 8; void SLsmg_write_nchars (char *str, int n) { register unsigned short *p, old, neew; unsigned char ch; unsigned int flags; int len, start_len = Start_Column, max_len; p = SL_Screen[This_Row].neew; if (This_Col > 0) p += This_Col; max_len = start_len + Screen_Cols; len = This_Col + Start_Column; flags = SL_Screen[This_Row].flags; while ((len < max_len) && (0 != (ch = (unsigned char) *str++))) { if (((ch >= ' ') && (ch < 127)) || (ch >= 160)) { len += 1; if (len > start_len) { old = *p; neew = (This_Color << 8) | (unsigned short) ch; if (old != neew) { flags |= TOUCHED; *p = neew; } p++; } } else if ((ch == '\t') && (SLsmg_Tab_Width > 0)) { n = len; n += SLsmg_Tab_Width; n = SLsmg_Tab_Width - (n % SLsmg_Tab_Width); if (len + n > max_len) n = max_len - len; neew = (This_Color << 8) | (unsigned short) ' '; while (n--) { len += 1; if (len > start_len) { if (*p != neew) { flags |= TOUCHED; *p = neew; } p++; } } } else if (ch == '\n') break; else { if (ch & 0x80) { neew = (This_Color << 8) | (unsigned short) '~'; len += 1; if (len > start_len) { if (*p != neew) { *p = neew; flags |= TOUCHED; } p++; if (len == max_len) break; ch &= 0x7F; } } len += 1; if (len > start_len) { neew = (This_Color << 8) | (unsigned short) '^'; if (*p != neew) { *p = neew; flags |= TOUCHED; } p++; if (len == max_len) break; } if (ch == 127) ch = '?'; else ch = ch + '@'; len++; if (len > start_len) { neew = (This_Color << 8) | (unsigned short) ch; if (*p != neew) { *p = neew; flags |= TOUCHED; } p++; } } } SL_Screen[This_Row].flags = flags; This_Col = len - start_len; } void SLsmg_write_char (char ch) { char str[2]; str[1] = 0; str[0] = ch; SLsmg_write_string (str); } static int Cls_Flag; void SLsmg_cls (void) { clear_region (0, Screen_Rows); Cls_Flag = 1; } static void do_copy (unsigned short *a, unsigned short *b) { unsigned short *amax = a + Screen_Cols; while (a < amax) *a++ = *b++; } static unsigned long compute_hash (unsigned short *s, int n) { register unsigned long h = 0, g; register unsigned long sum = 0; while (n--) { sum += *s++; h = sum + (h << 3); if ((g = h & 0xE0000000L) != 0) { h = h ^ (g >> 24); h = h ^ g; } } return h; } #ifndef pc_system unsigned long Blank_Hash; void try_scroll (void) { int i, j, di, r1, r2, rmin, rmax; unsigned long hash; int color, did_scroll = 0; unsigned short *tmp; /* find region limits */ for (rmax = Screen_Rows - 1; rmax > 0; rmax--) { if (SL_Screen[rmax].new_hash != SL_Screen[rmax].old_hash) break; } for (rmin = 0; rmin < rmax; rmin++) { if (SL_Screen[rmin].new_hash != SL_Screen[rmin].old_hash) break; } for (i = rmax; i > rmin; i--) { hash = SL_Screen[i].new_hash; if (hash == Blank_Hash) continue; if (hash == SL_Screen[i].old_hash) continue; for (j = i - 1; j >= rmin; j--) { if (hash == SL_Screen[j].old_hash) break; } if (j < rmin) continue; r2 = i; /* end scroll region */ di = i - j; j--; while ((j >= rmin) && (SL_Screen[j].old_hash == SL_Screen[j + di].new_hash)) { j--; } r1 = j + 1; /* If there is anything in the scrolling region that is ok, abort the * scroll. */ for (j = r1; j <= r2; j++) { if ((SL_Screen[j].old_hash != Blank_Hash) && (SL_Screen[j].old_hash == SL_Screen[j].new_hash)) break; } if (j <= r2) continue; color = This_Color; This_Color = 0; did_scroll = 1; SLtt_normal_video (); SLtt_set_scroll_region (r1, r2); SLtt_goto_rc (0, 0); SLtt_reverse_index (di); SLtt_reset_scroll_region (); /* Now we have a hole in the screen. Make the virtual screen look * like it. */ for (j = r1; j <= r2; j++) SL_Screen[j].flags = TOUCHED; while (di--) { tmp = SL_Screen[r2].old; for (j = r2; j > r1; j--) { SL_Screen[j].old = SL_Screen[j - 1].old; SL_Screen[j].old_hash = SL_Screen[j - 1].old_hash; } SL_Screen[r1].old = tmp; blank_line (SL_Screen[r1].old, Screen_Cols); SL_Screen[r1].old_hash = Blank_Hash; r1++; } This_Color = color; } if (did_scroll) return; /* Try other direction */ for (i = rmin; i < rmax; i++) { hash = SL_Screen[i].new_hash; if (hash == Blank_Hash) continue; if (hash == SL_Screen[i].old_hash) continue; /* find a match further down screen */ for (j = i + 1; j <= rmax; j++) { if (hash == SL_Screen[j].old_hash) break; } if (j > rmax) continue; r1 = i; /* beg scroll region */ di = j - i; /* number of lines to scroll */ j++; /* since we know this is a match */ /* find end of scroll region */ while ((j <= rmax) && (SL_Screen[j].old_hash == SL_Screen[j - di].new_hash)) { j++; } r2 = j - 1; /* end of scroll region */ /* If there is anything in the scrolling region that is ok, abort the * scroll. */ for (j = r1; j <= r2; j++) { if ((SL_Screen[j].old_hash != Blank_Hash) && (SL_Screen[j].old_hash == SL_Screen[j].new_hash)) break; } if (j <= r2) continue; color = This_Color; This_Color = 0; SLtt_normal_video (); SLtt_set_scroll_region (r1, r2); SLtt_goto_rc (0, 0); /* relative to scroll region */ SLtt_delete_nlines (di); SLtt_reset_scroll_region (); /* Now we have a hole in the screen. Make the virtual screen look * like it. */ for (j = r1; j <= r2; j++) SL_Screen[j].flags = TOUCHED; while (di--) { tmp = SL_Screen[r1].old; for (j = r1; j < r2; j++) { SL_Screen[j].old = SL_Screen[j + 1].old; SL_Screen[j].old_hash = SL_Screen[j + 1].old_hash; } SL_Screen[r2].old = tmp; blank_line (SL_Screen[r2].old, Screen_Cols); SL_Screen[r2].old_hash = Blank_Hash; r2--; } This_Color = color; } } #endif void SLsmg_refresh (void) { int i, c; #ifndef pc_system for (i = 0; i < Screen_Rows; i++) { if (SL_Screen[i].flags == 0) continue; SL_Screen[i].new_hash = compute_hash (SL_Screen[i].neew, Screen_Cols); } #endif if (Cls_Flag) { SLtt_normal_video (); SLtt_cls (); } #ifndef pc_system else if (SLtt_Term_Cannot_Scroll == 0) try_scroll (); #endif for (i = 0; i < Screen_Rows; i++) { if (SL_Screen[i].flags == 0) continue; if (SL_Screen[i].flags & TRASHED) { SLtt_goto_rc (i, -1); /* Force cursor to move */ SLtt_goto_rc (i, 0); if (Cls_Flag == 0) SLtt_del_eol (); } if (Cls_Flag) blank_line (SL_Screen[i].old, Screen_Cols); SL_Screen[i].old[Screen_Cols] = 0; SL_Screen[i].neew[Screen_Cols] = 0; SLtt_smart_puts (SL_Screen[i].neew, SL_Screen[i].old, Screen_Cols, i); do_copy (SL_Screen[i].old, SL_Screen[i].neew); SL_Screen[i].flags = 0; #ifndef pc_system SL_Screen[i].old_hash = SL_Screen[i].new_hash; #endif } c = This_Col; if (c < 0) c = 0; else if (c >= Screen_Cols) c = Screen_Cols - 1; SLtt_goto_rc (This_Row, c); Cls_Flag = 0; SLtt_flush_output (); } void SLsmg_touch_lines (int row, int n) { int i; for (i = row; i < row + n; i++) { SL_Screen[i].flags |= TRASHED; } } static int Smg_Inited; int SLsmg_init_smg (void) { int i, len; unsigned short *old, *neew; if (Smg_Inited) SLsmg_reset_smg (); SLtt_init_video (); Screen_Cols = SLtt_Screen_Cols; Screen_Rows = SLtt_Screen_Rows; This_Col = This_Row = Start_Column = 0; This_Color = 0; Cls_Flag = 1; len = Screen_Cols + 3; for (i = 0; i < Screen_Rows; i++) { if ((NULL == (old = (unsigned short *) MALLOC (sizeof(short) * len))) || ((NULL == (neew = (unsigned short *) MALLOC (sizeof(short) * len))))) { SLang_Error = SL_MALLOC_ERROR; return 0; } blank_line (old, len); blank_line (neew, len); SL_Screen[i].old = old; SL_Screen[i].neew = neew; SL_Screen[i].flags = 0; #ifndef pc_system Blank_Hash = compute_hash (old, Screen_Cols); SL_Screen[i].new_hash = SL_Screen[i].old_hash = Blank_Hash; #endif } Smg_Inited = 1; return 1; } void SLsmg_reset_smg (void) { int i; for (i = 0; i < Screen_Rows; i++) { if (SL_Screen[i].old != NULL) FREE (SL_Screen[i].old); if (SL_Screen[i].neew != NULL) FREE (SL_Screen[i].neew); SL_Screen[i].old = SL_Screen[i].neew = NULL; } SLtt_reset_video (); This_Color = 0; Smg_Inited = 0; } unsigned short SLsmg_char_at (void) { if ((This_Col < 0) || (This_Col >= Screen_Cols)) return 0; return SL_Screen[This_Row].neew[This_Col]; } void SLsmg_vprintf (char *fmt, va_list ap) { char p[1000]; (void) vsprintf(p, fmt, ap); SLsmg_write_string (p); } int SLsmg_set_start_column (int c) { int old = Start_Column; if (c < 0) c = 0; Start_Column = c; return old; }