3 * - By John Hodge (thePowersGang)
6 * - Abstract display manipulation methods
9 #include "include/display.h"
10 #include <acess/sys.h> // _SysDebug
11 #include <stdlib.h> // exit
15 #include <axwin3/axwin.h>
16 #include <axwin3/richtext.h>
21 # define DEBUGS(v...) _SysDebug(v)
23 # define DEBUGS(v...) do{}while(0)
26 #define UNIMPLIMENTED() do{_SysDebug("UNIMPLIMENTED %s", __func__); exit(-1);}while(0)
28 static inline int MIN(int a, int b) { return (a < b ? a : b); }
29 static inline int MAX(int a, int b) { return (a > b ? a : b); }
32 extern tHWND gMainWindow;
34 typedef struct sLine tLine;
38 // TODO: Cache offsets to avoid scan-forward
60 bool bHaveSwappedBuffers;
73 void Display_int_SetCursor(tTerminal *Term, int Row, int Col);
76 tTerminal gMainBuffer;
78 int giCurrentLinePos; // byte offset, not column
80 int giFirstDispLine; // First displayed line
81 int giFirstLine; // Ring buffer start
82 char **gasDisplayLines;
83 int *gaiDisplayLineSizes;
84 char *gabDisplayLinesDirty;
87 tTerminal *Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
89 tTerminal *term = &gMainBuffer;
90 term->ViewCols = Cols;
91 term->ViewRows = Lines;
92 term->TotalLines = Lines + ExtraScrollbackLines;
93 term->PriBuf = calloc( sizeof(tLine), (Lines + ExtraScrollbackLines) );
95 AxWin3_RichText_SetLineCount(gMainWindow, Lines+ExtraScrollbackLines);
96 AxWin3_RichText_SetCursorType(gMainWindow, 1); // TODO: enum
100 // Return the byte length of a single on-screen character
101 size_t _GetCharLength(size_t AvailLength, const char *Text, uint32_t *BaseCodepoint)
106 size_t charlen = ReadUTF8(Text, BaseCodepoint);
108 while(charlen < AvailLength)
111 size_t size = ReadUTF8(Text+charlen, &cp);
112 if( Unicode_IsPrinting(cp) )
120 tLine *Display_int_GetCurLine(tTerminal *Term)
122 int lineidx = Term->CursorRow + (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
123 //_SysDebug("lineidx = %i", lineidx);
124 return (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf) + lineidx;
127 size_t Display_int_PushCharacter(tTerminal *Term, size_t AvailLength, const char *Text)
129 tLine *lineptr = Display_int_GetCurLine(Term);
131 size_t charlen = _GetCharLength(AvailLength, Text, &cp);
132 bool bOverwrite = Unicode_IsPrinting(cp);
134 // Figure out how much we need to shift the stream
137 //_SysDebug("GetCharLen(%i-%i, %p+%i, NULL)", lineptr->Len, Term->CursorByte,
138 // lineptr->Data, Term->CursorByte);
139 if( Term->CursorByte )
140 assert(lineptr->Data);
141 size_t nextlen = _GetCharLength(
142 lineptr->Len-Term->CursorByte,
143 lineptr->Data+Term->CursorByte,
145 shift = charlen - nextlen;
151 // Ensure we have space enough
152 if( !lineptr->Data || shift > 0 ) {
153 const size_t size_step = 64;
155 size_t newsize = (lineptr->Len+shift+1 + size_step-1) & ~(size_step-1);
156 if( newsize > lineptr->Size ) {
157 lineptr->Size = newsize;
158 void *tmp = realloc(lineptr->Data, lineptr->Size);
159 if( !tmp ) perror("Display_int_PushCharacter - realloc");
160 //_SysDebug("realloc gave %p from %p for line %i", tmp, lineptr->Data,
166 // Insert into stream
167 char *base = lineptr->Data + Term->CursorByte;
168 if( Term->CursorByte == lineptr->Len ) {
169 // No shifting needed
171 else if( shift >= 0 ) {
172 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
173 memmove(base+shift, base, bytes);
177 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
178 memmove(base, base+shift, bytes);
180 memcpy(base, Text, charlen);
181 lineptr->IsDirty = true;
182 lineptr->Len += shift;
183 lineptr->Data[lineptr->Len] = 0; // NULL Terminate
185 Term->CursorByte += charlen;
187 // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
194 void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
196 DEBUGS("%i += %i '%.*s'", Term->CursorRow, Length, Length, UTF8Text);
199 if( Term->CursorCol == Term->ViewCols ) {
200 Display_Newline(Term, 1);
202 size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
211 void Display_Newline(tTerminal *Term, bool bCarriageReturn)
214 if( Term->bUsingAltBuf )
216 if( Term->CursorRow == Term->ScrollTop + Term->ScrollRows-1 ) {
217 Display_ScrollDown(Term, 1);
219 else if( Term->CursorRow == Term->ViewRows-1 ) {
220 if( Term->ScrollRows == 0 ) {
221 // Scroll entire buffer
222 Display_ScrollDown(Term, 1);
225 // Don't go down a line
235 if( Term->CursorRow == Term->TotalLines-1 ) {
236 Display_ScrollDown(Term, 1);
243 if( bCarriageReturn )
245 Term->CursorByte = 0;
250 tLine *line = Display_int_GetCurLine(Term);
252 Term->CursorByte = 0;
253 int old_col = Term->CursorCol;
257 while( Term->CursorCol < old_col && ofs < line->Len ) {
258 ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
261 Term->CursorByte = ofs;
263 while( Term->CursorCol < old_col )
264 Display_AddText(Term, 1, " ");
268 void Display_SetScrollArea(tTerminal *Term, int Start, int Count)
272 Term->ScrollTop = Start;
273 Term->ScrollRows = MIN(Count, Term->ViewRows - Start);
276 void Display_ScrollDown(tTerminal *Term, int Count)
281 if( Term->bUsingAltBuf )
283 top = (Term->ScrollRows == 0 ? 0 : Term->ScrollTop);
284 max = (Term->ScrollRows == 0 ? Term->ViewRows : Term->ScrollRows);
285 buffer = Term->AltBuf;
290 max = Term->TotalLines;
291 buffer = Term->PriBuf;
295 assert(Count > -max);
297 DEBUGS("Scroll %p %i-%i down by %i", buffer, top, max, Count);
301 int clear_top, clear_max;
304 // -ve: Scroll up, move buffer contents down
306 for( int i = max-Count; i < max; i ++ )
307 free(buffer[i].Data);
308 memmove(buffer+Count, buffer, (max-Count)*sizeof(*buffer));
315 for( int i = 0; i < Count; i ++ )
316 free(buffer[i].Data);
317 memmove(buffer, buffer+Count, (max-Count)*sizeof(*buffer));
318 clear_top = max-Count;
321 // Clear exposed lines
322 for( int i = clear_top; i < clear_max; i ++ )
324 buffer[i].Data = NULL;
327 buffer[i].IsDirty = true;
329 // Send scroll command to GUI
330 AxWin3_RichText_ScrollRange(gMainWindow, top, max, Count);
332 Display_int_SetCursor(Term, Term->CursorRow, Term->CursorCol);
335 void Display_SetCursor(tTerminal *Term, int Row, int Col)
340 DEBUGS("Set cursor R%i,C%i", Row, Col);
342 if( !Term->bUsingAltBuf ) {
343 _SysDebug("NOTE: Using \\e[%i;%iH outside of alternat buffer is undefined", Row, Col);
346 // NOTE: This may be interesting outside of AltBuffer
347 Display_int_SetCursor(Term, Row, Col);
349 void Display_int_SetCursor(tTerminal *Term, int Row, int Col)
351 Term->CursorRow = Row;
352 tLine *line = Display_int_GetCurLine(Term);
355 for( i = 0; i < Col; i ++ )
358 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
365 Term->CursorByte = ofs;
366 // Move to exactly the column specified
367 for( ; i < Col; i ++ ) {
368 Display_int_PushCharacter(Term, 1, " ");
373 void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
382 int req_col = Term->CursorCol + RelCol;
383 if( req_col < 0 ) req_col = 0;
384 if( req_col > Term->ViewCols ) req_col = Term->ViewCols;
386 tLine *line = Display_int_GetCurLine(Term);
388 for( int i = 0; i < req_col; i ++ )
390 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
398 Term->CursorCol = req_col;
399 Term->CursorByte = ofs;
403 void Display_SaveCursor(tTerminal *Term)
405 Term->SavedRow = Term->CursorRow;
406 Term->SavedCol = Term->CursorCol;
408 void Display_RestoreCursor(tTerminal *Term)
410 Display_SetCursor(Term, Term->SavedRow, Term->SavedCol);
413 void Display_ClearLine(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
417 tLine *line = Display_int_GetCurLine(Term);
418 // Completely clear line
422 line->IsDirty = true;
424 Term->CursorByte = 0;
428 // Forward clear (truncate)
432 // Reverse clear (replace with spaces)
440 void Display_ClearLines(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
444 // Push giDisplayLines worth of empty lines
445 // Move cursor back up by giDisplayLines
449 // Push (giDisplayLines - (giCurrentLine-giFirstDispLine)) and reverse
453 // Reverse clear (replace with spaces)
461 void Display_ResetAttributes(tTerminal *Term)
465 void Display_SetForeground(tTerminal *Term, uint32_t RGB)
468 sprintf(buf, "\1%06x", RGB&0xFFFFFF);
469 Display_AddText(Term, 7, buf);
472 void Display_SetBackground(tTerminal *Term, uint32_t RGB)
475 sprintf(buf, "\2%06x", RGB&0xFFFFFF);
476 Display_AddText(Term, 7, buf);
479 void Display_Flush(tTerminal *Term)
481 int viewOfs = (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
482 tLine *buffer = (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf );
483 AxWin3_RichText_SetLineCount(gMainWindow, (Term->bUsingAltBuf ? Term->ViewRows : Term->TotalLines));
484 for( int i = 0; i < Term->ViewRows; i ++ )
486 int line = (viewOfs + i) % Term->TotalLines;
487 tLine *lineptr = &buffer[line];
488 // Swapping buffers should cause a full resend
489 if( !Term->bHaveSwappedBuffers && !lineptr->IsDirty )
491 DEBUGS("Line %i+%i %p '%.*s'", viewOfs, i, lineptr->Data, lineptr->Len, lineptr->Data);
492 AxWin3_RichText_SendLine(gMainWindow, viewOfs + i,
493 lineptr->Data ? lineptr->Data : "" );
494 lineptr->IsDirty = false;
496 AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
497 Term->bHaveSwappedBuffers = false;
500 void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)
502 if( Term->bUsingAltBuf == AltBufEnabled )
504 // Nothing to do, so do nothing
508 int row = Term->OtherBufRow;
509 int col = Term->OtherBufCol;
510 Term->OtherBufRow = Term->CursorRow;
511 Term->OtherBufCol = Term->CursorCol;
513 Term->bUsingAltBuf = AltBufEnabled;
514 Term->bHaveSwappedBuffers = true;
519 Term->AltBuf = calloc( sizeof(Term->AltBuf[0]), Term->ViewRows );
521 AxWin3_RichText_SetLineCount(gMainWindow, Term->ViewRows);
525 AxWin3_RichText_SetLineCount(gMainWindow, Term->TotalLines);
527 Display_int_SetCursor(Term, row, col);