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:
parent
d609e1f7f8
commit
7ecf13564a
15
Recipe
15
Recipe
@ -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
|
||||
|
20
doc/udp.but
20
doc/udp.but
@ -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++
|
||||
|
||||
|
@ -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
23
misc.c
@ -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
3
misc.h
@ -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
71
pinger.c
Normal 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
48
psftp.c
@ -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);
|
||||
|
6
psftp.h
6
psftp.h
@ -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
44
putty.h
@ -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
1
raw.c
@ -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. */
|
||||
|
1
rlogin.c
1
rlogin.c
@ -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
13
ssh.c
@ -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 */
|
||||
}
|
||||
|
||||
|
9
telnet.c
9
telnet.c
@ -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 */
|
||||
}
|
||||
|
||||
|
321
terminal.c
321
terminal.c
@ -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;
|
||||
}
|
||||
|
||||
|
20
terminal.h
20
terminal.h
@ -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
173
timing.c
Normal 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);
|
||||
}
|
50
unix/pterm.c
50
unix/pterm.c
@ -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 |
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
156
unix/uxsftp.c
156
unix/uxsftp.c
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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];
|
||||
|
@ -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)
|
||||
{
|
||||
|
149
windows/window.c
149
windows/window.c
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user