Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should Cursor and Mode Savepoints be stacks? #1264

Closed
tarmack opened this issue Dec 23, 2018 · 6 comments
Closed

Should Cursor and Mode Savepoints be stacks? #1264

tarmack opened this issue Dec 23, 2018 · 6 comments

Comments

@tarmack
Copy link
Contributor

tarmack commented Dec 23, 2018

I have been running into issues with the way the "save cursor" function in implemented. Dstat uses it to redraw a line multiple times, but because the cursor restore pops the stack a second attempt to restore the cursor will move it to the default top left position.

I went and try to find the reason for using a stack here and could only find that this implementation goes back to the origins of pyte before their current github repo. I tried in a few other terminal emulators and they all seem to just hold one (the most recent) save point. Is there a reason for using a stack that I'm missing? I did find a little comment in the history of xterm where the author mentions that using a stack would solve some problem but would be incompatible with a lot of applications.

In the documentation of DEC and other documents describing the cursor save and restore functions there is never any mention of having more than just one save point (also nothing explicitly saying there can only be one though).

I suspect it is save and probably even more compatible to have just one saved state per buffer (main and alternate).

The screen mode saving and restoring uses the same style, I am not sure if that is useful either but did not go as deep with my research. But it may very well also be simplified to just one saved state.

If you have no objections I will put a pull request with the cursor changes. Please let me know if you think the save/restore mode functions could also be simplified, I will then do that as well.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Dec 23, 2018

I'm not sure I understand the issue. Are you saying that multiple DECRC (restore cursor codes) should not move the cursor to (0. 0)? Because according to the VT100 manuals, they should: https://vt100.net/docs/vt510-rm/DECRC.html

Or are you saying that a DECSC will then affect the behavior of all subsequent DECRC codes? That seems very wrong to me. It would mean that if application A did a DECSC followed by a DECRC and quit. Then the meaning of DECRC for all subsequent applications is now changed, as it will restore cursor state to whatever application A saved it as. I cant believe that is the intended mode of operation of DECSC/DECRC

@tarmack
Copy link
Contributor Author

tarmack commented Dec 24, 2018

I am reading that manual as saying DECRC should only move the cursor to 0,0 when nothing was ever saved before. For my problem, imagine the following: (DECSC, print line, DECRC, print line, DECRC). What happens on the bold DECRC? Any other terminal I tried will move the cursor to the saved position (in my opinion in accordance with the linked manual), Kitty currently will move the cursor to 0,0 on the second restore code.

I see the problem with one program changing the saved position for another program. However it seems that no other commonly used terminal emulator is preventing this and I highly doubt that the DEC terminals did to be honest. Thinking about it, in the current implementation (stack) if the second program actually does a DECSC but never sends a DECRC, the first program will end up with the wrong savepoint as well. This is actually inevitable with the terminal state being a shared resource.

I think of these issues as two mutually exclusive problems and as such there is no perfect solution. Most terminals seem to opt for the single savepoint approach, Kitty seems to be the odd one out that's I why I brought this question up.

@kovidgoyal
Copy link
Owner

The manual does not say if nothing was ever saved before, it says if
nothing was saved before. I choose to interpret that as meaning if there
was no matching call to DECSC, not as, if there was never a call to
DECSC.

The solution to your problem is obvious: Do DECSC, print line, DECRC,
DECSC, print line instead. It will work in all terminals.

And no, other terminals do not handle the case of one program doing
decsc/decrc and the then quitting. It is impossible since there is no
way for a terminal, in general, to know anything about the programs
running on it. Remember that terminals historically connected over
serial lines and even today using ssh, a terminal cannot know when one
program ends and another starts.

As for a program doing decsc and no matching decrc, that is a
misbehaving program. Obviously terminal states are not robust against
misbehaving programs. However, with your proposal, terminals are not
robust against even well behaved programs. This is not acceptable to me.

Finally, on a purely technical note, there is no reason to not have a
stack. If you did want to implement the behavior you describe, the
solution would be to never allow DECRC to empty the stack.

@kovidgoyal
Copy link
Owner

Let me illustrate the incorrectness of not using a stack, with an example. Suppose you have a program that does the following:

  1. DECRC
  2. some output
  3. DECSC
  4. some output
  5. DECRC
  6. some output

