summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/cmumble.c4
-rw-r--r--src/cmumble.h2
-rw-r--r--src/io.c133
-rw-r--r--src/io.h21
6 files changed, 164 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 34d399e..e109315 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,6 +20,8 @@ AC_PROG_CC
AC_PROG_SED
AC_PATH_PROG([PROTOC_C], [protoc-c], [false])
+AC_CHECK_LIB(readline, readline)
+
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(PROTOBUF, [libprotobuf-c],[], [
AC_CHECK_LIB([protobuf-c], [protobuf_c_message_pack_to_buffer], [],
diff --git a/src/Makefile.am b/src/Makefile.am
index 300ff70..e8b74f0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,9 +1,9 @@
bin_PROGRAMS = cmumble
-noinst_HEADERS = cmumble.h varint.h
+noinst_HEADERS = cmumble.h varint.h io.h
nodist_noinst_HEADERS = mumble.pb-c.h messages.h
-cmumble_SOURCES = cmumble.c messages.c varint.c
+cmumble_SOURCES = cmumble.c messages.c varint.c io.c
nodist_cmumble_SOURCES = mumble.pb-c.c
cmumble_LDADD = $(PROTOBUF_LIBS) $(GLIB_LIBS) $(GIO_LIBS) \
diff --git a/src/cmumble.c b/src/cmumble.c
index 99a4fc8..5c42521 100644
--- a/src/cmumble.c
+++ b/src/cmumble.c
@@ -472,9 +472,13 @@ int main(int argc, char **argv)
g_source_attach(source, NULL);
g_source_unref(source);
+ cmumble_io_init(&ctx);
+
g_main_loop_run(ctx.loop);
g_main_loop_unref(ctx.loop);
+ cmumble_io_fini(&ctx);
+
return 0;
}
diff --git a/src/cmumble.h b/src/cmumble.h
index af8396a..d9d1d53 100644
--- a/src/cmumble.h
+++ b/src/cmumble.h
@@ -15,8 +15,10 @@
#include "mumble.pb-c.h"
#include "messages.h"
+#include "io.h"
struct context {
+ struct cmumble_io io;
GMainLoop *loop;
uint32_t session;
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;
+ }
+
+}
diff --git a/src/io.h b/src/io.h
new file mode 100644
index 0000000..2f48df7
--- /dev/null
+++ b/src/io.h
@@ -0,0 +1,21 @@
+#ifndef _IO_H_
+#define _IO_H_
+
+#include <glib.h>
+#include <termios.h>
+
+struct cmumble_io {
+ GIOChannel *input_channel;
+
+ struct termios term;
+};
+
+struct context;
+
+int
+cmumble_io_init(struct context *ctx);
+
+int
+cmumble_io_fini(struct context *ctx);
+
+#endif /* _IO_H_ */