@@ -39,7 +39,7 @@ internal set
3939        } 
4040    } 
4141
42-     public  override  bool  SupportsTrueColor  =>  false ; 
42+     public  override  bool  SupportsTrueColor  =>  true ; 
4343
4444    /// <inheritdoc/> 
4545    public  override  bool  EnsureCursorVisibility  ( )  {  return  false ;  } 
@@ -200,8 +200,12 @@ public override void Suspend ()
200200        if  ( ! RunningUnitTests ) 
201201        { 
202202            Platform . Suspend  ( ) ; 
203-             Curses . Window . Standard . redrawwin  ( ) ; 
204-             Curses . refresh  ( ) ; 
203+ 
204+             if  ( Force16Colors ) 
205+             { 
206+                 Curses . Window . Standard . redrawwin  ( ) ; 
207+                 Curses . refresh  ( ) ; 
208+             } 
205209        } 
206210
207211        StartReportingMouseMoves  ( ) ; 
@@ -214,74 +218,232 @@ public override void UpdateCursor ()
214218        if  ( ! RunningUnitTests  &&  Col  >=  0  &&  Col  <  Cols  &&  Row  >=  0  &&  Row  <  Rows ) 
215219        { 
216220            Curses . move  ( Row ,  Col ) ; 
217-             Curses . raw  ( ) ; 
218-             Curses . noecho  ( ) ; 
219-             Curses . refresh  ( ) ; 
221+ 
222+             if  ( Force16Colors ) 
223+             { 
224+                 Curses . raw  ( ) ; 
225+                 Curses . noecho  ( ) ; 
226+                 Curses . refresh  ( ) ; 
227+             } 
220228        } 
221229    } 
222230
223- 
224231    public  override  void  UpdateScreen  ( ) 
225232    { 
226-         for   ( var   row   =   0 ;   row   <   Rows ;   row ++ ) 
233+         if   ( Force16Colors ) 
227234        { 
228-             if   ( ! _dirtyLines   [ row ] ) 
235+             for   ( var   row   =   0 ;   row   <   Rows ;   row ++ ) 
229236            { 
230-                 continue ; 
231-             } 
232- 
233-             _dirtyLines  [ row ]  =  false ; 
234- 
235-             for  ( var  col  =  0 ;  col  <  Cols ;  col ++ ) 
236-             { 
237-                 if  ( Contents  [ row ,  col ] . IsDirty  ==  false ) 
237+                 if  ( ! _dirtyLines  [ row ] ) 
238238                { 
239239                    continue ; 
240240                } 
241241
242-                 if  ( RunningUnitTests ) 
242+                 _dirtyLines  [ row ]  =  false ; 
243+ 
244+                 for  ( var  col  =  0 ;  col  <  Cols ;  col ++ ) 
243245                { 
244-                     // In unit tests, we don't want to actually write to the screen. 
245-                     continue ; 
246-                 } 
246+                     if  ( Contents  [ row ,  col ] . IsDirty  ==  false ) 
247+                     { 
248+                         continue ; 
249+                     } 
247250
248-                 Curses . attrset  ( Contents  [ row ,  col ] . Attribute . GetValueOrDefault  ( ) . PlatformColor ) ; 
251+                     if  ( RunningUnitTests ) 
252+                     { 
253+                         // In unit tests, we don't want to actually write to the screen. 
254+                         continue ; 
255+                     } 
249256
250-                 Rune   rune   =   Contents  [ row ,  col ] . Rune ; 
257+                      Curses . attrset   ( Contents  [ row ,  col ] . Attribute . GetValueOrDefault   ( ) . PlatformColor ) ; 
251258
252-                 if  ( rune . IsBmp ) 
253-                 { 
254-                     // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell. 
255-                     if  ( rune . GetColumns  ( )  <  2 ) 
259+                     Rune  rune  =  Contents  [ row ,  col ] . Rune ; 
260+ 
261+                     if  ( rune . IsBmp ) 
256262                    { 
257-                         Curses . mvaddch  ( row ,  col ,  rune . Value ) ; 
263+                         // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell. 
264+                         if  ( rune . GetColumns  ( )  <  2 ) 
265+                         { 
266+                             Curses . mvaddch  ( row ,  col ,  rune . Value ) ; 
267+                         } 
268+                         else  /*if (col + 1 < Cols)*/ 
269+                         { 
270+                             Curses . mvaddwstr  ( row ,  col ,  rune . ToString  ( ) ) ; 
271+                         } 
258272                    } 
259-                     else   /*if (col + 1 < Cols)*/ 
273+                     else 
260274                    { 
261275                        Curses . mvaddwstr  ( row ,  col ,  rune . ToString  ( ) ) ; 
276+ 
277+                         if  ( rune . GetColumns  ( )  >  1  &&  col  +  1  <  Cols ) 
278+                         { 
279+                             // TODO: This is a hack to deal with non-BMP and wide characters. 
280+                             //col++; 
281+                             Curses . mvaddch  ( row ,  ++ col ,  '*' ) ; 
282+                         } 
262283                    } 
263284                } 
264-                 else 
285+             } 
286+ 
287+             if  ( ! RunningUnitTests ) 
288+             { 
289+                 Curses . move  ( Row ,  Col ) ; 
290+                 _window . wrefresh  ( ) ; 
291+             } 
292+         } 
293+         else 
294+         { 
295+             if  ( RunningUnitTests 
296+                 ||  Console . WindowHeight  <  1 
297+                 ||  Contents . Length  !=  Rows  *  Cols 
298+                 ||  Rows  !=  Console . WindowHeight ) 
299+             { 
300+                 return ; 
301+             } 
302+ 
303+             var  top  =  0 ; 
304+             var  left  =  0 ; 
305+             int  rows  =  Rows ; 
306+             int  cols  =  Cols ; 
307+             var  output  =  new  StringBuilder  ( ) ; 
308+             Attribute ?  redrawAttr  =  null ; 
309+             int  lastCol  =  - 1 ; 
310+ 
311+             CursorVisibility ?  savedVisibility  =  _currentCursorVisibility ; 
312+             SetCursorVisibility  ( CursorVisibility . Invisible ) ; 
313+ 
314+             for  ( int  row  =  top ;  row  <  rows ;  row ++ ) 
315+             { 
316+                 if  ( Console . WindowHeight  <  1 ) 
317+                 { 
318+                     return ; 
319+                 } 
320+ 
321+                 if  ( ! _dirtyLines  [ row ] ) 
322+                 { 
323+                     continue ; 
324+                 } 
325+ 
326+                 if  ( ! SetCursorPosition  ( 0 ,  row ) ) 
265327                { 
266-                     Curses . mvaddwstr  ( row ,  col ,  rune . ToString  ( ) ) ; 
328+                     return ; 
329+                 } 
330+ 
331+                 _dirtyLines  [ row ]  =  false ; 
332+                 output . Clear  ( ) ; 
267333
268-                     if  ( rune . GetColumns  ( )  >  1  &&  col  +  1  <  Cols ) 
334+                 for  ( int  col  =  left ;  col  <  cols ;  col ++ ) 
335+                 { 
336+                     lastCol  =  - 1 ; 
337+                     var  outputWidth  =  0 ; 
338+ 
339+                     for  ( ;  col  <  cols ;  col ++ ) 
269340                    { 
270-                         // TODO: This is a hack to deal with non-BMP and wide characters. 
271-                         //col++; 
272-                         Curses . mvaddch  ( row ,  ++ col ,  '*' ) ; 
341+                         if  ( ! Contents  [ row ,  col ] . IsDirty ) 
342+                         { 
343+                             if  ( output . Length  >  0 ) 
344+                             { 
345+                                 WriteToConsole  ( output ,  ref  lastCol ,  row ,  ref  outputWidth ) ; 
346+                             } 
347+                             else  if  ( lastCol  ==  - 1 ) 
348+                             { 
349+                                 lastCol  =  col ; 
350+                             } 
351+ 
352+                             if  ( lastCol  +  1  <  cols ) 
353+                             { 
354+                                 lastCol ++ ; 
355+                             } 
356+ 
357+                             continue ; 
358+                         } 
359+ 
360+                         if  ( lastCol  ==  - 1 ) 
361+                         { 
362+                             lastCol  =  col ; 
363+                         } 
364+ 
365+                         Attribute  attr  =  Contents  [ row ,  col ] . Attribute . Value ; 
366+ 
367+                         // Performance: Only send the escape sequence if the attribute has changed. 
368+                         if  ( attr  !=  redrawAttr ) 
369+                         { 
370+                             redrawAttr  =  attr ; 
371+ 
372+                             output . Append  ( 
373+                                            EscSeqUtils . CSI_SetForegroundColorRGB  ( 
374+                                                                                   attr . Foreground . R , 
375+                                                                                   attr . Foreground . G , 
376+                                                                                   attr . Foreground . B 
377+                                                                                  ) 
378+                                           ) ; 
379+ 
380+                             output . Append  ( 
381+                                            EscSeqUtils . CSI_SetBackgroundColorRGB  ( 
382+                                                                                   attr . Background . R , 
383+                                                                                   attr . Background . G , 
384+                                                                                   attr . Background . B 
385+                                                                                  ) 
386+                                           ) ; 
387+                         } 
388+ 
389+                         outputWidth ++ ; 
390+                         Rune  rune  =  Contents  [ row ,  col ] . Rune ; 
391+                         output . Append  ( rune ) ; 
392+ 
393+                         if  ( Contents  [ row ,  col ] . CombiningMarks . Count  >  0 ) 
394+                         { 
395+                             // AtlasEngine does not support NON-NORMALIZED combining marks in a way 
396+                             // compatible with the driver architecture. Any CMs (except in the first col) 
397+                             // are correctly combined with the base char, but are ALSO treated as 1 column 
398+                             // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é  ]`. 
399+                             //  
400+                             // For now, we just ignore the list of CMs. 
401+                             //foreach (var combMark in Contents [row, col].CombiningMarks) { 
402+                             //	output.Append (combMark); 
403+                             //} 
404+                             // WriteToConsole (output, ref lastCol, row, ref outputWidth); 
405+                         } 
406+                         else  if  ( rune . IsSurrogatePair  ( )  &&  rune . GetColumns  ( )  <  2 ) 
407+                         { 
408+                             WriteToConsole  ( output ,  ref  lastCol ,  row ,  ref  outputWidth ) ; 
409+                             SetCursorPosition  ( col  -  1 ,  row ) ; 
410+                         } 
411+ 
412+                         Contents  [ row ,  col ] . IsDirty  =  false ; 
273413                    } 
274414                } 
415+ 
416+                 if  ( output . Length  >  0 ) 
417+                 { 
418+                     SetCursorPosition  ( lastCol ,  row ) ; 
419+                     Console . Write  ( output ) ; 
420+                 } 
275421            } 
276-         } 
277422
278-         if  ( ! RunningUnitTests ) 
279-         { 
280-             Curses . move  ( Row ,  Col ) ; 
281-             _window . wrefresh  ( ) ; 
423+             SetCursorPosition  ( 0 ,  0 ) ; 
424+ 
425+             _currentCursorVisibility  =  savedVisibility ; 
426+ 
427+             void  WriteToConsole  ( StringBuilder  output ,  ref  int  lastCol ,  int  row ,  ref  int  outputWidth ) 
428+             { 
429+                 SetCursorPosition  ( lastCol ,  row ) ; 
430+                 Console . Write  ( output ) ; 
431+                 output . Clear  ( ) ; 
432+                 lastCol  +=  outputWidth ; 
433+                 outputWidth  =  0 ; 
434+             } 
282435        } 
283436    } 
284437
438+     private  bool  SetCursorPosition  ( int  col ,  int  row ) 
439+     { 
440+         // + 1 is needed because non-Windows is based on 1 instead of 0 and 
441+         // Console.CursorTop/CursorLeft isn't reliable. 
442+         Console . Out . Write  ( EscSeqUtils . CSI_SetCursorPosition  ( row  +  1 ,  col  +  1 ) ) ; 
443+ 
444+         return  true ; 
445+     } 
446+ 
285447    internal  override  void  End  ( ) 
286448    { 
287449        StopReportingMouseMoves  ( ) ; 
@@ -405,7 +567,11 @@ internal override MainLoop Init ()
405567        if  ( ! RunningUnitTests ) 
406568        { 
407569            Curses . CheckWinChange  ( ) ; 
408-             Curses . refresh  ( ) ; 
570+ 
571+             if  ( Force16Colors ) 
572+             { 
573+                 Curses . refresh  ( ) ; 
574+             } 
409575        } 
410576
411577        return  new  MainLoop  ( _mainLoopDriver ) ; 
@@ -852,7 +1018,8 @@ bool IsButtonClickedOrDoubleClicked (MouseFlags flag)
8521018    /// <returns></returns> 
8531019    private  static Attribute  MakeColor  ( short  foreground ,  short  background ) 
8541020    { 
855-         var  v  =  ( short ) ( ( ushort ) foreground  |  ( background  <<  4 ) ) ; 
1021+         //var v = (short)((ushort)foreground | (background << 4)); 
1022+         var  v  =  ( short ) ( ( ( ushort ) ( foreground  &  0xffff )  <<  16 )  |  ( background  &  0xffff ) ) ; 
8561023
8571024        // TODO: for TrueColor - Use InitExtendedPair 
8581025        Curses . InitColorPair  ( v ,  foreground ,  background ) ; 
@@ -872,7 +1039,7 @@ private static Attribute MakeColor (short foreground, short background)
8721039    /// </remarks> 
8731040    public  override  Attribute  MakeColor  ( in  Color  foreground ,  in  Color  background ) 
8741041    { 
875-         if  ( ! RunningUnitTests ) 
1042+         if  ( ! RunningUnitTests   &&   Force16Colors ) 
8761043        { 
8771044            return  MakeColor  ( 
8781045                              ColorNameToCursesColorNumber  ( foreground . GetClosestNamedColor16  ( ) ) , 
0 commit comments