mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-01 11:32:48 -05: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:
@ -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.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user