]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/commitdiff
Add a general way to request an immediate top-level callback.
authorSimon Tatham <anakin@pobox.com>
Sat, 17 Aug 2013 16:06:08 +0000 (16:06 +0000)
committerSimon Tatham <anakin@pobox.com>
Sat, 17 Aug 2013 16:06:08 +0000 (16:06 +0000)
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

Recipe
callback.c [new file with mode: 0644]
putty.h
unix/gtkwin.c
unix/uxplink.c
unix/uxsftp.c
windows/window.c
windows/winplink.c
windows/winsftp.c

diff --git a/Recipe b/Recipe
index 3b65c83bd0a643c839107d49f92f91225efb636d..dee4da8ff4840ceb4b2c0c2139eb7832f33a72e1 100644 (file)
--- a/Recipe
+++ b/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
diff --git a/callback.c b/callback.c
new file mode 100644 (file)
index 0000000..e62fcd7
--- /dev/null
@@ -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;
+}
diff --git a/putty.h b/putty.h
index e5c641d30faa03fce236d3c000e70afa1b66206c..d1a5daeb6a37bd2468463aabdf07686c1e215067 100644 (file)
--- a/putty.h
+++ b/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
index 667c72b3076f118140bc4b8a4bb8407bc8cb2546..2b66073954398bc7465da4db2d2f81d1caa47bb6 100644 (file)
@@ -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);
index 898f27c3fe4dc7eb182dc827cd941c5c4e35ccad..36f3e2fce88cc8403a5944e8c7e7b06fb5b16945 100644 (file)
@@ -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)
index 57f28416262c3ab62df669fa43d1b8430e22209b..44e1176401eb3772e6e23b0d0dbe70024afbd291 100644 (file)
@@ -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;
 }
 
index 8d13ff911754f2abb0a545d7dc779200faea834a..15e77d7236138dee621717ef6f5c8367ab765b3b 100644 (file)
@@ -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 */
index 37453bb734ae87abeed26f7f7359db6759d1d41f..9d29c20b9c3818e4c5279551fc0cdc9f7187065a 100644 (file)
@@ -738,6 +738,8 @@ int main(int argc, char **argv)
            }
        }
 
+        run_toplevel_callbacks();
+
        if (n == WAIT_TIMEOUT) {
            now = next;
        } else {
index 33acaf62ed4efe615bbbfc77dfa6187c115cdc94..23273507e36455883445ec833f0f367e6427bc56 100644 (file)
@@ -585,6 +585,8 @@ int do_eventsel_loop(HANDLE other_event)
 
     sfree(handles);
 
+    run_toplevel_callbacks();
+
     if (n == WAIT_TIMEOUT) {
        now = next;
     } else {