commit 511f74d0b8278686633b0a0a23a0ceb96154e138
parent d41764edb947e68e7c23da4ec1f1fc9d04101cf8
Author: Brian Swetland <swetland@frotz.net>
Date:   Sat, 20 Jun 2015 18:59:19 -0700
linenoise: add mechnism for preempting linenoise control of stdio
Diffstat:
2 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/tools/linenoise.c b/tools/linenoise.c
@@ -715,6 +715,97 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
     refreshLine(l);
 }
 
+#ifndef LINENOISE_INTERRUPTIBLE
+#define linenoiseRead(l, buf, count) read((*(l)).ifd, buf, count)
+#define linenoiseLock() do {} while (0)
+#define linenoiseUnlock() do {} while (0)
+#else
+#include <pthread.h>
+#include <poll.h>
+
+static pthread_mutex_t linenoise_lock = PTHREAD_MUTEX_INITIALIZER;
+static int linenoise_active = 0;
+static int linenoise_repaint = 0;
+static int linenoise_pipefds[2] = { -1, -1 };
+
+/* allow linenoise to be interruptible */
+void linenoiseInit(void) {
+    if (pipe(linenoise_pipefds) < 0) ;
+}
+
+/* Call before writing to stdout when linenoise may be active in
+ * another thread.  This will suspend linenoise activity and 
+ * clear the text entry line.  linenoiseResume() must be called
+ * after your io is complete so that linenoise may continue.  */
+void linenoisePause(void) {
+    pthread_mutex_lock(&linenoise_lock);
+    if (linenoise_active) {
+        if (write(1, "\x1b[0G\x1b[0K", 8) < 0) ;
+        disableRawMode(STDIN_FILENO);
+    }
+}
+
+/* Call when io is done after you've called linenoisePause()
+ * to suspend linenoise processing during your io. */
+void linenoiseResume(void) {
+    if (linenoise_active) {
+        char x = 0;
+        linenoise_repaint = 1;
+        enableRawMode(STDIN_FILENO);
+        if (write(linenoise_pipefds[1], &x, 1) < 0) ;
+    }
+    pthread_mutex_unlock(&linenoise_lock);
+}
+
+void linenoiseLock(void) {
+    pthread_mutex_lock(&linenoise_lock);
+    linenoise_active = 1;
+}
+
+void linenoiseUnlock(void) {
+    linenoise_active = 0;
+    pthread_mutex_unlock(&linenoise_lock);
+}
+
+ssize_t linenoiseRead(struct linenoiseState *l, void *ptr, int len) {
+    struct pollfd fds[2];
+    int nfds, r;
+
+    for(;;) {
+        fds[0].fd = l->ifd;
+        fds[0].events = POLLIN;
+        fds[0].revents = 0;
+        if (linenoise_pipefds[0] < 0) {
+            nfds = 1;
+            fds[1].fd = linenoise_pipefds[0];
+            fds[1].events = POLLIN;
+        } else {
+            nfds = 2;
+        }
+        fds[1].revents = 0;
+
+        /* wait for input or refresh request */
+        pthread_mutex_unlock(&linenoise_lock);
+        r = poll(fds, nfds, -1);
+        pthread_mutex_lock(&linenoise_lock);
+
+        if (linenoise_repaint) {
+            linenoise_repaint = 0;
+            refreshLine(l);
+        }
+        if (r < 0) return -1;
+        if (fds[1].revents & POLLIN) {
+            char x;
+            if (read(linenoise_pipefds[0], &x, 1) < 0) ;
+            continue;
+        }
+        if (fds[0].revents & POLLIN) {
+            return read(l->ifd, ptr, len);
+        }
+    }
+}
+#endif
+
 /* This function is the core of the line editing capability of linenoise.
  * It expects 'fd' to be already in "raw mode" so that every key pressed
  * will be returned ASAP to read().
@@ -755,7 +846,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, 
         int nread;
         char seq[3];
 
-        nread = read(l.ifd,&c,1);
+        nread = linenoiseRead(&l,&c,1);
         if (nread <= 0) return l.len;
 
         /* Only autocomplete when the callback is set. It returns < 0 when
@@ -946,10 +1037,15 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
         }
     } else {
         /* Interactive editing. */
-        if (enableRawMode(STDIN_FILENO) == -1) return -1;
+        linenoiseLock();
+        if (enableRawMode(STDIN_FILENO) == -1) {
+            linenoiseUnlock();
+            return -1;
+        }
         count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
         disableRawMode(STDIN_FILENO);
         printf("\n");
+        linenoiseUnlock();
     }
     return count;
 }
diff --git a/tools/linenoise.h b/tools/linenoise.h
@@ -61,6 +61,10 @@ void linenoiseClearScreen(void);
 void linenoiseSetMultiLine(int ml);
 void linenoisePrintKeyCodes(void);
 
+void linenoiseInit(void);
+void linenoisePause(void);
+void linenoiseResume(void);
+
 #ifdef __cplusplus
 }
 #endif