degesch: add support for libedit
Just another kind of evil.
This commit is contained in:
parent
53894e3909
commit
a5a1079a9c
|
@ -1,6 +1,10 @@
|
||||||
project (uirc3 C)
|
project (uirc3 C)
|
||||||
cmake_minimum_required (VERSION 2.8.5)
|
cmake_minimum_required (VERSION 2.8.5)
|
||||||
|
|
||||||
|
# Options
|
||||||
|
option (WANT_READLINE "Use GNU Readline for the UI (better)" ON)
|
||||||
|
option (WANT_EDITLINE "Use BSD libedit for the UI" OFF)
|
||||||
|
|
||||||
# Moar warnings
|
# Moar warnings
|
||||||
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
|
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
|
||||||
# -Wunused-function is pretty annoying here, as everything is static
|
# -Wunused-function is pretty annoying here, as everything is static
|
||||||
|
@ -38,7 +42,25 @@ else (CURSES_FOUND)
|
||||||
message (SEND_ERROR "Curses not found")
|
message (SEND_ERROR "Curses not found")
|
||||||
endif (ncursesw_FOUND)
|
endif (ncursesw_FOUND)
|
||||||
|
|
||||||
|
if ((WANT_READLINE AND WANT_EDITLINE) OR (NOT WANT_READLINE AND NOT WANT_EDITLINE))
|
||||||
|
message (SEND_ERROR "You have to choose either GNU Readline or libedit")
|
||||||
|
elseif (WANT_READLINE)
|
||||||
|
list (APPEND project_libraries readline)
|
||||||
|
elseif (WANT_EDITLINE)
|
||||||
|
pkg_check_modules (libedit REQUIRED libedit)
|
||||||
|
list (APPEND project_libraries ${libedit_LIBRARIES})
|
||||||
|
include_directories (${libedit_INCLUDE_DIRS})
|
||||||
|
endif ((WANT_READLINE AND WANT_EDITLINE) OR (NOT WANT_READLINE AND NOT WANT_EDITLINE))
|
||||||
|
|
||||||
# Generate a configuration file
|
# Generate a configuration file
|
||||||
|
if (WANT_READLINE)
|
||||||
|
set (HAVE_READLINE 1)
|
||||||
|
endif (WANT_READLINE)
|
||||||
|
|
||||||
|
if (WANT_EDITLINE)
|
||||||
|
set (HAVE_EDITLINE 1)
|
||||||
|
endif (WANT_EDITLINE)
|
||||||
|
|
||||||
include (GNUInstallDirs)
|
include (GNUInstallDirs)
|
||||||
set (plugin_dir ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME})
|
set (plugin_dir ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME})
|
||||||
configure_file (${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
configure_file (${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
||||||
|
@ -62,7 +84,7 @@ target_link_libraries (zyklonb ${project_libraries})
|
||||||
|
|
||||||
add_executable (degesch degesch.c kike-replies.c
|
add_executable (degesch degesch.c kike-replies.c
|
||||||
${common_sources} ${common_headers})
|
${common_sources} ${common_headers})
|
||||||
target_link_libraries (degesch ${project_libraries} readline)
|
target_link_libraries (degesch ${project_libraries})
|
||||||
|
|
||||||
add_executable (kike kike.c kike-replies.c ${common_sources} ${common_headers})
|
add_executable (kike kike.c kike-replies.c ${common_sources} ${common_headers})
|
||||||
target_link_libraries (kike ${project_libraries})
|
target_link_libraries (kike ${project_libraries})
|
||||||
|
|
5
README
5
README
|
@ -50,14 +50,15 @@ Notable features:
|
||||||
Building
|
Building
|
||||||
--------
|
--------
|
||||||
Build dependencies: CMake, pkg-config, help2man, awk, sh, liberty (included)
|
Build dependencies: CMake, pkg-config, help2man, awk, sh, liberty (included)
|
||||||
Runtime dependencies: openssl, curses (degesch), readline (degesch)
|
Runtime dependencies: openssl, curses (degesch), readline or libedit (degesch)
|
||||||
|
|
||||||
$ git clone https://github.com/pjanouch/uirc3.git
|
$ git clone https://github.com/pjanouch/uirc3.git
|
||||||
$ git submodule init
|
$ git submodule init
|
||||||
$ git submodule update
|
$ git submodule update
|
||||||
$ mkdir build
|
$ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
|
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug \
|
||||||
|
-DWANT_READLINE=ON -DWANT_LIBEDIT=OFF
|
||||||
$ make
|
$ make
|
||||||
|
|
||||||
To install the application, you can do either the usual:
|
To install the application, you can do either the usual:
|
||||||
|
|
|
@ -4,4 +4,7 @@
|
||||||
#define PROGRAM_VERSION "${project_VERSION}"
|
#define PROGRAM_VERSION "${project_VERSION}"
|
||||||
#define PLUGIN_DIR "${CMAKE_INSTALL_PREFIX}/${plugin_dir}"
|
#define PLUGIN_DIR "${CMAKE_INSTALL_PREFIX}/${plugin_dir}"
|
||||||
|
|
||||||
|
#cmakedefine HAVE_READLINE
|
||||||
|
#cmakedefine HAVE_EDITLINE
|
||||||
|
|
||||||
#endif // ! CONFIG_H
|
#endif // ! CONFIG_H
|
||||||
|
|
511
degesch.c
511
degesch.c
|
@ -69,32 +69,55 @@ enum
|
||||||
#undef lines
|
#undef lines
|
||||||
#undef columns
|
#undef columns
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
#include <readline/readline.h>
|
#include <readline/readline.h>
|
||||||
#include <readline/history.h>
|
#include <readline/history.h>
|
||||||
|
#endif // HAVE_READLINE
|
||||||
|
|
||||||
|
#ifdef HAVE_EDITLINE
|
||||||
|
#include <histedit.h>
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
|
|
||||||
// --- User interface ----------------------------------------------------------
|
// --- User interface ----------------------------------------------------------
|
||||||
|
|
||||||
// Currently provided by GNU Readline. A libedit backend is also possible.
|
// I'm not sure which one of these backends is worse: whether it's GNU Readline
|
||||||
|
// or BSD Editline. They both have their own annoying problems.
|
||||||
|
|
||||||
struct input_buffer
|
struct input_buffer
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
HISTORY_STATE *history; ///< Saved history state
|
HISTORY_STATE *history; ///< Saved history state
|
||||||
char *saved_line; ///< Saved line content
|
char *saved_line; ///< Saved line content
|
||||||
int saved_point; ///< Saved cursor position
|
|
||||||
int saved_mark; ///< Saved mark
|
int saved_mark; ///< Saved mark
|
||||||
|
#elif defined HAVE_EDITLINE
|
||||||
|
HistoryW *history; ///< The history object
|
||||||
|
wchar_t *saved_line; ///< Saved line content
|
||||||
|
int saved_len; ///< Length of the saved line
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
|
int saved_point; ///< Saved cursor position
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct input_buffer *
|
static struct input_buffer *
|
||||||
input_buffer_new (void)
|
input_buffer_new (void)
|
||||||
{
|
{
|
||||||
struct input_buffer *self = xcalloc (1, sizeof *self);
|
struct input_buffer *self = xcalloc (1, sizeof *self);
|
||||||
|
#ifdef HAVE_EDITLINE
|
||||||
|
self->history = history_winit ();
|
||||||
|
|
||||||
|
HistEventW ev;
|
||||||
|
history_w (self->history, &ev, H_SETSIZE, HISTORY_LIMIT);
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_buffer_destroy (struct input_buffer *self)
|
input_buffer_destroy (struct input_buffer *self)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
// Can't really free "history" from here
|
// Can't really free "history" from here
|
||||||
|
#elif defined HAVE_EDITLINE
|
||||||
|
history_wend (self->history);
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
free (self->saved_line);
|
free (self->saved_line);
|
||||||
free (self);
|
free (self);
|
||||||
}
|
}
|
||||||
|
@ -103,9 +126,15 @@ struct input
|
||||||
{
|
{
|
||||||
bool active; ///< Are we a thing?
|
bool active; ///< Are we a thing?
|
||||||
|
|
||||||
|
#if defined HAVE_READLINE
|
||||||
char *saved_line; ///< Saved line content
|
char *saved_line; ///< Saved line content
|
||||||
int saved_point; ///< Saved cursor position
|
int saved_point; ///< Saved cursor position
|
||||||
int saved_mark; ///< Saved mark
|
int saved_mark; ///< Saved mark
|
||||||
|
#elif defined HAVE_EDITLINE
|
||||||
|
EditLine *editline; ///< The EditLine object
|
||||||
|
char *(*saved_prompt) (EditLine *); ///< Saved prompt function
|
||||||
|
char saved_char; ///< Saved char for the prompt
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
|
|
||||||
char *prompt; ///< The prompt we use
|
char *prompt; ///< The prompt we use
|
||||||
int prompt_shown; ///< Whether the prompt is shown now
|
int prompt_shown; ///< Whether the prompt is shown now
|
||||||
|
@ -122,12 +151,19 @@ input_init (struct input *self)
|
||||||
static void
|
static void
|
||||||
input_free (struct input *self)
|
input_free (struct input *self)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
free (self->saved_line);
|
free (self->saved_line);
|
||||||
|
#endif // HAVE_READLINE
|
||||||
free (self->prompt);
|
free (self->prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
|
||||||
|
#define INPUT_START_IGNORE RL_PROMPT_START_IGNORE
|
||||||
|
#define INPUT_END_IGNORE RL_PROMPT_END_IGNORE
|
||||||
|
|
||||||
#define input_ding(self) rl_ding ()
|
#define input_ding(self) rl_ding ()
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -181,8 +217,14 @@ static int app_readline_init (void);
|
||||||
static void on_readline_input (char *line);
|
static void on_readline_input (char *line);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_start (struct input *self)
|
input_start (struct input *self, const char *program_name)
|
||||||
{
|
{
|
||||||
|
(void) program_name;
|
||||||
|
|
||||||
|
using_history ();
|
||||||
|
// This can cause memory leaks, or maybe even a segfault. Funny, eh?
|
||||||
|
stifle_history (HISTORY_LIMIT);
|
||||||
|
|
||||||
rl_startup_hook = app_readline_init;
|
rl_startup_hook = app_readline_init;
|
||||||
rl_catch_sigwinch = false;
|
rl_catch_sigwinch = false;
|
||||||
rl_callback_handler_install (self->prompt, on_readline_input);
|
rl_callback_handler_install (self->prompt, on_readline_input);
|
||||||
|
@ -203,53 +245,6 @@ input_stop (struct input *self)
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static void
|
|
||||||
input_save (struct input *self)
|
|
||||||
{
|
|
||||||
hard_assert (!self->saved_line);
|
|
||||||
|
|
||||||
self->saved_point = rl_point;
|
|
||||||
self->saved_mark = rl_mark;
|
|
||||||
self->saved_line = rl_copy_text (0, rl_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
input_restore (struct input *self)
|
|
||||||
{
|
|
||||||
hard_assert (self->saved_line);
|
|
||||||
|
|
||||||
rl_set_prompt (self->prompt);
|
|
||||||
rl_replace_line (self->saved_line, 0);
|
|
||||||
rl_point = self->saved_point;
|
|
||||||
rl_mark = self->saved_mark;
|
|
||||||
free (self->saved_line);
|
|
||||||
self->saved_line = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
static void
|
|
||||||
input_hide (struct input *self)
|
|
||||||
{
|
|
||||||
if (!self->active || self->prompt_shown-- < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
input_save (self);
|
|
||||||
input_erase (self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
input_show (struct input *self)
|
|
||||||
{
|
|
||||||
if (!self->active || ++self->prompt_shown < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
input_restore (self);
|
|
||||||
rl_redisplay ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
// The following part shows you why it's not a good idea to use
|
// The following part shows you why it's not a good idea to use
|
||||||
// GNU Readline for this kind of software. Or for anything else, really.
|
// GNU Readline for this kind of software. Or for anything else, really.
|
||||||
|
|
||||||
|
@ -337,7 +332,7 @@ input_destroy_buffer (struct input *self, struct input_buffer *buffer)
|
||||||
#if RL_READLINE_VERSION >= 0x0603
|
#if RL_READLINE_VERSION >= 0x0603
|
||||||
if (buffer->history)
|
if (buffer->history)
|
||||||
{
|
{
|
||||||
// See buffer_activate() for why we need to do this BS
|
// See input_switch_buffer() for why we need to do this BS
|
||||||
rl_free_undo_list ();
|
rl_free_undo_list ();
|
||||||
|
|
||||||
// This is probably the only way we can free the history fully
|
// This is probably the only way we can free the history fully
|
||||||
|
@ -355,6 +350,275 @@ input_destroy_buffer (struct input *self, struct input_buffer *buffer)
|
||||||
input_buffer_destroy (buffer);
|
input_buffer_destroy (buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_save (struct input *self)
|
||||||
|
{
|
||||||
|
hard_assert (!self->saved_line);
|
||||||
|
|
||||||
|
self->saved_point = rl_point;
|
||||||
|
self->saved_mark = rl_mark;
|
||||||
|
self->saved_line = rl_copy_text (0, rl_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_restore (struct input *self)
|
||||||
|
{
|
||||||
|
hard_assert (self->saved_line);
|
||||||
|
|
||||||
|
rl_set_prompt (self->prompt);
|
||||||
|
rl_replace_line (self->saved_line, 0);
|
||||||
|
rl_point = self->saved_point;
|
||||||
|
rl_mark = self->saved_mark;
|
||||||
|
free (self->saved_line);
|
||||||
|
self->saved_line = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_hide (struct input *self)
|
||||||
|
{
|
||||||
|
if (!self->active || self->prompt_shown-- < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_save (self);
|
||||||
|
input_erase (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_show (struct input *self)
|
||||||
|
{
|
||||||
|
if (!self->active || ++self->prompt_shown < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_restore (self);
|
||||||
|
rl_redisplay ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_READLINE
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
#ifdef HAVE_EDITLINE
|
||||||
|
|
||||||
|
#define INPUT_START_IGNORE '\x01'
|
||||||
|
#define INPUT_END_IGNORE '\x01'
|
||||||
|
|
||||||
|
static void app_editline_init (struct input *self);
|
||||||
|
static void on_editline_input (struct input *self, char *line);
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_ding (struct input *self)
|
||||||
|
{
|
||||||
|
(void) self;
|
||||||
|
|
||||||
|
// XXX: this isn't probably very portable
|
||||||
|
putc ('\a', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_on_terminal_resized (struct input *self)
|
||||||
|
{
|
||||||
|
el_resize (self->editline);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_redisplay (struct input *self)
|
||||||
|
{
|
||||||
|
// See rl_redisplay()
|
||||||
|
// The character is VREPRINT (usually C-r)
|
||||||
|
// TODO: read it from terminal info
|
||||||
|
// XXX: could we potentially break UTF-8 with this?
|
||||||
|
char x[] = { ('R' - 'A' + 1), 0 };
|
||||||
|
el_push (self->editline, x);
|
||||||
|
|
||||||
|
// We have to do this or it gets stuck and nothing is done
|
||||||
|
(void) el_gets (self->editline, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_set_prompt (struct input *self, char *prompt)
|
||||||
|
{
|
||||||
|
free (self->prompt);
|
||||||
|
self->prompt = prompt;
|
||||||
|
|
||||||
|
if (self->prompt_shown)
|
||||||
|
input_redisplay (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
input_make_prompt (EditLine *editline)
|
||||||
|
{
|
||||||
|
struct input *self;
|
||||||
|
el_get (editline, EL_CLIENTDATA, &self);
|
||||||
|
return self->prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
input_make_empty_prompt (EditLine *editline)
|
||||||
|
{
|
||||||
|
(void) editline;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_erase (struct input *self)
|
||||||
|
{
|
||||||
|
const LineInfoW *info = el_wline (self->editline);
|
||||||
|
int len = info->lastchar - info->buffer;
|
||||||
|
int point = info->cursor - info->buffer;
|
||||||
|
el_cursor (self->editline, len - point);
|
||||||
|
el_wdeletestr (self->editline, len);
|
||||||
|
|
||||||
|
// XXX: this doesn't seem to save the escape character
|
||||||
|
el_get (self->editline, EL_PROMPT, &self->saved_prompt, &self->saved_char);
|
||||||
|
el_set (self->editline, EL_PROMPT, input_make_empty_prompt);
|
||||||
|
input_redisplay (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_start (struct input *self, const char *program_name)
|
||||||
|
{
|
||||||
|
self->editline = el_init (program_name, stdin, stdout, stderr);
|
||||||
|
el_set (self->editline, EL_CLIENTDATA, self);
|
||||||
|
el_set (self->editline, EL_PROMPT_ESC,
|
||||||
|
input_make_prompt, INPUT_START_IGNORE);
|
||||||
|
el_set (self->editline, EL_SIGNAL, false);
|
||||||
|
el_set (self->editline, EL_UNBUFFERED, true);
|
||||||
|
el_set (self->editline, EL_EDITOR, "emacs");
|
||||||
|
|
||||||
|
app_editline_init (self);
|
||||||
|
self->prompt_shown = 1;
|
||||||
|
self->active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_stop (struct input *self)
|
||||||
|
{
|
||||||
|
if (self->prompt_shown > 0)
|
||||||
|
input_erase (self);
|
||||||
|
|
||||||
|
el_end (self->editline);
|
||||||
|
self->editline = NULL;
|
||||||
|
self->active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_save_buffer (struct input *self, struct input_buffer *buffer)
|
||||||
|
{
|
||||||
|
const LineInfoW *info = el_wline (self->editline);
|
||||||
|
int len = info->lastchar - info->buffer;
|
||||||
|
int point = info->cursor - info->buffer;
|
||||||
|
|
||||||
|
wchar_t *line = calloc (sizeof *info->buffer, len + 1);
|
||||||
|
memcpy (line, info->buffer, sizeof *info->buffer * len);
|
||||||
|
el_cursor (self->editline, len - point);
|
||||||
|
el_wdeletestr (self->editline, len);
|
||||||
|
|
||||||
|
buffer->saved_line = line;
|
||||||
|
buffer->saved_point = point;
|
||||||
|
buffer->saved_len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_restore_buffer (struct input *self, struct input_buffer *buffer)
|
||||||
|
{
|
||||||
|
if (buffer->saved_line)
|
||||||
|
{
|
||||||
|
el_winsertstr (self->editline, buffer->saved_line);
|
||||||
|
el_cursor (self->editline,
|
||||||
|
-(buffer->saved_len - buffer->saved_point));
|
||||||
|
free (buffer->saved_line);
|
||||||
|
buffer->saved_line = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_switch_buffer (struct input *self, struct input_buffer *buffer)
|
||||||
|
{
|
||||||
|
if (self->current)
|
||||||
|
input_save_buffer (self, self->current);
|
||||||
|
|
||||||
|
input_restore_buffer (self, buffer);
|
||||||
|
el_wset (self->editline, EL_HIST, history, buffer->history);
|
||||||
|
self->current = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_destroy_buffer (struct input *self, struct input_buffer *buffer)
|
||||||
|
{
|
||||||
|
(void) self;
|
||||||
|
input_buffer_destroy (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_save (struct input *self)
|
||||||
|
{
|
||||||
|
input_save_buffer (self, self->current);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_restore (struct input *self)
|
||||||
|
{
|
||||||
|
input_restore_buffer (self, self->current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_hide (struct input *self)
|
||||||
|
{
|
||||||
|
if (!self->active || self->prompt_shown-- < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_save (self);
|
||||||
|
input_erase (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_show (struct input *self)
|
||||||
|
{
|
||||||
|
if (!self->active || ++self->prompt_shown < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_restore (self);
|
||||||
|
// Would have used "saved_char" but it doesn't seem to work.
|
||||||
|
// And it doesn't even when it does anyway (it seems to just strip it).
|
||||||
|
el_set (self->editline,
|
||||||
|
EL_PROMPT_ESC, input_make_prompt, INPUT_START_IGNORE);
|
||||||
|
input_redisplay (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_on_readable (struct input *self)
|
||||||
|
{
|
||||||
|
// We bind the return key to process it how we need to
|
||||||
|
|
||||||
|
// el_gets() with EL_UNBUFFERED doesn't work with UTF-8,
|
||||||
|
// we must use the wide-character interface
|
||||||
|
int count = 0;
|
||||||
|
const wchar_t *buf = el_wgets (self->editline, &count);
|
||||||
|
if (!buf || count-- <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The character is VEOF (usually C-d)
|
||||||
|
// TODO: read it from terminal info
|
||||||
|
if (count == 0 && buf[0] == ('D' - 'A' + 1))
|
||||||
|
input_ding (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
|
|
||||||
// --- Application data --------------------------------------------------------
|
// --- Application data --------------------------------------------------------
|
||||||
|
|
||||||
// All text stored in our data structures is encoded in UTF-8.
|
// All text stored in our data structures is encoded in UTF-8.
|
||||||
|
@ -2560,18 +2824,18 @@ refresh_prompt (struct app_context *ctx)
|
||||||
make_prompt (ctx, &prompt);
|
make_prompt (ctx, &prompt);
|
||||||
str_append_c (&prompt, ' ');
|
str_append_c (&prompt, ' ');
|
||||||
|
|
||||||
if (!have_attributes)
|
if (have_attributes)
|
||||||
input_set_prompt (&ctx->input, xstrdup (prompt.str));
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// XXX: to be completely correct, we should use tputs, but we cannot
|
// XXX: to be completely correct, we should use tputs, but we cannot
|
||||||
input_set_prompt (&ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
|
input_set_prompt (&ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
|
||||||
RL_PROMPT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
|
INPUT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
|
||||||
RL_PROMPT_END_IGNORE,
|
INPUT_END_IGNORE,
|
||||||
prompt.str,
|
prompt.str,
|
||||||
RL_PROMPT_START_IGNORE, ctx->attrs[ATTR_RESET],
|
INPUT_START_IGNORE, ctx->attrs[ATTR_RESET],
|
||||||
RL_PROMPT_END_IGNORE));
|
INPUT_END_IGNORE));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
input_set_prompt (&ctx->input, xstrdup (prompt.str));
|
||||||
str_free (&prompt);
|
str_free (&prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4738,6 +5002,8 @@ irc_connect (struct server *s, bool *should_retry, struct error **e)
|
||||||
|
|
||||||
// --- User interface actions --------------------------------------------------
|
// --- User interface actions --------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
|
||||||
static int
|
static int
|
||||||
on_readline_goto_buffer (int count, int key)
|
on_readline_goto_buffer (int count, int key)
|
||||||
{
|
{
|
||||||
|
@ -4755,7 +5021,7 @@ on_readline_goto_buffer (int count, int key)
|
||||||
if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
|
if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
|
||||||
// Fast switching between two buffers
|
// Fast switching between two buffers
|
||||||
buffer_activate (ctx, ctx->last_buffer);
|
buffer_activate (ctx, ctx->last_buffer);
|
||||||
else if (!buffer_goto (ctx, n == 0 ? 10 : n))
|
else if (!buffer_goto (ctx, n))
|
||||||
input_ding (self);
|
input_ding (self);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4766,7 +5032,6 @@ on_readline_previous_buffer (int count, int key)
|
||||||
(void) key;
|
(void) key;
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
struct app_context *ctx = g_ctx;
|
||||||
if (ctx->current_buffer)
|
|
||||||
buffer_activate (ctx, buffer_previous (ctx, count));
|
buffer_activate (ctx, buffer_previous (ctx, count));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4777,7 +5042,6 @@ on_readline_next_buffer (int count, int key)
|
||||||
(void) key;
|
(void) key;
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
struct app_context *ctx = g_ctx;
|
||||||
if (ctx->current_buffer)
|
|
||||||
buffer_activate (ctx, buffer_next (ctx, count));
|
buffer_activate (ctx, buffer_next (ctx, count));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4862,6 +5126,125 @@ app_readline_init (void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_READLINE
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
#ifdef HAVE_EDITLINE
|
||||||
|
|
||||||
|
static unsigned char
|
||||||
|
on_editline_goto_buffer (EditLine *editline, int key)
|
||||||
|
{
|
||||||
|
(void) editline;
|
||||||
|
|
||||||
|
int n = key - '0';
|
||||||
|
if (n < 0 || n > 9)
|
||||||
|
return CC_ERROR;
|
||||||
|
|
||||||
|
// There's no buffer zero
|
||||||
|
if (n == 0)
|
||||||
|
n = 10;
|
||||||
|
|
||||||
|
struct app_context *ctx = g_ctx;
|
||||||
|
if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
|
||||||
|
// Fast switching between two buffers
|
||||||
|
buffer_activate (ctx, ctx->last_buffer);
|
||||||
|
else if (!buffer_goto (ctx, n))
|
||||||
|
return CC_ERROR;
|
||||||
|
return CC_NORM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char
|
||||||
|
on_editline_previous_buffer (EditLine *editline, int key)
|
||||||
|
{
|
||||||
|
(void) editline;
|
||||||
|
(void) key;
|
||||||
|
|
||||||
|
struct app_context *ctx = g_ctx;
|
||||||
|
buffer_activate (ctx, buffer_previous (ctx, 1));
|
||||||
|
return CC_NORM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char
|
||||||
|
on_editline_next_buffer (EditLine *editline, int key)
|
||||||
|
{
|
||||||
|
(void) editline;
|
||||||
|
(void) key;
|
||||||
|
|
||||||
|
struct app_context *ctx = g_ctx;
|
||||||
|
buffer_activate (ctx, buffer_next (ctx, 1));
|
||||||
|
return CC_NORM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char
|
||||||
|
on_editline_return (EditLine *editline, int key)
|
||||||
|
{
|
||||||
|
(void) key;
|
||||||
|
struct input *self = &g_ctx->input;
|
||||||
|
|
||||||
|
const LineInfoW *info = el_wline (editline);
|
||||||
|
int len = info->lastchar - info->buffer;
|
||||||
|
int point = info->cursor - info->buffer;
|
||||||
|
|
||||||
|
wchar_t *line = calloc (sizeof *info->buffer, len + 1);
|
||||||
|
memcpy (line, info->buffer, sizeof *info->buffer * len);
|
||||||
|
|
||||||
|
// XXX: Editline seems to remember its position in history,
|
||||||
|
// so it's not going to work as you'd expect it to
|
||||||
|
if (*line)
|
||||||
|
{
|
||||||
|
HistEventW ev;
|
||||||
|
history_w (self->current->history, &ev, H_ENTER, line);
|
||||||
|
print_debug ("history: %d %ls", ev.num, ev.str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it to multibyte to reflect the Readline interface
|
||||||
|
size_t needed = wcstombs (NULL, line, 0) + 1;
|
||||||
|
char converted[needed];
|
||||||
|
if (!needed || wcstombs (converted, line, needed) == (size_t) -1)
|
||||||
|
print_error ("encoding conversion failed");
|
||||||
|
else
|
||||||
|
process_input (g_ctx, converted);
|
||||||
|
|
||||||
|
free (line);
|
||||||
|
|
||||||
|
el_cursor (editline, len - point);
|
||||||
|
el_wdeletestr (editline, len);
|
||||||
|
return CC_REFRESH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_editline_init (struct input *self)
|
||||||
|
{
|
||||||
|
el_set (self->editline, EL_ADDFN, "goto-buffer",
|
||||||
|
"Go to buffer", on_editline_goto_buffer);
|
||||||
|
el_set (self->editline, EL_ADDFN, "previous-buffer",
|
||||||
|
"Previous buffer", on_editline_previous_buffer);
|
||||||
|
el_set (self->editline, EL_ADDFN, "next-buffer",
|
||||||
|
"Next buffer", on_editline_next_buffer);
|
||||||
|
|
||||||
|
// Redefine M-0 through M-9 to switch buffers
|
||||||
|
for (size_t i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
char keyseq[] = { 'M', '-', '0' + i, 0 };
|
||||||
|
el_set (self->editline, EL_BIND, keyseq, "goto-buffer", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
el_set (self->editline, EL_BIND, "M-p", "ed-prev-history", NULL);
|
||||||
|
el_set (self->editline, EL_BIND, "M-n", "ed-next-history", NULL);
|
||||||
|
el_set (self->editline, EL_BIND, "^P", "previous-buffer", NULL);
|
||||||
|
el_set (self->editline, EL_BIND, "^N", "next-buffer", NULL);
|
||||||
|
|
||||||
|
// Source the user's defaults file
|
||||||
|
el_source (self->editline, NULL);
|
||||||
|
|
||||||
|
el_set (self->editline, EL_ADDFN, "send-line",
|
||||||
|
"Send line", on_editline_return);
|
||||||
|
el_set (self->editline, EL_BIND, "\n", "send-line", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
|
|
||||||
// --- I/O event handlers ------------------------------------------------------
|
// --- I/O event handlers ------------------------------------------------------
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -5095,10 +5478,6 @@ main (int argc, char *argv[])
|
||||||
SSL_load_error_strings ();
|
SSL_load_error_strings ();
|
||||||
atexit (ERR_free_strings);
|
atexit (ERR_free_strings);
|
||||||
|
|
||||||
using_history ();
|
|
||||||
// This can cause memory leaks, or maybe even a segfault. Funny, eh?
|
|
||||||
stifle_history (HISTORY_LIMIT);
|
|
||||||
|
|
||||||
setup_signal_handlers ();
|
setup_signal_handlers ();
|
||||||
register_config_modules (&ctx);
|
register_config_modules (&ctx);
|
||||||
load_configuration (&ctx);
|
load_configuration (&ctx);
|
||||||
|
@ -5109,7 +5488,7 @@ main (int argc, char *argv[])
|
||||||
buffer_activate (&ctx, ctx.server.buffer);
|
buffer_activate (&ctx, ctx.server.buffer);
|
||||||
|
|
||||||
refresh_prompt (&ctx);
|
refresh_prompt (&ctx);
|
||||||
input_start (&ctx.input);
|
input_start (&ctx.input, argv[0]);
|
||||||
|
|
||||||
// Connect to the server ASAP
|
// Connect to the server ASAP
|
||||||
poller_timer_set (&ctx.server.reconnect_tmr, 0);
|
poller_timer_set (&ctx.server.reconnect_tmr, 0);
|
||||||
|
|
Loading…
Reference in New Issue