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)
22 extern tHWND gMainWindow;
24 typedef struct sLine tLine;
28 // TODO: Cache offsets to avoid scan-forward
53 tTerminal gMainBuffer;
55 int giCurrentLinePos; // byte offset, not column
57 int giFirstDispLine; // First displayed line
58 int giFirstLine; // Ring buffer start
59 char **gasDisplayLines;
60 int *gaiDisplayLineSizes;
61 char *gabDisplayLinesDirty;
64 tTerminal *Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
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) );
72 AxWin3_RichText_SetLineCount(gMainWindow, Lines+ExtraScrollbackLines);
73 AxWin3_RichText_SetCursorType(gMainWindow, 1); // TODO: enum
77 // Return the byte length of a single on-screen character
78 size_t _GetCharLength(size_t AvailLength, const char *Text, uint32_t *BaseCodepoint)
83 size_t charlen = ReadUTF8(Text, BaseCodepoint);
85 while(charlen < AvailLength)
88 size_t size = ReadUTF8(Text+charlen, &cp);
89 if( Unicode_IsPrinting(cp) )
97 tLine *Display_int_GetCurLine(tTerminal *Term)
99 int lineidx = Term->CursorRow + (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
100 return (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf) + lineidx;
103 size_t Display_int_PushCharacter(tTerminal *Term, size_t AvailLength, const char *Text)
105 tLine *lineptr = Display_int_GetCurLine(Term);
107 size_t charlen = _GetCharLength(AvailLength, Text, &cp);
108 bool bOverwrite = Unicode_IsPrinting(cp);
110 // Figure out how much we need to shift the stream
113 size_t nextlen = _GetCharLength(
114 lineptr->Len-Term->CursorByte,
115 lineptr->Data+Term->CursorByte,
117 shift = charlen - nextlen;
123 // Ensure we have space enough
124 if( !lineptr->Data || shift > 0 ) {
125 const size_t size_step = 64;
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");
133 // Insert into stream
134 char *base = lineptr->Data + Term->CursorByte;
135 if( Term->CursorByte == lineptr->Len ) {
136 // No shifting needed
138 else if( shift >= 0 ) {
139 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
140 memmove(base+shift, base, bytes);
144 size_t bytes = lineptr->Len - (Term->CursorByte+shift);
145 memmove(base, base+shift, bytes);
147 memcpy(base, Text, charlen);
148 lineptr->IsDirty = true;
149 lineptr->Len += shift;
150 lineptr->Data[lineptr->Len] = 0; // NULL Terminate
152 Term->CursorByte += charlen;
154 // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
161 void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
163 //_SysDebug("%i '%.*s'", Length, Length, UTF8Text);
166 size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
172 if( Term->CursorCol == Term->ViewCols ) {
173 Display_Newline(Term, 1);
178 void Display_Newline(tTerminal *Term, bool bCarriageReturn)
184 if( Term->CursorRow == Term->TotalLines ) {
188 if( bCarriageReturn ) {
189 Term->CursorByte = 0;
194 tLine *line = Display_int_GetCurLine(Term);
196 Term->CursorByte = 0;
197 int old_col = Term->CursorCol;
201 while( Term->CursorCol < old_col && ofs < line->Len ) {
202 ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
205 Term->CursorByte = ofs;
207 while( Term->CursorCol < old_col )
208 Display_AddText(Term, 1, " ");
211 void Display_SetCursor(tTerminal *Term, int Row, int Col)
216 void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
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;
229 tLine *line = Display_int_GetCurLine(Term);
231 for( int i = 0; i < req_col; i ++ )
233 size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
241 Term->CursorCol = req_col;
242 Term->CursorByte = ofs;
246 void Display_ClearLine(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
250 tLine *line = Display_int_GetCurLine(Term);
251 // Completely clear line
255 line->IsDirty = true;
259 // Forward clear (truncate)
263 // Reverse clear (replace with spaces)
271 void Display_ClearLines(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
275 // Push giDisplayLines worth of empty lines
276 // Move cursor back up by giDisplayLines
280 // Push (giDisplayLines - (giCurrentLine-giFirstDispLine)) and reverse
284 // Reverse clear (replace with spaces)
292 void Display_SetForeground(tTerminal *Term, uint32_t RGB)
295 sprintf(buf, "\1%06x", RGB&0xFFFFFF);
296 Display_AddText(Term, 7, buf);
299 void Display_SetBackground(tTerminal *Term, uint32_t RGB)
302 sprintf(buf, "\2%06x", RGB&0xFFFFFF);
303 Display_AddText(Term, 7, buf);
306 void Display_Flush(tTerminal *Term)
308 for( int i = 0; i < Term->ViewRows; i ++ )
310 int line = (Term->ViewOffset + i) % Term->TotalLines;
311 tLine *lineptr = &Term->PriBuf[line];
312 if( !lineptr->IsDirty )
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;
318 AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
321 void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)