mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Revamp the terminal paste mechanism using toplevel callbacks.
I've removed the ad-hoc front-end bodgery in the Windows and GTK ports to arrange for term_paste to be called at the right moments, and instead, terminal.c itself deals with knowing when to send the next chunk of pasted data using a combination of timers and the new top-level callback mechanism. As a happy side effect, it's now all in one place so I can actually understand what it's doing! It turns out that what all that confusing code was up to is: send a line of pasted data, and delay sending the next line until either a CR or LF is returned from the server (typically indicating that the pasted text has been received and echoed) or 450ms elapse, whichever comes first. [originally from svn r10020]
This commit is contained in:
parent
75c79e318f
commit
7be9af74ec
2
putty.h
2
putty.h
@ -977,8 +977,6 @@ void term_update(Terminal *);
|
||||
void term_invalidate(Terminal *);
|
||||
void term_blink(Terminal *, int set_cursor);
|
||||
void term_do_paste(Terminal *);
|
||||
int term_paste_pending(Terminal *);
|
||||
void term_paste(Terminal *);
|
||||
void term_nopaste(Terminal *);
|
||||
int term_ldisc(Terminal *, int option);
|
||||
void term_copyall(Terminal *);
|
||||
|
125
terminal.c
125
terminal.c
@ -109,6 +109,9 @@ static void scroll(Terminal *, int, int, int, int);
|
||||
#ifdef OPTIMISE_SCROLL
|
||||
static void scroll_display(Terminal *, int, int, int);
|
||||
#endif /* OPTIMISE_SCROLL */
|
||||
static void term_resume_pasting(Terminal *term);
|
||||
static void term_paste_callback(void *vterm);
|
||||
static void term_paste_queue(Terminal *term, int timed);
|
||||
|
||||
static termline *newline(Terminal *term, int cols, int bce)
|
||||
{
|
||||
@ -1524,7 +1527,6 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata,
|
||||
term->cblink_pending = term->tblink_pending = FALSE;
|
||||
term->paste_buffer = NULL;
|
||||
term->paste_len = 0;
|
||||
term->last_paste = 0;
|
||||
bufchain_init(&term->inbuf);
|
||||
bufchain_init(&term->printer_buf);
|
||||
term->printing = term->only_printing = FALSE;
|
||||
@ -2967,7 +2969,7 @@ static void term_out(Terminal *term)
|
||||
term->curs.x = 0;
|
||||
term->wrapnext = FALSE;
|
||||
seen_disp_event(term);
|
||||
term->paste_hold = 0;
|
||||
term_resume_pasting(term);
|
||||
|
||||
if (term->crhaslf) {
|
||||
if (term->curs.y == term->marg_b)
|
||||
@ -2998,7 +3000,7 @@ static void term_out(Terminal *term)
|
||||
term->curs.x = 0;
|
||||
term->wrapnext = FALSE;
|
||||
seen_disp_event(term);
|
||||
term->paste_hold = 0;
|
||||
term_resume_pasting(term);
|
||||
if (term->logctx)
|
||||
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
|
||||
break;
|
||||
@ -5706,6 +5708,75 @@ static void sel_spread(Terminal *term)
|
||||
}
|
||||
}
|
||||
|
||||
static void term_resume_pasting(Terminal *term)
|
||||
{
|
||||
expire_timer_context(&term->paste_timer_ctx);
|
||||
term_paste_queue(term, FALSE);
|
||||
}
|
||||
|
||||
static void term_paste_timing_callback(void *vterm, unsigned long now)
|
||||
{
|
||||
Terminal *term = *(Terminal **)vterm;
|
||||
term_resume_pasting(term);
|
||||
}
|
||||
|
||||
static void term_paste_queue(Terminal *term, int timed)
|
||||
{
|
||||
if (timed) {
|
||||
/*
|
||||
* Delay sending the rest of the paste buffer until we have
|
||||
* seen a newline coming back from the server (indicating that
|
||||
* it's absorbed the data we've sent so far). As a fallback,
|
||||
* continue sending anyway after a longish timeout.
|
||||
*
|
||||
* We use the pointless structure field term->paste_timer_ctx
|
||||
* (which is a Terminal *, and we'll make sure it points
|
||||
* straight back to term) as our timer context, so that it can
|
||||
* be distinguished from term itself. That way, if we see a
|
||||
* reason to continue pasting before the timer goes off, we
|
||||
* can cancel just this timer and none of the other terminal
|
||||
* timers handling display updates, blinking text and cursor,
|
||||
* and visual bells.
|
||||
*/
|
||||
term->paste_timer_ctx = term;
|
||||
schedule_timer(450, term_paste_timing_callback,
|
||||
&term->paste_timer_ctx);
|
||||
} else {
|
||||
/*
|
||||
* Just arrange to call term_paste_callback from the top level
|
||||
* at the next opportunity.
|
||||
*/
|
||||
queue_toplevel_callback(term_paste_callback, term);
|
||||
}
|
||||
}
|
||||
|
||||
static void term_paste_callback(void *vterm)
|
||||
{
|
||||
Terminal *term = (Terminal *)vterm;
|
||||
|
||||
if (term->paste_len == 0)
|
||||
return;
|
||||
|
||||
while (term->paste_pos < term->paste_len) {
|
||||
int n = 0;
|
||||
while (n + term->paste_pos < term->paste_len) {
|
||||
if (term->paste_buffer[term->paste_pos + n++] == '\015')
|
||||
break;
|
||||
}
|
||||
if (term->ldisc)
|
||||
luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0);
|
||||
term->paste_pos += n;
|
||||
|
||||
if (term->paste_pos < term->paste_len) {
|
||||
term_paste_queue(term, TRUE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
sfree(term->paste_buffer);
|
||||
term->paste_buffer = NULL;
|
||||
term->paste_len = 0;
|
||||
}
|
||||
|
||||
void term_do_paste(Terminal *term)
|
||||
{
|
||||
wchar_t *data;
|
||||
@ -5719,7 +5790,7 @@ void term_do_paste(Terminal *term)
|
||||
|
||||
if (term->paste_buffer)
|
||||
sfree(term->paste_buffer);
|
||||
term->paste_pos = term->paste_hold = term->paste_len = 0;
|
||||
term->paste_pos = term->paste_len = 0;
|
||||
term->paste_buffer = snewn(len + 12, wchar_t);
|
||||
|
||||
if (term->bracketed_paste) {
|
||||
@ -5762,10 +5833,12 @@ void term_do_paste(Terminal *term)
|
||||
if (term->paste_buffer)
|
||||
sfree(term->paste_buffer);
|
||||
term->paste_buffer = 0;
|
||||
term->paste_pos = term->paste_hold = term->paste_len = 0;
|
||||
term->paste_pos = term->paste_len = 0;
|
||||
}
|
||||
}
|
||||
get_clip(term->frontend, NULL, NULL);
|
||||
|
||||
term_paste_queue(term, FALSE);
|
||||
}
|
||||
|
||||
void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
|
||||
@ -6052,47 +6125,7 @@ void term_nopaste(Terminal *term)
|
||||
{
|
||||
if (term->paste_len == 0)
|
||||
return;
|
||||
sfree(term->paste_buffer);
|
||||
term->paste_buffer = NULL;
|
||||
term->paste_len = 0;
|
||||
}
|
||||
|
||||
int term_paste_pending(Terminal *term)
|
||||
{
|
||||
return term->paste_len != 0;
|
||||
}
|
||||
|
||||
void term_paste(Terminal *term)
|
||||
{
|
||||
long now, paste_diff;
|
||||
|
||||
if (term->paste_len == 0)
|
||||
return;
|
||||
|
||||
/* Don't wait forever to paste */
|
||||
if (term->paste_hold) {
|
||||
now = GETTICKCOUNT();
|
||||
paste_diff = now - term->last_paste;
|
||||
if (paste_diff >= 0 && paste_diff < 450)
|
||||
return;
|
||||
}
|
||||
term->paste_hold = 0;
|
||||
|
||||
while (term->paste_pos < term->paste_len) {
|
||||
int n = 0;
|
||||
while (n + term->paste_pos < term->paste_len) {
|
||||
if (term->paste_buffer[term->paste_pos + n++] == '\015')
|
||||
break;
|
||||
}
|
||||
if (term->ldisc)
|
||||
luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0);
|
||||
term->paste_pos += n;
|
||||
|
||||
if (term->paste_pos < term->paste_len) {
|
||||
term->paste_hold = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
expire_timer_context(&term->paste_timer_ctx);
|
||||
sfree(term->paste_buffer);
|
||||
term->paste_buffer = NULL;
|
||||
term->paste_len = 0;
|
||||
|
@ -222,8 +222,8 @@ struct terminal_tag {
|
||||
int attr_mask;
|
||||
|
||||
wchar_t *paste_buffer;
|
||||
int paste_len, paste_pos, paste_hold;
|
||||
long last_paste;
|
||||
int paste_len, paste_pos;
|
||||
Terminal *paste_timer_ctx;
|
||||
|
||||
void (*resize_fn)(void *, int, int);
|
||||
void *resize_ctx;
|
||||
|
@ -93,7 +93,6 @@ struct gui_data {
|
||||
int ignore_sbar;
|
||||
int mouseptr_visible;
|
||||
int busy_status;
|
||||
guint term_paste_idle_id;
|
||||
guint term_exit_idle_id;
|
||||
guint toplevel_callback_idle_id;
|
||||
int alt_keycode;
|
||||
@ -1999,28 +1998,12 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
|
||||
|
||||
term_do_paste(inst->term);
|
||||
|
||||
if (term_paste_pending(inst->term))
|
||||
inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst);
|
||||
|
||||
if (free_list_required)
|
||||
XFreeStringList(list);
|
||||
if (free_required)
|
||||
XFree(text);
|
||||
}
|
||||
|
||||
gint idle_paste_func(gpointer data)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)data;
|
||||
|
||||
if (term_paste_pending(inst->term))
|
||||
term_paste(inst->term);
|
||||
else
|
||||
gtk_idle_remove(inst->term_paste_idle_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void get_clip(void *frontend, wchar_t ** p, int *len)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
|
@ -877,8 +877,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
|
||||
if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
|
||||
DispatchMessage(&msg);
|
||||
/* Send the paste buffer if there's anything to send */
|
||||
term_paste(term);
|
||||
|
||||
if (must_close_session)
|
||||
close_session();
|
||||
|
Loading…
Reference in New Issue
Block a user