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 void Display_int_SetCursor(tTerminal *Term, int Row, int Col);
66 tTerminal gMainBuffer;
68 int giCurrentLinePos; // byte offset, not column
70 int giFirstDispLine; // First displayed line
71 int giFirstLine; // Ring buffer start
72 char **gasDisplayLines;
73 int *gaiDisplayLineSizes;
74 char *gabDisplayLinesDirty;
77 tTerminal *Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
79 tTerminal *term = &gMainBuffer;
80 term->ViewCols = Cols;
81 term->ViewRows = Lines;
82 term->TotalLines = Lines + ExtraScrollbackLines;
83 term->PriBuf = calloc( sizeof(tLine), (Lines + ExtraScrollbackLines) );
85 AxWin3_RichText_SetLineCount(gMainWindow, Lines+ExtraScrollbackLines);
86 AxWin3_RichText_SetCursorType(gMainWindow, 1); // TODO: enum
90 // Return the byte length of a single on-screen character
91 size_t _GetCharLength(size_t AvailLength, const char *Text, uint32_t *BaseCodepoint)
96 size_t charlen = ReadUTF8(Text, BaseCodepoint);
98 while(charlen < AvailLength)
101 size_t size = ReadUTF8(Text+charlen, &cp);
102 if( Unicode_IsPrinting(cp) )
110 tLine *Display_int_GetCurLine(tTerminal *Term)
112 int lineidx = Term->CursorRow + (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
113 //_SysDebug("lineidx = %i", lineidx);
114 return (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf) + lineidx;
117 size_t Display_int_PushCharacter(tTerminal *Term, size_t AvailLength, const char *Text)
119 tLine *lineptr = Display_int_GetCurLine(Term);
121 size_t charlen = _GetCharLength(AvailLength, Text, &cp);
122 bool bOverwrite = Unicode_IsPrinting(cp);
124 // Figure out how much we need to shift the stream
127 //_SysDebug("GetCharLen(%i-%i, %p+%i, NULL)", lineptr->Len, Term->CursorByte,
128 // lineptr->Data, Term->CursorByte);
129 size_t nextlen = _GetCharLength(
130 lineptr->Len-Term->CursorByte,
131 lineptr->Data+Term->CursorByte,
133 shift = charlen - nextlen;
139 // Ensure we have space enough
140 if( !lineptr->Data || shift > 0 ) {
141 const size_t size_step = 64;
143 size_t newsize = (lineptr->Len+shift+1 + size_step-1) & ~(size_step-1);
144 if( newsize > lineptr->Size ) {
145 lineptr->Size = newsize;
146 void *tmp = realloc(lineptr->Data, lineptr->Size);
147 if( !tmp ) perror("Display_int_PushCharacter - realloc");
148 //_SysDebug("realloc gave %p from %p for line %i", tmp, lineptr->Data,
154 // Insert into stream
155 char *base = lineptr->Data + Term->CursorByte;
156 if( Term->CursorByte == lineptr->Len ) {
157 // No shifting needed
159 else if( shift >= 0 ) {
160 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
161 memmove(base+shift, base, bytes);
165 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
166 memmove(base, base+shift, bytes);
168 memcpy(base, Text, charlen);
169 lineptr->IsDirty = true;
170 lineptr->Len += shift;
171 lineptr->Data[lineptr->Len] = 0; // NULL Terminate
173 Term->CursorByte += charlen;
175 // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
182 void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
184 _SysDebug("%i '%.*s'", Length, Length, UTF8Text);
187 if( Term->CursorCol == Term->ViewCols ) {
188 Display_Newline(Term, 1);
190 size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
199 void Display_Newline(tTerminal *Term, bool bCarriageReturn)
202 if( Term->bUsingAltBuf )
204 if( Term->CursorRow == Term->ScrollTop + Term->ScrollRows-1 ) {
205 Display_ScrollDown(Term, 1);
207 else if( Term->CursorRow == Term->ViewRows-1 ) {
208 if( Term->ScrollRows == 0 ) {
209 // Scroll entire buffer
210 Display_ScrollDown(Term, 1);
213 // Don't go down a line
223 if( Term->CursorRow == Term->TotalLines-1 ) {
224 Display_ScrollDown(Term, 1);
231 if( bCarriageReturn )
233 Term->CursorByte = 0;
238 tLine *line = Display_int_GetCurLine(Term);
240 Term->CursorByte = 0;
241 int old_col = Term->CursorCol;
245 while( Term->CursorCol < old_col && ofs < line->Len ) {
246 ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
249 Term->CursorByte = ofs;
251 while( Term->CursorCol < old_col )
252 Display_AddText(Term, 1, " ");
256 void Display_SetScrollArea(tTerminal *Term, int Start, int Count)
260 Term->ScrollTop = Start;
261 Term->ScrollRows = MIN(Count, Term->ViewRows - Start);
264 void Display_ScrollDown(tTerminal *Term, int Count)
269 if( Term->bUsingAltBuf )
271 top = (Term->ScrollRows == 0 ? 0 : Term->ScrollTop);
272 max = (Term->ScrollRows == 0 ? Term->ViewRows : Term->ScrollRows);
273 buffer = Term->AltBuf;
278 max = Term->TotalLines;
279 buffer = Term->PriBuf;
283 assert(Count > -max);
285 _SysDebug("Scroll %p %i-%i down by %i", buffer, top, max, Count);
289 int clear_top, clear_max;
292 // -ve: Scroll up, move buffer contents down
294 for( int i = max-Count; i < max; i ++ )
295 free(buffer[i].Data);
296 memmove(buffer+Count, buffer, (max-Count)*sizeof(*buffer));
303 for( int i = 0; i < Count; i ++ )
304 free(buffer[i].Data);
305 memmove(buffer, buffer+Count, (max-Count)*sizeof(*buffer));
306 clear_top = max-Count;
309 // Clear exposed lines
310 for( int i = clear_top; i < clear_max; i ++ )
312 buffer[i].Data = NULL;
315 buffer[i].IsDirty = true;
317 // TODO: Send scroll command to GUI
319 Display_int_SetCursor(Term, Term->CursorRow, Term->CursorCol);
322 void Display_SetCursor(tTerminal *Term, int Row, int Col)
327 _SysDebug("Set cursor R%i,C%i", Row, Col);
329 if( !Term->bUsingAltBuf ) {
330 _SysDebug("NOTE: Using \\e[%i;%iH outside of alternat buffer is undefined", Row, Col);
333 // NOTE: This may be interesting outside of AltBuffer
334 Term->CursorRow = Row;
336 Display_int_SetCursor(Term, Row, Col);
338 void Display_int_SetCursor(tTerminal *Term, int Row, int Col)
340 tLine *line = Display_int_GetCurLine(Term);
343 for( i = 0; i < Col; i ++ )
346 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
353 Term->CursorByte = ofs;
354 // Move to exactly the column specified
355 for( ; i < Col; i ++ ) {
356 Display_int_PushCharacter(Term, 1, " ");
361 void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
370 int req_col = Term->CursorCol + RelCol;
371 if( req_col < 0 ) req_col = 0;
372 if( req_col > Term->ViewCols ) req_col = Term->ViewCols;
374 tLine *line = Display_int_GetCurLine(Term);
376 for( int i = 0; i < req_col; i ++ )
378 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
386 Term->CursorCol = req_col;
387 Term->CursorByte = ofs;
391 void Display_SaveCursor(tTerminal *Term)
393 Term->SavedRow = Term->CursorRow;
394 Term->SavedCol = Term->CursorCol;
396 void Display_RestoreCursor(tTerminal *Term)
398 Display_SetCursor(Term, Term->SavedRow, Term->SavedCol);
401 void Display_ClearLine(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
405 tLine *line = Display_int_GetCurLine(Term);
406 // Completely clear line
410 line->IsDirty = true;
414 // Forward clear (truncate)
418 // Reverse clear (replace with spaces)
426 void Display_ClearLines(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
430 // Push giDisplayLines worth of empty lines
431 // Move cursor back up by giDisplayLines
435 // Push (giDisplayLines - (giCurrentLine-giFirstDispLine)) and reverse
439 // Reverse clear (replace with spaces)
447 void Display_SetForeground(tTerminal *Term, uint32_t RGB)
450 sprintf(buf, "\1%06x", RGB&0xFFFFFF);
451 Display_AddText(Term, 7, buf);
454 void Display_SetBackground(tTerminal *Term, uint32_t RGB)
457 sprintf(buf, "\2%06x", RGB&0xFFFFFF);
458 Display_AddText(Term, 7, buf);
461 void Display_Flush(tTerminal *Term)
463 int viewOfs = (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
464 tLine *buffer = (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf );
465 for( int i = 0; i < Term->ViewRows; i ++ )
467 int line = (viewOfs + i) % Term->TotalLines;
468 tLine *lineptr = &buffer[line];
469 // Swapping buffers should cause a full resend
470 if( !Term->bHaveSwappedBuffers && !lineptr->IsDirty )
472 _SysDebug("Line %i+%i %p '%.*s'", viewOfs, i, lineptr->Data, lineptr->Len, lineptr->Data);
473 AxWin3_RichText_SendLine(gMainWindow, viewOfs + i,
474 lineptr->Data ? lineptr->Data : "" );
475 lineptr->IsDirty = 0;
477 AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
478 Term->bHaveSwappedBuffers = false;
481 void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)
483 if( Term->bUsingAltBuf == AltBufEnabled )
485 // Nothing to do, so do nothing
489 Term->bUsingAltBuf = AltBufEnabled;
490 Term->bHaveSwappedBuffers = true;
495 Term->AltBuf = calloc( sizeof(Term->AltBuf[0]), Term->ViewRows );