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.
git-svn-id: http://svn.tartarus.org/sgt/putty@10019
cda61777-01e9-0310-a592-
d414129be87e
+ 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
# 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
--- /dev/null
+/*
+ * 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;
+}
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
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;
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);
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);
net_pending_errors();
+ run_toplevel_callbacks();
+
if ((!connopen || !back->connected(backhandle)) &&
bufchain_size(&stdout_data) == 0 &&
bufchain_size(&stderr_data) == 0)
sfree(fdlist);
+ run_toplevel_callbacks();
+
return FD_ISSET(0, &rset) ? 1 : 0;
}
} else
sfree(handles);
+ run_toplevel_callbacks();
+
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
goto finished; /* two-level break */
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 */
}
}
+ run_toplevel_callbacks();
+
if (n == WAIT_TIMEOUT) {
now = next;
} else {
sfree(handles);
+ run_toplevel_callbacks();
+
if (n == WAIT_TIMEOUT) {
now = next;
} else {