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;
33 extern int giPTYHandle;
35 typedef struct sLine tLine;
39 // TODO: Cache offsets to avoid scan-forward
63 bool bHaveSwappedBuffers;
76 void Display_int_SetCursor(tTerminal *Term, int Row, int Col);
79 tTerminal gMainBuffer;
81 int giCurrentLinePos; // byte offset, not column
83 int giFirstDispLine; // First displayed line
84 int giFirstLine; // Ring buffer start
85 char **gasDisplayLines;
86 int *gaiDisplayLineSizes;
87 char *gabDisplayLinesDirty;
90 tTerminal *Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
92 tTerminal *term = &gMainBuffer;
93 term->ViewCols = Cols;
94 term->ViewRows = Lines;
95 term->TotalLines = Lines + ExtraScrollbackLines;
96 term->PriBuf = calloc( sizeof(tLine), (Lines + ExtraScrollbackLines) );
98 AxWin3_RichText_SetLineCount(gMainWindow, Lines+ExtraScrollbackLines);
99 AxWin3_RichText_SetCursorType(gMainWindow, 1); // TODO: enum
103 void *Display_GetTermState(tTerminal *Term) {
104 return Term->TermState;
106 void Display_SetTermState(tTerminal *Term, void *State) {
107 Term->TermState = State;
110 void Display_SendInput(tTerminal *Term, const char *String)
112 _SysWrite(giPTYHandle, String, strlen(String));
115 // Return the byte length of a single on-screen character
116 size_t _GetCharLength(size_t AvailLength, const char *Text, uint32_t *BaseCodepoint)
121 size_t charlen = ReadUTF8(Text, BaseCodepoint);
123 while(charlen < AvailLength)
126 size_t size = ReadUTF8(Text+charlen, &cp);
127 if( Unicode_IsPrinting(cp) )
135 tLine *Display_int_GetCurLine(tTerminal *Term)
137 int lineidx = Term->CursorRow + (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
138 //_SysDebug("lineidx = %i", lineidx);
139 return (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf) + lineidx;
142 size_t Display_int_PushCharacter(tTerminal *Term, size_t AvailLength, const char *Text)
144 tLine *lineptr = Display_int_GetCurLine(Term);
146 size_t charlen = _GetCharLength(AvailLength, Text, &cp);
147 bool bOverwrite = Unicode_IsPrinting(cp);
149 // Figure out how much we need to shift the stream
152 //_SysDebug("GetCharLen(%i-%i, %p+%i, NULL)", lineptr->Len, Term->CursorByte,
153 // lineptr->Data, Term->CursorByte);
154 if( Term->CursorByte )
155 assert(lineptr->Data);
156 size_t nextlen = _GetCharLength(
157 lineptr->Len-Term->CursorByte,
158 lineptr->Data+Term->CursorByte,
160 shift = charlen - nextlen;
166 // Ensure we have space enough
167 if( !lineptr->Data || shift > 0 ) {
168 const size_t size_step = 64;
170 size_t newsize = (lineptr->Len+shift+1 + size_step-1) & ~(size_step-1);
171 if( newsize > lineptr->Size ) {
172 lineptr->Size = newsize;
173 void *tmp = realloc(lineptr->Data, lineptr->Size);
174 if( !tmp ) perror("Display_int_PushCharacter - realloc");
175 //_SysDebug("realloc gave %p from %p for line %i", tmp, lineptr->Data,
181 // Insert into stream
182 char *base = lineptr->Data + Term->CursorByte;
183 if( Term->CursorByte == lineptr->Len ) {
184 // No shifting needed
186 else if( shift >= 0 ) {
187 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
188 memmove(base+shift, base, bytes);
192 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
193 memmove(base, base+shift, bytes);
195 memcpy(base, Text, charlen);
196 lineptr->IsDirty = true;
197 lineptr->Len += shift;
198 lineptr->Data[lineptr->Len] = 0; // NULL Terminate
200 Term->CursorByte += charlen;
202 // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
209 void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
211 DEBUGS("%i += %i '%.*s'", Term->CursorRow, Length, Length, UTF8Text);
214 if( Term->CursorCol == Term->ViewCols ) {
215 Display_Newline(Term, 1);
217 size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
226 void Display_Newline(tTerminal *Term, bool bCarriageReturn)
229 if( Term->bUsingAltBuf )
231 if( Term->CursorRow == Term->ScrollTop + Term->ScrollRows-1 ) {
232 Display_ScrollDown(Term, 1);
234 else if( Term->CursorRow == Term->ViewRows-1 ) {
235 if( Term->ScrollRows == 0 ) {
236 // Scroll entire buffer
237 Display_ScrollDown(Term, 1);
240 // Don't go down a line
250 if( Term->CursorRow == Term->TotalLines-1 ) {
251 Display_ScrollDown(Term, 1);
258 if( bCarriageReturn )
260 Term->CursorByte = 0;
265 tLine *line = Display_int_GetCurLine(Term);
267 Term->CursorByte = 0;
268 int old_col = Term->CursorCol;
272 while( Term->CursorCol < old_col && ofs < line->Len ) {
273 ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
276 Term->CursorByte = ofs;
278 while( Term->CursorCol < old_col )
279 Display_AddText(Term, 1, " ");
283 void Display_SetScrollArea(tTerminal *Term, int Start, int Count)
287 Term->ScrollTop = Start;
288 Term->ScrollRows = MIN(Count, Term->ViewRows - Start);
291 void Display_ScrollDown(tTerminal *Term, int Count)
296 if( Term->bUsingAltBuf )
298 top = (Term->ScrollRows == 0 ? 0 : Term->ScrollTop);
299 max = (Term->ScrollRows == 0 ? Term->ViewRows : Term->ScrollRows);
300 buffer = Term->AltBuf;
305 max = Term->TotalLines;
306 buffer = Term->PriBuf;
310 assert(Count > -max);
312 DEBUGS("Scroll %p %i-%i down by %i", buffer, top, max, Count);
316 int clear_top, clear_max;
319 // -ve: Scroll up, move buffer contents down
321 for( int i = max-Count; i < max; i ++ )
322 free(buffer[i].Data);
323 memmove(buffer+Count, buffer, (max-Count)*sizeof(*buffer));
330 for( int i = 0; i < Count; i ++ )
331 free(buffer[i].Data);
332 memmove(buffer, buffer+Count, (max-Count)*sizeof(*buffer));
333 clear_top = max-Count;
336 // Clear exposed lines
337 for( int i = clear_top; i < clear_max; i ++ )
339 buffer[i].Data = NULL;
342 buffer[i].IsDirty = true;
344 // Send scroll command to GUI
345 AxWin3_RichText_ScrollRange(gMainWindow, top, max, Count);
347 Display_int_SetCursor(Term, Term->CursorRow, Term->CursorCol);
350 void Display_SetCursor(tTerminal *Term, int Row, int Col)
355 DEBUGS("Set cursor R%i,C%i", Row, Col);
357 if( !Term->bUsingAltBuf ) {
358 _SysDebug("NOTE: Using \\e[%i;%iH outside of alternat buffer is undefined", Row, Col);
361 // NOTE: This may be interesting outside of AltBuffer
362 Display_int_SetCursor(Term, Row, Col);
364 void Display_int_SetCursor(tTerminal *Term, int Row, int Col)
366 Term->CursorRow = Row;
367 tLine *line = Display_int_GetCurLine(Term);
370 for( i = 0; i < Col; i ++ )
373 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
380 Term->CursorByte = ofs;
381 // Move to exactly the column specified
382 for( ; i < Col; i ++ ) {
383 Display_int_PushCharacter(Term, 1, " ");
388 void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
397 int req_col = Term->CursorCol + RelCol;
398 if( req_col < 0 ) req_col = 0;
399 if( req_col > Term->ViewCols ) req_col = Term->ViewCols;
401 tLine *line = Display_int_GetCurLine(Term);
403 for( int i = 0; i < req_col; i ++ )
405 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
413 Term->CursorCol = req_col;
414 Term->CursorByte = ofs;
418 void Display_SaveCursor(tTerminal *Term)
420 Term->SavedRow = Term->CursorRow;
421 Term->SavedCol = Term->CursorCol;
423 void Display_RestoreCursor(tTerminal *Term)
425 Display_SetCursor(Term, Term->SavedRow, Term->SavedCol);
428 void Display_ClearLine(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
432 tLine *line = Display_int_GetCurLine(Term);
433 // Completely clear line
437 line->IsDirty = true;
439 Term->CursorByte = 0;
443 // Forward clear (truncate)
447 // Reverse clear (replace with spaces)
455 void Display_ClearLines(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
459 // Push giDisplayLines worth of empty lines
460 // Move cursor back up by giDisplayLines
464 // Push (giDisplayLines - (giCurrentLine-giFirstDispLine)) and reverse
468 // Reverse clear (replace with spaces)
476 void Display_ResetAttributes(tTerminal *Term)
478 Display_SetForeground(Term, 0xFFFFFF);
479 Display_SetBackground(Term, 0x000000);
481 void Display_SetForeground(tTerminal *Term, uint32_t RGB)
484 sprintf(buf, "\1%06x", RGB&0xFFFFFF);
485 Display_AddText(Term, 7, buf);
488 void Display_SetBackground(tTerminal *Term, uint32_t RGB)
491 sprintf(buf, "\2%06x", RGB&0xFFFFFF);
492 Display_AddText(Term, 7, buf);
495 void Display_Flush(tTerminal *Term)
497 int viewOfs = (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
498 tLine *buffer = (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf );
499 AxWin3_RichText_SetLineCount(gMainWindow, (Term->bUsingAltBuf ? Term->ViewRows : Term->TotalLines));
500 for( int i = 0; i < Term->ViewRows; i ++ )
502 int line = (viewOfs + i) % Term->TotalLines;
503 tLine *lineptr = &buffer[line];
504 // Swapping buffers should cause a full resend
505 if( !Term->bHaveSwappedBuffers && !lineptr->IsDirty )
507 DEBUGS("Line %i+%i %p '%.*s'", viewOfs, i, lineptr->Data, lineptr->Len, lineptr->Data);
508 AxWin3_RichText_SendLine(gMainWindow, viewOfs + i,
509 lineptr->Data ? lineptr->Data : "" );
510 lineptr->IsDirty = false;
512 AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
513 Term->bHaveSwappedBuffers = false;
516 void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)
518 if( Term->bUsingAltBuf == AltBufEnabled )
520 // Nothing to do, so do nothing
524 int row = Term->OtherBufRow;
525 int col = Term->OtherBufCol;
526 Term->OtherBufRow = Term->CursorRow;
527 Term->OtherBufCol = Term->CursorCol;
529 Term->bUsingAltBuf = AltBufEnabled;
530 Term->bHaveSwappedBuffers = true;
535 Term->AltBuf = calloc( sizeof(Term->AltBuf[0]), Term->ViewRows );
537 AxWin3_RichText_SetLineCount(gMainWindow, Term->ViewRows);
541 AxWin3_RichText_SetLineCount(gMainWindow, Term->TotalLines);
543 Display_int_SetCursor(Term, row, col);
546 void Display_SetTitle(tTerminal *Term, const char *Title)
548 _SysDebug("TODO: Set window title to '%s'", Title);