3 * - By John Hodge (thePowersGang)
6 * - Abstract display manipulation methods
8 #include "include/display.h"
9 #include <acess/sys.h> // _SysDebug
10 #include <stdlib.h> // exit
14 #include <axwin3/axwin.h>
15 #include <axwin3/richtext.h>
19 #define UNIMPLIMENTED() do{_SysDebug("UNIMPLIMENTED %s", __func__); exit(-1);}while(0)
21 static inline int MIN(int a, int b) { return (a < b ? a : b); }
22 static inline int MAX(int a, int b) { return (a > b ? a : b); }
25 extern tHWND gMainWindow;
27 typedef struct sLine tLine;
31 // TODO: Cache offsets to avoid scan-forward
53 bool bHaveSwappedBuffers;
63 tTerminal gMainBuffer;
65 int giCurrentLinePos; // byte offset, not column
67 int giFirstDispLine; // First displayed line
68 int giFirstLine; // Ring buffer start
69 char **gasDisplayLines;
70 int *gaiDisplayLineSizes;
71 char *gabDisplayLinesDirty;
74 tTerminal *Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
76 tTerminal *term = &gMainBuffer;
77 term->ViewCols = Cols;
78 term->ViewRows = Lines;
79 term->TotalLines = Lines + ExtraScrollbackLines;
80 term->PriBuf = calloc( sizeof(tLine), (Lines + ExtraScrollbackLines) );
82 AxWin3_RichText_SetLineCount(gMainWindow, Lines+ExtraScrollbackLines);
83 AxWin3_RichText_SetCursorType(gMainWindow, 1); // TODO: enum
87 // Return the byte length of a single on-screen character
88 size_t _GetCharLength(size_t AvailLength, const char *Text, uint32_t *BaseCodepoint)
93 size_t charlen = ReadUTF8(Text, BaseCodepoint);
95 while(charlen < AvailLength)
98 size_t size = ReadUTF8(Text+charlen, &cp);
99 if( Unicode_IsPrinting(cp) )
107 tLine *Display_int_GetCurLine(tTerminal *Term)
109 int lineidx = Term->CursorRow + (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
110 //_SysDebug("lineidx = %i", lineidx);
111 return (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf) + lineidx;
114 size_t Display_int_PushCharacter(tTerminal *Term, size_t AvailLength, const char *Text)
116 tLine *lineptr = Display_int_GetCurLine(Term);
118 size_t charlen = _GetCharLength(AvailLength, Text, &cp);
119 bool bOverwrite = Unicode_IsPrinting(cp);
121 // Figure out how much we need to shift the stream
124 //_SysDebug("GetCharLen(%i-%i, %p+%i, NULL)", lineptr->Len, Term->CursorByte,
125 // lineptr->Data, Term->CursorByte);
126 size_t nextlen = _GetCharLength(
127 lineptr->Len-Term->CursorByte,
128 lineptr->Data+Term->CursorByte,
130 shift = charlen - nextlen;
136 // Ensure we have space enough
137 if( !lineptr->Data || shift > 0 ) {
138 const size_t size_step = 64;
140 size_t newsize = (lineptr->Len+shift+1 + size_step-1) & ~(size_step-1);
141 if( newsize > lineptr->Size ) {
142 lineptr->Size = newsize;
143 void *tmp = realloc(lineptr->Data, lineptr->Size);
144 if( !tmp ) perror("Display_int_PushCharacter - realloc");
149 // Insert into stream
150 char *base = lineptr->Data + Term->CursorByte;
151 if( Term->CursorByte == lineptr->Len ) {
152 // No shifting needed
154 else if( shift >= 0 ) {
155 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
156 memmove(base+shift, base, bytes);
160 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
161 memmove(base, base+shift, bytes);
163 memcpy(base, Text, charlen);
164 lineptr->IsDirty = true;
165 lineptr->Len += shift;
166 lineptr->Data[lineptr->Len] = 0; // NULL Terminate
168 Term->CursorByte += charlen;
170 // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
177 void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
179 _SysDebug("%i '%.*s'", Length, Length, UTF8Text);
182 if( Term->CursorCol == Term->ViewCols ) {
183 Display_Newline(Term, 1);
185 size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
194 void Display_Newline(tTerminal *Term, bool bCarriageReturn)
197 if( Term->bUsingAltBuf )
199 if( Term->CursorRow == Term->ScrollTop + Term->ScrollRows-1 ) {
200 Display_ScrollDown(Term, 1);
202 else if( Term->CursorRow == Term->ViewRows-1 ) {
203 if( Term->ScrollRows == 0 ) {
204 // Scroll entire buffer
205 Display_ScrollDown(Term, 1);
208 // Don't go down a line
218 if( Term->CursorRow == Term->TotalLines-1 ) {
219 Display_ScrollDown(Term, 1);
224 if( bCarriageReturn )
226 Term->CursorByte = 0;
231 tLine *line = Display_int_GetCurLine(Term);
233 Term->CursorByte = 0;
234 int old_col = Term->CursorCol;
238 while( Term->CursorCol < old_col && ofs < line->Len ) {
239 ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
242 Term->CursorByte = ofs;
244 while( Term->CursorCol < old_col )
245 Display_AddText(Term, 1, " ");
249 void Display_SetScrollArea(tTerminal *Term, int Start, int Count)
253 Term->ScrollTop = Start;
254 Term->ScrollRows = MIN(Count, Term->ViewRows - Start);
257 void Display_ScrollDown(tTerminal *Term, int Count)
262 if( Term->bUsingAltBuf )
264 top = (Term->ScrollRows == 0 ? 0 : Term->ScrollTop);
265 max = (Term->ScrollRows == 0 ? Term->ViewRows : Term->ScrollRows);
266 buffer = Term->AltBuf;
271 max = Term->TotalLines;
272 buffer = Term->PriBuf;
276 assert(Count > -max);
278 _SysDebug("Scroll %p %i-%i down by %i", buffer, top, max, Count);
282 if( Term->CursorRow >= top && Term->CursorRow < top+max ) {
283 Term->CursorRow -= Count;
284 Term->CursorRow = MAX(Term->CursorRow, top);
285 Term->CursorRow = MIN(Term->CursorRow, top+max);
288 int clear_top, clear_max;
291 // -ve: Scroll up, move buffer contents down
293 memmove(buffer+Count, buffer, (max-Count)*sizeof(*buffer));
300 memmove(buffer, buffer+Count, (max-Count)*sizeof(*buffer));
301 clear_top = max-Count;
304 // Clear exposed lines
305 for( int i = clear_top; i < clear_max; i ++ )
307 free(buffer[i].Data);
308 buffer[i].Data = NULL;
311 buffer[i].IsDirty = true;
312 if( Term->CursorRow == i ) {
314 Term->CursorByte = 0;
319 void Display_SetCursor(tTerminal *Term, int Row, int Col)
324 _SysDebug("Set cursor R%i,C%i", Row, Col);
326 if( !Term->bUsingAltBuf ) {
327 _SysDebug("NOTE: Using \\e[%i;%iH outside of alternat buffer is undefined", Row, Col);
330 // NOTE: This may be interesting outside of AltBuffer
331 Term->CursorRow = Row;
333 tLine *line = Display_int_GetCurLine(Term);
335 for( int i = 0; i < Col; i ++ )
338 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
340 // TODO: Should moving the cursor up to an unoccupied cell cause a jump?
347 Term->CursorCol = Col;
348 Term->CursorByte = ofs;
351 void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
360 int req_col = Term->CursorCol + RelCol;
361 if( req_col < 0 ) req_col = 0;
362 if( req_col > Term->ViewCols ) req_col = Term->ViewCols;
364 tLine *line = Display_int_GetCurLine(Term);
366 for( int i = 0; i < req_col; i ++ )
368 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
376 Term->CursorCol = req_col;
377 Term->CursorByte = ofs;
381 void Display_SaveCursor(tTerminal *Term)
383 Term->SavedRow = Term->CursorRow;
384 Term->SavedCol = Term->CursorCol;
386 void Display_RestoreCursor(tTerminal *Term)
388 Display_SetCursor(Term, Term->SavedRow, Term->SavedCol);
391 void Display_ClearLine(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
395 tLine *line = Display_int_GetCurLine(Term);
396 // Completely clear line
400 line->IsDirty = true;
404 // Forward clear (truncate)
408 // Reverse clear (replace with spaces)
416 void Display_ClearLines(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
420 // Push giDisplayLines worth of empty lines
421 // Move cursor back up by giDisplayLines
425 // Push (giDisplayLines - (giCurrentLine-giFirstDispLine)) and reverse
429 // Reverse clear (replace with spaces)
437 void Display_SetForeground(tTerminal *Term, uint32_t RGB)
440 sprintf(buf, "\1%06x", RGB&0xFFFFFF);
441 Display_AddText(Term, 7, buf);
444 void Display_SetBackground(tTerminal *Term, uint32_t RGB)
447 sprintf(buf, "\2%06x", RGB&0xFFFFFF);
448 Display_AddText(Term, 7, buf);
451 void Display_Flush(tTerminal *Term)
453 int viewOfs = (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
454 tLine *buffer = (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf );
455 for( int i = 0; i < Term->ViewRows; i ++ )
457 int line = (viewOfs + i) % Term->TotalLines;
458 tLine *lineptr = &buffer[line];
459 // Swapping buffers should cause a full resend
460 if( !Term->bHaveSwappedBuffers && !lineptr->IsDirty )
462 _SysDebug("Line %i+%i '%.*s'", viewOfs, i, lineptr->Len, lineptr->Data);
463 AxWin3_RichText_SendLine(gMainWindow, viewOfs + i,
464 lineptr->Data ? lineptr->Data : "" );
465 lineptr->IsDirty = 0;
467 AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
468 Term->bHaveSwappedBuffers = false;
471 void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)
473 if( Term->bUsingAltBuf == AltBufEnabled )
475 // Nothing to do, so do nothing
479 Term->bUsingAltBuf = AltBufEnabled;
480 Term->bHaveSwappedBuffers = true;
485 Term->AltBuf = calloc( sizeof(Term->AltBuf[0]), Term->ViewRows );