Now the first time it runs, the output from step (2) above will be at (0, 0) which is what the program would expect from reading the VT100 manual. Now suppose the program is run again in the same terminal. The output from step (2) will now not be at (0, 0).

Not using a stack for save/restore essentially means that a restore without a preceding save is undefined. Whereas using a stack means that as long as programs are well behaved (they drain the stack before quitting), restore is well defined.

@zsugabubus
Copy link

The manual does not say if nothing was ever saved before, it says if
nothing was saved before. I choose to interpret that as meaning if there
was no matching call to DECSC, not as, if there was never a call to
DECSC.

TL;DR: It’s praiseworthy that you interpret it in such way, but every other terminal and application isn’t.

You miss one word “ever” from the standard so you say it’s bullshit. But you imagine there multiple words, like “matching call” and non-existing specification sentences that you believe is more true than the way everybody else is doing.

DECRC:

Restores the terminal to the state saved by the save cursor (DECSC) function.

If nothing was saved by DECSC…

  • (big boom.)
  • Was anything saved by DECSC? No.
  • DECSC -> state has been saved
  • Was anything saved by DECSC? Yes, there was something saved before.
  • DECRC -> “Restores the terminal to the state saved by DECSC”
  • Was anything saved by DECSC? Yes, there was something saved before.
  • DECRC -> “Restores the terminal to the state saved by DECSC”
  • Was anything saved by DECSC? Yes, there was something saved before.

Obviously, the “If nothing was saved by DECSC…” brach was tailored for a zeroed-out memory, so DECRC do not have to care about/maintain plus fields whether a DECSC did or did not happen before.

If standard would require stacking behavior that would got more emphasis. And really, an old hardware terminal should be able to dynamically allocate infinite memory? An upper limit would be surely mentioned in case of stacking behavior, no?

The solution to your problem is obvious: Do DECSC, print line, DECRC,
DECSC, print line instead. It will work in all terminals.

For whoever would not it be obvious, we obviously need to modify all terminals which are not stacking and all programs that currently not expecting a stacking behavior. Obviously, newly written programs who have not heard about kitty and its special behavior will not emit the sequences in pair; however programs which expect stacking behavior have to emulate it somehow in every other terminal or will not be portable otherwise.

Let me illustrate the incorrectness of not using a stack, with an example. Suppose you have a program that does the following:

Let me illustr… it’s an artifact example that goes against the standard. You try to prove the truth of stacking behavior through an example. But who told that stacking behavior matches the standard at all?

Prove that 1+1=3, assuming that 1+1=3. True? True. ■

Why do not you give such correctness example:

  • Cursor at (10, 10).
  • DECSC
  • DECRC
  • DECRC

“My stacking behavior will be at (0, 0) that is non-standard. Some other stuff has been also restored to its original value that programs do not expect.”

Not using a stack for save/restore essentially means that a restore without a preceding save is undefined. Whereas using a stack means that as long as programs are well behaved…

So you also say that stacking just protects you form nothing? Because a well-behaved program always emits a DECSC before they doing DECRC, so it’s always defined. (When it cannot make sure that a DECRC will be defined it simply does not do it.) Also, a well-behaved program does not expect a stacking behavior.

…(they drain the stack before quitting)…

So neither your stacking behavior can be used across program invocations.

Now suppose the program is run again in the same terminal.

But… but… then… your whole example is completely false. You have disproved it yourself. You start your program with a “1. DECRC” but how you made sure that a previous program have completely drained the whole stack? As mentioned above, a program will not use “DECRC” without a previous “DECSC”. Your stacking thing then would be only interesting inside a single program. But such complex programs will use explicit cursor movements instead of messing with position stack.

Anyway:

  • Black text.
  • Set foreground to red.
  • Red text.

Run again. OHHH NOOO! Black text gone red. In the same terminal. BlackRedmagic.


If you really want to keep stacking behavior, just go. But couldn’t you assign a separate sequence to it?

@kovidgoyal
Copy link
Owner

What a farrago of nonsense Go read what I wrote again. Actually, better yet, dont. I dont have time to waste explaining elementary things to you.

Repository owner locked as spam and limited conversation to collaborators Jun 26, 2020
kovidgoyal added a commit that referenced this issue Dec 1, 2020
For the sake of interoperability. This means that doing a DECRC without
a prior DECSC is now undefined. However, one can now issue multiple
DECRC for a single DECSC. Fixes #1264
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants