summaryrefslogtreecommitdiff
path: root/src/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/io.c')
-rw-r--r--src/io.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/io.c b/src/io.c
new file mode 100644
index 0000000..906cc88
--- /dev/null
+++ b/src/io.c
@@ -0,0 +1,133 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <unistd.h>
+#include <termios.h>
+
+#include "io.h"
+#include "cmumble.h"
+
+/* Readline has no user_data in callbacks,
+ * so set global_rl_user_data before dispatching
+ * (and unset afterwards - so we can assert user_data,
+ * and find where callbacks are called, that we didnt
+ * expect)
+ */
+static gpointer global_rl_user_data = NULL;
+
+static gboolean
+read_cb(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ struct context *ctx = data;
+
+ if (condition & G_IO_IN) {
+ global_rl_user_data = ctx;
+ rl_callback_read_char();
+ global_rl_user_data = NULL;
+ }
+}
+
+static void
+print_preserve_prompt(const gchar *string)
+{
+ int point;
+ char *line;
+
+ /* FIXME */
+ gboolean preserve = (rl_readline_state & RL_STATE_TERMPREPPED);
+ //preserve = 1;
+
+ if (preserve) {
+ point = rl_point;
+ line = rl_copy_text(0, rl_end);
+ rl_save_prompt();
+ rl_replace_line("", 0);
+ rl_redisplay();
+ }
+
+ fputs(string, stdout);
+
+ if (preserve) {
+ rl_restore_prompt();
+ rl_replace_line(line, 0);
+ rl_point = point;
+ rl_redisplay();
+ free(line);
+ }
+}
+
+static void
+process_line(char *line)
+{
+ struct context *ctx = global_rl_user_data;
+
+ g_assert(global_rl_user_data);
+
+ rl_reset_line_state();
+
+ if (line == NULL) {
+ printf("quit");
+ rl_already_prompted = 1;
+ rl_crlf();
+ g_main_loop_quit(ctx->loop);
+ return;
+ }
+
+ if (strcmp(line, "quit") == 0) {
+ rl_already_prompted = 1;
+ g_main_loop_quit(ctx->loop);
+ } else if (strcmp(line, "clear") == 0) {
+ rl_clear_screen(0,0);
+ rl_reset_line_state();
+ } else
+ g_print("readline: %s\n", line);
+
+ if (strlen(line))
+ add_history(line);
+}
+
+int
+cmumble_io_init(struct context *ctx)
+{
+ struct termios term;
+
+ ctx->io.input_channel = g_io_channel_unix_new(STDIN_FILENO);
+
+ g_io_add_watch(ctx->io.input_channel, G_IO_IN | G_IO_HUP,
+ read_cb, ctx);
+
+ if (tcgetattr(STDIN_FILENO, &term) < 0) {
+ g_printerr("tcgetattr failed");
+ return -1;
+ }
+
+ ctx->io.term = term;
+ term.c_lflag &= ~ICANON;
+ term.c_cc[VTIME] = 1;
+
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) {
+ g_printerr("tcsetattr failed");
+ return -1;
+ }
+
+ rl_callback_handler_install("cmumble> ", process_line);
+
+ g_set_print_handler(print_preserve_prompt);
+}
+
+int
+cmumble_io_fini(struct context *ctx)
+{
+ rl_callback_handler_remove();
+ g_io_channel_unref(ctx->io.input_channel);
+
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &ctx->io.term) < 0) {
+ g_printerr("tcsetattr failed");
+ return -1;
+ }
+
+}