bug-groff
[Top][All Lists]
Advanced

[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/




reply via email to

[Prev in Thread] Current Thread [Next in Thread]