Usermode/GUI Terminal - Reworking terminal code. Incomplete
[tpg/acess2.git] / Usermode / Applications / gui_shell_src / display.c
1 /*
2  * Acess GUI Terminal
3  * - By John Hodge (thePowersGang)
4  *
5  * display.c
6  * - Abstract display manipulation methods
7  */
8 #include "include/display.h"
9 #include <acess/sys.h>  // _SysDebug
10 #include <stdlib.h>     // exit
11 #include <string.h>
12 #include <unicode.h>
13 #include <stdio.h>
14 #include <axwin3/axwin.h>
15 #include <axwin3/richtext.h>
16 #include <stdbool.h>
17 #include <assert.h>
18
19 #define UNIMPLIMENTED() do{_SysDebug("UNIMPLIMENTED %s", __func__); exit(-1);}while(0)
20
21 // === EXTERN ==
22 extern tHWND    gMainWindow;
23
24 typedef struct sLine    tLine;
25
26 struct sLine {
27         char    *Data;
28         // TODO: Cache offsets to avoid scan-forward
29         size_t  Len;
30         size_t  Size;
31         bool    IsDirty;
32 };
33
34 struct sTerminal {
35          int    ViewCols;
36          int    ViewRows;
37
38          int    CursorRow;
39          int    CursorCol;
40
41         size_t  CursorByte;
42
43         bool    bUsingAltBuf;
44
45         size_t  ViewOffset;     
46         size_t  TotalLines;
47         struct sLine    *PriBuf;
48         
49         struct sLine    *AltBuf;
50 };
51
52 // === GLOBALS ===
53 tTerminal       gMainBuffer;
54  int    giCurrentLine;
55  int    giCurrentLinePos;       // byte offset, not column
56  int    giCurrentCol;
57  int    giFirstDispLine;        // First displayed line
58  int    giFirstLine;    // Ring buffer start
59 char    **gasDisplayLines;
60  int    *gaiDisplayLineSizes;
61 char    *gabDisplayLinesDirty;
62
63 // === CODE ===
64 tTerminal *Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
65 {
66         tTerminal       *term = &gMainBuffer;
67         term->ViewCols = Cols;
68         term->ViewRows = Lines;
69         term->TotalLines = Lines + ExtraScrollbackLines;
70         term->PriBuf = calloc( sizeof(tLine), (Lines + ExtraScrollbackLines) );
71         
72         AxWin3_RichText_SetLineCount(gMainWindow, Lines+ExtraScrollbackLines);
73         AxWin3_RichText_SetCursorType(gMainWindow, 1);  // TODO: enum
74         return term;
75 }
76
77 // Return the byte length of a single on-screen character
78 size_t _GetCharLength(size_t AvailLength, const char *Text, uint32_t *BaseCodepoint)
79 {
80         if( !AvailLength )
81                 return 0;
82         
83         size_t  charlen = ReadUTF8(Text, BaseCodepoint);
84         
85         while(charlen < AvailLength)
86         {
87                 uint32_t        cp;
88                 size_t size = ReadUTF8(Text+charlen, &cp);
89                 if( Unicode_IsPrinting(cp) )
90                         break;
91                 charlen += size;
92         }
93         
94         return charlen;
95 }
96
97 tLine *Display_int_GetCurLine(tTerminal *Term)
98 {
99          int    lineidx = Term->CursorRow + (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
100         return (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf) + lineidx;
101 }
102
103 size_t Display_int_PushCharacter(tTerminal *Term, size_t AvailLength, const char *Text)
104 {
105         tLine   *lineptr = Display_int_GetCurLine(Term);
106         uint32_t        cp;
107         size_t  charlen = _GetCharLength(AvailLength, Text, &cp);
108         bool    bOverwrite = Unicode_IsPrinting(cp);
109         
110         _SysDebug("Line %b:%i += %i '%.*s'", Term->bUsingAltBuf, Term->CursorRow, charlen, charlen, Text);
111
112         // Figure out how much we need to shift the stream
113          int    shift;
114         if( bOverwrite ) {
115                 size_t nextlen = _GetCharLength(
116                         lineptr->Len-Term->CursorByte,
117                         lineptr->Data+Term->CursorByte,
118                         NULL);
119                 _SysDebug("Char at +%i is %i long (%.*s)", Term->CursorByte, nextlen,
120                                 nextlen, lineptr->Data+Term->CursorByte);
121                 shift = charlen - nextlen;
122         }
123         else {
124                 shift = charlen;
125         }
126         _SysDebug("shift = %i", shift);
127         
128         // Ensure we have space enough
129         if( !lineptr->Data || shift > 0 ) {
130                 const size_t    size_step = 64;
131                 assert(shift > 0);
132                 lineptr->Size = (lineptr->Len+shift+1 + size_step-1) & ~(size_step-1);
133                 void *tmp = realloc(lineptr->Data, lineptr->Size);
134                 if( !tmp )      perror("Display_int_PushCharacter - realloc");
135                 lineptr->Data = tmp;
136         }
137         
138         // Insert into stream
139         char    *base = lineptr->Data + Term->CursorByte;
140         if( Term->CursorByte == lineptr->Len ) {
141                 // No shifting needed
142         }
143         else if( shift >= 0 ) {
144                 size_t  bytes = lineptr->Len - (Term->CursorByte+shift);
145                 _SysDebug("memmove(base+%i, base, %i)", shift, bytes);
146                 memmove(base+shift, base, bytes);
147         }
148         else {
149                 shift = -shift;
150                 size_t  bytes = lineptr->Len - (Term->CursorByte+shift);
151                 _SysDebug("memmove(base, base+%i, %i)", shift, bytes);
152                 memmove(base, base+shift, bytes);
153         }
154         memcpy(base, Text, charlen);
155         lineptr->IsDirty = true;
156         lineptr->Len += shift;
157         lineptr->Data[lineptr->Len] = 0;        // NULL Terminate
158
159         Term->CursorByte += charlen;
160         
161         // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
162         if( !bOverwrite )
163                 Term->CursorCol --;
164         
165         return charlen;
166 }
167
168 void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
169 {
170         _SysDebug("%i '%.*s'", Length, Length, UTF8Text);
171         while( Length > 0 )
172         {
173                 size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
174         
175                 Length -= used;
176                 UTF8Text += used;
177                 
178                 Term->CursorCol ++;
179                 if( Term->CursorCol == Term->ViewCols ) {
180                         Display_Newline(Term, 1);
181                 }
182         }
183 }
184
185 void Display_Newline(tTerminal *Term, bool bCarriageReturn)
186 {
187 //      Display_Flush();
188
189         // Going down!
190         Term->CursorRow ++;
191         if( Term->CursorRow == Term->TotalLines ) {
192                 // TODO: Scrolling
193         }
194         
195         if( bCarriageReturn ) {
196                 Term->CursorByte = 0;
197                 Term->CursorCol = 0;
198                 return ;
199         }
200
201         tLine   *line = Display_int_GetCurLine(Term);
202         
203         Term->CursorByte = 0;
204          int    old_col = Term->CursorCol;
205         Term->CursorCol = 0;
206
207         size_t  ofs = 0;
208         while( Term->CursorCol < old_col && ofs < line->Len ) {
209                 ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
210                 Term->CursorCol ++;
211         }
212         Term->CursorByte = ofs;
213
214         while( Term->CursorCol < old_col )
215                 Display_AddText(Term, 1, " ");
216 }
217
218 void Display_SetCursor(tTerminal *Term, int Row, int Col)
219 {
220         UNIMPLIMENTED();
221 }
222
223 void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
224 {
225         if( RelRow != 0 )
226         {
227                 UNIMPLIMENTED();
228         }
229         
230         if( RelCol != 0 )
231         {
232                 int req_col = Term->CursorCol + RelCol;
233                 if( req_col < 0 )       req_col = 0;
234                 if( req_col > Term->ViewCols )  req_col = Term->ViewCols;
235
236                 tLine   *line = Display_int_GetCurLine(Term);
237                 size_t  ofs = 0;
238                 for( int i = 0; i < req_col; i ++ )
239                 {
240                         size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
241                         if( clen == 0 ) {
242                                 req_col = i;
243                                 break;
244                         }
245                         ofs += clen;
246                 }
247
248                 Term->CursorCol = req_col;
249                 Term->CursorByte = ofs;
250         }
251 }
252
253 void Display_ClearLine(tTerminal *Term, int Dir)        // 0: All, 1: Forward, -1: Reverse
254 {
255         if( Dir == 0 )
256         {
257                 tLine   *line = Display_int_GetCurLine(Term);
258                 // Completely clear line
259                 if( line->Data )
260                         free(line->Data);
261                 line->Data = NULL;
262                 line->IsDirty = true;
263         }
264         else if( Dir == 1 )
265         {
266                 // Forward clear (truncate)
267         }
268         else if( Dir == -1 )
269         {
270                 // Reverse clear (replace with spaces)
271         }
272         else
273         {
274                 // BUGCHECK
275         }
276 }
277
278 void Display_ClearLines(tTerminal *Term, int Dir)       // 0: All, 1: Forward, -1: Reverse
279 {
280         if( Dir == 0 )
281         {
282                 // Push giDisplayLines worth of empty lines
283                 // Move cursor back up by giDisplayLines
284         }
285         else if( Dir == 1 )
286         {
287                 // Push (giDisplayLines - (giCurrentLine-giFirstDispLine)) and reverse
288         }
289         else if( Dir == -1 )
290         {
291                 // Reverse clear (replace with spaces)
292         }
293         else
294         {
295                 // BUGCHECK
296         }
297 }
298
299 void Display_SetForeground(tTerminal *Term, uint32_t RGB)
300 {
301         char    buf[7+1];
302         sprintf(buf, "\1%06x", RGB&0xFFFFFF);
303         Display_AddText(Term, 7, buf);
304 }
305
306 void Display_SetBackground(tTerminal *Term, uint32_t RGB)
307 {
308         char    buf[7+1];
309         sprintf(buf, "\2%06x", RGB&0xFFFFFF);
310         Display_AddText(Term, 7, buf);
311 }
312
313 void Display_Flush(tTerminal *Term)
314 {
315         for( int i = 0; i < Term->ViewRows; i ++ )
316         {
317                  int    line = (Term->ViewOffset + i) % Term->TotalLines;
318                 tLine   *lineptr = &Term->PriBuf[line];
319                 if( !lineptr->IsDirty )
320                         continue;
321                 _SysDebug("Line %i+%i '%.*s'", Term->ViewOffset, i, lineptr->Len, lineptr->Data);
322                 AxWin3_RichText_SendLine(gMainWindow, Term->ViewOffset + i, lineptr->Data );
323                 lineptr->IsDirty = 0;
324         }
325         AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
326 }
327
328 void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)
329 {
330         UNIMPLIMENTED();
331 }
332

UCC git Repository :: git.ucc.asn.au