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

New timing infrastructure. There's a new function schedule_timer()

which pretty much any module can call to request a call-back in the
future. So terminal.c can do its own handling of blinking, visual
bells and deferred screen updates, without having to rely on
term_update() being called 50 times a second (fixes: pterm-timer);
and ssh.c and telnet.c both invoke a new module pinger.c which takes
care of sending keepalives, so they get sent uniformly in all front
ends (fixes: plink-keepalives, unix-keepalives).

[originally from svn r4906]
[this svn revision also touched putty-wishlist]
This commit is contained in:
Simon Tatham 2004-11-27 13:20:21 +00:00
parent d609e1f7f8
commit 7ecf13564a
30 changed files with 1109 additions and 369 deletions

15
Recipe
View File

@ -182,14 +182,15 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint
# Same thing on Unix.
UXTERM = TERMINAL pterm uxcfg gtkdlg gtkcols gtkpanel uxucs uxprint xkeysym
+ timing
# Non-SSH back ends (putty, puttytel, plink).
NONSSH = telnet raw rlogin ldisc
NONSSH = telnet raw rlogin ldisc pinger
# SSH back end (putty, plink, pscp, psftp).
SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshsh512 sshbn wildcard
+ sshaes sshsh512 sshbn wildcard pinger
WINSSH = SSH winnoise winpgntc
UXSSH = SSH uxnoise uxagentc
MACSSH = SSH macnoise
@ -199,12 +200,10 @@ SFTP = sftp int64 logging
# Miscellaneous objects appearing in all the network utilities (not
# Pageant or PuTTYgen).
WINMISC = misc version winstore settings tree234 winnet proxy cmdline
+ windefs winmisc pproxy
UXMISC = misc version uxstore settings tree234 uxsel uxnet proxy cmdline
+ uxmisc uxproxy
MACMISC = misc version macstore settings tree234 macnet mtcpnet otnet proxy
+ macmisc macabout pproxy
MISC = timing misc version settings tree234 proxy
WINMISC = MISC winstore winnet cmdline windefs winmisc pproxy
UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy
MACMISC = MISC macstore macnet mtcpnet otnet macmisc macabout pproxy
# Character set library, for use in pterm.
CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc

View File

@ -75,21 +75,23 @@ Some ports of PuTTY - notably the in-progress Mac port - are
constrained by the operating system to run as a single process
potentially managing multiple sessions.
Therefore, the platform-independent parts of PuTTY use \e{hardly
any} global variables. The very few that do exist, such as
\c{flags}, are tolerated because they are not specific to a
particular login session: instead, they define properties that are
expected to apply equally to \e{all} the sessions run by a single
PuTTY process. Any data that is specific to a particular network
session is stored in dynamically allocated data structures, and
pointers to these structures are passed around between functions.
Therefore, the platform-independent parts of PuTTY never use global
variables to store per-session data. The global variables that do
exist are tolerated because they are not specific to a particular
login session: \c{flags} defines properties that are expected to
apply equally to \e{all} the sessions run by a single PuTTY process,
the random number state in \cw{sshrand.c} and the timer list in
\cw{timing.c} serve all sessions equally, and so on. But most data
is specific to a particular network session, and is therefore stored
in dynamically allocated data structures, and pointers to these
structures are passed around between functions.
Platform-specific code can reverse this decision if it likes. The
Windows code, for historical reasons, stores most of its data as
global variables. That's OK, because \e{on Windows} we know there is
only one session per PuTTY process, so it's safe to do that. But
changes to the platform-independent code should avoid introducing
any more global variables than already exist.
global variables, unless they are genuinely cross-session.
\H{udp-pure-c} C, not C++

View File

@ -1,4 +1,4 @@
/* $Id: macterm.c,v 1.78 2004/10/14 16:42:43 simon Exp $ */
/* $Id$ */
/*
* Copyright (c) 1999 Simon Tatham
* Copyright (c) 1999, 2002 Ben Harris
@ -314,7 +314,6 @@ void mac_pollterm(void)
Session *s;
for (s = sesslist; s != NULL; s = s->next) {
term_out(s->term);
term_update(s->term);
}
}

23
misc.c
View File

@ -141,6 +141,29 @@ char *dupvprintf(const char *fmt, va_list ap)
}
}
/*
* Read an entire line of text from a file. Return a buffer
* malloced to be as big as necessary (caller must free).
*/
char *fgetline(FILE *fp)
{
char *ret = snewn(512, char);
int size = 512, len = 0;
while (fgets(ret + len, size - len, fp)) {
len += strlen(ret + len);
if (ret[len-1] == '\n')
break; /* got a newline, we're done */
size = len + 512;
ret = sresize(ret, size, char);
}
if (len == 0) { /* first fgets returned NULL */
sfree(ret);
return NULL;
}
ret[len] = '\0';
return ret;
}
/* ----------------------------------------------------------------------
* Base64 encoding routine. This is required in public-key writing
* but also in HTTP proxy handling, so it's centralised here.

3
misc.h
View File

@ -3,6 +3,7 @@
#include "puttymem.h"
#include <stdio.h> /* for FILE * */
#include <stdarg.h> /* for va_list */
#ifndef FALSE
@ -20,6 +21,8 @@ char *dupcat(const char *s1, ...);
char *dupprintf(const char *fmt, ...);
char *dupvprintf(const char *fmt, va_list ap);
char *fgetline(FILE *fp);
void base64_encode_atom(unsigned char *data, int n, char *out);
struct bufchain_granule;

71
pinger.c Normal file
View File

@ -0,0 +1,71 @@
/*
* pinger.c: centralised module that deals with sending TS_PING
* keepalives, to avoid replicating this code in multiple backends.
*/
#include "putty.h"
struct pinger_tag {
int interval;
int pending;
long next;
Backend *back;
void *backhandle;
};
static void pinger_schedule(Pinger pinger);
static void pinger_timer(void *ctx, long now)
{
Pinger pinger = (Pinger)ctx;
if (pinger->pending && now - pinger->next >= 0) {
pinger->back->special(pinger->backhandle, TS_PING);
pinger->pending = FALSE;
pinger_schedule(pinger);
}
}
static void pinger_schedule(Pinger pinger)
{
int next;
if (!pinger->interval) {
pinger->pending = FALSE; /* cancel any pending ping */
return;
}
next = schedule_timer(pinger->interval * TICKSPERSEC,
pinger_timer, pinger);
if (!pinger->pending || next < pinger->next) {
pinger->next = next;
pinger->pending = TRUE;
}
}
Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)
{
Pinger pinger = snew(struct pinger_tag);
pinger->interval = cfg->ping_interval;
pinger->pending = FALSE;
pinger->back = back;
pinger->backhandle = backhandle;
pinger_schedule(pinger);
return pinger;
}
void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg)
{
if (oldcfg->ping_interval != newcfg->ping_interval) {
pinger->interval = newcfg->ping_interval;
pinger_schedule(pinger);
}
}
void pinger_free(Pinger pinger)
{
expire_timer_context(pinger);
sfree(pinger);
}

48
psftp.c
View File

@ -1361,45 +1361,34 @@ static int sftp_cmd_help(struct sftp_command *cmd)
struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
{
char *line;
int linelen, linesize;
struct sftp_command *cmd;
char *p, *q, *r;
int quoting;
if ((mode == 0) || (modeflags & 1)) {
printf("psftp> ");
}
fflush(stdout);
cmd = snew(struct sftp_command);
cmd->words = NULL;
cmd->nwords = 0;
cmd->wordssize = 0;
line = NULL;
linesize = linelen = 0;
while (1) {
int len;
char *ret;
linesize += 512;
line = sresize(line, linesize, char);
ret = fgets(line + linelen, linesize - linelen, fp);
if (!ret || (linelen == 0 && line[0] == '\0')) {
cmd->obey = sftp_cmd_quit;
if ((mode == 0) || (modeflags & 1))
printf("quit\n");
return cmd; /* eof */
}
len = linelen + strlen(line + linelen);
linelen += len;
if (line[linelen - 1] == '\n') {
linelen--;
line[linelen] = '\0';
break;
}
if (fp) {
if (modeflags & 1)
printf("psftp> ");
line = fgetline(fp);
} else {
line = ssh_sftp_get_cmdline("psftp> ");
}
if (!line || !*line) {
cmd->obey = sftp_cmd_quit;
if ((mode == 0) || (modeflags & 1))
printf("quit\n");
return cmd; /* eof */
}
line[strcspn(line, "\r\n")] = '\0';
if (modeflags & 1) {
printf("%s\n", line);
}
@ -1464,7 +1453,8 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
}
}
sfree(line);
sfree(line);
/*
* Now parse the first word and assign a function.
*/
@ -1551,7 +1541,7 @@ void do_sftp(int mode, int modeflags, char *batchfile)
*/
while (1) {
struct sftp_command *cmd;
cmd = sftp_getcmd(stdin, 0, 0);
cmd = sftp_getcmd(NULL, 0, 0);
if (!cmd)
break;
ret = cmd->obey(cmd);

View File

@ -32,6 +32,12 @@ void get_file_times(char *filename, unsigned long *mtime,
*/
int ssh_sftp_loop_iteration(void);
/*
* Read a command line for PSFTP from standard input. Caller must
* free.
*/
char *ssh_sftp_get_cmdline(char *prompt);
/*
* The main program in psftp.c. Called from main() in the platform-
* specific code, after doing any platform-specific initialisation.

44
putty.h
View File

@ -578,6 +578,7 @@ void ldisc_update(void *frontend, int echo, int edit);
* shutdown. */
void update_specials_menu(void *frontend);
int from_backend(void *frontend, int is_stderr, const char *data, int len);
void notify_remote_exit(void *frontend);
#define OPTIMISE_IS_SCROLL 1
void set_iconic(void *frontend, int iconic);
@ -744,6 +745,14 @@ int random_byte(void);
void random_get_savedata(void **data, int *len);
extern int random_active;
/*
* Exports from pinger.c.
*/
typedef struct pinger_tag *Pinger;
Pinger pinger_new(Config *cfg, Backend *back, void *backhandle);
void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg);
void pinger_free(Pinger);
/*
* Exports from misc.c.
*/
@ -895,4 +904,39 @@ int filename_is_null(Filename fn);
char *get_username(void); /* return value needs freeing */
char *get_random_data(int bytes); /* used in cmdgen.c */
/*
* Exports and imports from timing.c.
*
* schedule_timer() asks the front end to schedule a callback to a
* timer function in a given number of ticks. The returned value is
* the time (in ticks since an arbitrary offset) at which the
* callback can be expected. This value will also be passed as the
* `now' parameter to the callback function. Hence, you can (for
* example) schedule an event at a particular time by calling
* schedule_timer() and storing the return value in your context
* structure as the time when that event is due. The first time a
* callback function gives you that value or more as `now', you do
* the thing.
*
* expire_timer_context() drops all current timers associated with
* a given value of ctx (for when you're about to free ctx).
*
* run_timers() is called from the front end when it has reason to
* think some timers have reached their moment, or when it simply
* needs to know how long to wait next. We pass it the time we
* think it is. It returns TRUE and places the time when the next
* timer needs to go off in `next', or alternatively it returns
* FALSE if there are no timers at all pending.
*
* timer_change_notify() must be supplied by the front end; it
* notifies the front end that a new timer has been added to the
* list which is sooner than any existing ones. It provides the
* time when that timer needs to go off.
*/
typedef void (*timer_fn_t)(void *ctx, long now);
long schedule_timer(int ticks, timer_fn_t fn, void *ctx);
void expire_timer_context(void *ctx);
int run_timers(long now, long *next);
void timer_change_notify(long next);
#endif

1
raw.c
View File

@ -37,6 +37,7 @@ static int raw_closing(Plug plug, const char *error_msg, int error_code,
if (raw->s) {
sk_close(raw->s);
raw->s = NULL;
notify_remote_exit(raw->frontend);
}
if (error_msg) {
/* A socket error has occurred. */

View File

@ -39,6 +39,7 @@ static int rlogin_closing(Plug plug, const char *error_msg, int error_code,
if (rlogin->s) {
sk_close(rlogin->s);
rlogin->s = NULL;
notify_remote_exit(rlogin->frontend);
}
if (error_msg) {
/* A socket error has occurred. */

13
ssh.c
View File

@ -698,6 +698,11 @@ struct ssh_tag {
* with at any time.
*/
handler_fn_t packet_dispatch[256];
/*
* This module deals with sending keepalives.
*/
Pinger pinger;
};
#define logevent(s) logevent(ssh->frontend, s)
@ -2157,6 +2162,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
}
update_specials_menu(ssh->frontend);
ssh->state = SSH_STATE_BEFORE_SIZE;
ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh);
sfree(s->vstring);
@ -2216,6 +2222,7 @@ static void ssh_do_close(Ssh ssh)
if (ssh->s) {
sk_close(ssh->s);
ssh->s = NULL;
notify_remote_exit(ssh->frontend);
}
/*
* Now we must shut down any port and X forwardings going
@ -2327,6 +2334,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg);
if ((err = sk_socket_error(ssh->s)) != NULL) {
ssh->s = NULL;
notify_remote_exit(ssh->frontend);
return err;
}
@ -6933,6 +6941,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->protocol_initial_phase_done = FALSE;
ssh->pinger = NULL;
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
if (p != NULL)
return p;
@ -7012,6 +7022,8 @@ static void ssh_free(void *handle)
if (ssh->s)
ssh_do_close(ssh);
sfree(ssh);
if (ssh->pinger)
pinger_free(ssh->pinger);
}
/*
@ -7026,6 +7038,7 @@ static void ssh_free(void *handle)
static void ssh_reconfig(void *handle, Config *cfg)
{
Ssh ssh = (Ssh) handle;
pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
ssh->cfg = *cfg; /* STRUCTURE COPY */
}

View File

@ -244,6 +244,8 @@ typedef struct telnet_tag {
} state;
Config cfg;
Pinger pinger;
} *Telnet;
#define TELNET_MAX_BACKLOG 4096
@ -644,6 +646,7 @@ static int telnet_closing(Plug plug, const char *error_msg, int error_code,
if (telnet->s) {
sk_close(telnet->s);
telnet->s = NULL;
notify_remote_exit(telnet->frontend);
}
if (error_msg) {
/* A socket error has occurred. */
@ -704,6 +707,7 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
telnet->term_height = telnet->cfg.height;
telnet->state = TOP_LEVEL;
telnet->ldisc = NULL;
telnet->pinger = NULL;
*backend_handle = telnet;
/*
@ -739,6 +743,8 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
if ((err = sk_socket_error(telnet->s)) != NULL)
return err;
telnet->pinger = pinger_new(&telnet->cfg, &telnet_backend, telnet);
/*
* Initialise option states.
*/
@ -778,6 +784,8 @@ static void telnet_free(void *handle)
sfree(telnet->sb_buf);
if (telnet->s)
sk_close(telnet->s);
if (telnet->pinger)
pinger_free(telnet->pinger);
sfree(telnet);
}
/*
@ -788,6 +796,7 @@ static void telnet_free(void *handle)
static void telnet_reconfig(void *handle, Config *cfg)
{
Telnet telnet = (Telnet) handle;
pinger_reconfig(telnet->pinger, &telnet->cfg, cfg);
telnet->cfg = *cfg; /* STRUCTURE COPY */
}

View File

@ -43,6 +43,11 @@
#define TM_PUTTY (0xFFFF)
#define UPDATE_DELAY ((TICKSPERSEC+49)/50)/* ticks to defer window update */
#define TBLINK_DELAY ((TICKSPERSEC*9+19)/20)/* ticks between text blinks*/
#define CBLINK_DELAY (CURSORBLINK) /* ticks between cursor blinks */
#define VBELL_DELAY (VBELL_TIMEOUT) /* visual bell timeout in ticks */
#define compatibility(x) \
if ( ((CL_##x)&term->compatibility_level) == 0 ) { \
term->termstate=TOPLEVEL; \
@ -987,6 +992,117 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen)
#define lineptr(x) (lineptr)(term,x,__LINE__,FALSE)
#define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE)
static void term_schedule_tblink(Terminal *term);
static void term_schedule_cblink(Terminal *term);
static void term_timer(void *ctx, long now)
{
Terminal *term = (Terminal *)ctx;
int update = FALSE;
if (term->tblink_pending && now - term->next_tblink >= 0) {
term->tblinker = !term->tblinker;
term->tblink_pending = FALSE;
term_schedule_tblink(term);
update = TRUE;
}
if (term->cblink_pending && now - term->next_cblink >= 0) {
term->cblinker = !term->cblinker;
term->cblink_pending = FALSE;
term_schedule_cblink(term);
update = TRUE;
}
if (term->in_vbell && now - term->vbell_end >= 0) {
term->in_vbell = FALSE;
update = TRUE;
}
if (update ||
(term->window_update_pending && now - term->next_update >= 0))
term_update(term);
}
/*
* Call this whenever the terminal window state changes, to queue
* an update.
*/
static void seen_disp_event(Terminal *term)
{
term->seen_disp_event = TRUE; /* for scrollback-reset-on-activity */
if (!term->window_update_pending) {
term->window_update_pending = TRUE;
term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term);
}
}
/*
* Call when the terminal's blinking-text settings change, or when
* a text blink has just occurred.
*/
static void term_schedule_tblink(Terminal *term)
{
if (term->tblink_pending)
return; /* already well in hand */
if (term->blink_is_real) {
term->next_tblink = schedule_timer(TBLINK_DELAY, term_timer, term);
term->tblink_pending = TRUE;
} else {
term->tblinker = 1; /* reset when not in use */
term->tblink_pending = FALSE;
}
}
/*
* Likewise with cursor blinks.
*/
static void term_schedule_cblink(Terminal *term)
{
if (term->cblink_pending)
return; /* already well in hand */
if (term->cfg.blink_cur) {
term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term);
term->cblink_pending = TRUE;
} else {
term->cblinker = 1; /* reset when not in use */
term->cblink_pending = FALSE;
}
}
/*
* Call to reset cursor blinking on new output.
*/
static void term_reset_cblink(Terminal *term)
{
seen_disp_event(term);
term->cblinker = 1;
term->cblink_pending = FALSE;
term_schedule_cblink(term);
}
/*
* Call to begin a visual bell.
*/
static void term_schedule_vbell(Terminal *term, int already_started,
long startpoint)
{
long ticks_already_gone;
if (already_started)
ticks_already_gone = GETTICKCOUNT() - startpoint;
else
ticks_already_gone = 0;
if (ticks_already_gone < VBELL_DELAY) {
term->in_vbell = TRUE;
term->vbell_end = schedule_timer(VBELL_DELAY - ticks_already_gone,
term_timer, term);
} else {
term->in_vbell = FALSE;
}
}
/*
* Set up power-on settings for the terminal.
*/
@ -1038,6 +1154,8 @@ static void power_on(Terminal *term)
swap_screen(term, 0, FALSE, FALSE);
erase_lots(term, FALSE, TRUE, TRUE);
}
term_schedule_tblink(term);
term_schedule_cblink(term);
}
/*
@ -1046,6 +1164,9 @@ static void power_on(Terminal *term)
void term_update(Terminal *term)
{
Context ctx;
term->window_update_pending = FALSE;
ctx = get_ctx(term->frontend);
if (ctx) {
int need_sbar_update = term->seen_disp_event;
@ -1090,7 +1211,7 @@ void term_seen_key_event(Terminal *term)
*/
if (term->cfg.scroll_on_key) {
term->disptop = 0; /* return to main screen */
term->seen_disp_event = 1;
seen_disp_event(term);
}
}
@ -1130,13 +1251,13 @@ void term_reconfig(Terminal *term, Config *cfg)
* default one. The full list is: Auto wrap mode, DEC Origin
* Mode, BCE, blinking text, character classes.
*/
int reset_wrap, reset_decom, reset_bce, reset_blink, reset_charclass;
int reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass;
int i;
reset_wrap = (term->cfg.wrap_mode != cfg->wrap_mode);
reset_decom = (term->cfg.dec_om != cfg->dec_om);
reset_bce = (term->cfg.bce != cfg->bce);
reset_blink = (term->cfg.blinktext != cfg->blinktext);
reset_tblink = (term->cfg.blinktext != cfg->blinktext);
reset_charclass = 0;
for (i = 0; i < lenof(term->cfg.wordness); i++)
if (term->cfg.wordness[i] != cfg->wordness[i])
@ -1168,8 +1289,9 @@ void term_reconfig(Terminal *term, Config *cfg)
term->use_bce = term->cfg.bce;
set_erase_char(term);
}
if (reset_blink)
if (reset_tblink) {
term->blink_is_real = term->cfg.blinktext;
}
if (reset_charclass)
for (i = 0; i < 256; i++)
term->wordness[i] = term->cfg.wordness[i];
@ -1188,6 +1310,8 @@ void term_reconfig(Terminal *term, Config *cfg)
if (!*term->cfg.printer) {
term_print_finish(term);
}
term_schedule_tblink(term);
term_schedule_cblink(term);
}
/*
@ -1224,7 +1348,7 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
term->logctx = NULL;
term->compatibility_level = TM_PUTTY;
strcpy(term->id_string, "\033[?6c");
term->last_blink = term->last_tblink = 0;
term->cblink_pending = term->tblink_pending = FALSE;
term->paste_buffer = NULL;
term->paste_len = 0;
term->last_paste = 0;
@ -1237,7 +1361,7 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
term->seen_disp_event = FALSE;
term->xterm_mouse = term->mouse_is_down = FALSE;
term->reset_132 = FALSE;
term->blinker = term->tblinker = 0;
term->cblinker = term->tblinker = 0;
term->has_focus = 1;
term->repeat_off = FALSE;
term->termstate = TOPLEVEL;
@ -1271,6 +1395,8 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
term->wcTo = NULL;
term->wcFromTo_size = 0;
term->window_update_pending = FALSE;
term->bidi_cache_size = 0;
term->pre_bidi_cache = term->post_bidi_cache = NULL;
@ -1324,6 +1450,8 @@ void term_free(Terminal *term)
sfree(term->pre_bidi_cache);
sfree(term->post_bidi_cache);
expire_timer_context(term);
sfree(term);
}
@ -2044,8 +2172,6 @@ static void insch(Terminal *term, int n)
*/
static void toggle_mode(Terminal *term, int mode, int query, int state)
{
unsigned long ticks;
if (query)
switch (mode) {
case 1: /* DECCKM: application cursor keys */
@ -2059,6 +2185,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
} else {
term->blink_is_real = term->cfg.blinktext;
}
term_schedule_tblink(term);
break;
case 3: /* DECCOLM: 80/132 columns */
deselect(term);
@ -2077,27 +2204,15 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
* effective visual bell, so that ESC[?5hESC[?5l will
* always be an actually _visible_ visual bell.
*/
ticks = GETTICKCOUNT();
/* turn off a previous vbell to avoid inconsistencies */
if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
term->in_vbell = FALSE;
if (term->rvideo && !state && /* we're turning it off... */
(ticks - term->rvbell_startpoint) < VBELL_TIMEOUT) {/*...soon*/
/* If there's no vbell timeout already, or this one lasts
* longer, replace vbell_timeout with ours. */
if (!term->in_vbell ||
(term->rvbell_startpoint - term->vbell_startpoint <
VBELL_TIMEOUT))
term->vbell_startpoint = term->rvbell_startpoint;
term->in_vbell = TRUE; /* may clear rvideo but set in_vbell */
if (term->rvideo && !state) {
/* This is an OFF, so set up a vbell */
term_schedule_vbell(term, TRUE, term->rvbell_startpoint);
} else if (!term->rvideo && state) {
/* This is an ON, so we notice the time and save it. */
term->rvbell_startpoint = ticks;
term->rvbell_startpoint = GETTICKCOUNT();
}
term->rvideo = state;
term->seen_disp_event = TRUE;
if (state)
term_update(term);
seen_disp_event(term);
break;
case 6: /* DECOM: DEC origin mode */
term->dec_om = state;
@ -2116,7 +2231,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
case 25: /* DECTCEM: enable/disable cursor */
compatibility2(OTHER, VT220);
term->cursor_on = state;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 47: /* alternate screen */
compatibility(OTHER);
@ -2141,12 +2256,12 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
case 1048: /* save/restore cursor */
if (!term->cfg.no_alt_screen)
save_cursor(term, state);
if (!state) term->seen_disp_event = TRUE;
if (!state) seen_disp_event(term);
break;
case 1049: /* cursor & alternate screen */
if (state && !term->cfg.no_alt_screen)
save_cursor(term, state);
if (!state) term->seen_disp_event = TRUE;
if (!state) seen_disp_event(term);
compatibility(OTHER);
deselect(term);
swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE);
@ -2254,7 +2369,7 @@ static void term_print_finish(Terminal *term)
* in-memory display. There's a big state machine in here to
* process escape sequences...
*/
void term_out(Terminal *term)
static void term_out(Terminal *term)
{
unsigned long c;
int unget;
@ -2567,13 +2682,12 @@ void term_out(Terminal *term)
*/
if (!term->cfg.bellovl || !term->beep_overloaded) {
beep(term->frontend, term->cfg.beep);
if (term->cfg.beep == BELL_VISUAL) {
term->in_vbell = TRUE;
term->vbell_startpoint = ticks;
term_update(term);
term_schedule_vbell(term, FALSE, 0);
}
}
term->seen_disp_event = TRUE;
seen_disp_event(term);
}
break;
case '\b': /* BS: Back space */
@ -2586,7 +2700,7 @@ void term_out(Terminal *term)
term->wrapnext = FALSE;
else
term->curs.x--;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case '\016': /* LS1: Locking-shift one */
compatibility(VT100);
@ -2608,7 +2722,7 @@ void term_out(Terminal *term)
case '\015': /* CR: Carriage return */
term->curs.x = 0;
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
seen_disp_event(term);
term->paste_hold = 0;
if (term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
@ -2619,7 +2733,7 @@ void term_out(Terminal *term)
erase_lots(term, FALSE, FALSE, TRUE);
term->disptop = 0;
term->wrapnext = FALSE;
term->seen_disp_event = 1;
seen_disp_event(term);
break;
}
case '\013': /* VT: Line tabulation */
@ -2632,7 +2746,7 @@ void term_out(Terminal *term)
if (term->cfg.lfhascr)
term->curs.x = 0;
term->wrapnext = FALSE;
term->seen_disp_event = 1;
seen_disp_event(term);
term->paste_hold = 0;
if (term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
@ -2657,7 +2771,7 @@ void term_out(Terminal *term)
check_selection(term, old_curs, term->curs);
}
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
}
} else
@ -2776,7 +2890,7 @@ void term_out(Terminal *term)
}
add_cc(cline, x, c);
term->seen_disp_event = 1;
seen_disp_event(term);
}
continue;
default:
@ -2796,7 +2910,7 @@ void term_out(Terminal *term)
term->wrapnext = FALSE;
}
}
term->seen_disp_event = 1;
seen_disp_event(term);
}
break;
@ -2841,7 +2955,7 @@ void term_out(Terminal *term)
case '8': /* DECRC: restore cursor */
compatibility(VT100);
save_cursor(term, FALSE);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case '=': /* DECKPAM: Keypad application mode */
compatibility(VT100);
@ -2858,7 +2972,7 @@ void term_out(Terminal *term)
else if (term->curs.y < term->rows - 1)
term->curs.y++;
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'E': /* NEL: exactly equivalent to CR-LF */
compatibility(VT100);
@ -2868,7 +2982,7 @@ void term_out(Terminal *term)
else if (term->curs.y < term->rows - 1)
term->curs.y++;
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'M': /* RI: reverse index - backwards LF */
compatibility(VT100);
@ -2877,7 +2991,7 @@ void term_out(Terminal *term)
else if (term->curs.y > 0)
term->curs.y--;
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'Z': /* DECID: terminal type query */
compatibility(VT100);
@ -2896,7 +3010,7 @@ void term_out(Terminal *term)
term->reset_132 = 0;
}
term->disptop = 0;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'H': /* HTS: set a tab */
compatibility(VT100);
@ -2920,7 +3034,7 @@ void term_out(Terminal *term)
ldata->lattr = LATTR_NORM;
}
term->disptop = 0;
term->seen_disp_event = TRUE;
seen_disp_event(term);
scrtop.x = scrtop.y = 0;
scrbot.x = 0;
scrbot.y = term->rows;
@ -3036,7 +3150,7 @@ void term_out(Terminal *term)
case 'A': /* CUU: move up N lines */
move(term, term->curs.x,
term->curs.y - def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'e': /* VPR: move down N lines */
compatibility(ANSI);
@ -3044,7 +3158,7 @@ void term_out(Terminal *term)
case 'B': /* CUD: Cursor down */
move(term, term->curs.x,
term->curs.y + def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case ANSI('c', '>'): /* DA: report xterm version */
compatibility(OTHER);
@ -3059,31 +3173,31 @@ void term_out(Terminal *term)
case 'C': /* CUF: Cursor right */
move(term, term->curs.x + def(term->esc_args[0], 1),
term->curs.y, 1);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'D': /* CUB: move left N cols */
move(term, term->curs.x - def(term->esc_args[0], 1),
term->curs.y, 1);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'E': /* CNL: move down N lines and CR */
compatibility(ANSI);
move(term, 0,
term->curs.y + def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'F': /* CPL: move up N lines and CR */
compatibility(ANSI);
move(term, 0,
term->curs.y - def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'G': /* CHA */
case '`': /* HPA: set horizontal posn */
compatibility(ANSI);
move(term, def(term->esc_args[0], 1) - 1,
term->curs.y, 0);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'd': /* VPA: set vertical posn */
compatibility(ANSI);
@ -3091,7 +3205,7 @@ void term_out(Terminal *term)
((term->dec_om ? term->marg_t : 0) +
def(term->esc_args[0], 1) - 1),
(term->dec_om ? 2 : 0));
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'H': /* CUP */
case 'f': /* HVP: set horz and vert posns at once */
@ -3101,7 +3215,7 @@ void term_out(Terminal *term)
((term->dec_om ? term->marg_t : 0) +
def(term->esc_args[0], 1) - 1),
(term->dec_om ? 2 : 0));
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'J': /* ED: erase screen or parts of it */
{
@ -3111,7 +3225,7 @@ void term_out(Terminal *term)
erase_lots(term, FALSE, !!(i & 2), !!(i & 1));
}
term->disptop = 0;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'K': /* EL: erase line or parts of it */
{
@ -3120,14 +3234,14 @@ void term_out(Terminal *term)
i = 0;
erase_lots(term, TRUE, !!(i & 2), !!(i & 1));
}
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'L': /* IL: insert lines */
compatibility(VT102);
if (term->curs.y <= term->marg_b)
scroll(term, term->curs.y, term->marg_b,
-def(term->esc_args[0], 1), FALSE);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'M': /* DL: delete lines */
compatibility(VT102);
@ -3135,18 +3249,18 @@ void term_out(Terminal *term)
scroll(term, term->curs.y, term->marg_b,
def(term->esc_args[0], 1),
TRUE);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case '@': /* ICH: insert chars */
/* XXX VTTEST says this is vt220, vt510 manual says vt102 */
compatibility(VT102);
insch(term, def(term->esc_args[0], 1));
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'P': /* DCH: delete chars */
compatibility(VT102);
insch(term, -def(term->esc_args[0], 1));
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'c': /* DA: terminal type query */
compatibility(VT100);
@ -3244,7 +3358,7 @@ void term_out(Terminal *term)
*/
term->curs.y = (term->dec_om ?
term->marg_t : 0);
term->seen_disp_event = TRUE;
seen_disp_event(term);
}
}
break;
@ -3302,6 +3416,7 @@ void term_out(Terminal *term)
compatibility(SCOANSI);
term->blink_is_real = FALSE;
term->curr_attr |= ATTR_BLINK;
term_schedule_tblink(term);
break;
case 7: /* enable reverse video */
term->curr_attr |= ATTR_REVERSE;
@ -3406,7 +3521,7 @@ void term_out(Terminal *term)
break;
case 'u': /* restore cursor */
save_cursor(term, FALSE);
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 't': /* DECSLPP: set page size - ie window height */
/*
@ -3548,14 +3663,14 @@ void term_out(Terminal *term)
scroll(term, term->marg_t, term->marg_b,
def(term->esc_args[0], 1), TRUE);
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case 'T': /* SD: Scroll down */
compatibility(SCOANSI);
scroll(term, term->marg_t, term->marg_b,
-def(term->esc_args[0], 1), TRUE);
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
seen_disp_event(term);
break;
case ANSI('|', '*'): /* DECSNLS */
/*
@ -3608,7 +3723,7 @@ void term_out(Terminal *term)
while (n--)
copy_termchar(cline, p++,
&term->erase_char);
term->seen_disp_event = TRUE;
seen_disp_event(term);
}
break;
case 'x': /* DECREQTPARM: report terminal characteristics */
@ -3672,6 +3787,7 @@ void term_out(Terminal *term)
case ANSI('D', '='):
compatibility(SCOANSI);
term->blink_is_real = FALSE;
term_schedule_tblink(term);
if (term->esc_args[0]>=1)
term->curr_attr |= ATTR_BLINK;
else
@ -3680,6 +3796,7 @@ void term_out(Terminal *term)
case ANSI('E', '='):
compatibility(SCOANSI);
term->blink_is_real = (term->esc_args[0] >= 1);
term_schedule_tblink(term);
break;
case ANSI('F', '='): /* set normal foreground */
compatibility(SCOANSI);
@ -3910,7 +4027,7 @@ void term_out(Terminal *term)
break;
case VT52_ESC:
term->termstate = TOPLEVEL;
term->seen_disp_event = TRUE;
seen_disp_event(term);
switch (c) {
case 'A':
move(term, term->curs.x, term->curs.y - 1, 1);
@ -4018,6 +4135,7 @@ void term_out(Terminal *term)
*/
term->vt52_mode = FALSE;
term->blink_is_real = term->cfg.blinktext;
term_schedule_tblink(term);
break;
#if 0
case '^':
@ -4245,7 +4363,6 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
wchar_t *ch;
int chlen;
termchar cursor_background;
unsigned long ticks;
#ifdef OPTIMISE_SCROLL
struct scrollregion *sr;
#endif /* OPTIMISE_SCROLL */
@ -4255,28 +4372,19 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
chlen = 1024;
ch = snewn(chlen, wchar_t);
/*
* Check the visual bell state.
*/
if (term->in_vbell) {
ticks = GETTICKCOUNT();
if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
term->in_vbell = FALSE;
}
rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
/* Depends on:
* screen array, disptop, scrtop,
* selection, rv,
* cfg.blinkpc, blink_is_real, tblinker,
* curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
* curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
*/
/* Has the cursor position or type changed ? */
if (term->cursor_on) {
if (term->has_focus) {
if (term->blinker || !term->cfg.blink_cur)
if (term->cblinker || !term->cfg.blink_cur)
cursor = TATTR_ACTCURS;
else
cursor = 0;
@ -4678,39 +4786,6 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
sfree(ch);
}
/*
* Flick the switch that says if blinking things should be shown or hidden.
*/
void term_blink(Terminal *term, int flg)
{
long now, blink_diff;
now = GETTICKCOUNT();
blink_diff = now - term->last_tblink;
/* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */
if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) {
term->last_tblink = now;
term->tblinker = !term->tblinker;
}
if (flg) {
term->blinker = 1;
term->last_blink = now;
return;
}
blink_diff = now - term->last_blink;
/* Make sure the cursor blinks no faster than system blink rate */
if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
return;
term->last_blink = now;
term->blinker = !term->blinker;
}
/*
* Invalidate the whole screen so it will be repainted in full.
*/
@ -4744,12 +4819,14 @@ void term_paint(Terminal *term, Context ctx,
term->disptext[i]->chars[j].attr = ATTR_INVALID;
}
/* This should happen soon enough, also for some reason it sometimes
* fails to actually do anything when re-sizing ... painting the wrong
* window perhaps ?
*/
if (immediately)
if (immediately) {
do_paint (term, ctx, FALSE);
} else {
if (!term->window_update_pending) {
term->window_update_pending = TRUE;
term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term);
}
}
}
/*
@ -5960,8 +6037,14 @@ int term_data(Terminal *term, int is_stderr, const char *data, int len)
if (!term->in_term_out) {
term->in_term_out = TRUE;
term_blink(term, 1);
term_out(term);
term_reset_cblink(term);
/*
* During drag-selects, we do not process terminal input,
* because the user will want the screen to hold still to
* be selected.
*/
if (term->selstate != DRAGGING)
term_out(term);
term->in_term_out = FALSE;
}

View File

@ -117,7 +117,7 @@ struct terminal_tag {
int cursor_on; /* cursor enabled flag */
int reset_132; /* Flag ESC c resets to 80 cols */
int use_bce; /* Use Background coloured erase */
int blinker; /* When blinking is the cursor on ? */
int cblinker; /* When blinking is the cursor on ? */
int tblinker; /* When the blinking text is on */
int blink_is_real; /* Actually blink blinking text */
int term_echoing; /* Does terminal want local echo? */
@ -136,15 +136,12 @@ struct terminal_tag {
int rows, cols, savelines;
int has_focus;
int in_vbell;
unsigned long vbell_startpoint;
long vbell_end;
int app_cursor_keys, app_keypad_keys, vt52_mode;
int repeat_off, cr_lf_return;
int seen_disp_event;
int big_cursor;
long last_blink; /* used for real blinking control */
long last_tblink;
int xterm_mouse; /* send mouse messages to app */
int mouse_is_down; /* used while tracking mouse buttons */
@ -245,6 +242,19 @@ struct terminal_tag {
*/
int in_term_out;
/*
* We schedule a window update shortly after receiving terminal
* data. This tracks whether one is currently pending.
*/
int window_update_pending;
long next_update;
/*
* Track pending blinks and tblinks.
*/
int tblink_pending, cblink_pending;
long next_tblink, next_cblink;
/*
* These are buffers used by the bidi and Arabic shaping code.
*/

173
timing.c Normal file
View File

@ -0,0 +1,173 @@
/*
* timing.c
*
* This module tracks any timers set up by schedule_timer(). It
* keeps all the currently active timers in a list; it informs the
* front end of when the next timer is due to go off if that
* changes; and, very importantly, it tracks the context pointers
* passed to schedule_timer(), so that if a context is freed all
* the timers associated with it can be immediately annulled.
*/
#include <assert.h>
#include <stdio.h>
#include "putty.h"
#include "tree234.h"
struct timer {
timer_fn_t fn;
void *ctx;
long now;
};
static tree234 *timers = NULL;
static tree234 *timer_contexts = NULL;
static long now = 0L;
static int compare_timers(void *av, void *bv)
{
struct timer *a = (struct timer *)av;
struct timer *b = (struct timer *)bv;
long at = a->now - now;
long bt = b->now - now;
if (at < bt)
return -1;
else if (at > bt)
return +1;
/*
* Failing that, compare on the other two fields, just so that
* we don't get unwanted equality.
*/
if (a->fn < b->fn)
return -1;
else if (a->fn > b->fn)
return +1;
if (a->ctx < b->ctx)
return -1;
else if (a->ctx > b->ctx)
return +1;
/*
* Failing _that_, the two entries genuinely are equal, and we
* never have a need to store them separately in the tree.
*/
return 0;
}
static int compare_timer_contexts(void *av, void *bv)
{
char *a = (char *)av;
char *b = (char *)bv;
if (a < b)
return -1;
else if (a > b)
return +1;
return 0;
}
static void init_timers(void)
{
if (!timers) {
timers = newtree234(compare_timers);
timer_contexts = newtree234(compare_timer_contexts);
now = GETTICKCOUNT();
}
}
long schedule_timer(int ticks, timer_fn_t fn, void *ctx)
{
long when;
struct timer *t, *first;
init_timers();
when = ticks + GETTICKCOUNT();
assert(when - now > 0);
t = snew(struct timer);
t->fn = fn;
t->ctx = ctx;
t->now = when;
if (t != add234(timers, t)) {
sfree(t); /* identical timer already exists */
} else {
add234(timer_contexts, t->ctx);/* don't care if this fails */
}
first = (struct timer *)index234(timers, 0);
if (first == t) {
/*
* This timer is the very first on the list, so we must
* notify the front end.
*/
timer_change_notify(first->now);
}
return when;
}
/*
* Call to run any timers whose time has reached the present.
* Returns the time (in ticks) expected until the next timer after
* that triggers.
*/
int run_timers(long anow, long *next)
{
struct timer *first;
init_timers();
now = anow;
while (1) {
first = (struct timer *)index234(timers, 0);
if (!first)
return FALSE; /* no timers remaining */
if (find234(timer_contexts, first->ctx, NULL) == NULL) {
/*
* This timer belongs to a context that has been
* expired. Delete it without running.
*/
delpos234(timers, 0);
sfree(first);
} else if (first->now - now <= 0) {
/*
* This timer is active and has reached its running
* time. Run it.
*/
delpos234(timers, 0);
first->fn(first->ctx, first->now);
sfree(first);
} else {
/*
* This is the first still-active timer that is in the
* future. Return how long it has yet to go.
*/
*next = first->now;
return TRUE;
}
}
}
/*
* Call to expire all timers associated with a given context.
*/
void expire_timer_context(void *ctx)
{
init_timers();
/*
* We don't bother to check the return value; if the context
* already wasn't in the tree (presumably because no timers
* ever actually got scheduled for it) then that's fine and we
* simply don't need to do anything.
*/
del234(timer_contexts, ctx);
}

View File

@ -40,6 +40,13 @@ GdkAtom compound_text_atom, utf8_string_atom;
extern char **pty_argv; /* declared in pty.c */
extern int use_pty_argv;
/*
* Timers are global across all sessions (even if we were handling
* multiple sessions, which we aren't), so the current timer ID is
* a global variable.
*/
static guint timer_id = 0;
struct gui_data {
GtkWidget *window, *area, *sbar;
GtkBox *hbox;
@ -1022,7 +1029,6 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
show_mouseptr(inst, 0);
term_seen_key_event(inst->term);
term_out(inst->term);
}
return TRUE;
@ -1130,9 +1136,9 @@ void frontend_keypress(void *handle)
exit(0);
}
gint timer_func(gpointer data)
void notify_remote_exit(void *frontend)
{
struct gui_data *inst = (struct gui_data *)data;
struct gui_data *inst = (struct gui_data *)frontend;
int exitcode;
if (!inst->exited &&
@ -1153,10 +1159,40 @@ gint timer_func(gpointer data)
}
gtk_widget_show(inst->restartitem);
}
}
term_update(inst->term);
term_blink(inst->term, 0);
return TRUE;
static gint timer_trigger(gpointer data)
{
long now = GPOINTER_TO_INT(data);
long next;
long ticks;
if (run_timers(now, &next)) {
ticks = next - GETTICKCOUNT();
timer_id = gtk_timeout_add(ticks > 0 ? ticks : 1, timer_trigger,
GINT_TO_POINTER(next));
}
/*
* Never let a timer resume. If we need another one, we've
* asked for it explicitly above.
*/
return FALSE;
}
void timer_change_notify(long next)
{
long ticks;
if (timer_id)
gtk_timeout_remove(timer_id);
ticks = next - GETTICKCOUNT();
if (ticks <= 0)
ticks = 1; /* just in case */
timer_id = gtk_timeout_add(ticks, timer_trigger,
GINT_TO_POINTER(next));
}
void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
@ -1183,7 +1219,6 @@ gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
{
struct gui_data *inst = (struct gui_data *)data;
inst->term->has_focus = event->in;
term_out(inst->term);
term_update(inst->term);
show_mouseptr(inst, 1);
return FALSE;
@ -3392,7 +3427,6 @@ int pt_main(int argc, char **argv)
if (inst->cfg.scrollbar)
gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
GTK_SIGNAL_FUNC(scrollbar_moved), inst);
gtk_timeout_add(20, timer_func, inst);
gtk_widget_add_events(GTK_WIDGET(inst->area),
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |

View File

@ -474,6 +474,8 @@ int pty_select_result(int fd, int event)
#endif
from_backend(pty_frontend, 0, message, strlen(message));
}
notify_remote_exit(pty_frontend);
}
return !finished;
}

