@@ -39,7 +39,7 @@ internal set
39
39
}
40
40
}
41
41
42
- public override bool SupportsTrueColor => false ;
42
+ public override bool SupportsTrueColor => true ;
43
43
44
44
/// <inheritdoc/>
45
45
public override bool EnsureCursorVisibility ( ) { return false ; }
@@ -200,8 +200,12 @@ public override void Suspend ()
200
200
if ( ! RunningUnitTests )
201
201
{
202
202
Platform . Suspend ( ) ;
203
- Curses . Window . Standard . redrawwin ( ) ;
204
- Curses . refresh ( ) ;
203
+
204
+ if ( Force16Colors )
205
+ {
206
+ Curses . Window . Standard . redrawwin ( ) ;
207
+ Curses . refresh ( ) ;
208
+ }
205
209
}
206
210
207
211
StartReportingMouseMoves ( ) ;
@@ -214,74 +218,232 @@ public override void UpdateCursor ()
214
218
if ( ! RunningUnitTests && Col >= 0 && Col < Cols && Row >= 0 && Row < Rows )
215
219
{
216
220
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
+ }
220
228
}
221
229
}
222
230
223
-
224
231
public override void UpdateScreen ( )
225
232
{
226
- for ( var row = 0 ; row < Rows ; row ++ )
233
+ if ( Force16Colors )
227
234
{
228
- if ( ! _dirtyLines [ row ] )
235
+ for ( var row = 0 ; row < Rows ; row ++ )
229
236
{
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 ] )
238
238
{
239
239
continue ;
240
240
}
241
241
242
- if ( RunningUnitTests )
242
+ _dirtyLines [ row ] = false ;
243
+
244
+ for ( var col = 0 ; col < Cols ; col ++ )
243
245
{
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
+ }
247
250
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
+ }
249
256
250
- Rune rune = Contents [ row , col ] . Rune ;
257
+ Curses . attrset ( Contents [ row , col ] . Attribute . GetValueOrDefault ( ) . PlatformColor ) ;
251
258
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 )
256
262
{
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
+ }
258
272
}
259
- else /*if (col + 1 < Cols)*/
273
+ else
260
274
{
261
275
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
+ }
262
283
}
263
284
}
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 ) )
265
327
{
266
- Curses . mvaddwstr ( row , col , rune . ToString ( ) ) ;
328
+ return ;
329
+ }
330
+
331
+ _dirtyLines [ row ] = false ;
332
+ output . Clear ( ) ;
267
333
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 ++ )
269
340
{
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 ;
273
413
}
274
414
}
415
+
416
+ if ( output . Length > 0 )
417
+ {
418
+ SetCursorPosition ( lastCol , row ) ;
419
+ Console . Write ( output ) ;
420
+ }
275
421
}
276
- }
277
422
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
+ }
282
435
}
283
436
}
284
437
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
+
285
447
internal override void End ( )
286
448
{
287
449
StopReportingMouseMoves ( ) ;
@@ -405,7 +567,11 @@ internal override MainLoop Init ()
405
567
if ( ! RunningUnitTests )
406
568
{
407
569
Curses . CheckWinChange ( ) ;
408
- Curses . refresh ( ) ;
570
+
571
+ if ( Force16Colors )
572
+ {
573
+ Curses . refresh ( ) ;
574
+ }
409
575
}
410
576
411
577
return new MainLoop ( _mainLoopDriver ) ;
@@ -852,7 +1018,8 @@ bool IsButtonClickedOrDoubleClicked (MouseFlags flag)
852
1018
/// <returns></returns>
853
1019
private static Attribute MakeColor ( short foreground , short background )
854
1020
{
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 ) ) ;
856
1023
857
1024
// TODO: for TrueColor - Use InitExtendedPair
858
1025
Curses . InitColorPair ( v , foreground , background ) ;
@@ -872,7 +1039,7 @@ private static Attribute MakeColor (short foreground, short background)
872
1039
/// </remarks>
873
1040
public override Attribute MakeColor ( in Color foreground , in Color background )
874
1041
{
875
- if ( ! RunningUnitTests )
1042
+ if ( ! RunningUnitTests && Force16Colors )
876
1043
{
877
1044
return MakeColor (
878
1045
ColorNameToCursesColorNumber ( foreground . GetClosestNamedColor16 ( ) ) ,
0 commit comments