f067129db75936a2347a640f89538e2912f1f51e
[tpg/acess2.git] / Usermode / Applications / gui_terminal_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         // Figure out how much we need to shift the stream
111          int    shift;
112         if( bOverwrite ) {
113                 size_t nextlen = _GetCharLength(
114                         lineptr->Len-Term->CursorByte,
115                         lineptr->Data+Term->CursorByte,
116                         NULL);
117                 shift = charlen - nextlen;
118         }
119         else {
120                 shift = charlen;
121         }
122         
123         // Ensure we have space enough
124         if( !lineptr->Data || shift > 0 ) {
125                 const size_t    size_step = 64;
126                 assert(shift > 0);
127                 lineptr->Size = (lineptr->Len+shift+1 + size_step-1) & ~(size_step-1);
128                 void *tmp = realloc(lineptr->Data, lineptr->Size);
129                 if( !tmp )      perror("Display_int_PushCharacter - realloc");
130                 lineptr->Data = tmp;
131         }
132         
133         // Insert into stream
134         char    *base = lineptr->Data + Term->CursorByte;
135         if( Term->CursorByte == lineptr->Len ) {
136                 // No shifting needed
137         }
138         else if( shift >= 0 ) {
139                 size_t  bytes = lineptr->Len - (Term->CursorByte+shift);
140                 memmove(base+shift, base, bytes);
141         }
142         else {
143                 shift = -shift;
144                 size_t  bytes = lineptr->Len - (Term->CursorByte+shift);
145                 memmove(base, base+shift, bytes);
146         }
147         memcpy(base, Text, charlen);
148         lineptr->IsDirty = true;
149         lineptr->Len += shift;
150         lineptr->Data[lineptr->Len] = 0;        // NULL Terminate
151
152         Term->CursorByte += charlen;
153         
154         // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
155         if( !bOverwrite )
156                 Term->CursorCol --;
157         
158         return charlen;
159 }
160
161 void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
162 {
163         //_SysDebug("%i '%.*s'", Length, Length, UTF8Text);
164         while( Length > 0 )
165         {
166                 size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
167         
168                 Length -= used;
169                 UTF8Text += used;
170                 
171                 Term->CursorCol ++;
172                 if( Term->CursorCol == Term->ViewCols ) {
173                         Display_Newline(Term, 1);
174                 }
175         }
176 }
177
178 void Display_Newline(tTerminal *Term, bool bCarriageReturn)
179 {
180 //      Display_Flush();
181
182         // Going down!
183         Term->CursorRow ++;
184         if( Term->CursorRow == Term->TotalLines ) {
185                 // TODO: Scrolling
186         }
187         
188         if( bCarriageReturn ) {
189                 Term->CursorByte = 0;
190                 Term->CursorCol = 0;
191                 return ;
192         }
193
194         tLine   *line = Display_int_GetCurLine(Term);
195         
196         Term->CursorByte = 0;
197          int    old_col = Term->CursorCol;
198         Term->CursorCol = 0;
199
200         size_t  ofs = 0;
201         while( Term->CursorCol < old_col && ofs < line->Len ) {
202                 ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
203                 Term->CursorCol ++;
204         }
205         Term->CursorByte = ofs;
206
207         while( Term->CursorCol < old_col )
208                 Display_AddText(Term, 1, " ");
209 }
210
211 void Display_SetCursor(tTerminal *Term, int Row, int Col)
212 {
213         UNIMPLIMENTED();
214 }
215
216 void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
217 {
218         if( RelRow != 0 )
219         {
220                 UNIMPLIMENTED();
221         }
222         
223         if( RelCol != 0 )
224         {
225                 int req_col = Term->CursorCol + RelCol;
226                 if( req_col < 0 )       req_col = 0;
227                 if( req_col > Term->ViewCols )  req_col = Term->ViewCols;
228
229                 tLine   *line = Display_int_GetCurLine(Term);
230                 size_t  ofs = 0;
231                 for( int i = 0; i < req_col; i ++ )
232                 {
233                         size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
234                         if( clen == 0 ) {
235                                 req_col = i;
236                                 break;
237                         }
238                         ofs += clen;
239                 }
240
241                 Term->CursorCol = req_col;
242                 Term->CursorByte = ofs;
243         }
244 }
245
246 void Display_ClearLine(tTerminal *Term, int Dir)        // 0: All, 1: Forward, -1: Reverse
247 {
248         if( Dir == 0 )
249         {
250                 tLine   *line = Display_int_GetCurLine(Term);
251                 // Completely clear line
252                 if( line->Data )
253                         free(line->Data);
254                 line->Data = NULL;
255                 line->IsDirty = true;
256         }
257         else if( Dir == 1 )
258         {
259                 // Forward clear (truncate)
260         }
261         else if( Dir == -1 )
262         {
263                 // Reverse clear (replace with spaces)
264         }
265         else
266         {
267                 // BUGCHECK
268         }
269 }
270
271 void Display_ClearLines(tTerminal *Term, int Dir)       // 0: All, 1: Forward, -1: Reverse
272 {
273         if( Dir == 0 )
274         {
275                 // Push giDisplayLines worth of empty lines
276                 // Move cursor back up by giDisplayLines
277         }
278         else if( Dir == 1 )
279         {
280                 // Push (giDisplayLines - (giCurrentLine-giFirstDispLine)) and reverse
281         }
282         else if( Dir == -1 )
283         {
284                 // Reverse clear (replace with spaces)
285         }
286         else
287         {
288                 // BUGCHECK
289         }
290 }
291
292 void Display_SetForeground(tTerminal *Term, uint32_t RGB)
293 {
294         char    buf[7+1];
295         sprintf(buf, "\1%06x", RGB&0xFFFFFF);
296         Display_AddText(Term, 7, buf);
297 }
298
299 void Display_SetBackground(tTerminal *Term, uint32_t RGB)
300 {
301         char    buf[7+1];
302         sprintf(buf, "\2%06x", RGB&0xFFFFFF);
303         Display_AddText(Term, 7, buf);
304 }
305
306 void Display_Flush(tTerminal *Term)
307 {
308         for( int i = 0; i < Term->ViewRows; i ++ )
309         {
310                  int    line = (Term->ViewOffset + i) % Term->TotalLines;
311                 tLine   *lineptr = &Term->PriBuf[line];
312                 if( !lineptr->IsDirty )
313                         continue;
314                 _SysDebug("Line %i+%i '%.*s'", Term->ViewOffset, i, lineptr->Len, lineptr->Data);
315                 AxWin3_RichText_SendLine(gMainWindow, Term->ViewOffset + i, lineptr->Data );
316                 lineptr->IsDirty = 0;
317         }
318         AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
319 }
320
321 void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)
322 {
323         UNIMPLIMENTED();
324 }
325

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