1 /*
2   Console
3 */
4 
5 #include "console.h"
6 
7 #include "alloc.h"
8 #include "head.h"
9 #include "infocom.h"
10 #include "os.h"
11 #include "print.h"
12 #include "queue.h"
13 #include "wio.h"
14 
15 console con;
16 bool newlines_are_significant;
17 
18 static int quick_redraw;
19 #define reformatting 0
20 
wipe_slack(int x1,int y1,int x2,int y2)21 static void wipe_slack(int x1, int y1, int x2, int y2)
22 {
23   if(y1 < y2 && x1 < x2)
24   {
25     int i, j;
26     console_cell blank;
27     start_update();
28     /* Build a blank character */
29     blank.text = ' ';
30     blank.attr = con.cursor.attr;
31     blank.attr.font = FONT_NORMAL;
32     /* Blank the first row */
33     for(j = x1; j < x2; ++j)
34       con.row[y1].slack[j] = blank;
35     /* Blat that into the subsequent rows */
36     for(i = y1 + 1; i < y2; ++i)
37       os_mcpy(&con.row[i].slack[x1], &con.row[y1].slack[x1], (x2-x1) * sizeof(blank));
38     /* Touch the lines and adjust cached lengths */
39     for(i = y1; i < y2; ++i)
40     {
41       int last = con.row[i].held; /* Index of last known character */
42       /* Line needs redrawing if characters up to the last were altered */
43       if(x1 <= last)
44         touch(i);
45       /* Line has been truncated if the last character has changed */
46       if(x1 <= last && last < x2)
47         con.row[i].held = x1;
48     }
49     finish_update();
50   }
51 }
52 
wipe_fixed(int x1,int y1,int x2,int y2)53 static void wipe_fixed(int x1, int y1, int x2, int y2)
54 {
55   int i;
56   console_cell blank;
57   blank.text = 0;
58   blank.attr = con.cursor.attr;
59   blank.attr.font = FONT_NORMAL;
60   start_update();
61   for(i = y1; i < y2; ++i)
62     if(con.row[i].fix)
63     {
64       int j;
65       bool f;
66       for(j = x1; j < x2; ++j)
67         con.row[i].fixed[j] = blank;
68       f = 0;
69       for(j = 0; j < x1 && !f; ++j)
70         f = con.row[i].fixed[j].text != 0;
71       for(j = x2; j < MAX_FIXED && !f; ++j)
72         f = con.row[i].fixed[j].text != 0;
73       con.row[i].fix = f;
74       touch(i);
75     }
76   finish_update();
77 }
78 
find_eol(int row)79 int find_eol(int row)
80 {
81   int i = con.row[row].held;
82   while(i && is_a_space(con.row[row].slack[i - 1])) --i;
83   con.row[row].held = i;
84   return i;
85 }
86 
87 
init_console(int rows,int fixed)88 void init_console(int rows, int fixed)
89 {
90   if(!con.cursor.attr.font)
91   {
92     con.cursor.attr.font = FONT_NORMAL;
93     con.cursor.attr.fore = 2;
94     con.cursor.attr.back = 9;
95   }
96   con.shape.fixed   = min(MAX_FIXED, fixed);
97   con.shape.height  = min(MAX_HEIGHT, rows);
98   /* con.top        = 0; */
99   con.bottom        = con.shape.height;
100   hd_set_size(con.shape.height, con.shape.fixed);
101   {
102     erase_window(0, MAX_HEIGHT);
103     con.cursor.y = con.shape.height - 1;
104   }
105 }
106 
start_update(void)107 void start_update(void)
108 {
109   ++quick_redraw;
110 }
111 
finish_update(void)112 void finish_update(void)
113 {
114   if(--quick_redraw <= 0)
115     force_update();
116 }
117 
touch(int row)118 void touch(int row)
119 {
120   if(quick_redraw > 0)
121     con.row[row].flag = 1;
122   else
123     repaint(row);
124 }
125 
force_update(void)126 void force_update(void)
127 {
128   int y = con.shape.height;
129   while(--y >= 0)
130     if(con.row[y].flag)
131       repaint(y);
132   redraw_cursor();
133 }
134 
set_xy(int x,int y)135 void set_xy(int x, int y)
136 {
137   hide_cursor();
138   if(x < 0) x = 0;
139   if(y < 0) y = 0;
140   if(con.cursor.align)
141   {
142     con.cursor.x = min(x, MAX_FIXED - 1);
143     con.cursor.y = min(y, MAX_HEIGHT - 1);
144   }
145   else
146   {
147     con.cursor.x = min(x, MAX_SLACK - 1);
148     con.cursor.y = min(y, con.shape.height - 1);
149   }
150   show_cursor();
151 }
152 
goto_xy(int x,int y)153 void goto_xy(int x, int y)
154 {
155   con.cursor.align = 1;
156   set_xy(x, y);
157 }
158 
put_char(word c)159 void put_char(word c)
160 {
161   extern bool enable_screen;
162 
163   if(!enable_screen)
164     return;
165   if(is_font_request(c))
166   {
167     con.cursor.attr.font = c;
168   }
169   else if(is_attr_request(c))
170   {
171     byte b = request_get_data(c);
172     word fore = attr_get_fore(b);
173     word back = attr_get_back(b);
174     if(fore == 1) fore = 2;
175     if(2 <= fore && fore <= 9)
176       con.cursor.attr.fore = fore;
177     if(back == 1) back = 9;
178     if(2 <= back && back <= 9)
179       con.cursor.attr.back = back;
180   }
181   else if(kind(c) == 0)
182   {
183     start_update();
184     switch(c)
185     {
186       case 0:
187         c = ' ';
188       /* Fall through ... */
189       default:
190         if(c >= 32)
191         {
192           console_row *r  = &con.row[con.cursor.y];
193           console_cell *p = con.cursor.align
194                           ? &r->fixed[con.cursor.x]
195                           : &r->slack[con.cursor.x];
196           if(con.cursor.align)
197             r->fix = 1;
198           else if(con.cursor.x >= r->held)
199             r->held = con.cursor.x + 1;
200           r->flag = 1;
201           p->text = c;
202           p->attr = con.cursor.attr;
203           if(p->attr.font != FONT_FIXED
204           && p->attr.font != FONT_FIXED_REVS
205           && (con.cursor.align || hd_get_fixed()))
206             p->attr.font = p->attr.font == FONT_REVS ? FONT_FIXED_REVS : FONT_FIXED;
207           set_xy(con.cursor.x + 1, con.cursor.y);
208         }
209         break;
210       case '\r':
211         set_xy(0, con.cursor.y);
212         break;
213       case '\f':
214         /* Enable justification on current line */
215         con.row[con.cursor.y].just = 1;
216         con.row[con.cursor.y].flag = 1;
217         break;
218            case '\n':
219         if(con.cursor.align
220         || con.cursor.y + 1 < con.bottom)
221         {
222           set_xy(0, con.cursor.y + 1);
223         }
224         else if(con.top < con.bottom)
225         {
226           int i;
227           console_cell *s = con.row[con.top].slack;
228           /* Scroll the slack plane and clear its last line */
229           for(i = con.top; i < con.bottom - 1; ++i)
230           {
231             con.row[i].slack = con.row[i+1].slack;
232             con.row[i].flag  = con.row[i+1].flag;
233             con.row[i].just  = con.row[i+1].just;
234             con.row[i].held  = con.row[i+1].held;
235           }
236           con.row[con.bottom - 1].slack = s;
237           con.row[con.bottom - 1].just  = 0;
238           wipe_slack(0, con.bottom - 1, MAX_SLACK, con.bottom);
239           set_xy(0, con.bottom - 1);
240           /* Scroll the fixed plane (possibly more than before) unless we are reformatting */
241           if(!reformatting)
242           {
243             int fixed_bottom = con.bottom == con.shape.height ? MAX_HEIGHT : con.bottom;
244             if(con.top < fixed_bottom)
245             {
246               console_cell *f = con.row[con.top].fixed;
247               int b = con.row[con.top].fix;
248               for(i = con.top; i < fixed_bottom - 1; ++i)
249               {
250                 con.row[i].fixed = con.row[i+1].fixed;
251                 con.row[i].fix   = con.row[i+1].fix;
252               }
253               con.row[fixed_bottom - 1].fixed = f;
254               con.row[fixed_bottom - 1].fix   = b;
255               wipe_fixed(0, fixed_bottom - 1, MAX_FIXED, fixed_bottom);
256             }
257             scrollup();
258           }
259         }
260         else
261         {
262           set_xy(0, con.cursor.y);
263           erase_to_eoln();
264         }
265         break;
266     }
267     finish_update();
268   }
269 }
270 
use_window(int window)271 void use_window(int window)
272 {
273   static int active_window = FULL_SCREEN;
274   extern word status_height;
275   if(window == STATUS_WINDOW && active_window != STATUS_WINDOW)
276     start_update();
277   if(window != STATUS_WINDOW && active_window == STATUS_WINDOW)
278     finish_update();
279   switch(window)
280   {
281     case TEXT_WINDOW:
282       con.top          = status_height;
283       con.bottom       = con.shape.height;
284       break;
285     case STATUS_WINDOW:
286       con.top          = 0;
287       con.bottom       = status_height;
288       break;
289     case FULL_SCREEN:
290       con.top          = 0;
291       con.bottom       = con.shape.height;
292       break;
293   }
294   active_window = window;
295 }
296 
erase_to_eoln(void)297 void erase_to_eoln(void)
298 {
299   if(con.cursor.align)
300     wipe_fixed(con.cursor.x, con.cursor.y, MAX_FIXED, con.cursor.y + 1);
301   else
302     wipe_slack(con.cursor.x, con.cursor.y, MAX_SLACK, con.cursor.y + 1);
303 }
304 
erase_window(word top_of_window,word bottom_of_window)305 void erase_window(word top_of_window, word bottom_of_window)
306 {
307   start_update();
308   wipe_fixed(0, top_of_window, MAX_FIXED, bottom_of_window);
309   wipe_slack(0, top_of_window, MAX_SLACK, bottom_of_window);
310   newlines_are_significant = 0;
311   finish_update();
312 }
313 
swap_font_status(int * font)314 void swap_font_status(int *font)
315 {
316   int t = con.cursor.attr.font; con.cursor.attr.font = *font; *font = t;
317 }
318 
319 static console_context saved_cursor[2];
320 
save_attributes(int context)321 void save_attributes(int context)
322 {
323   saved_cursor[context] = con.cursor;
324 }
325 
restore_attributes(int context)326 void restore_attributes(int context)
327 {
328   con.cursor = saved_cursor[context];
329 }
330 
clip_attributes(void)331 void clip_attributes(void)
332 {
333   saved_cursor[0].x = con.cursor.x;
334   saved_cursor[0].y = con.cursor.y;
335 }
336 
get_xy(int * x,int * y)337 void get_xy(int *x, int *y)
338 {
339   *x = con.cursor.x;
340   *y = con.cursor.y;
341 }
342 
343 #ifdef OLD
is_a_space(const console_cell * c)344 int is_a_space(const console_cell *c)
345 {
346   return c->text == ' ' && c->attr.font != FONT_FIXED_REVS;
347 }
348 #endif
349 
allocate_console(void)350 void allocate_console(void)
351 {
352   int i, lump = 8; /* Do this many lines at a time */
353   console_cell *s, *f;
354   for(i = 0; i < MAX_HEIGHT; ++i)
355   {
356     int r = i % lump;
357     if(r == 0)
358     {
359       s = alloc0(lump * MAX_SLACK, console_cell);
360       f = alloc0(lump * MAX_FIXED, console_cell);
361     }
362     con.row[i].held  = MAX_SLACK;
363     con.row[i].slack = s + r * MAX_SLACK;
364     con.row[i].fixed = f + r * MAX_FIXED;
365   }
366 }
367 
368