View File

@ -45,8 +45,8 @@ extern Backend pty_backend;
/* Simple wraparound timer function */
unsigned long getticks(void); /* based on gettimeofday(2) */
#define GETTICKCOUNT getticks
#define TICKSPERSEC 1000000 /* gettimeofday returns microseconds */
#define CURSORBLINK 450000 /* no standard way to set this */
#define TICKSPERSEC 1000 /* we choose to use milliseconds */
#define CURSORBLINK 450 /* no standard way to set this */
#define WCHAR wchar_t
#define BYTE unsigned char

View File

@ -35,6 +35,14 @@ void update_specials_menu(void *frontend)
{
}
void notify_remote_exit(void *frontend)
{
}
void timer_change_notify(long next)
{
}
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint)
{

View File

@ -15,14 +15,13 @@ unsigned long getticks(void)
struct timeval tv;
gettimeofday(&tv, NULL);
/*
* This will wrap around approximately every 4000 seconds, i.e.
* just over an hour, which is more than enough.
* We want to use milliseconds rather than microseconds,
* because we need a decent number of them to fit into a 32-bit
* word so it can be used for keepalives.
*/
return tv.tv_sec * 1000000 + tv.tv_usec;
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
Filename filename_from_str(const char *str)
{
Filename ret;

View File

@ -252,6 +252,7 @@ int main(int argc, char **argv)
int errors;
int use_subsystem = 0;
void *ldisc, *logctx;
long now;
ssh_get_line = console_get_line;
@ -584,6 +585,7 @@ int main(int argc, char **argv)
atexit(cleanup_termios);
ldisc_update(NULL, 1, 1);
sending = FALSE;
now = GETTICKCOUNT();
while (1) {
fd_set rset, wset, xset;
@ -644,7 +646,23 @@ int main(int argc, char **argv)
}
do {
ret = select(maxfd, &rset, &wset, &xset, NULL);
long next, ticks;
struct timeval tv, *ptv;
if (run_timers(now, &next)) {
ticks = next - GETTICKCOUNT();
if (ticks < 0) ticks = 0; /* just in case */
tv.tv_sec = ticks / 1000;
tv.tv_usec = ticks % 1000 * 1000;
ptv = &tv;
} else {
ptv = NULL;
}
ret = select(maxfd, &rset, &wset, &xset, ptv);
if (ret == 0)
now = next;
else
now = GETTICKCOUNT();
} while (ret < 0 && errno == EINTR);
if (ret < 0) {

View File

@ -321,55 +321,80 @@ char *dir_file_cat(char *dir, char *file)
}
/*
* Wait for some network data and process it.
* Do a select() between all currently active network fds and
* optionally stdin.
*/
int ssh_sftp_loop_iteration(void)
static int ssh_sftp_do_select(int include_stdin)
{
fd_set rset, wset, xset;
int i, fdcount, fdsize, *fdlist;
int fd, fdstate, rwx, ret, maxfd;
long now = GETTICKCOUNT();
fdlist = NULL;
fdcount = fdsize = 0;
/* Count the currently active fds. */
i = 0;
for (fd = first_fd(&fdstate, &rwx); fd >= 0;
fd = next_fd(&fdstate, &rwx)) i++;
if (i < 1)
return -1; /* doom */
/* Expand the fdlist buffer if necessary. */
if (i > fdsize) {
fdsize = i + 16;
fdlist = sresize(fdlist, fdsize, int);
}
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&xset);
maxfd = 0;
/*
* Add all currently open fds to the select sets, and store
* them in fdlist as well.
*/
fdcount = 0;
for (fd = first_fd(&fdstate, &rwx); fd >= 0;
fd = next_fd(&fdstate, &rwx)) {
fdlist[fdcount++] = fd;
if (rwx & 1)
FD_SET_MAX(fd, maxfd, rset);
if (rwx & 2)
FD_SET_MAX(fd, maxfd, wset);
if (rwx & 4)
FD_SET_MAX(fd, maxfd, xset);
}
do {
ret = select(maxfd, &rset, &wset, &xset, NULL);
} while (ret < 0 && errno == EINTR);
/* Count the currently active fds. */
i = 0;
for (fd = first_fd(&fdstate, &rwx); fd >= 0;
fd = next_fd(&fdstate, &rwx)) i++;
if (i < 1)
return -1; /* doom */
/* Expand the fdlist buffer if necessary. */
if (i > fdsize) {
fdsize = i + 16;
fdlist = sresize(fdlist, fdsize, int);
}
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&xset);
maxfd = 0;
/*
* Add all currently open fds to the select sets, and store
* them in fdlist as well.
*/
fdcount = 0;
for (fd = first_fd(&fdstate, &rwx); fd >= 0;
fd = next_fd(&fdstate, &rwx)) {
fdlist[fdcount++] = fd;
if (rwx & 1)
FD_SET_MAX(fd, maxfd, rset);
if (rwx & 2)
FD_SET_MAX(fd, maxfd, wset);
if (rwx & 4)
FD_SET_MAX(fd, maxfd, xset);
}
if (include_stdin)
FD_SET_MAX(0, maxfd, rset);
do {
long next, ticks;
struct timeval tv, *ptv;
if (run_timers(now, &next)) {
ticks = next - GETTICKCOUNT();
if (ticks <= 0)
ticks = 1; /* just in case */
tv.tv_sec = ticks / 1000;
tv.tv_usec = ticks % 1000 * 1000;
ptv = &tv;
} else {
ptv = NULL;
}
ret = select(maxfd, &rset, &wset, &xset, ptv);
if (ret == 0)
now = next;
else
now = GETTICKCOUNT();
} while (ret < 0 && errno != EINTR);
} while (ret == 0);
if (ret < 0) {
perror("select");
@ -393,7 +418,58 @@ int ssh_sftp_loop_iteration(void)
sfree(fdlist);
return 0;
return FD_ISSET(0, &rset) ? 1 : 0;
}
/*
* Wait for some network data and process it.
*/
int ssh_sftp_loop_iteration(void)
{
return ssh_sftp_do_select(FALSE);
}
/*
* Read a PSFTP command line from stdin.
*/
char *ssh_sftp_get_cmdline(char *prompt)
{
char *buf;
int buflen, bufsize, ret;
fputs(prompt, stdout);
fflush(stdout);
buf = NULL;
buflen = bufsize = 0;
while (1) {
ret = ssh_sftp_do_select(TRUE);
if (ret < 0) {
printf("connection died\n");
return NULL; /* woop woop */
}
if (ret > 0) {
if (buflen >= bufsize) {
bufsize = buflen + 512;
buf = sresize(buf, bufsize, char);
}
ret = read(0, buf+buflen, 1);
if (ret < 0) {
perror("read");
return NULL;
}
if (ret == 0) {
/* eof on stdin; no error, but no answer either */
return NULL;
}
if (buf[buflen++] == '\n') {
/* we have a full line */
return buf;
}
}
}
}
/*

View File

@ -105,29 +105,6 @@ static void make_filename(char *filename, int index, const char *subname)
filename[FILENAME_MAX-1] = '\0';
}
/*
* Read an entire line of text from a file. Return a buffer
* malloced to be as big as necessary (caller must free).
*/
static char *fgetline(FILE *fp)
{
char *ret = snewn(512, char);
int size = 512, len = 0;
while (fgets(ret + len, size - len, fp)) {
len += strlen(ret + len);
if (ret[len-1] == '\n')
break; /* got a newline, we're done */
size = len + 512;
ret = sresize(ret, size, char);
}
if (len == 0) { /* first fgets returned NULL */
sfree(ret);
return NULL;
}
ret[len] = '\0';
return ret;
}
void *open_settings_w(const char *sessionname, char **errmsg)
{
char filename[FILENAME_MAX];

View File

@ -33,6 +33,14 @@ void cleanup_exit(int code)
exit(code);
}
void notify_remote_exit(void *frontend)
{
}
void timer_change_notify(long next)
{
}
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint)
{

View File

@ -92,17 +92,12 @@ static int offset_width, offset_height;
static int was_zoomed = 0;
static int prev_rows, prev_cols;
static int pending_netevent = 0;
static WPARAM pend_netevent_wParam = 0;
static LPARAM pend_netevent_lParam = 0;
static void enact_pending_netevent(void);
static void enact_netevent(WPARAM, LPARAM);
static void flash_window(int mode);
static void sys_cursor_update(void);
static int is_shift_pressed(void);
static int get_fullscreen_rect(RECT * ss);
static time_t last_movement = 0;
static int caret_x = -1, caret_y = -1;
static int kbd_codepage;
@ -117,6 +112,9 @@ static int session_closed;
static const struct telnet_special *specials;
static int n_specials;
#define TIMING_TIMER_ID 1234
static long timing_next_time;
static struct {
HMENU menu;
int specials_submenu_pos;
@ -776,30 +774,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
UpdateWindow(hwnd);
if (GetMessage(&msg, NULL, 0, 0) == 1) {
int timer_id = 0, long_timer = 0;
while (msg.message != WM_QUIT) {
/* Sometimes DispatchMessage calls routines that use their own
* GetMessage loop, setup this timer so we get some control back.
*
* Also call term_update() from the timer so that if the host
* is sending data flat out we still do redraws.
*/
if (timer_id && long_timer) {
KillTimer(hwnd, timer_id);
long_timer = timer_id = 0;
}
if (!timer_id)
timer_id = SetTimer(hwnd, 1, 20, NULL);
if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
DispatchMessage(&msg);
/* Make sure we blink everything that needs it. */
term_blink(term, 0);
/* Send the paste buffer if there's anything to send */
term_paste(term);
/* If there's nothing new in the queue then we can do everything
* we've delayed, reading the socket, writing, and repainting
* the window.
@ -807,42 +786,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
continue;
if (pending_netevent) {
enact_pending_netevent();
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
continue;
}
/* Okay there is now nothing to do so we make sure the screen is
* completely up to date then tell windows to call us in a little
* while.
*/
if (timer_id) {
KillTimer(hwnd, timer_id);
timer_id = 0;
}
HideCaret(hwnd);
if (GetCapture() != hwnd ||
(send_raw_mouse &&
!(cfg.mouse_override && is_shift_pressed())))
term_out(term);
term_update(term);
ShowCaret(hwnd);
flash_window(1); /* maintain */
/* The messages seem unreliable; especially if we're being tricky */
term->has_focus = (GetForegroundWindow() == hwnd);
if (term->in_vbell)
/* Hmm, term_update didn't want to do an update too soon ... */
timer_id = SetTimer(hwnd, 1, 50, NULL);
else if (!term->has_focus)
timer_id = SetTimer(hwnd, 1, 500, NULL);
else
timer_id = SetTimer(hwnd, 1, 100, NULL);
long_timer = 1;
net_pending_errors();
/* There's no point rescanning everything in the message queue
* so we do an apparently unnecessary wait here
@ -1035,7 +982,7 @@ void cmdline_error(char *fmt, ...)
/*
* Actually do the job requested by a WM_NETEVENT
*/
static void enact_pending_netevent(void)
static void enact_netevent(WPARAM wParam, LPARAM lParam)
{
static int reentering = 0;
extern int select_result(WPARAM, LPARAM);
@ -1044,10 +991,8 @@ static void enact_pending_netevent(void)
if (reentering)
return; /* don't unpend the pending */
pending_netevent = FALSE;
reentering = 1;
ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
ret = select_result(wParam, lParam);
reentering = 0;
if (ret == 0 && !session_closed) {
@ -1795,6 +1740,17 @@ static int is_shift_pressed(void)
static int resizing;
void notify_remote_exit(void *fe) { /* stub not needed in this frontend */ }
void timer_change_notify(long next)
{
long ticks = next - GETTICKCOUNT();
if (ticks <= 0) ticks = 1; /* just in case */
KillTimer(hwnd, TIMING_TIMER_ID);
SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL);
timing_next_time = next;
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
@ -1806,25 +1762,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
switch (message) {
case WM_TIMER:
if (pending_netevent)
enact_pending_netevent();
if (GetCapture() != hwnd ||
(send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
term_out(term);
noise_regular();
HideCaret(hwnd);
term_update(term);
ShowCaret(hwnd);
if (cfg.ping_interval > 0) {
time_t now;
time(&now);
if (now - last_movement > cfg.ping_interval) {
if (back)
back->special(backhandle, TS_PING);
last_movement = now;
if ((UINT_PTR)wParam == TIMING_TIMER_ID) {
long next;
KillTimer(hwnd, TIMING_TIMER_ID);
if (run_timers(timing_next_time, &next)) {
timer_change_notify(next);
} else {
}
}
net_pending_errors();
return 0;
case WM_CREATE:
break;
@ -2304,18 +2250,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
case WM_PAINT:
{
PAINTSTRUCT p;
HideCaret(hwnd);
hdc = BeginPaint(hwnd, &p);
if (pal) {
SelectPalette(hdc, pal, TRUE);
RealizePalette(hdc);
}
term_paint(term, hdc,
(p.rcPaint.left-offset_width)/font_width,
(p.rcPaint.top-offset_height)/font_height,
(p.rcPaint.right-offset_width-1)/font_width,
(p.rcPaint.bottom-offset_height-1)/font_height,
is_alt_pressed());
TRUE);
if (p.fErase ||
p.rcPaint.left < offset_width ||
@ -2365,20 +2313,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
}
return 0;
case WM_NETEVENT:
/* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
* but the only one that's likely to try to overload us is FD_READ.
* This means buffering just one is fine.
*/
if (pending_netevent)
enact_pending_netevent();
pending_netevent = TRUE;
pend_netevent_wParam = wParam;
pend_netevent_lParam = lParam;
if (WSAGETSELECTEVENT(lParam) != FD_READ)
enact_pending_netevent();
time(&last_movement);
enact_netevent(wParam, lParam);
net_pending_errors();
return 0;
case WM_SETFOCUS:
term->has_focus = TRUE;
@ -2386,7 +2322,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
ShowCaret(hwnd);
flash_window(0); /* stop */
compose_state = 0;
term_out(term);
term_update(term);
break;
case WM_KILLFOCUS:
@ -2394,7 +2329,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
term->has_focus = FALSE;
DestroyCaret();
caret_x = caret_y = -1; /* ensure caret is replaced next time */
term_out(term);
term_update(term);
break;
case WM_ENTERSIZEMOVE:
@ -4624,14 +4558,23 @@ void modalfatalbox(char *fmt, ...)
cleanup_exit(1);
}
static void flash_window(int mode);
static long next_flash;
static int flashing = 0;
static void flash_window_timer(void *ctx, long now)
{
if (flashing && now - next_flash >= 0) {
flash_window(1);
}
}
/*
* Manage window caption / taskbar flashing, if enabled.
* 0 = stop, 1 = maintain, 2 = start
*/
static void flash_window(int mode)
{
static long last_flash = 0;
static int flashing = 0;
if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
/* stop */
if (flashing) {
@ -4642,20 +4585,16 @@ static void flash_window(int mode)
} else if (mode == 2) {
/* start */
if (!flashing) {
last_flash = GetTickCount();
flashing = 1;
FlashWindow(hwnd, TRUE);
next_flash = schedule_timer(450, flash_window_timer, hwnd);
}
} else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
/* maintain */
if (flashing) {
long now = GetTickCount();
long fdiff = now - last_flash;
if (fdiff < 0 || fdiff > 450) {
last_flash = now;
FlashWindow(hwnd, TRUE); /* toggle */
}
FlashWindow(hwnd, TRUE); /* toggle */
next_flash = schedule_timer(450, flash_window_timer, hwnd);
}
}
}

View File

@ -1353,6 +1353,16 @@ SOCKET next_socket(int *state)
return s ? s->s : INVALID_SOCKET;
}
extern int socket_writable(SOCKET skt)
{
Actual_Socket s = find234(sktree, (void *)skt, cmpforsearch);
if (s)
return bufchain_size(&s->output_data) > 0;
else
return 0;
}
int net_service_lookup(char *service)
{
struct servent *se;

View File

@ -279,6 +279,7 @@ int main(int argc, char **argv)
int exitcode;
int errors;
int use_subsystem = 0;
long now, next;
ssh_get_line = console_get_line;
@ -631,8 +632,11 @@ int main(int argc, char **argv)
cleanup_exit(1);
}
now = GETTICKCOUNT();
while (1) {
int n;
DWORD ticks;
if (!sending && back->sendok(backhandle)) {
/*
@ -661,9 +665,16 @@ int main(int argc, char **argv)
sending = TRUE;
}
n = MsgWaitForMultipleObjects(4, handles, FALSE, INFINITE,
if (run_timers(now, &next)) {
ticks = next - GETTICKCOUNT();
if (ticks < 0) ticks = 0; /* just in case */
} else {
ticks = INFINITE;
}
n = MsgWaitForMultipleObjects(4, handles, FALSE, ticks,
QS_POSTMESSAGE);
if (n == 0) {
if (n == WAIT_OBJECT_0 + 0) {
WSANETWORKEVENTS things;
SOCKET socket;
extern SOCKET first_socket(int *), next_socket(int *);
@ -724,7 +735,7 @@ int main(int argc, char **argv)
}
}
}
} else if (n == 1) {
} else if (n == WAIT_OBJECT_0 + 1) {
reading = 0;
noise_ultralight(idata.len);
if (connopen && back->socket(backhandle) != NULL) {
@ -734,7 +745,7 @@ int main(int argc, char **argv)
back->special(backhandle, TS_EOF);
}
}
} else if (n == 2) {
} else if (n == WAIT_OBJECT_0 + 2) {
odata.busy = 0;
if (!odata.writeret) {
fprintf(stderr, "Unable to write to standard output\n");
@ -747,7 +758,7 @@ int main(int argc, char **argv)
back->unthrottle(backhandle, bufchain_size(&stdout_data) +
bufchain_size(&stderr_data));
}
} else if (n == 3) {
} else if (n == WAIT_OBJECT_0 + 3) {
edata.busy = 0;
if (!edata.writeret) {
fprintf(stderr, "Unable to write to standard output\n");
@ -760,7 +771,7 @@ int main(int argc, char **argv)
back->unthrottle(backhandle, bufchain_size(&stdout_data) +
bufchain_size(&stderr_data));
}
} else if (n == 4) {
} else if (n == WAIT_OBJECT_0 + 4) {
MSG msg;
while (PeekMessage(&msg, INVALID_HANDLE_VALUE,
WM_AGENT_CALLBACK, WM_AGENT_CALLBACK,
@ -770,6 +781,13 @@ int main(int argc, char **argv)
sfree(c);
}
}
if (n == WAIT_TIMEOUT) {
now = next;
} else {
now = GETTICKCOUNT();
}
if (!reading && back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
SetEvent(idata.eventback);
reading = 1;

View File

@ -2,6 +2,8 @@
* winsftp.c: the Windows-specific parts of PSFTP and PSCP.
*/
#include <assert.h>
#include "putty.h"
#include "psftp.h"
@ -461,35 +463,255 @@ char *dir_file_cat(char *dir, char *file)
* Be told what socket we're supposed to be using.
*/
static SOCKET sftp_ssh_socket;
static HANDLE netevent = NULL;
char *do_select(SOCKET skt, int startup)
{
int events;
if (startup)
sftp_ssh_socket = skt;
else
sftp_ssh_socket = INVALID_SOCKET;
if (p_WSAEventSelect) {
if (startup) {
events = (FD_CONNECT | FD_READ | FD_WRITE |
FD_OOB | FD_CLOSE | FD_ACCEPT);
netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
} else {
events = 0;
}
if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
switch (p_WSAGetLastError()) {
case WSAENETDOWN:
return "Network is down";
default:
return "WSAEventSelect(): unknown error";
}
}
}
return NULL;
}
extern int select_result(WPARAM, LPARAM);
int do_eventsel_loop(HANDLE other_event)
{
int n;
long next, ticks;
HANDLE handles[2];
SOCKET *sklist;
int skcount;
long now = GETTICKCOUNT();
if (!netevent) {
return -1; /* doom */
}
handles[0] = netevent;
handles[1] = other_event;
if (run_timers(now, &next)) {
ticks = next - GETTICKCOUNT();
if (ticks < 0) ticks = 0; /* just in case */
} else {
ticks = INFINITE;
}
n = MsgWaitForMultipleObjects(other_event ? 2 : 1, handles, FALSE, ticks,
QS_POSTMESSAGE);
if (n == WAIT_OBJECT_0 + 0) {
WSANETWORKEVENTS things;
SOCKET socket;
extern SOCKET first_socket(int *), next_socket(int *);
extern int select_result(WPARAM, LPARAM);
int i, socketstate;
/*
* We must not call select_result() for any socket
* until we have finished enumerating within the
* tree. This is because select_result() may close
* the socket and modify the tree.
*/
/* Count the active sockets. */
i = 0;
for (socket = first_socket(&socketstate);
socket != INVALID_SOCKET;
socket = next_socket(&socketstate)) i++;
/* Expand the buffer if necessary. */
sklist = snewn(i, SOCKET);
/* Retrieve the sockets into sklist. */
skcount = 0;
for (socket = first_socket(&socketstate);
socket != INVALID_SOCKET;
socket = next_socket(&socketstate)) {
sklist[skcount++] = socket;
}
/* Now we're done enumerating; go through the list. */
for (i = 0; i < skcount; i++) {
WPARAM wp;
socket = sklist[i];
wp = (WPARAM) socket;
if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {
static const struct { int bit, mask; } eventtypes[] = {
{FD_CONNECT_BIT, FD_CONNECT},
{FD_READ_BIT, FD_READ},
{FD_CLOSE_BIT, FD_CLOSE},
{FD_OOB_BIT, FD_OOB},
{FD_WRITE_BIT, FD_WRITE},
{FD_ACCEPT_BIT, FD_ACCEPT},
};
int e;
noise_ultralight(socket);
noise_ultralight(things.lNetworkEvents);
for (e = 0; e < lenof(eventtypes); e++)
if (things.lNetworkEvents & eventtypes[e].mask) {
LPARAM lp;
int err = things.iErrorCode[eventtypes[e].bit];
lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
select_result(wp, lp);
}
}
}
sfree(sklist);
}
if (n == WAIT_TIMEOUT) {
now = next;
} else {
now = GETTICKCOUNT();
}
if (other_event && n == WAIT_OBJECT_0 + 1)
return 1;
return 0;
}
/*
* Wait for some network data and process it.
*
* We have two variants of this function. One uses select() so that
* it's compatible with WinSock 1. The other uses WSAEventSelect
* and MsgWaitForMultipleObjects, so that we can consistently use
* WSAEventSelect throughout; this enables us to also implement
* ssh_sftp_get_cmdline() using a parallel mechanism.
*/
int ssh_sftp_loop_iteration(void)
{
fd_set readfds;
if (sftp_ssh_socket == INVALID_SOCKET)
return -1; /* doom */
FD_ZERO(&readfds);
FD_SET(sftp_ssh_socket, &readfds);
if (p_select(1, &readfds, NULL, NULL, NULL) < 0)
return -1; /* doom */
if (p_WSAEventSelect == NULL) {
fd_set readfds;
int ret;
long now = GETTICKCOUNT();
if (socket_writable(sftp_ssh_socket))
select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE);
do {
long next, ticks;
struct timeval tv, *ptv;
if (run_timers(now, &next)) {
ticks = next - GETTICKCOUNT();
if (ticks <= 0)
ticks = 1; /* just in case */
tv.tv_sec = ticks / 1000;
tv.tv_usec = ticks % 1000 * 1000;
ptv = &tv;
} else {
ptv = NULL;
}
FD_ZERO(&readfds);
FD_SET(sftp_ssh_socket, &readfds);
ret = p_select(1, &readfds, NULL, NULL, ptv);
if (ret < 0)
return -1; /* doom */
else if (ret == 0)
now = next;
else
now = GETTICKCOUNT();
} while (ret == 0);
select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
return 0;
} else {
return do_eventsel_loop(NULL);
}
}
/*
* Read a command line from standard input.
*
* In the presence of WinSock 2, we can use WSAEventSelect to
* mediate between the socket and stdin, meaning we can send
* keepalives and respond to server events even while waiting at
* the PSFTP command prompt. Without WS2, we fall back to a simple
* fgets.
*/
struct command_read_ctx {
HANDLE event;
char *line;
};
static DWORD WINAPI command_read_thread(void *param)
{
struct command_read_ctx *ctx = (struct command_read_ctx *) param;
ctx->line = fgetline(stdin);
SetEvent(ctx->event);
select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
return 0;
}
char *ssh_sftp_get_cmdline(char *prompt)
{
int ret;
struct command_read_ctx actx, *ctx = &actx;
DWORD threadid;
fputs(prompt, stdout);
fflush(stdout);
if (sftp_ssh_socket == INVALID_SOCKET || p_WSAEventSelect == NULL) {
return fgetline(stdin); /* very simple */
}
/*
* Create a second thread to read from stdin. Process network
* and timing events until it terminates.
*/
ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);
ctx->line = NULL;
if (!CreateThread(NULL, 0, command_read_thread,
ctx, 0, &threadid)) {
fprintf(stderr, "Unable to create command input thread\n");
cleanup_exit(1);
}
do {
ret = do_eventsel_loop(ctx->event);
/* Error return can only occur if netevent==NULL, and it ain't. */
assert(ret >= 0);
} while (ret == 0);
return ctx->line;
}
/* ----------------------------------------------------------------------
* Main program. Parse arguments etc.
*/

View File

@ -150,6 +150,8 @@ extern int (WINAPI *p_WSAGetLastError)(void);
extern int (WINAPI *p_WSAEnumNetworkEvents)
(SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
extern int socket_writable(SOCKET skt);
/*
* Exports from winctrls.c.
*/