1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-25 01:02:24 +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:
Simon Tatham 2013-08-17 16:06:12 +00:00
parent 75c79e318f
commit 7be9af74ec
5 changed files with 81 additions and 69 deletions

View File

@ -977,8 +977,6 @@ void term_update(Terminal *);
void term_invalidate(Terminal *); void term_invalidate(Terminal *);
void term_blink(Terminal *, int set_cursor); void term_blink(Terminal *, int set_cursor);
void term_do_paste(Terminal *); void term_do_paste(Terminal *);
int term_paste_pending(Terminal *);
void term_paste(Terminal *);
void term_nopaste(Terminal *); void term_nopaste(Terminal *);
int term_ldisc(Terminal *, int option); int term_ldisc(Terminal *, int option);
void term_copyall(Terminal *); void term_copyall(Terminal *);

View File

@ -109,6 +109,9 @@ static void scroll(Terminal *, int, int, int, int);
#ifdef OPTIMISE_SCROLL #ifdef OPTIMISE_SCROLL
static void scroll_display(Terminal *, int, int, int); static void scroll_display(Terminal *, int, int, int);
#endif /* OPTIMISE_SCROLL */ #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) 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->cblink_pending = term->tblink_pending = FALSE;
term->paste_buffer = NULL; term->paste_buffer = NULL;
term->paste_len = 0; term->paste_len = 0;
term->last_paste = 0;
bufchain_init(&term->inbuf); bufchain_init(&term->inbuf);
bufchain_init(&term->printer_buf); bufchain_init(&term->printer_buf);
term->printing = term->only_printing = FALSE; term->printing = term->only_printing = FALSE;
@ -2967,7 +2969,7 @@ static void term_out(Terminal *term)
term->curs.x = 0; term->curs.x = 0;
term->wrapnext = FALSE; term->wrapnext = FALSE;
seen_disp_event(term); seen_disp_event(term);
term->paste_hold = 0; term_resume_pasting(term);
if (term->crhaslf) { if (term->crhaslf) {
if (term->curs.y == term->marg_b) if (term->curs.y == term->marg_b)
@ -2998,7 +3000,7 @@ static void term_out(Terminal *term)
term->curs.x = 0; term->curs.x = 0;
term->wrapnext = FALSE; term->wrapnext = FALSE;
seen_disp_event(term); seen_disp_event(term);
term->paste_hold = 0; term_resume_pasting(term);
if (term->logctx) if (term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
break; 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) void term_do_paste(Terminal *term)
{ {
wchar_t *data; wchar_t *data;
@ -5719,7 +5790,7 @@ void term_do_paste(Terminal *term)
if (term->paste_buffer) if (term->paste_buffer)
sfree(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); term->paste_buffer = snewn(len + 12, wchar_t);
if (term->bracketed_paste) { if (term->bracketed_paste) {
@ -5762,10 +5833,12 @@ void term_do_paste(Terminal *term)
if (term->paste_buffer) if (term->paste_buffer)
sfree(term->paste_buffer); sfree(term->paste_buffer);
term->paste_buffer = 0; 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); get_clip(term->frontend, NULL, NULL);
term_paste_queue(term, FALSE);
} }
void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, 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) if (term->paste_len == 0)
return; return;
sfree(term->paste_buffer); expire_timer_context(&term->paste_timer_ctx);
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;
}
}
sfree(term->paste_buffer); sfree(term->paste_buffer);
term->paste_buffer = NULL; term->paste_buffer = NULL;
term->paste_len = 0; term->paste_len = 0;

View File

@ -222,8 +222,8 @@ struct terminal_tag {
int attr_mask; int attr_mask;
wchar_t *paste_buffer; wchar_t *paste_buffer;
int paste_len, paste_pos, paste_hold; int paste_len, paste_pos;
long last_paste; Terminal *paste_timer_ctx;
void (*resize_fn)(void *, int, int); void (*resize_fn)(void *, int, int);
void *resize_ctx; void *resize_ctx;

View File

@ -93,7 +93,6 @@ struct gui_data {
int ignore_sbar; int ignore_sbar;
int mouseptr_visible; int mouseptr_visible;
int busy_status; int busy_status;
guint term_paste_idle_id;
guint term_exit_idle_id; guint term_exit_idle_id;
guint toplevel_callback_idle_id; guint toplevel_callback_idle_id;
int alt_keycode; int alt_keycode;
@ -1999,28 +1998,12 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
term_do_paste(inst->term); 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) if (free_list_required)
XFreeStringList(list); XFreeStringList(list);
if (free_required) if (free_required)
XFree(text); 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) void get_clip(void *frontend, wchar_t ** p, int *len)
{ {
struct gui_data *inst = (struct gui_data *)frontend; struct gui_data *inst = (struct gui_data *)frontend;

View File

@ -877,8 +877,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg))) if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
DispatchMessage(&msg); DispatchMessage(&msg);
/* Send the paste buffer if there's anything to send */
term_paste(term);
if (must_close_session) if (must_close_session)
close_session(); close_session();