mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +00:00
Add a general way to request an immediate top-level callback.
This is a little like schedule_timer, in that the callback you provide will be run from the top-level message loop of whatever application you're in; but unlike the timer mechanism, it will happen _immediately_. The aim is to provide a general way to avoid re-entrance of code, in cases where just _doing_ the thing you want done is liable to trigger a confusing recursive call to the function in which you came to the decision to do it; instead, you just request a top-level callback at the message loop's earliest convenience, and do it then. [originally from svn r10019]
This commit is contained in:
parent
883641845f
commit
75c79e318f
4
Recipe
4
Recipe
@ -288,7 +288,7 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint
|
||||
+ winutils wincfg sercfg winhelp winjump
|
||||
|
||||
# Same thing on Unix.
|
||||
UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing
|
||||
UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback
|
||||
GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols xkeysym
|
||||
OSXTERM = UXTERM osxwin osxdlg osxctrls
|
||||
|
||||
@ -308,7 +308,7 @@ SFTP = sftp int64 logging
|
||||
|
||||
# Miscellaneous objects appearing in all the network utilities (not
|
||||
# Pageant or PuTTYgen).
|
||||
MISC = timing misc version settings tree234 proxy conf
|
||||
MISC = timing callback misc version settings tree234 proxy conf
|
||||
WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy
|
||||
+ wintime
|
||||
UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time
|
||||
|
68
callback.c
Normal file
68
callback.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Facility for queueing callback functions to be run from the
|
||||
* top-level event loop after the current top-level activity finishes.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
struct callback {
|
||||
struct callback *next;
|
||||
|
||||
toplevel_callback_fn_t fn;
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
struct callback *cbhead = NULL, *cbtail = NULL;
|
||||
|
||||
toplevel_callback_notify_fn_t notify_frontend = NULL;
|
||||
void *frontend = NULL;
|
||||
|
||||
void request_callback_notifications(toplevel_callback_notify_fn_t fn,
|
||||
void *fr)
|
||||
{
|
||||
notify_frontend = fn;
|
||||
frontend = fr;
|
||||
}
|
||||
|
||||
void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
|
||||
{
|
||||
struct callback *cb;
|
||||
|
||||
cb = snew(struct callback);
|
||||
cb->fn = fn;
|
||||
cb->ctx = ctx;
|
||||
|
||||
/* If the front end has requested notification of pending
|
||||
* callbacks, and we didn't already have one queued, let it know
|
||||
* we do have one now. */
|
||||
if (notify_frontend && !cbhead)
|
||||
notify_frontend(frontend);
|
||||
|
||||
if (cbtail)
|
||||
cbtail->next = cb;
|
||||
else
|
||||
cbhead = cb;
|
||||
cbtail = cb;
|
||||
cb->next = NULL;
|
||||
}
|
||||
|
||||
void run_toplevel_callbacks(void)
|
||||
{
|
||||
while (cbhead) {
|
||||
struct callback *cb = cbhead;
|
||||
/*
|
||||
* Careful ordering here. We call the function _before_
|
||||
* advancing cbhead (though, of course, we must free cb
|
||||
* _after_ advancing it). This means that if the very last
|
||||
* callback schedules another callback, cbhead does not become
|
||||
* NULL at any point in this while loop, and so the frontend
|
||||
* notification function won't be needlessly pestered.
|
||||
*/
|
||||
cb->fn(cb->ctx);
|
||||
cbhead = cb->next;
|
||||
sfree(cb);
|
||||
}
|
||||
cbtail = NULL;
|
||||
}
|
25
putty.h
25
putty.h
@ -1390,6 +1390,31 @@ void expire_timer_context(void *ctx);
|
||||
int run_timers(unsigned long now, unsigned long *next);
|
||||
void timer_change_notify(unsigned long next);
|
||||
|
||||
/*
|
||||
* Exports from callback.c.
|
||||
*
|
||||
* This provides a method of queuing function calls to be run at the
|
||||
* earliest convenience from the top-level event loop. Use it if
|
||||
* you're deep in a nested chain of calls and want to trigger an
|
||||
* action which will probably lead to your function being re-entered
|
||||
* recursively if you just call the initiating function the normal
|
||||
* way.
|
||||
*
|
||||
* Most front ends run the queued callbacks by simply calling
|
||||
* run_toplevel_callbacks() after handling each event in their
|
||||
* top-level event loop. However, if a front end doesn't have control
|
||||
* over its own event loop (e.g. because it's using GTK) then it can
|
||||
* instead request notifications when a callback is available, so that
|
||||
* it knows to ask its delegate event loop to do the same thing.
|
||||
*/
|
||||
typedef void (*toplevel_callback_fn_t)(void *ctx);
|
||||
void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx);
|
||||
void run_toplevel_callbacks(void);
|
||||
|
||||
typedef void (*toplevel_callback_notify_fn_t)(void *frontend);
|
||||
void request_callback_notifications(toplevel_callback_notify_fn_t notify,
|
||||
void *frontend);
|
||||
|
||||
/*
|
||||
* Define no-op macros for the jump list functions, on platforms that
|
||||
* don't support them. (This is a bit of a hack, and it'd be nicer to
|
||||
|
@ -95,6 +95,7 @@ struct gui_data {
|
||||
int busy_status;
|
||||
guint term_paste_idle_id;
|
||||
guint term_exit_idle_id;
|
||||
guint toplevel_callback_idle_id;
|
||||
int alt_keycode;
|
||||
int alt_digits;
|
||||
char *wintitle;
|
||||
@ -1399,6 +1400,25 @@ void notify_remote_exit(void *frontend)
|
||||
inst->term_exit_idle_id = gtk_idle_add(idle_exit_func, inst);
|
||||
}
|
||||
|
||||
static gint idle_toplevel_callback_func(gpointer data)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)data;
|
||||
|
||||
run_toplevel_callbacks();
|
||||
|
||||
gtk_idle_remove(inst->toplevel_callback_idle_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void notify_toplevel_callback(void *frontend)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
|
||||
inst->toplevel_callback_idle_id =
|
||||
gtk_idle_add(idle_toplevel_callback_func, inst);
|
||||
}
|
||||
|
||||
static gint timer_trigger(gpointer data)
|
||||
{
|
||||
unsigned long now = GPOINTER_TO_LONG(data);
|
||||
@ -3858,6 +3878,8 @@ int pt_main(int argc, char **argv)
|
||||
|
||||
inst->eventlogstuff = eventlogstuff_new();
|
||||
|
||||
request_callback_notifications(notify_toplevel_callback, inst);
|
||||
|
||||
inst->term = term_init(inst->conf, &inst->ucsdata, inst);
|
||||
inst->logctx = log_init(inst, inst->conf);
|
||||
term_provide_logctx(inst->term, inst->logctx);
|
||||
|
@ -1118,6 +1118,8 @@ int main(int argc, char **argv)
|
||||
|
||||
net_pending_errors();
|
||||
|
||||
run_toplevel_callbacks();
|
||||
|
||||
if ((!connopen || !back->connected(backhandle)) &&
|
||||
bufchain_size(&stdout_data) == 0 &&
|
||||
bufchain_size(&stderr_data) == 0)
|
||||
|
@ -536,6 +536,8 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
|
||||
|
||||
sfree(fdlist);
|
||||
|
||||
run_toplevel_callbacks();
|
||||
|
||||
return FD_ISSET(0, &rset) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
@ -869,6 +869,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
} else
|
||||
sfree(handles);
|
||||
|
||||
run_toplevel_callbacks();
|
||||
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT)
|
||||
goto finished; /* two-level break */
|
||||
@ -877,12 +879,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
DispatchMessage(&msg);
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
if (must_close_session)
|
||||
close_session();
|
||||
|
||||
run_toplevel_callbacks();
|
||||
}
|
||||
|
||||
/* The messages seem unreliable; especially if we're being tricky */
|
||||
|
@ -738,6 +738,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
run_toplevel_callbacks();
|
||||
|
||||
if (n == WAIT_TIMEOUT) {
|
||||
now = next;
|
||||
} else {
|
||||
|
@ -585,6 +585,8 @@ int do_eventsel_loop(HANDLE other_event)
|
||||
|
||||
sfree(handles);
|
||||
|
||||
run_toplevel_callbacks();
|
||||
|
||||
if (n == WAIT_TIMEOUT) {
|
||||
now = next;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user