diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/cmumble.c | 4 | ||||
-rw-r--r-- | src/cmumble.h | 2 | ||||
-rw-r--r-- | src/io.c | 133 | ||||
-rw-r--r-- | src/io.h | 21 |
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_ */ |