@@ -838,6 +838,7 @@ void Screen<Cell>::sendTerminalId()
838
838
_terminal.reply (" \033 [>{};{};{}c" , Pp, Pv, Pc);
839
839
}
840
840
841
+ // {{{ ED
841
842
template <typename Cell>
842
843
void Screen<Cell>::clearToEndOfScreen()
843
844
{
@@ -871,6 +872,7 @@ void Screen<Cell>::clearScreen()
871
872
// up in case the content is still needed.
872
873
scrollUp (_state.pageSize .lines );
873
874
}
875
+ // }}}
874
876
875
877
template <typename Cell>
876
878
void Screen<Cell>::eraseCharacters(ColumnCount _n)
@@ -890,6 +892,139 @@ void Screen<Cell>::eraseCharacters(ColumnCount _n)
890
892
line.useCellAt (_state.cursor .position .column + i).reset (_state.cursor .graphicsRendition );
891
893
}
892
894
895
+ // {{{ DECSEL
896
+ template <typename Cell>
897
+ void Screen<Cell>::selectiveEraseToEndOfLine()
898
+ {
899
+ if (_terminal.isFullHorizontalMargins () && _state.cursor .position .column .value == 0 )
900
+ selectiveEraseLine (_state.cursor .position .line );
901
+ else
902
+ selectiveErase (_state.cursor .position .line ,
903
+ _state.cursor .position .column ,
904
+ ColumnOffset::cast_from (_state.pageSize .columns ));
905
+ }
906
+
907
+ template <typename Cell>
908
+ void Screen<Cell>::selectiveEraseToBeginOfLine()
909
+ {
910
+ if (_terminal.isFullHorizontalMargins ()
911
+ && _state.cursor .position .column .value == _state.pageSize .columns .value )
912
+ selectiveEraseLine (_state.cursor .position .line );
913
+ else
914
+ selectiveErase (_state.cursor .position .line , ColumnOffset (0 ), _state.cursor .position .column + 1 );
915
+ }
916
+
917
+ template <typename Cell>
918
+ void Screen<Cell>::selectiveEraseLine(LineOffset line)
919
+ {
920
+ if (containsProtectedCharacters (line, ColumnOffset (0 ), ColumnOffset::cast_from (_state.pageSize .columns )))
921
+ {
922
+ selectiveErase (line, ColumnOffset (0 ), ColumnOffset::cast_from (_state.pageSize .columns ));
923
+ return ;
924
+ }
925
+
926
+ currentLine ().reset (grid ().defaultLineFlags (), _state.cursor .graphicsRendition );
927
+
928
+ auto const left = ColumnOffset (0 );
929
+ auto const right = boxed_cast<ColumnOffset>(_state.pageSize .columns - 1 );
930
+ auto const area = Rect { Top (*line), Left (*left), Bottom (*line), Right (*right) };
931
+ _terminal.markRegionDirty (area);
932
+ }
933
+
934
+ template <typename Cell>
935
+ void Screen<Cell>::selectiveErase(LineOffset line, ColumnOffset begin, ColumnOffset end)
936
+ {
937
+ Cell* i = &at (line, begin);
938
+ Cell const * e = i + unbox<uintptr_t >(end - begin);
939
+ while (i != e)
940
+ {
941
+ if (i->isFlagEnabled (CellFlags::CharacterProtected))
942
+ {
943
+ ++i;
944
+ continue ;
945
+ }
946
+ i->reset (_state.cursor .graphicsRendition );
947
+ ++i;
948
+ }
949
+
950
+ auto const left = begin;
951
+ auto const right = end - 1 ;
952
+ auto const area = Rect { Top (*line), Left (*left), Bottom (*line), Right (*right) };
953
+ _terminal.markRegionDirty (area);
954
+ }
955
+
956
+ template <typename Cell>
957
+ bool Screen<Cell>::containsProtectedCharacters(LineOffset line, ColumnOffset begin, ColumnOffset end) const
958
+ {
959
+ Cell const * i = &at (line, begin);
960
+ Cell const * e = i + unbox<uintptr_t >(end - begin);
961
+ while (i != e)
962
+ {
963
+ if (i->isFlagEnabled (CellFlags::CharacterProtected))
964
+ return true ;
965
+ ++i;
966
+ }
967
+ return false ;
968
+ }
969
+ // }}}
970
+ // {{{ DECSED
971
+ template <typename Cell>
972
+ void Screen<Cell>::selectiveEraseToEndOfScreen()
973
+ {
974
+ selectiveEraseToEndOfLine ();
975
+
976
+ auto const lineStart = unbox<int >(_state.cursor .position .line ) + 1 ;
977
+ auto const lineEnd = unbox<int >(_state.pageSize .lines );
978
+
979
+ for (auto const lineOffset: ranges::views::iota (lineStart, lineEnd))
980
+ selectiveEraseLine (LineOffset::cast_from (lineOffset));
981
+ }
982
+
983
+ template <typename Cell>
984
+ void Screen<Cell>::selectiveEraseToBeginOfScreen()
985
+ {
986
+ selectiveEraseToBeginOfLine ();
987
+
988
+ for (auto const lineOffset: ranges::views::iota (0 , *_state.cursor .position .line ))
989
+ selectiveEraseLine (LineOffset::cast_from (lineOffset));
990
+ }
991
+
992
+ template <typename Cell>
993
+ void Screen<Cell>::selectiveEraseScreen()
994
+ {
995
+ for (auto const lineOffset: ranges::views::iota (0 , *_state.pageSize .lines ))
996
+ selectiveEraseLine (LineOffset::cast_from (lineOffset));
997
+ }
998
+ // }}}
999
+ // {{{ DECSERA
1000
+ template <typename Cell>
1001
+ void Screen<Cell>::selectiveEraseArea(Rect area)
1002
+ {
1003
+ auto const [top, left, bottom, right] = applyOriginMode (area).clampTo (_state.pageSize );
1004
+ assert (unbox<int >(right) <= unbox<int >(_state.pageSize .columns ));
1005
+ assert (unbox<int >(bottom) <= unbox<int >(_state.pageSize .lines ));
1006
+
1007
+ if (top.value > bottom.value || left.value > right.value )
1008
+ return ;
1009
+
1010
+ for (int y = top.value ; y <= bottom.value ; ++y)
1011
+ {
1012
+ for (Cell& cell: grid ()
1013
+ .lineAt (LineOffset::cast_from (y))
1014
+ .useRange (ColumnOffset::cast_from (left),
1015
+ ColumnCount::cast_from (right.value - left.value + 1 )))
1016
+ {
1017
+ if (!cell.isFlagEnabled (CellFlags::CharacterProtected))
1018
+ {
1019
+ cell.writeTextOnly (L' ' , 1 );
1020
+ cell.setHyperlink (HyperlinkId (0 ));
1021
+ }
1022
+ }
1023
+ }
1024
+ }
1025
+ // }}}
1026
+
1027
+ // {{{ EL
893
1028
template <typename Cell>
894
1029
void Screen<Cell>::clearToEndOfLine()
895
1030
{
@@ -943,6 +1078,7 @@ void Screen<Cell>::clearLine()
943
1078
auto const area = Rect { Top (*line), Left (*left), Bottom (*line), Right (*right) };
944
1079
_terminal.markRegionDirty (area);
945
1080
}
1081
+ // }}}
946
1082
947
1083
template <typename Cell>
948
1084
void Screen<Cell>::moveCursorToNextLine(LineCount _n)
@@ -1821,9 +1957,11 @@ void Screen<Cell>::requestStatusString(RequestStatusString _value)
1821
1957
case RequestStatusString::DECSNLS: return fmt::format (" {}*|" , _state.pageSize .lines );
1822
1958
case RequestStatusString::SGR:
1823
1959
return fmt::format (" 0;{}m" , vtSequenceParameterString (_state.cursor .graphicsRendition ));
1824
- case RequestStatusString::DECSCA: // TODO
1825
- errorlog ()(fmt::format (" Requesting device status for {} not implemented yet." , _value));
1826
- break ;
1960
+ case RequestStatusString::DECSCA: {
1961
+ auto const isProtected =
1962
+ _state.cursor .graphicsRendition .styles & CellFlags::CharacterProtected;
1963
+ return fmt::format (" {}\" q" , isProtected ? 1 : 2 );
1964
+ }
1827
1965
case RequestStatusString::DECSASD:
1828
1966
switch (_state.activeStatusDisplay )
1829
1967
{
@@ -3184,6 +3322,48 @@ ApplyResult Screen<Cell>::apply(FunctionDefinition const& function, Sequence con
3184
3322
break ;
3185
3323
case DECDC: deleteColumns (seq.param_or (0 , ColumnCount (1 ))); break ;
3186
3324
case DECIC: insertColumns (seq.param_or (0 , ColumnCount (1 ))); break ;
3325
+ case DECSCA: {
3326
+ auto const Pc = seq.param_or (0 , 0 );
3327
+ switch (Pc)
3328
+ {
3329
+ case 1 :
3330
+ _state.cursor .graphicsRendition .styles |= CellFlags::CharacterProtected;
3331
+ return ApplyResult::Ok;
3332
+ case 0 :
3333
+ case 2 :
3334
+ _state.cursor .graphicsRendition .styles &= ~CellFlags::CharacterProtected;
3335
+ return ApplyResult::Ok;
3336
+ default : return ApplyResult::Invalid;
3337
+ }
3338
+ }
3339
+ case DECSED: {
3340
+ switch (seq.param_or (0 , Sequence::Parameter { 0 }))
3341
+ {
3342
+ case 0 : selectiveEraseToEndOfScreen (); break ;
3343
+ case 1 : selectiveEraseToBeginOfScreen (); break ;
3344
+ case 2 : selectiveEraseScreen (); break ;
3345
+ default : return ApplyResult::Unsupported;
3346
+ }
3347
+ return ApplyResult::Ok;
3348
+ }
3349
+ case DECSERA: {
3350
+ auto const top = seq.param_or (0 , Top (1 )) - 1 ;
3351
+ auto const left = seq.param_or (1 , Left (1 )) - 1 ;
3352
+ auto const bottom = seq.param_or (2 , Bottom::cast_from (_state.pageSize .lines )) - 1 ;
3353
+ auto const right = seq.param_or (3 , Right::cast_from (_state.pageSize .columns )) - 1 ;
3354
+ selectiveEraseArea (Rect { top, left, bottom, right });
3355
+ return ApplyResult::Ok;
3356
+ }
3357
+ case DECSEL: {
3358
+ switch (seq.param_or (0 , Sequence::Parameter { 0 }))
3359
+ {
3360
+ case 0 : selectiveEraseToEndOfLine (); break ;
3361
+ case 1 : selectiveEraseToBeginOfLine (); break ;
3362
+ case 2 : selectiveEraseLine (_state.cursor .position .line ); break ;
3363
+ default : return ApplyResult::Invalid;
3364
+ }
3365
+ return ApplyResult::Ok;
3366
+ }
3187
3367
case DECRM: {
3188
3368
ApplyResult r = ApplyResult::Ok;
3189
3369
crispy::for_each (crispy::times (seq.parameterCount ()), [&](size_t i) {
0 commit comments