Merge branch 'master' of git://localhost/acess2
[tpg/acess2.git] / Usermode / Applications / axwin3_src / WM / renderers / richtext.c
1 /*
2  * Acess2 Window Manager v3
3  * - By John Hodge (thePowersGang)
4  *
5  * render/richtext.c
6  * - Formatted Line Editor
7  */
8 #include <common.h>
9 #include <wm_renderer.h>
10 #include <wm_messages.h>
11 #include <richtext_messages.h>
12 #include <stdio.h>      // sscanf
13 #include <string.h>     // memcpy
14
15 #define LINES_PER_BLOCK 30
16
17 // === TYPES ===
18 typedef struct sRichText_Line
19 {
20         struct sRichText_Line   *Next;
21         struct sRichText_Line   *Prev;
22          int    Num;
23         char    bIsClean;
24         // TODO: Pre-rendered cache?
25         short   ByteLength;
26         short   Space;
27         char    Data[];
28 } tRichText_Line;
29 typedef struct sRichText_Window
30 {
31          int    DispLines, DispCols;
32          int    FirstVisRow, FirstVisCol;
33          int    nLines, nCols;
34          int    CursorRow, CursorCol;
35         tRichText_Line  *FirstLine;
36         tRichText_Line  *FirstVisLine;
37         tColour DefaultFG;
38         tColour DefaultBG;
39         tFont   *Font;
40         char    bNeedsFullRedraw;
41         
42         short   LineHeight;
43 } tRichText_Window;
44
45 // === PROTOTYPES ===
46  int    Renderer_RichText_Init(void);
47 tWindow *Renderer_RichText_Create(int Flags);
48 void    Renderer_RichText_Destroy(tWindow *Window);
49 void    Renderer_RichText_Redraw(tWindow *Window);
50  int    Renderer_RichText_HandleIPC_SetAttr(tWindow *Window, size_t Len, const void *Data);
51  int    Renderer_RichText_HandleIPC_WriteLine(tWindow *Window, size_t Len, const void *Data);
52  int    Renderer_RichText_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data);
53
54 // === GLOBALS ===
55 tWMRenderer     gRenderer_RichText = {
56         .Name = "RichText",
57         .CreateWindow   = Renderer_RichText_Create,
58         .DestroyWindow  = Renderer_RichText_Destroy,
59         .Redraw         = Renderer_RichText_Redraw,
60         .HandleMessage  = Renderer_RichText_HandleMessage,
61         .nIPCHandlers = N_IPC_RICHTEXT,
62         .IPCHandlers = {
63                 [IPC_RICHTEXT_SETATTR] = Renderer_RichText_HandleIPC_SetAttr,
64                 [IPC_RICHTEXT_WRITELINE] = Renderer_RichText_HandleIPC_WriteLine
65         }
66 };
67
68 // === CODE ===
69 int Renderer_RichText_Init(void)
70 {
71         WM_RegisterRenderer(&gRenderer_RichText);       
72         return 0;
73 }
74
75 tWindow *Renderer_RichText_Create(int Flags)
76 {
77         tRichText_Window        *info;
78         tWindow *ret = WM_CreateWindowStruct( sizeof(*info) );
79         if(!ret)        return NULL;
80         info = ret->RendererInfo;
81         
82         // Initialise font (get an idea of dimensions)
83         int h;
84         WM_Render_GetTextDims(NULL, "yY!", 3, NULL, &h);
85         info->LineHeight = h;
86         
87         return ret;
88 }
89
90 void Renderer_RichText_Destroy(tWindow *Window)
91 {
92         tRichText_Window        *info = Window->RendererInfo;
93
94         // TODO: Is locking needed? WM_Destroy should have taken us off the render tree
95         while( info->FirstLine )
96         {
97                 tRichText_Line *line = info->FirstLine;
98                 info->FirstLine = line->Next;
99
100                 free(line);
101         }
102
103         if( info->Font )
104                 _SysDebug("RichText_Destroy - TODO: Free font");
105 }
106
107 static inline int Renderer_RichText_RenderText_Act(tWindow *Window, tRichText_Window *info, int X, int Row, const char *Text, int Bytes, tColour FG, tColour BG, int Flags)
108 {
109          int    rwidth;
110         // TODO: Fill only what is needed? What about the rest of the line?
111         WM_Render_DrawRect(Window, X, Row*info->LineHeight,
112                 Window->W - X, info->LineHeight,
113                 BG
114                 );
115         // TODO: Bold, Italic, Underline
116         rwidth = WM_Render_DrawText(Window,
117                 X, Row*info->LineHeight,
118                 Window->W - X, info->LineHeight,
119                 info->Font, FG,
120                 Text, Bytes
121                 );
122         return rwidth;
123 }
124
125 void Renderer_RichText_RenderText(tWindow *Window, int Line, const char *Text)
126 {
127         tRichText_Window        *info = Window->RendererInfo;
128         tColour fg = info->DefaultFG;
129         tColour bg = info->DefaultBG;
130          int    flagset = 0;
131          int    bRender = 0;
132          int    curx = 0;
133         const char      *oldtext = Text;
134         
135         for( int i = 0; curx < Window->W; i ++ )
136         {
137                 char    ch, flags;
138                  int    len;
139
140                 if( i == info->FirstVisCol )
141                         bRender = 1;
142
143                 ch = *Text++;
144                 if( ch == 0 )   break;
145
146                 // Not an escape - move along
147                 if( ch > 4 )
148                         continue ;              
149
150                 if( bRender ) {
151                         // Render previous characters
152                         curx += Renderer_RichText_RenderText_Act(Window, info, curx, Line,
153                                 oldtext, Text - oldtext - 1, fg, bg, flagset);
154                         if( curx >= Window->W )
155                                 break;
156                 }
157                 oldtext = Text;
158                 switch(ch)
159                 {
160                 case 1: // FG Select (\1 RRGGBB)
161                         if( sscanf(Text, "%6x%n", &fg, &len) != 1 || len != 6 ) {
162                                 // Bad client
163                                 _SysDebug("foreground scanf failed - len=%i", len);
164                                 len = 0;
165                         }
166                         Text += len;
167                         oldtext = Text;
168                         _SysDebug("FG update to %x", fg);
169                         break ;
170                 case 2: // BG Select (\2 RRGGBB)
171                         if( sscanf(Text, "%6x%n", &bg, &len) != 1 || len != 6 ) {
172                                 // Bad client
173                                 _SysDebug("background scanf failed - len=%i", len);
174                                 len = 0;
175                         }
176                         Text += len;
177                         oldtext = Text;
178                         _SysDebug("BG update to %x", bg);
179                         break ;
180                 case 3: // Flagset (0,it,uline,bold)
181                         if( sscanf(Text, "%1hhx%n", &flags, &len) != 1 || len != 1 ) {
182                                 // Bad client
183                                 _SysDebug("Flagset scanf failed - len=%i", len);
184                         }
185                         flagset = flags & 7;
186                         Text += len;
187                         oldtext = Text;
188                         break ;
189                 case 4: // Escape (do nothing)
190                         Text ++;
191                         // NOTE: No update to oldtext
192                         break;
193                 default: // Error.
194                         break;
195                 }
196         }
197         curx += Renderer_RichText_RenderText_Act(Window, info, curx,
198                 Line, oldtext, Text - oldtext + 1, fg, bg, flagset);
199         WM_Render_DrawRect(Window, curx, Line * info->LineHeight,
200                 Window->W - curx, info->LineHeight, info->DefaultBG);
201 }
202
203 void Renderer_RichText_Redraw(tWindow *Window)
204 {
205         tRichText_Window        *info = Window->RendererInfo;
206         tRichText_Line  *line = info->FirstVisLine;
207         
208         if( !line ) {
209                 line = info->FirstLine;
210                 while(line && line->Num < info->FirstVisRow )
211                         line = line->Next;
212                 info->FirstVisLine = line;
213         }
214         while( line && line->Prev && line->Prev->Num > info->FirstVisRow )
215                 line = line->Prev;
216
217          int    i;
218         for( i = 0; i < info->DispLines && line; i ++ )
219         {
220                 if( i >= info->nLines - info->FirstVisRow )
221                         break;
222                 // Empty line is noted by a discontinuity
223                 if( line->Num > info->FirstVisRow + i ) {
224                         // Clear line if window needs full redraw
225                         if( info->bNeedsFullRedraw ) {
226                                 WM_Render_FillRect(Window,
227                                         0, i*info->LineHeight,
228                                         Window->W, info->LineHeight,
229                                         info->DefaultBG
230                                         );
231                         }
232                         else {
233                                 // Hack to clear cursor on NULL lines
234                                 WM_Render_FillRect(Window,
235                                         0, i*info->LineHeight,
236                                         1, info->LineHeight,
237                                         info->DefaultBG
238                                         );
239                         }
240                         continue ;
241                 }
242
243                 if( info->bNeedsFullRedraw || !line->bIsClean )
244                 {
245                         WM_Render_FillRect(Window,
246                                 0, i*info->LineHeight,
247                                 Window->W, info->LineHeight,
248                                 info->DefaultBG
249                                 );
250                         
251                         // Formatted text out
252                         Renderer_RichText_RenderText(Window, i, line->Data);
253                         _SysDebug("RichText: %p - Render %i '%.*s'", Window,
254                                 line->Num, line->ByteLength, line->Data);
255                         line->bIsClean = 1;
256                 }
257
258                 line = line->Next;
259         }
260         // Clear out lines i to info->DispLines-1
261         if( info->bNeedsFullRedraw )
262         {
263                 _SysDebug("RichText: %p - Clear %i px lines with %06x starting at %i",
264                         Window, (info->DispLines-i)*info->LineHeight, info->DefaultBG, i*info->LineHeight);
265                 WM_Render_FillRect(Window,
266                         0, i*info->LineHeight,
267                         Window->W, (info->DispLines-i)*info->LineHeight,
268                         info->DefaultBG
269                         );
270         }
271         info->bNeedsFullRedraw = 0;
272
273         // HACK: Hardcoded text width of 8
274         info->DispCols = Window->W / 8; 
275
276         // Text cursor
277         _SysDebug("Cursor at %i,%i", info->CursorCol, info->CursorRow);
278         _SysDebug(" Range [%i+%i],[%i+%i]", info->FirstVisRow, info->DispLines, info->FirstVisCol, info->DispCols);
279         if( info->CursorRow >= info->FirstVisRow && info->CursorRow < info->FirstVisRow + info->DispLines )
280         {
281                 if( info->CursorCol >= info->FirstVisCol && info->CursorCol < info->FirstVisCol + info->DispCols )
282                 {
283                         // TODO: Kill hardcoded 8 with cached text distance
284                         WM_Render_FillRect(Window,
285                                 (info->CursorCol - info->FirstVisCol) * 8,
286                                 (info->CursorRow - info->FirstVisRow) * info->LineHeight,
287                                 1,
288                                 info->LineHeight,
289                                 info->DefaultFG
290                                 );
291                 }
292         }
293 }
294
295 tRichText_Line *Renderer_RichText_int_GetLine(tWindow *Window, int LineNum, tRichText_Line **Prev)
296 {
297         tRichText_Window        *info = Window->RendererInfo;
298         tRichText_Line  *line = info->FirstLine;
299         tRichText_Line  *prev = NULL;
300         while(line && line->Num < LineNum)
301                 prev = line, line = line->Next;
302         
303         if( Prev )
304                 *Prev = prev;
305         
306         if( !line || line->Num > LineNum )
307                 return NULL;
308         return line;
309 }
310
311 int Renderer_RichText_HandleIPC_SetAttr(tWindow *Window, size_t Len, const void *Data)
312 {
313         tRichText_Window        *info = Window->RendererInfo;
314         const struct sRichTextIPC_SetAttr *msg = Data;
315         if(Len < sizeof(*msg))  return -1;
316
317         _SysDebug("RichText Attr %i set to %x", msg->Attr, msg->Value);
318         switch(msg->Attr)
319         {
320         case _ATTR_DEFBG:
321                 info->DefaultBG = msg->Value;
322                 break;
323         case _ATTR_DEFFG:
324                 info->DefaultFG = msg->Value;
325                 break;
326         case _ATTR_CURSORPOS: {
327                  int    newRow = msg->Value >> 12;
328                  int    newCol = msg->Value & 0xFFF;
329                 // Force redraw of old and new row
330                 tRichText_Line  *line = Renderer_RichText_int_GetLine(Window, info->CursorRow, NULL);
331                 if( line )
332                         line->bIsClean = 0;
333                 if( newRow != info->CursorRow ) {
334                         line = Renderer_RichText_int_GetLine(Window, newRow, NULL);
335                         if(line)
336                                 line->bIsClean = 0;
337                 }
338                 info->CursorRow = newRow;
339                 info->CursorCol = newCol;
340                 WM_Invalidate(Window, 1);
341                 break; }
342         case _ATTR_SCROLL:
343                 // TODO: Set scroll flag
344                 break;
345         case _ATTR_LINECOUNT:
346                 info->nLines = msg->Value;
347                 break;
348         }
349         
350         return 0;
351 }
352
353 int Renderer_RichText_HandleIPC_WriteLine(tWindow *Window, size_t Len, const void *Data)
354 {
355         tRichText_Window        *info = Window->RendererInfo;
356         const struct sRichTextIPC_WriteLine     *msg = Data;
357         if( Len < sizeof(*msg) )        return -1;
358         if( msg->Line >= info->nLines ) return 1;       // Bad count
359
360         tRichText_Line  *prev = NULL;
361         tRichText_Line  *line = Renderer_RichText_int_GetLine(Window, msg->Line, &prev);
362         if( !line )
363         {
364                 // New line!
365                 // Round up to 32
366                  int    space = ((Len - sizeof(*msg)) + 32-1) & ~(32-1);
367                 tRichText_Line  *new = malloc(sizeof(*line) + space);
368                 // TODO: Bookkeeping on how much memory each window uses
369                 new->Next = (prev ? prev->Next : NULL);
370                 new->Prev = prev;
371                 new->Num = msg->Line;
372                 new->Space = space;
373                 if(new->Prev)   new->Prev->Next = new;
374                 else    info->FirstLine = new;
375                 if(new->Next)   new->Next->Prev = new;
376                 line = new;
377         }
378         else if( line->Space < Len - sizeof(*msg) )
379         {
380                 // Need to allocate more space
381                  int    space = ((Len - sizeof(*msg)) + 32-1) & ~(32-1);
382                 tRichText_Line *new = realloc(line, space);
383                 // TODO: Bookkeeping on how much memory each window uses
384                 new->Space = space;
385
386                 if(new->Prev)   new->Prev->Next = new;
387                 else    info->FirstLine = new;
388                 if(new->Next)   new->Next->Prev = new;
389                 line = new;
390         }
391         else
392         {
393                 // It fits :)
394         }
395         line->ByteLength = Len - sizeof(*msg);
396         memcpy(line->Data, msg->LineData, Len - sizeof(*msg));
397         line->bIsClean = 0;
398
399 //      WM_Invalidate(Window, 1);
400
401         return 0;
402 }
403
404 int Renderer_RichText_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
405 {
406         tRichText_Window        *info = Target->RendererInfo;
407         switch(Msg)
408         {
409         case WNDMSG_RESIZE: {
410                 const struct sWndMsg_Resize *msg = Data;
411                 if(Len < sizeof(*msg))  return -1;
412                 info->DispLines = msg->H / info->LineHeight;
413                 info->bNeedsFullRedraw = 1;     // force full rerender
414                 return 1; }
415         case WNDMSG_KEYDOWN:
416         case WNDMSG_KEYUP:
417         case WNDMSG_KEYFIRE:
418                 return 1;
419         }
420         return 0;
421 }

UCC git Repository :: git.ucc.asn.au