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:
parent
75c79e318f
commit
7be9af74ec
2
putty.h
2
putty.h
@ -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 *);
|
||||||
|
125
terminal.c
125
terminal.c
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user