1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00
Commit Graph

388 Commits

Author SHA1 Message Date
Simon Tatham
cd8a7181fd Complete rework of terminal userpass input system.
The system for handling seat_get_userpass_input has always been
structured differently between GUI PuTTY and CLI tools like Plink.

In the CLI tools, password input is read directly from the OS
terminal/console device by console_get_userpass_input; this means that
you need to ensure the same terminal input data _hasn't_ already been
consumed by the main event loop and sent on to the backend. This is
achieved by the backend_sendok() method, which tells the event loop
when the backend has finished issuing password prompts, and hence,
when it's safe to start passing standard input to backend_send().

But in the GUI tools, input generated by the terminal window has
always been sent straight to backend_send(), regardless of whether
backend_sendok() says it wants it. So the terminal-based
implementation of username and password prompts has to work by
consuming input data that had _already_ been passed to the backend -
hence, any backend that needs to do that must keep its input on a
bufchain, and pass that bufchain to seat_get_userpass_input.

It's awkward that these two totally different systems coexist in the
first place. And now that SSH proxying needs to present interactive
prompts of its own, it's clear which one should win: the CLI style is
the Right Thing. So this change reworks the GUI side of the mechanism
to be more similar: terminal data now goes into a queue in the Ldisc,
and is not sent on to the backend until the backend says it's ready
for it via backend_sendok(). So terminal-based userpass prompts can
now consume data directly from that queue during the connection setup
stage.

As a result, the 'bufchain *' parameter has vanished from all the
userpass_input functions (both the official implementations of the
Seat trait method, and term_get_userpass_input() to which some of
those implementations delegate). The only function that actually used
that bufchain, namely term_get_userpass_input(), now instead reads
from the ldisc's input queue via a couple of new Ldisc functions.

(Not _trivial_ functions, since input buffered by Ldisc can be a
mixture of raw bytes and session specials like SS_EOL! The input queue
inside Ldisc is a bufchain containing a fiddly binary encoding that
can represent an arbitrary interleaving of those things.)

This greatly simplifies the calls to seat_get_userpass_input in
backends, which now don't have to mess about with passing their own
user_input bufchain around, or toggling their want_user_input flag
back and forth to request data to put on to that bufchain.

But the flip side is that now there has to be some _other_ method for
notifying the terminal when there's more input to be consumed during
an interactive prompt, and for notifying the backend when prompt input
has finished so that it can proceed to the next stage of the protocol.
This is done by a pair of extra callbacks: when more data is put on to
Ldisc's input queue, it triggers a call to term_get_userpass_input,
and when term_get_userpass_input finishes, it calls a callback
function provided in the prompts_t.

Therefore, any use of a prompts_t which *might* be asynchronous must
fill in the latter callback when setting up the prompts_t. In SSH, the
callback is centralised into a common PPL helper function, which
reinvokes the same PPL's process_queue coroutine; in rlogin we have to
set it up ourselves.

I'm sorry for this large and sprawling patch: I tried fairly hard to
break it up into individually comprehensible sub-patches, but I just
couldn't tease out any part of it that would stand sensibly alone.
2021-09-14 13:19:33 +01:00
Simon Tatham
7a25599d84 Fix terminal redraw slowdown in presence of true colour.
When do_paint breaks up a line of terminal text into contiguous runs
of characters to treat the same, one of the criteria it uses is, 'Does
this character even need redrawing? (or is it already displayed
correctly from the previous redraw?)' When we encounter a character
that matches its previous value, we end the previous run of
characters, so that we can skip the one we've just encountered.

That check was not taking account of the 'truecolour' field of the
termchar it was checking. So it would sometimes falsely believe the
character to be equivalent to its previously drawn value, even when in
fact it was not, and hence insert a run break, anticipating that the
previous character needed drawing and the current one did not.

This didn't cause a _wrong_ redraw, because there's a separate loop
further on which re-checks whether to actually draw things, which
didn't make the same error. So the character that loop #1 thought
didn't need a redraw, loop #2 knew _did_ need a redraw, and hence,
everything did get redrawn.

But by the time loop #2 is running, it's too late to change the run
boundaries. So everything does get redrawn, but in much smaller chunks
than it could have been. The net effect was that if the screen was
filled with text displayed in true colour, and you changed it to the
_same_ text in a different colour, then the whole terminal would be
redrawn in one-character increments instead of the usual behaviour of
folding together runs that can be drawn in one go.

Thanks to Bradley Smith for debugging this very confusing issue!
2021-07-15 20:16:49 +01:00
Jacob Nevins
6d05e20a0e Remove a futile term_schedule_update().
term_invalidate() already implies it. (The extra call was harmless.)
2021-06-13 00:25:18 +01:00
Jacob Nevins
5677da6481 Fix palette escape sequences sometimes not working.
If a batch of palette changes were seen in between window updates, only
the last one would take immediate effect.
2021-06-13 00:18:42 +01:00
Jacob Nevins
1e726c94e8 Fix changing colours in Change Settings.
Since ca9cd983e1, changing colour config mid-session had no effect
(until the palette was reset for some other reason). Now it does take
effect immediately (provided that the palette has not been overridden by
escape sequence -- this is new with ca9cd983e1).

