[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[bug #61561] [grotty] 'sgr' device control command takes effect only at
From: |
G. Branden Robinson |
Subject: |
[bug #61561] [grotty] 'sgr' device control command takes effect only at page breaks |
Date: |
Fri, 26 Nov 2021 18:15:44 -0500 (EST) |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 |
Update of bug #61561 (project groff):
Status: Need Info => Confirmed
_______________________________________________________
Follow-up Comment #6:
[comment #3 comment #3:]
> It seems that the state of SGR at the bottom of the page (when it gets
ejected?) is the one that wins.
> I'll see if I can find in the code how the fonts get re-initialized, or what
exactly is going on there.
Okay, here it is. grotty's `end_page()` is huge--nearly 200 lines. Near the
end of it we have the following nearly 100 lines of undoing all the possible
character attributes (bold, underlining, color, etc.) that might have been
applied.
790 if (hpos > p->hpos) {
791 do {
792 putchar('\b');
793 hpos--;
794 } while (hpos > p->hpos);
795 }
796 else {
797 if (want_horizontal_tabs) {
798 for (;;) {
799 int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) *
TAB_WIDTH;
800 if (next_tab_pos > p->hpos)
801 break;
802 if (is_continuously_underlining)
803 make_underline(p->w);
804 else if (!use_overstriking_drawing_scheme
805 && is_underlining) {
806 if (do_sgr_italics)
807 putstring(SGR_NO_ITALIC);
808 else if (do_reverse_video)
809 putstring(SGR_NO_REVERSE);
810 else
811 putstring(SGR_NO_UNDERLINE);
812 is_underlining = false;
813 }
814 putchar('\t');
815 hpos = next_tab_pos;
816 }
817 }
818 for (; hpos < p->hpos; hpos++) {
819 if (is_continuously_underlining)
820 make_underline(p->w);
821 else if (!use_overstriking_drawing_scheme && is_underlining) {
822 if (do_sgr_italics)
823 putstring(SGR_NO_ITALIC);
824 else if (do_reverse_video)
825 putstring(SGR_NO_REVERSE);
826 else
827 putstring(SGR_NO_UNDERLINE);
828 is_underlining = false;
829 }
830 putchar(' ');
831 }
832 }
833 assert(hpos == p->hpos);
834 if (p->mode & COLOR_CHANGE) {
835 if (!use_overstriking_drawing_scheme) {
836 if (p->fore_color_idx != curr_fore_idx) {
837 put_color(p->fore_color_idx, 0);
838 curr_fore_idx = p->fore_color_idx;
839 }
840 if (p->back_color_idx != curr_back_idx) {
841 put_color(p->back_color_idx, 1);
842 curr_back_idx = p->back_color_idx;
843 }
844 }
845 continue;
846 }
847 if (p->mode & UNDERLINE_MODE)
848 make_underline(p->w);
849 else if (!use_overstriking_drawing_scheme && is_underlining) {
850 if (do_sgr_italics)
851 putstring(SGR_NO_ITALIC);
852 else if (do_reverse_video)
853 putstring(SGR_NO_REVERSE);
854 else
855 putstring(SGR_NO_UNDERLINE);
856 is_underlining = false;
857 }
858 if (p->mode & BOLD_MODE)
859 make_bold(p->code, p->w);
860 else if (!use_overstriking_drawing_scheme && is_boldfacing) {
861 putstring(SGR_NO_BOLD);
862 is_boldfacing = false;
863 }
864 if (!use_overstriking_drawing_scheme) {
865 if (p->fore_color_idx != curr_fore_idx) {
866 put_color(p->fore_color_idx, 0);
867 curr_fore_idx = p->fore_color_idx;
868 }
869 if (p->back_color_idx != curr_back_idx) {
870 put_color(p->back_color_idx, 1);
871 curr_back_idx = p->back_color_idx;
872 }
873 }
874 put_char(p->code);
875 hpos += p->w / font::hor;
876 }
877 if (!use_overstriking_drawing_scheme
878 && (is_boldfacing || is_underlining
879 || curr_fore_idx != DEFAULT_COLOR_IDX
880 || curr_back_idx != DEFAULT_COLOR_IDX))
881 putstring(SGR_DEFAULT);
882 putchar('\n');
These undoings use the current state of SGR enablement _as configured by the
user_, not whatever state(s) might be active in the output stream going to the
terminal, so that addresses the point that surprised me. I'll say one thing
for legacy output mode--it's not stateful, or at least not for more than one
character cell. SGR is heavily so.
What the above does is unwind all the crazy stuff that might be in effect at
the end of a page so that the next one starts over from a clean slate. It
occurs to me that you could break this by enabling SGR (the default), going
nuts with SGR character attributes, and then contriving to disable SGR in the
midst of the page, which, as we've seen, won't actually turn anything off in
the terminal. When the page bottom is hit, all the logic that cleans up
character attribute state will be skipped, because you're in nice, clean
legacy output mode. Let's see.
Input:
$ cat EXPERIMENTS/grotty-make-a-mess.groff
.fcolor magenta
.gcolor white
.ft BI
obnoxious\X'tty: sgr'
.bp
still obnoxious\[em]ha ha ha
Hex dump of output:
$ ./build/test-groff -b -ww -T utf8 EXPERIMENTS/grotty-make-a-mess.groff |
xxd
grotty:<standard input>:16: debug: turning overstriking drawing scheme OFF
00000000: 1b5b 3337 6d1b 5b34 356d 1b5b 346d 1b5b .[37m.[45m.[4m.[
00000010: 316d 6f62 6e6f 7869 6f75 731b 5b30 6d0a 1mobnoxious.[0m.
00000020: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ................
00000030: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ................
00000040: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ................
00000050: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ................
00000060: 0a1b 5b34 6d1b 5b31 6d1b 5b33 376d 1b5b ..[4m.[1m.[37m.[
00000070: 3435 6d73 7469 6c6c 1b5b 3234 6d20 1b5b 45mstill.[24m .[
00000080: 346d 6f62 6e6f 7869 6f75 73e2 8094 6861 4mobnoxious...ha
00000090: 1b5b 3234 6d20 1b5b 346d 6861 1b5b 3234 .[24m .[4mha.[24
000000a0: 6d20 1b5b 346d 6861 1b5b 306d 0a0a 0a0a m .[4mha.[0m....
000000b0: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ................
000000c0: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ................
000000d0: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ................
000000e0: 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a 0a0a ..............
Yup.
In other words, most of the logic quoted above should have been moved into
`update_options()` or, even better, its own function, called from there _and_
`end_page()`.
I plan to experiment with terminfo, but if that goes badly or takes too long,
I'll take a stab at the "should have been" approach.
But even if I do the latter, I think this device control command needs to go
away. Terminal capabilities do not change in the course of rendering a
document[1]. If terminfo is infeasible, I reckon we'll need our own miniature
terminal capability description feature, produced by the formatter (troff) and
consumed by grotty(1) when it initializes so that it knows what to do. That
is pretty close to what 'sgr' was supposed to be and the only way it reliably
works today, except we have the problem that it communicates only 1 bit of
information when we need more.
[1] Real terminal application developers know all too well the notorious
exception to this: terminal window size and the asynchronous hell of SIGWINCH.
Fortunately, grotty(1) is a non-interactive application (most of the
time--I'm not forgetting `.rd`) and in any event troff's `.pl` and `.ll` (and
`.lt`) decide the matter of terminal dimensions for us. So I think we get to
enjoy some blissful ignorance in that respect.
_______________________________________________________
Reply to this item at:
<https://savannah.gnu.org/bugs/?61561>
_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/