This changes the semantics of palette_reset(): the only important
parameter when doing that is whether we keep escape sequence overrides
-- there's no harm in re-fetching config and platform colours whether or
not they've changed -- so that's what the parameter becomes (with a
sense that doesn't require changing the call sites). The other part of
this change is actually remembering to trigger this when the
configuration is changed.
2021-05-20 23:39:42 +01:00
Simon Tatham
571fa3388d Make TermWin's palette_get_overrides() take a Terminal *.
Less than 12 hours after 0.75 went out of the door, a user pointed out
that enabling the 'Use system colours' config option causes an
immediate NULL-dereference crash. The reason is because a chain of
calls from term_init() ends up calling back to the Windows
implementation of the palette_get_overrides() method, which responds
by trying to call functions on the static variable 'term' in window.c,
which won't be initialised until term_init() has returned.

Simple fix: palette_get_overrides() is now given a pointer to the
Terminal that it should be updating, because it can't find it out any
other way.
2021-05-08 18:14:56 +01:00
Simon Tatham
ca48e2048c Make pterm's default window title more sensible.
In the rework last month, term_setup_window_titles made a centralised
decision about whether to make the default window title just the
application name, or the hostname and application name separated by a
dash. Unfortunately it based that decision on whether the hostname was
NULL, whereas I'd meant to include a _empty_ hostname as not worth
printing.

As a result, the default window title for pterm became " - pterm", not
just "pterm". Easily fixed.
2021-03-08 06:59:26 +00:00
Simon Tatham
334688db81 Apply UPDATE_DELAY in arrears, not in advance.
The original aim of the rate limit was to avoid having too many
updates per second. I implemented this by a deferment mechanism: when
any change occurs that makes the terminal want an update, it instead
sets a timer to go off after UPDATE_DELAY (1/50 second), and does the
update at the end of that interval.

Now it's done the other way round: if there has not been an update
within the last UPDATE_DELAY, then we can simply do an update _right
now_, in immediate response to whatever triggered it. And _then_ we
set a timer to track a cooldown period, within which any further
requests for updates will be deferred until the end of the cooldown.

This mechanism should still rate-limit updates, but now the latency in
normal interactive use should be lowered, because terminal updates in
response to keystrokes (which typically arrive separated by more than
UPDATE_DELAY) can now each be enacted as soon as possible after the
triggering keystroke.

This also reverses (in the common case) the slowdown of non-textual
window modifications introduced by the previous commit, in which lots
of them were brought under the umbrella of term_update and therefore
became subject to UPDATE_DELAY. Now they'll only be delayed in
conditions of high traffic, and not in interactive use.
2021-02-07 19:59:21 +00:00
Simon Tatham
d74308e90e Fold ancillary window changes into main redraw.
This fixes a long-standing inconsistency in updates to the terminal
window: redrawing of actual text was deferred for 1/50 second, but all
the other kinds of change the terminal can make to the window
(position, size, z-order, title, mouse pointer shape, scrollbar...)
were enacted immediately. In particular, this could mean that two
updates requested by the terminal output stream happened in reverse
order.

Now they're all done as part of term_update, which should mean that
things requested in the same chunk of terminal input happen at the
same time, or at the very least, not in reverse order compared to the
order the requests came in.

Also, the same timer-based UPDATE_DELAY mechanism that applies to the
text updates now applies to all the other window modifications, which
should prevent any of those from being the limiting factor to how fast
this terminal implementation can process input data (which is exactly
why I set up that system for the main text update).

This makes everything happen with a bit more latency, but I'm about to
reverse that in a follow-up commit.
2021-02-07 19:59:21 +00:00
Simon Tatham
99dfc66457 Decouple frontend's raw mouse mode from pointer shape.
This paves the way for a followup commit that will make them happen at
slightly different times.
2021-02-07 19:59:21 +00:00
Simon Tatham
07aff63e22 Centralise check of CONF_no_mouse_rep into Terminal.
This removes code duplication between the front ends: now the terminal
itself knows when the Conf is asking it not to turn on mouse
reporting, and the front ends can assume that if the terminal asks
them to then they should just do it.

This also makes the behaviour on mid-session reconfiguration more
sensible, in both code organisation and consistent behaviour.
Previously, term_reconfig would detect that CONF_no_mouse_rep had been
*set* in mid-session, and turn off mouse reporting mode in response.
But it would do it by clearing term->xterm_mouse, which isn't how the
front end enabled and disabled that feature, so things could get into
different states from different sequences of events that should have
ended up in the same place.

Also, the terminal wouldn't re-enable mouse reporting if
CONF_no_mouse_rep was *cleared* and the currently running terminal app
had been asking for mouse reports all along. Also, it was silly to
have half the CONF_no_mouse_rep handling in term_reconfig and the
other half in the front ends.

Now it should all be sensible, and also all centralised.
term->xterm_mouse consistently tracks whether the terminal application
is _requesting_ mouse reports; term->xterm_mouse_forbidden tracks
whether the client user is vetoing them; every change to either one of
those settings triggers a call to term_update_raw_mouse_mode which
sets up the front end appropriately for the current combination.
2021-02-07 19:59:21 +00:00
Simon Tatham
696550a5f2 Flip direction of window pos/size queries.
Similarly to other recent changes, the frontend now proactively keeps
Terminal up to date with the current position and size of the terminal
window, so that escape-sequence queries can be answered immediately
from the Terminal's own internal data structures without needing a
call back to the frontend.

Mostly this has let me remove explicit window-system API calls that
retrieve the window position and size, in favour of having the front
ends listen for WM_MOVE / WM_SIZE / ConfigureNotify events and track
the position and size that way. One exception is that the window pixel
size is still requested by Seat via a callback, to put in the
wire-encoded termios settings. That won't be happening very much, so
I'm leaving it this way round for the moment.
2021-02-07 19:59:21 +00:00
Simon Tatham
ca9cd983e1 Centralise palette setup into terminal.c.
Now terminal.c makes nearly all the decisions about what the colour
palette should actually contain: it does the job of reading the
GUI-configurable colours out of Conf, and also the job of making up
the rest of the xterm-256 palette. The only exception is that TermWin
can provide a method to override some of the default colours, which on
Windows is used to implement the 'Use system colours' config option.

This saves code overall, partly because the front ends don't have to
be able to send palette data back to the Terminal any more (the
Terminal keeps the master copy and can answer palette-query escape
sequences from its own knowledge), and also because now there's only
one copy of the xterm-256 palette setup code (previously gtkwin.c and
window.c each had their own version of it).

In this rewrite, I've also introduced a multi-layered storage system
for the palette data in Terminal. One layer contains the palette
information derived from Conf; the next contains platform overrides
(currently just Windows's 'Use system colours'); the last one contains
overrides set by escape sequences in the middle of the session. The
topmost two layers can each _conditionally_ override the ones below.
As a result, if a server-side application manually resets (say) the
default fg and bg colours in mid-session to something that works well
in a particular application, those changes won't be wiped out by a
change in the Windows system colours or the Conf, which they would
have been before. Instead, changes in Conf or the system colours alter
the lower layers of the structure, but then when palette_rebuild is
called, the upper layer continues to override them, until a palette
reset (ESC]R) or terminal reset (e.g. ESC c) removes those upper-layer
changes. This seems like a more consistent strategy, in that the same
set of configuration settings will produce the same end result
regardless of what order they were applied in.

The palette-related methods in TermWin have had a total rework.
palette_get and palette_reset are both gone; palette_set can now set a
contiguous range of colours in one go; and the new
palette_get_overrides replaces window.c's old systopalette().
2021-02-07 19:59:21 +00:00
Simon Tatham
cd32ef8733 Indentation fixes in terminal.c.
While working on the palette code I noticed several sections where a
2-space indent policy was used. Let's keep things consistent.

This patch is whitespace-only, with the single exception that I've
removed a pair of braces where a for statement contains only a switch
(and, cheatingly, put the for and switch on the same indent level so
that the interior of it doesn't go _too_ far off to the right).
2021-02-07 19:59:20 +00:00
Simon Tatham
da3197f395 Bring some order to colour palette indexing.
There are three separate indexing schemes in use by various bits of
the PuTTY front ends, and _none_ of them was clearly documented, let
alone all in the same place. Worse, functions that looked obviously
related, like win_palette_set and win_palette_get, used different
encodings.

Now all the encodings are defined together in putty.h, with
explanation of why there are three in the first place and clear
documentation of where each one is used; terminal.c provides mapping
tables that convert between them; the terminology is consistent
throughout; and win_palette_set has been converted to use the sensible
encoding.
2021-02-07 19:59:20 +00:00
Simon Tatham
61571376cc Remove TermWin's is_minimised method.
Again, I've replaced it with a push-based notification going in the
other direction, so that when the terminal output stream includes a
query for 'is the window minimised?', the Terminal doesn't have to
consult the TermWin, because it already knows the answer.

The GTK API I'm using here (getting a GdkEventWindowState via
GtkWidget's window-state-event) is not present in GTK 1. The API I was
previously using (gdk_window_is_viewable) _is_, but it turns out that
that API doesn't reliably give the right answer: it only checks
visibility of GDK window ancestors, not X window ancestors. So in fact
GTK 1 PuTTY/pterm was only ever _pretending_ to reliably support the
'am I minimised' terminal query. Now it won't pretend any more.
2021-02-07 19:59:20 +00:00
Simon Tatham
42ad454f4f Move all window-title management into Terminal.
Previously, window title management happened in a bipartisan sort of
way: front ends would choose their initial window title once they knew
what host name they were connecting to, but then Terminal would
override that later if the server set the window title by escape
sequences.

Now it's all done the same way round: the Terminal object is always
where titles are invented, and they only propagate in one direction,
from the Terminal to the TermWin.

This allows us to avoid duplicating in multiple front ends the logic
for what the initial window title should be. The frontend just has to
make one initial call to term_setup_window_titles, to tell the
terminal what hostname should go in the default title (if the Conf
doesn't override even that). Thereafter, all it has to do is respond
to the TermWin title-setting methods.

Similarly, the logic that handles window-title changes as a result of
the Change Settings dialog is also centralised into terminal.c. This
involved introducing an extra term_pre_reconfig() call that each
frontend can call to modify the Conf that will be used for the GUI
configurer; that's where the code now lives that copies the current
window title into there. (This also means that GTK PuTTY now behaves
consistently with Windows PuTTY on that point; GTK's previous
behaviour was less well thought out.)

It also means there's no longer any need for Terminal to talk to the
front end when a remote query wants to _find out_ the window title:
the Terminal knows the answer already. So TermWin's get_title method
can go.
2021-02-07 19:59:20 +00:00
Simon Tatham
45b03419fd Remove TermWin's is_utf8 method.
All implementations of it work by checking the line_codepage field in
the ucsdata structure that the terminal itself already has a pointer
to. Therefore, it's a totally unnecessary query function: the terminal
can check the same thing directly by inspecting that structure!

(In fact, it already _does_ do that, for the purpose of actually
deciding how to decode terminal output data. It only uses this query
function at all for the auxiliary purpose of inventing useful tty
modes to pass to the backend.)
2021-02-07 19:59:20 +00:00
Simon Tatham
9ea0dfd8c0 Fix memory corruption in scrollback compression.
Introduced by commit 5891142aee, in which I invented
strbuf_shrink_to() and went round replacing lots of assignments of the
form 'sb->len = smaller_value' with calls to it. The bug is also in
0.74, because 34a0460f05 is a cherry-pick of the commit that
introduced it.

The difference between that assignment and strbuf_shrink_to is partly
that the latter checks by assertion that the new length really is
_smaller_ - it doesn't let you accidentally grow a strbuf's length
field beyond the limit of its buffer (or indeed at all). But also,
strbuf_shrink_to re-establishes the strbuf invariant that the text
logically in the buffer is always followed by a zero byte, so that
it's a valid C string.

Unfortunately, in one of the places I made this change, I was storing
binary data in the strbuf (so the terminating NUL is unimportant), and
immediately after decreasing the strbuf's length, I was doing a memcmp
one of whose arguments was the data I'd just chopped off the end of
the strbuf. So it _mattered_ that no random NUL had been splurged over
it.

Specifically, this happened in the run-length encoder used to compress
scrollback data, and had the effect that two components of the
compressed scrollback could be spuriously considered equal, if one of
them started with a legitimate zero byte and the other had a zero byte
written over it by this bug. Thanks to Michael Weller for a nice test
case that demonstrated a compressed scrollback line being decompressed
again as the wrong thing:

"NORMAL TEXT, \033[42mGREEN BACKGROUND\033[0m, NORMAL TEXT AGAIN"

If the above line is printed to the terminal (after being decoded as
if it was a C string literal), then only the words "GREEN BACKGROUND"
get a green background. But after that line is scrolled off the top of
the window, if you find it in the scrollback, then the rest of the
line to the right has also become green-backgrounded due to this bug.
2020-10-27 18:38:35 +00:00
Simon Tatham
150089ac16 Fix bit rot in TERM_CC_DIAGS ifdef.
This is a piece of conditioned-out code that I haven't used since I
originally invented the compressed scrollback format, which
decompressed every scrollback line immediately after compressing it to
check that the round-trip conversion worked. Now I have occasion to
actually use it, I find that (of course) changes around it have made
it not quite work any more: the thing the diagnostic code is passing
to decompressline hasn't had its length field filled in yet, because
that gets done 20 lines later.

Now you can compile with -DTERM_CC_DIAGS again, and it doesn't crash
_unless_ it detects the actual bug it was intended to spot.
2020-10-27 18:15:12 +00:00
Simon Tatham
06a8d11964 Support SGR 9 for strikethrough effect on text.
This is mostly easy: it's just like drawing an underline, except that
you put it at a different height in the character cell. The only
question is _where_ in the character cell.

Pango, and Windows GetOutlineTextMetrics, will tell you exactly where
the font wants to have it. Following xterm, I fall back to 3/8 of the
font's ascent (above the baseline) if either of those is unavailable.
2020-08-13 21:08:53 +01:00
Simon Tatham
2762a2025f Merge the 0.74 release branch back to master.
Two minor memory-leak fixes on 0.74 seem not to be needed on master:
the fix in an early exit path of pageant_add_keyfile is done already
on master in a different way, and the missing sfree(fdlist) in
uxsftp.c is in code that's been completely rewritten in the uxcliloop
refactoring.

Other minor conflicts: the rework in commit b52641644905 of
ssh1login.c collided with the change from FLAG_VERBOSE to
seat_verbose(), and master and 0.74 each added an unrelated extra
field to the end of struct SshServerConfig.
2020-06-27 08:11:22 +01:00
Simon Tatham
2316b44426 Missing NULL check in swap_screen.
Coverity points out that this function is mostly written as if it's
intended to allow for term->screen and/or term->alt_screen to be NULL,
but makes an unguarded call to find_last_nonempty_line on one of them.

I don't immediately remember _why_ I needed to deal with those
pointers being null, but it was probably a safety precaution against
swap_screen being called during setup or during reconfiguration, in
which case it seems sensible to keep it even if it's not needed in the
_current_ state of the code. So, added the missing check.
2020-06-21 16:39:47 +01:00
Simon Tatham
bbf6daf612 Remove obsolete checks for ldisc_send with len == 0.
This reverts commit 4634cd47f7 and
commit 43a63019f5, both of which
introduced checks at ldisc_send call sites to avoid triggering the
assertion that len != 0 inside ldisc_send. Now that assertion is gone,
it's OK to call ldisc_send without checking the buffer size.

(cherry picked from commit 2bbed67d9e)
2020-06-14 15:49:36 +01:00
Simon Tatham
2bbed67d9e Remove obsolete checks for ldisc_send with len == 0.
This reverts commit 4634cd47f7 and
commit 43a63019f5, both of which
introduced checks at ldisc_send call sites to avoid triggering the
assertion that len != 0 inside ldisc_send. Now that assertion is gone,
it's OK to call ldisc_send without checking the buffer size.
2020-06-14 10:17:20 +01:00
Lars Brinkhoff
a8bb6456d1 Add a new seat method to return the cursor position.
The motivation is for the SUPDUP protocol.  The server may send a
signal for the terminal to reset any input buffers.  After this, the
server will not know the state of the terminal, so it is required to
send its cursor position back.
2020-03-10 07:01:46 +00:00
Simon Tatham
8d186c3c93 Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form

    switch (discriminant) {
      case SIMPLE:
        do stuff;
        break;
      case COMPLICATED:
        {
            declare variables;
            do stuff;
        }
        break;
    }

which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!

After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:

    switch (discriminant) {
      case SIMPLE:
        do stuff;
        break;
      case COMPLICATED: {
        declare variables;
        do stuff;
        break;
      }
    }

This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.

(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)

Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 11:26:21 +00:00
Simon Tatham
697cfa5b7f Use strbuf to store results in prompts_t.
UBsan pointed out another memcpy from NULL (again with length 0) in
the prompts_t system. When I looked at it, I realised that firstly
prompt_ensure_result_size was an early not-so-good implementation of
sgrowarray_nm that would benefit from being replaced with a call to
the real one, and secondly, the whole system for storing prompt
results should really have been replaced with strbufs with the no-move
option, because that's doing all the same jobs better.

So, now each prompt_t holds a strbuf in place of its previous manually
managed string. prompt_ensure_result_size is gone (the console
prompt-reading functions use strbuf_append, and everything else just
adds to the strbuf in the usual marshal.c way). New functions exist to
retrieve a prompt_t's result, either by reference or copied.

(cherry picked from commit cd6bc14f04)
2020-02-09 08:51:37 +00:00
Simon Tatham
34a0460f05 New functions to shrink a strbuf.
These are better than my previous approach of just assigning to
sb->len, because firstly they check by assertion that the new length
is within range, and secondly they preserve the invariant that the
byte stored in the buffer just after the length runs out is \0.

Switched to using the new functions everywhere a grep could turn up
opportunities.

(cherry picked from commit 5891142aee)
2020-02-09 08:51:37 +00:00
Simon Tatham
e1344d6ca7 Fix ldisc_send() assertion in terminal answerback.
A user reports that if the ^E answerback string is configured to be
empty, then causing the answerback to be sent fails the assertion in
ldisc_send introduced in commit c269dd013.

I thought I'd caught all of the remaining cases of this in commit
4634cd47f, but apparently not.

(cherry picked from commit 43a63019f5)
2020-02-09 08:51:37 +00:00
Simon Tatham
874ce8239c Fix handling of scroll position when swapping screens.
If the user is scrolled back in the scrollback when a screen-swap
takes place, and if we're not configured to reset the scrollback
completely on the grounds that the swap is display activity, then we
should do the same thing we do for other kinds of display activity:
strive to keep the scroll position pointing at the same text. In this
case, that means adjusting term->disptop by the number of virtual
lines added to the scrollback to allow the main screen to be viewed
while the alt screen is active.

This improves the quality of behaviour in that corner case, but more
importantly, it should also fix a case of the dreaded line==NULL
assertion failure, which someone just reported against 0.73 when
exiting tmux (hence, switching away from the alt screen) while
scrolled back in a purely virtual scrollback buffer: the virtual
scrollback lines vanished, but disptop was still set to a negative
value, which made it out of range.

(cherry picked from commit 22453b46da)
2020-02-09 08:19:21 +00:00
Simon Tatham
b77bcae021 Fix cursor save/restore with [?1047 alt-screen sequences.
A long time ago, in commit 09f86ce7e, I introduced a separate copy of
the saved cursor position (used by the ESC 7 / ESC 8 sequences) for
the main and alternate screens. The idea was to fix mishandling of an
input sequence of the form

  ESC 7        (save cursor)
  ESC [?47h    (switch to alternate screen)
  ...
  ESC 7 ESC 8  (save and restore cursor, while in alternate screen)
  ...
  ESC [?47l    (switch back from alternate screen)
  ESC 8        (restore cursor, expecting it to match the _first_ ESC 7)

in which, before the fix, the second ESC 7 would overwrite the
position saved by the first one. So the final ESC 8 would restore the
cursor position to wherever it happened to have been saved in the
alternate screen, instead of where it was saved before switching _to_
the alternate screen.

I've recently noticed that the same bug still happens if you use the
alternative escape sequences ESC[?1047h and ESC[?1047l to switch to
the alternate screen, instead of ESC[?47h and ESC[?47l. This is
because that version of the escape sequence sets the internal flag
'keep_cur_pos' in the call to swap_screen, whose job is to arrange
that the actual cursor position doesn't change at the instant of the
switch. But the code that swaps the _saved_ cursor position in and out
is also conditioned on keep_cur_pos, so the 1047 variant of the
screen-swap sequence was bypassing that too, and behaving as if there
was just a single saved cursor position inside and outside the
alternate screen.

I don't know why I did it that way in 2006. It could have been
deliberate for some reason, or it could just have been mindless copy
and paste from the existing cursor-related swap code. But checking
with xterm now, it definitely seems to be wrong: the 1047 screen swap
preserves the _actual_ cursor position across the swap, but still has
independent _saved_ cursor positions in the two screens. So now PuTTY
does the same.

(cherry picked from commit 421a8ca5d9)
2020-02-09 08:19:21 +00:00
Simon Tatham
8d747d8029 Add lots of missing 'static' keywords.
A trawl through the code with -Wmissing-prototypes and
-Wmissing-variable-declarations turned up a lot of things that should
have been internal to a particular source file, but were accidentally
global. Keep the namespace clean by making them all static.

(Also, while I'm here, a couple of them were missing a 'const': the
ONE and ZERO arrays in sshcrcda.c, and EMPTY_WINDOW_TITLE in
terminal.c.)
2020-01-29 06:44:18 +00:00
Simon Tatham
cd6bc14f04 Use strbuf to store results in prompts_t.
UBsan pointed out another memcpy from NULL (again with length 0) in
the prompts_t system. When I looked at it, I realised that firstly
prompt_ensure_result_size was an early not-so-good implementation of
sgrowarray_nm that would benefit from being replaced with a call to
the real one, and secondly, the whole system for storing prompt
results should really have been replaced with strbufs with the no-move
option, because that's doing all the same jobs better.

So, now each prompt_t holds a strbuf in place of its previous manually
managed string. prompt_ensure_result_size is gone (the console
prompt-reading functions use strbuf_append, and everything else just
adds to the strbuf in the usual marshal.c way). New functions exist to
retrieve a prompt_t's result, either by reference or copied.
2020-01-21 20:39:04 +00:00
Simon Tatham
5891142aee New functions to shrink a strbuf.
These are better than my previous approach of just assigning to
sb->len, because firstly they check by assertion that the new length
is within range, and secondly they preserve the invariant that the
byte stored in the buffer just after the length runs out is \0.

Switched to using the new functions everywhere a grep could turn up
opportunities.
2020-01-21 20:24:04 +00:00
Simon Tatham
43a63019f5 Fix ldisc_send() assertion in terminal answerback.
A user reports that if the ^E answerback string is configured to be
empty, then causing the answerback to be sent fails the assertion in
ldisc_send introduced in commit c269dd013.

I thought I'd caught all of the remaining cases of this in commit
4634cd47f, but apparently not.
2020-01-13 19:02:55 +00:00
Simon Tatham
22453b46da Fix handling of scroll position when swapping screens.
If the user is scrolled back in the scrollback when a screen-swap
takes place, and if we're not configured to reset the scrollback
completely on the grounds that the swap is display activity, then we
should do the same thing we do for other kinds of display activity:
strive to keep the scroll position pointing at the same text. In this
case, that means adjusting term->disptop by the number of virtual
lines added to the scrollback to allow the main screen to be viewed
while the alt screen is active.

This improves the quality of behaviour in that corner case, but more
importantly, it should also fix a case of the dreaded line==NULL
assertion failure, which someone just reported against 0.73 when
exiting tmux (hence, switching away from the alt screen) while
scrolled back in a purely virtual scrollback buffer: the virtual
scrollback lines vanished, but disptop was still set to a negative
value, which made it out of range.
2019-12-30 23:27:43 +00:00
Simon Tatham
421a8ca5d9 Fix cursor save/restore with [?1047 alt-screen sequences.
A long time ago, in commit 09f86ce7e, I introduced a separate copy of
the saved cursor position (used by the ESC 7 / ESC 8 sequences) for
the main and alternate screens. The idea was to fix mishandling of an
input sequence of the form

  ESC 7        (save cursor)
  ESC [?47h    (switch to alternate screen)
  ...
  ESC 7 ESC 8  (save and restore cursor, while in alternate screen)
  ...
  ESC [?47l    (switch back from alternate screen)
  ESC 8        (restore cursor, expecting it to match the _first_ ESC 7)

in which, before the fix, the second ESC 7 would overwrite the
position saved by the first one. So the final ESC 8 would restore the
cursor position to wherever it happened to have been saved in the
alternate screen, instead of where it was saved before switching _to_
the alternate screen.

I've recently noticed that the same bug still happens if you use the
alternative escape sequences ESC[?1047h and ESC[?1047l to switch to
the alternate screen, instead of ESC[?47h and ESC[?47l. This is
because that version of the escape sequence sets the internal flag
'keep_cur_pos' in the call to swap_screen, whose job is to arrange
that the actual cursor position doesn't change at the instant of the
switch. But the code that swaps the _saved_ cursor position in and out
is also conditioned on keep_cur_pos, so the 1047 variant of the
screen-swap sequence was bypassing that too, and behaving as if there
was just a single saved cursor position inside and outside the
alternate screen.

I don't know why I did it that way in 2006. It could have been
deliberate for some reason, or it could just have been mindless copy
and paste from the existing cursor-related swap code. But checking
with xterm now, it definitely seems to be wrong: the 1047 screen swap
preserves the _actual_ cursor position across the swap, but still has
independent _saved_ cursor positions in the two screens. So now PuTTY
does the same.
2019-12-24 13:47:46 +00:00
Simon Tatham
2c279283cc Don't call term_bracketed_paste_stop before pasted data.
The redesign in commit 9fccb065a arranged that all keystroke data goes
via term_keyinput_internal, which calls term_bracketed_paste_stop just
in case the keystroke had interrupted an in-progress paste.

But, embarrassingly, I forgot that _pasted_ data also goes via
term_keyinput_internal, and bracketed paste mode certainly should not
be terminated before _that_ is sent! I should have conditionalised the
call to term_bracketed_paste_stop on the 'interactive' flag parameter,
which is precisely there to tell the difference between pastes and
true keyboard input.
2019-09-19 17:59:37 +01:00
Simon Tatham
5d718ef64b Whitespace rationalisation of entire code base.
The number of people has been steadily increasing who read our source
code with an editor that thinks tab stops are 4 spaces apart, as
opposed to the traditional tty-derived 8 that the PuTTY code expects.

So I've been wondering for ages about just fixing it, and switching to
a spaces-only policy throughout the code. And I recently found out
about 'git blame -w', which should make this change not too disruptive
for the purposes of source-control archaeology; so perhaps now is the
time.

While I'm at it, I've also taken the opportunity to remove all the
trailing spaces from source lines (on the basis that git dislikes
them, and is the only thing that seems to have a strong opinion one
way or the other).
    
Apologies to anyone downstream of this code who has complicated patch
sets to rebase past this change. I don't intend it to be needed again.
2019-09-08 20:29:21 +01:00
Simon Tatham
6f4083e682 Fix double display glitch in erase_lots().
If the cursor is on the rightmost column of the terminal and
term->wrapnext is set, and the user asks to erase from the current
position to the end of (at least) the line, what should happen?

PuTTY's previous behaviour was to ignore term->wrapnext, and do the
same thing we would have done without it: erase from the current
physical cursor position to EOL inclusive, i.e. blank the character
cell we just printed.

But this is unfortunate if a program writes an interleaving of
printing characters and ESC[K, which I recently found out is what gcc
does in its colour-highlighted error messages: if the last printed
char just before an ESC[K pushes the cursor into the deferred-wrap
state, then the ESC[K blanks that character, and then we wrap to the
next line. So one character of the error message ends up missing.

xfce4-terminal and gnome-terminal take the approach in this situation
of regarding the cursor position as _right_ at the end of the line, so
no character cells get cleared at all, and the error message displays
as intended. I think that's more sensible, so I've switched to doing
the same thing.

(xterm has different behaviour again: it blanks the character cell and
also clears its analogue of the wrapnext flag. So in _their_ handling
of this sequence of output, one character of the error message is
still missing, but it looks as if it's been _omitted_ rather than
replaced by a space.)

Secondly, in the course of fixing that, I looked at the check_boundary
call in erase_lots, which is supposed to ensure that if a wide CJK
character straddles the boundary between what's being erased and what
isn't, then both halves of the character are deleted. I had to modify
that anyway because I was moving that very boundary, and in doing so,
I noticed that even according to the previous behaviour, it had an
off-by-one error. In the case where you send ESC[1K (meaning erase up
to and including the cursor position), the call to check_boundary was
performed on the _left_ edge of the cursor's character cell, when it
should have been the right edge. So you could end up with an
erase_char in the left half (i.e. a space) and still have the magic
value UCSWIDE in the right half, causing the terminal to think you had
a double-width U+0020 on the screen, which isn't supposed to be able
to happen.
2019-08-08 18:05:34 +01:00
Simon Tatham
5e2ac205fd term_clrsb(): call deselect() if the selection was affected.
Any terminal event that modifies the range of screen+scrollback
currently covered by the mouse selection should call deselect(), so
that users won't be misled into thinking that the new version of the
highlighted text would be pasted, and also so that using the
MBT_EXTEND action to modify the selection bounds won't start from
a selection you never had in the first place.

Clearing the scrollback is such an event, if the selection covered any
of the scrollback at all; so we should apply the same policy here as
everywhere else.
2019-07-24 19:00:52 +01:00
Simon Tatham
a6a13311b4 Revert "Bounds-check terminal selection when clearing scrollback."
This reverts commit 80f5a009f6.

After a bit more thought, I've decided it's the wrong way to solve the
problem. We shouldn't really be _changing_ the current selection
bounds in response to an event that touches the range they cover. With
this fix in place, if you clear the scrollback while a selection
partly overlaps it, and then extend the modified selection, you'll get
a selection one of whose endpoints is something you never specified as
a selection endpoint at all, and possibly paste the wrong text.

A better fix is to do the same thing we do about any other event that
touches the range covered by the selection: get rid of the selection
completely. For ease of cherry-picking (in case anyone needs to apply
the good fix in some downstream branch, or whatever), I'll make that
change separately in the next commit.
2019-07-24 18:56:07 +01:00
Simon Tatham
80f5a009f6 Bounds-check terminal selection when clearing scrollback.
term_clrsb() was emptying the tree234 of scrollback, without checking
whether term->selstart, term->selend and term->selanchor were pointing
at places in the now-removed scrollback. If they were, then a
subsequent extend-selection operation could give rise to the dreaded
'line==NULL' assertion box.

Thanks to the user who sent in one of those debugging dumps, that
finally enabled us to track down (at least one case of) this
long- standing but extremely rare crash!
2019-07-23 19:58:48 +01:00
Simon Tatham
c713ce4868 terminal.[ch]: refactor the 'pos' macros.
These are now inline functions (mostly, except for a couple of macro
wrappers to preserve the old call syntax), and they live in terminal.h
instead of at the top of terminal.c.

Also, I've added a few comments for clarity, and renamed the posPlt()
function, which didn't do at all what the name made it look (at least
to a mathematician familiar with product orders) as if it did.
2019-07-23 19:58:48 +01:00
Simon Tatham
54cd853a49 Fix buffer overrun on keypress in non-UTF-8 sessions.
Commit 71e42b04a's refactoring of terminal keyboard input, in the case
where a Unicode string derived from a keystroke is translated into
some other charset to put on the wire, had allocated the output buffer
for that translation one byte too small.
2019-07-02 21:22:01 +01:00
Simon Tatham
e3a14e1ad6 Withdraw support for the DECEDM escape sequence.
Having decided that the terminal's local echo setting shouldn't be
allowed to propagate through to termios, I think the local edit
setting shouldn't either. Also, no other terminal emulator I know
seems to implement this sequence, and if you enable it, things get
very confused in general. I think it's generally better off absent; if
somebody turns out to have been using it, then we'll at least be able
to find out what it's good for.
2019-06-18 06:58:51 +01:00
Simon Tatham
9fccb065a6 Rework handling of the SRM escape sequence.
This sequence (ESC[12l, ESC[12h) enables and disables local echo in
the terminal. We were previously implementing it by gatewaying it
directly through to the local echo facility in the line discipline,
which in turn would pass it on to the terminal it was running in (if
it was Plink).

This seems to be at odds with how other terminals do it: they treat
SRM as its own entirely separate thing, in which the terminal
_emulator_ performs its own echoing of input keypress data,
independently of whether the Unix terminal device (or closest
equivalent) is doing the same thing or not.

Now we're doing it the same way as everyone else (or at least I think
so): the new internal terminal function that the term_keyinput pair
feed to is also implementing SRM-driven local echo as another of its
side effects. One observable effect is that SRM now doesn't interfere
with the termios settings of the terminal it's running in; another is
that the echo now only applies to real keypress data, and not
sequences auto-generated by the terminal.
2019-06-18 06:58:51 +01:00
Simon Tatham
71e42b04a5 Refactor terminal input to remove ldiscucs.c.
The functions that previously lived in it now live in terminal.c
itself; they've been renamed term_keyinput and term_keyinputw, and
their function is to add data to the terminal's user input buffer from
a char or wchar_t string respectively.

They sit more comfortably in terminal.c anyway, because their whole
point is to translate into the character encoding that the terminal is
currently configured to use. Also, making them part of the terminal
code means they can also take care of calling term_seen_key_event(),
which simplifies most of the call sites in the GTK and Windows front
ends.

Generation of text _inside_ terminal.c, from responses to query escape
sequences, is therefore not done by calling those external entry
points: we send those responses directly to the ldisc, so that they
don't count as keypresses for all the user-facing purposes like bell
overload handling and scrollback reset. To make _that_ convenient,
I've arranged that most of the code that previously lived in
lpage_send and luni_send is now in separate translation functions, so
those can still be called from situations where you're not going to do
the default thing with the translated data.

(However, pasted data _does_ still count as close enough to a keypress
to call term_seen_key_event - but it clears the 'interactive' flag
when the data is passed on to the line discipline, which tweaks a
minor detail of control-char handling in line ending mode but mostly
just means pastes aren't interrupted.)
2019-06-18 06:58:51 +01:00
Simon Tatham
108baae60e Add further missing delete_callbacks_for_context.
Having explicitly _stated_ in commit 4dcc0fddf the principle that if
you ever queue a toplevel callback on a freeable object then you
should also call delete_callbacks_for_context on that object before
freeing it, I realised I'd never actually gone through and checked
methodically at every call site of queue_toplevel_callback. So I did,
and naturally, I found several missing ones.
2019-04-20 08:29:23 +01:00