degesch: use libffi to unify input callbacks
And fuck you both, Readline and Editline.
This commit is contained in:
parent
3304b718aa
commit
584d2f0295
@ -39,7 +39,7 @@ include (AddThreads)
|
|||||||
|
|
||||||
find_package (Curses)
|
find_package (Curses)
|
||||||
find_package (PkgConfig REQUIRED)
|
find_package (PkgConfig REQUIRED)
|
||||||
pkg_check_modules (libssl REQUIRED libssl libcrypto)
|
pkg_check_modules (dependencies REQUIRED libssl libcrypto libffi)
|
||||||
pkg_check_modules (ncursesw ncursesw)
|
pkg_check_modules (ncursesw ncursesw)
|
||||||
|
|
||||||
if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
|
if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
|
||||||
@ -50,9 +50,9 @@ if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
|
|||||||
add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1)
|
add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1)
|
||||||
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
|
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
|
||||||
|
|
||||||
list (APPEND project_libraries ${libssl_LIBRARIES})
|
list (APPEND project_libraries ${dependencies_LIBRARIES})
|
||||||
include_directories (${libssl_INCLUDE_DIRS})
|
include_directories (${dependencies_INCLUDE_DIRS})
|
||||||
link_directories (${libssl_LIBRARY_DIRS})
|
link_directories (${dependencies_LIBRARY_DIRS})
|
||||||
|
|
||||||
# FIXME: other Lua versions may be acceptable, don't know yet
|
# FIXME: other Lua versions may be acceptable, don't know yet
|
||||||
pkg_search_module (lua lua53 lua5.3 lua-5.3 lua>=5.3)
|
pkg_search_module (lua lua53 lua5.3 lua-5.3 lua>=5.3)
|
||||||
|
@ -10,7 +10,7 @@ All of them have these potentially interesting properties:
|
|||||||
|
|
||||||
- full IPv6 support
|
- full IPv6 support
|
||||||
- TLS support, including client certificates
|
- TLS support, including client certificates
|
||||||
- minimal dependencies
|
- lean on dependencies (with the exception of 'degesch')
|
||||||
- compact and arguably easy to hack on
|
- compact and arguably easy to hack on
|
||||||
- permissive license
|
- permissive license
|
||||||
|
|
||||||
@ -66,9 +66,9 @@ support (even though socksify can add that easily to any program).
|
|||||||
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),
|
Runtime dependencies: openssl +
|
||||||
readline >= 6.0 or libedit >= 2013-07-12 (degesch),
|
Additionally for degesch: curses, libffi, lua >= 5.3 (optional),
|
||||||
lua >= 5.3 (degesch, optional)
|
readline >= 6.0 or libedit >= 2013-07-12
|
||||||
|
|
||||||
$ git clone --recursive https://github.com/pjanouch/uirc3.git
|
$ git clone --recursive https://github.com/pjanouch/uirc3.git
|
||||||
$ mkdir uirc3/build
|
$ mkdir uirc3/build
|
||||||
|
670
degesch.c
670
degesch.c
@ -68,6 +68,8 @@ enum
|
|||||||
#undef lines
|
#undef lines
|
||||||
#undef columns
|
#undef columns
|
||||||
|
|
||||||
|
#include <ffi.h>
|
||||||
|
|
||||||
#ifdef HAVE_READLINE
|
#ifdef HAVE_READLINE
|
||||||
#include <readline/readline.h>
|
#include <readline/readline.h>
|
||||||
#include <readline/history.h>
|
#include <readline/history.h>
|
||||||
@ -134,6 +136,22 @@ input_buffer_destroy (struct input_buffer *self)
|
|||||||
free (self);
|
free (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef bool (*input_fn) (int count, int key, void *user_data);
|
||||||
|
|
||||||
|
struct input_fn_data
|
||||||
|
{
|
||||||
|
ffi_closure closure; ///< Closure
|
||||||
|
|
||||||
|
LIST_HEADER (struct input_fn_data)
|
||||||
|
input_fn callback; ///< Real callback
|
||||||
|
void *user_data; ///< Real callback user data
|
||||||
|
|
||||||
|
#ifdef HAVE_EDITLINE
|
||||||
|
wchar_t *name; ///< Function name
|
||||||
|
wchar_t *help; ///< Function help
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
|
};
|
||||||
|
|
||||||
struct input
|
struct input
|
||||||
{
|
{
|
||||||
bool active; ///< Are we a thing?
|
bool active; ///< Are we a thing?
|
||||||
@ -145,6 +163,7 @@ struct input
|
|||||||
#elif defined HAVE_EDITLINE
|
#elif defined HAVE_EDITLINE
|
||||||
EditLine *editline; ///< The EditLine object
|
EditLine *editline; ///< The EditLine object
|
||||||
#endif // HAVE_EDITLINE
|
#endif // HAVE_EDITLINE
|
||||||
|
struct input_fn_data *fns; ///< Functions
|
||||||
|
|
||||||
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
|
||||||
@ -164,6 +183,14 @@ input_free (struct input *self)
|
|||||||
#ifdef HAVE_READLINE
|
#ifdef HAVE_READLINE
|
||||||
free (self->saved_line);
|
free (self->saved_line);
|
||||||
#endif // HAVE_READLINE
|
#endif // HAVE_READLINE
|
||||||
|
LIST_FOR_EACH (struct input_fn_data, iter, self->fns)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_EDITLINE
|
||||||
|
free (iter->name);
|
||||||
|
free (iter->help);
|
||||||
|
#endif // HAVE_EDITLINE
|
||||||
|
ffi_closure_free (iter);
|
||||||
|
}
|
||||||
free (self->prompt);
|
free (self->prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +306,45 @@ input_get_content (struct input *self)
|
|||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_closure_forwarder (ffi_cif *cif, void *ret, void **args, void *user_data)
|
||||||
|
{
|
||||||
|
(void) cif;
|
||||||
|
|
||||||
|
struct input_fn_data *data = user_data;
|
||||||
|
if (!data->callback
|
||||||
|
(*(int *) args[0], UNMETA (*(int *) args[1]), data->user_data))
|
||||||
|
rl_ding ();
|
||||||
|
*(int *) ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_add_fn (struct input *self,
|
||||||
|
const char *name, const char *help, input_fn callback, void *user_data)
|
||||||
|
{
|
||||||
|
(void) help;
|
||||||
|
|
||||||
|
void *bound_fn = NULL;
|
||||||
|
struct input_fn_data *data = ffi_closure_alloc (sizeof *data, &bound_fn);
|
||||||
|
hard_assert (data);
|
||||||
|
|
||||||
|
static ffi_cif cif;
|
||||||
|
static ffi_type *args[2] = { &ffi_type_sint, &ffi_type_sint };
|
||||||
|
hard_assert (ffi_prep_cif
|
||||||
|
(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint, args) == FFI_OK);
|
||||||
|
|
||||||
|
data->prev = data->next = NULL;
|
||||||
|
data->callback = callback;
|
||||||
|
data->user_data = user_data;
|
||||||
|
hard_assert (ffi_prep_closure_loc (&data->closure,
|
||||||
|
&cif, input_closure_forwarder, data, bound_fn) == FFI_OK);
|
||||||
|
|
||||||
|
rl_add_defun (name, (rl_command_func_t *) bound_fn, -1);
|
||||||
|
LIST_PREPEND (self->fns, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static int app_readline_init (void);
|
static int app_readline_init (void);
|
||||||
static void on_readline_input (char *line);
|
static void on_readline_input (char *line);
|
||||||
static char **app_readline_completion (const char *text, int start, int end);
|
static char **app_readline_completion (const char *text, int start, int end);
|
||||||
@ -603,6 +669,52 @@ input_get_content (struct input *self)
|
|||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_closure_forwarder (ffi_cif *cif, void *ret, void **args, void *user_data)
|
||||||
|
{
|
||||||
|
(void) cif;
|
||||||
|
|
||||||
|
struct input_fn_data *data = user_data;
|
||||||
|
*(unsigned char *) ret = data->callback
|
||||||
|
(1, *(int *) args[1], data->user_data) ? CC_NORM : CC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static wchar_t *
|
||||||
|
ascii_to_wide (const char *ascii)
|
||||||
|
{
|
||||||
|
size_t len = strlen (ascii) + 1;
|
||||||
|
wchar_t *wide = xcalloc (sizeof *wide, len);
|
||||||
|
while (len--)
|
||||||
|
hard_assert ((wide[len] = (unsigned char) ascii[len]) < 0x80);
|
||||||
|
return wide;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_add_fn (struct input *self,
|
||||||
|
const char *name, const char *help, input_fn callback, void *user_data)
|
||||||
|
{
|
||||||
|
void *bound_fn = NULL;
|
||||||
|
struct input_fn_data *data = ffi_closure_alloc (sizeof *data, &bound_fn);
|
||||||
|
hard_assert (data);
|
||||||
|
|
||||||
|
static ffi_cif cif;
|
||||||
|
static ffi_type *args[2] = { &ffi_type_pointer, &ffi_type_sint };
|
||||||
|
hard_assert (ffi_prep_cif
|
||||||
|
(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_uchar, args) == FFI_OK);
|
||||||
|
|
||||||
|
data->user_data = user_data;
|
||||||
|
data->callback = callback;
|
||||||
|
data->name = ascii_to_wide (name);
|
||||||
|
data->help = ascii_to_wide (help);
|
||||||
|
hard_assert (ffi_prep_closure_loc (&data->closure,
|
||||||
|
&cif, input_closure_forwarder, data, bound_fn) == FFI_OK);
|
||||||
|
|
||||||
|
el_wset (self->editline, EL_ADDFN, data->name, data->help, bound_fn);
|
||||||
|
LIST_PREPEND (self->fns, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_start (struct input *self, const char *program_name)
|
input_start (struct input *self, const char *program_name)
|
||||||
{
|
{
|
||||||
@ -11161,40 +11273,6 @@ resume_terminal (struct app_context *ctx)
|
|||||||
input_show (&ctx->input);
|
input_show (&ctx->input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
static void
|
|
||||||
redraw_screen (struct app_context *ctx)
|
|
||||||
{
|
|
||||||
input_hide (&ctx->input);
|
|
||||||
|
|
||||||
// If by some circumstance we had the wrong idea
|
|
||||||
input_on_terminal_resized (&ctx->input);
|
|
||||||
update_screen_size ();
|
|
||||||
|
|
||||||
buffer_print_backlog (ctx, ctx->current_buffer);
|
|
||||||
|
|
||||||
input_show (&ctx->input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
jump_to_buffer (struct app_context *ctx, int n)
|
|
||||||
{
|
|
||||||
if (n < 0 || n > 9)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// There's no buffer zero
|
|
||||||
if (n == 0)
|
|
||||||
n = 10;
|
|
||||||
|
|
||||||
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 false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static pid_t
|
static pid_t
|
||||||
spawn_helper_child (struct app_context *ctx)
|
spawn_helper_child (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
@ -11222,69 +11300,20 @@ spawn_helper_child (struct app_context *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
launch_backlog_helper (struct app_context *ctx, int backlog_fd)
|
redraw_screen (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
hard_assert (!ctx->running_backlog_helper);
|
input_hide (&ctx->input);
|
||||||
switch (spawn_helper_child (ctx))
|
|
||||||
{
|
// If by some circumstance we had the wrong idea
|
||||||
case 0:
|
input_on_terminal_resized (&ctx->input);
|
||||||
dup2 (backlog_fd, STDIN_FILENO);
|
update_screen_size ();
|
||||||
execl ("/bin/sh", "/bin/sh", "-c", get_config_string
|
|
||||||
(ctx->config.root, "behaviour.backlog_helper"), NULL);
|
buffer_print_backlog (ctx, ctx->current_buffer);
|
||||||
print_error ("%s: %s",
|
|
||||||
"Failed to launch backlog helper", strerror (errno));
|
input_show (&ctx->input);
|
||||||
_exit (EXIT_FAILURE);
|
|
||||||
case -1:
|
|
||||||
log_global_error (ctx, "#s: #l",
|
|
||||||
"Failed to launch backlog helper", strerror (errno));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ctx->running_backlog_helper = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
display_backlog (struct app_context *ctx)
|
|
||||||
{
|
|
||||||
FILE *backlog = tmpfile ();
|
|
||||||
if (!backlog)
|
|
||||||
{
|
|
||||||
log_global_error (ctx, "#s: #l",
|
|
||||||
"Failed to create a temporary file", strerror (errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (struct buffer_line *line = ctx->current_buffer->lines;
|
|
||||||
line; line = line->next)
|
|
||||||
buffer_line_write_to_backlog (ctx, line, backlog);
|
|
||||||
|
|
||||||
rewind (backlog);
|
|
||||||
set_cloexec (fileno (backlog));
|
|
||||||
launch_backlog_helper (ctx, fileno (backlog));
|
|
||||||
fclose (backlog);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
display_full_log (struct app_context *ctx)
|
|
||||||
{
|
|
||||||
char *path = buffer_get_log_path (ctx->current_buffer);
|
|
||||||
FILE *full_log = fopen (path, "rb");
|
|
||||||
free (path);
|
|
||||||
|
|
||||||
if (!full_log)
|
|
||||||
{
|
|
||||||
log_global_error (ctx, "Failed to open log file for #s: #l",
|
|
||||||
ctx->current_buffer->name, strerror (errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->current_buffer->log_file)
|
|
||||||
fflush (ctx->current_buffer->log_file);
|
|
||||||
|
|
||||||
set_cloexec (fileno (full_log));
|
|
||||||
launch_backlog_helper (ctx, fileno (full_log));
|
|
||||||
fclose (full_log);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
dump_input_to_file (struct app_context *ctx, char *template, struct error **e)
|
dump_input_to_file (struct app_context *ctx, char *template, struct error **e)
|
||||||
@ -11324,12 +11353,16 @@ try_dump_input_to_file (struct app_context *ctx)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
launch_input_editor (struct app_context *ctx)
|
on_edit_input (int count, int key, void *user_data)
|
||||||
{
|
{
|
||||||
|
(void) count;
|
||||||
|
(void) key;
|
||||||
|
struct app_context *ctx = user_data;
|
||||||
|
|
||||||
char *filename;
|
char *filename;
|
||||||
if (!(filename = try_dump_input_to_file (ctx)))
|
if (!(filename = try_dump_input_to_file (ctx)))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
const char *command;
|
const char *command;
|
||||||
if (!(command = getenv ("VISUAL"))
|
if (!(command = getenv ("VISUAL"))
|
||||||
@ -11353,6 +11386,7 @@ launch_input_editor (struct app_context *ctx)
|
|||||||
ctx->running_editor = true;
|
ctx->running_editor = true;
|
||||||
ctx->editor_filename = filename;
|
ctx->editor_filename = filename;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -11390,10 +11424,185 @@ input_editor_cleanup (struct app_context *ctx)
|
|||||||
ctx->running_editor = false;
|
ctx->running_editor = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
launch_backlog_helper (struct app_context *ctx, int backlog_fd)
|
||||||
|
{
|
||||||
|
hard_assert (!ctx->running_backlog_helper);
|
||||||
|
switch (spawn_helper_child (ctx))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
dup2 (backlog_fd, STDIN_FILENO);
|
||||||
|
execl ("/bin/sh", "/bin/sh", "-c", get_config_string
|
||||||
|
(ctx->config.root, "behaviour.backlog_helper"), NULL);
|
||||||
|
print_error ("%s: %s",
|
||||||
|
"Failed to launch backlog helper", strerror (errno));
|
||||||
|
_exit (EXIT_FAILURE);
|
||||||
|
case -1:
|
||||||
|
log_global_error (ctx, "#s: #l",
|
||||||
|
"Failed to launch backlog helper", strerror (errno));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ctx->running_backlog_helper = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_display_backlog (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) count;
|
||||||
|
(void) key;
|
||||||
|
struct app_context *ctx = user_data;
|
||||||
|
|
||||||
|
FILE *backlog = tmpfile ();
|
||||||
|
if (!backlog)
|
||||||
|
{
|
||||||
|
log_global_error (ctx, "#s: #l",
|
||||||
|
"Failed to create a temporary file", strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (struct buffer_line *line = ctx->current_buffer->lines;
|
||||||
|
line; line = line->next)
|
||||||
|
buffer_line_write_to_backlog (ctx, line, backlog);
|
||||||
|
|
||||||
|
rewind (backlog);
|
||||||
|
set_cloexec (fileno (backlog));
|
||||||
|
launch_backlog_helper (ctx, fileno (backlog));
|
||||||
|
fclose (backlog);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_display_full_log (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) count;
|
||||||
|
(void) key;
|
||||||
|
struct app_context *ctx = user_data;
|
||||||
|
|
||||||
|
char *path = buffer_get_log_path (ctx->current_buffer);
|
||||||
|
FILE *full_log = fopen (path, "rb");
|
||||||
|
free (path);
|
||||||
|
|
||||||
|
if (!full_log)
|
||||||
|
{
|
||||||
|
log_global_error (ctx, "Failed to open log file for #s: #l",
|
||||||
|
ctx->current_buffer->name, strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->current_buffer->log_file)
|
||||||
|
fflush (ctx->current_buffer->log_file);
|
||||||
|
|
||||||
|
set_cloexec (fileno (full_log));
|
||||||
|
launch_backlog_helper (ctx, fileno (full_log));
|
||||||
|
fclose (full_log);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_goto_buffer (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) count;
|
||||||
|
struct app_context *ctx = user_data;
|
||||||
|
|
||||||
|
int n = key - '0';
|
||||||
|
if (n < 0 || n > 9)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// There's no buffer zero
|
||||||
|
if (n == 0)
|
||||||
|
n = 10;
|
||||||
|
|
||||||
|
if (!ctx->last_buffer || buffer_get_index (ctx, ctx->current_buffer) != n)
|
||||||
|
return buffer_goto (ctx, n);
|
||||||
|
|
||||||
|
// Fast switching between two buffers
|
||||||
|
buffer_activate (ctx, ctx->last_buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_previous_buffer (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) key;
|
||||||
|
buffer_activate (user_data, buffer_previous (user_data, count));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_next_buffer (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) key;
|
||||||
|
buffer_activate (user_data, buffer_next (user_data, count));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_switch_buffer (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) count;
|
||||||
|
(void) key;
|
||||||
|
struct app_context *ctx = user_data;
|
||||||
|
|
||||||
|
if (!ctx->last_buffer)
|
||||||
|
return false;
|
||||||
|
buffer_activate (ctx, ctx->last_buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_redraw_screen (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) count;
|
||||||
|
(void) key;
|
||||||
|
|
||||||
|
redraw_screen (user_data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_insert_attribute (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) count;
|
||||||
|
(void) key;
|
||||||
|
|
||||||
|
struct app_context *ctx = user_data;
|
||||||
|
ctx->awaiting_mirc_escape = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
on_start_paste_mode (int count, int key, void *user_data)
|
||||||
|
{
|
||||||
|
(void) count;
|
||||||
|
(void) key;
|
||||||
|
|
||||||
|
struct app_context *ctx = user_data;
|
||||||
|
ctx->in_bracketed_paste = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
bind_common_keys (struct app_context *ctx)
|
bind_common_keys (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
struct input *self = &ctx->input;
|
struct input *self = &ctx->input;
|
||||||
|
#define XX(...) input_add_fn (self, __VA_ARGS__, ctx);
|
||||||
|
XX ("previous-buffer", "Previous buffer", on_previous_buffer)
|
||||||
|
XX ("next-buffer", "Next buffer", on_next_buffer)
|
||||||
|
XX ("goto-buffer", "Go to buffer", on_goto_buffer)
|
||||||
|
XX ("switch-buffer", "Switch buffer", on_switch_buffer)
|
||||||
|
XX ("display-backlog", "Show backlog", on_display_backlog)
|
||||||
|
XX ("display-full-log", "Show full log", on_display_full_log)
|
||||||
|
XX ("edit-input", "Edit input", on_edit_input)
|
||||||
|
XX ("redraw-screen", "Redraw screen", on_redraw_screen)
|
||||||
|
XX ("insert-attribute", "mIRC formatting", on_insert_attribute)
|
||||||
|
XX ("start-paste-mode", "Bracketed paste", on_start_paste_mode)
|
||||||
|
#undef XX
|
||||||
|
|
||||||
input_bind_control (self, 'p', "previous-buffer");
|
input_bind_control (self, 'p', "previous-buffer");
|
||||||
input_bind_control (self, 'n', "next-buffer");
|
input_bind_control (self, 'n', "next-buffer");
|
||||||
|
|
||||||
@ -11406,12 +11615,9 @@ bind_common_keys (struct app_context *ctx)
|
|||||||
input_bind_meta (self, 'h', "display-full-log");
|
input_bind_meta (self, 'h', "display-full-log");
|
||||||
input_bind_meta (self, 'e', "edit-input");
|
input_bind_meta (self, 'e', "edit-input");
|
||||||
|
|
||||||
if (key_f5)
|
if (key_f5) input_bind (self, key_f5, "previous-buffer");
|
||||||
input_bind (self, key_f5, "previous-buffer");
|
if (key_f6) input_bind (self, key_f6, "next-buffer");
|
||||||
if (key_f6)
|
if (key_ppage) input_bind (self, key_ppage, "display-backlog");
|
||||||
input_bind (self, key_f6, "next-buffer");
|
|
||||||
if (key_ppage)
|
|
||||||
input_bind (self, key_ppage, "display-backlog");
|
|
||||||
|
|
||||||
if (clear_screen)
|
if (clear_screen)
|
||||||
input_bind_control (self, 'l', "redraw-screen");
|
input_bind_control (self, 'l', "redraw-screen");
|
||||||
@ -11423,129 +11629,17 @@ bind_common_keys (struct app_context *ctx)
|
|||||||
|
|
||||||
#ifdef HAVE_READLINE
|
#ifdef HAVE_READLINE
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_goto_buffer (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
if (!jump_to_buffer (ctx, UNMETA (key) - '0'))
|
|
||||||
input_ding (&ctx->input);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_previous_buffer (int count, int key)
|
|
||||||
{
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
buffer_activate (ctx, buffer_previous (ctx, count));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_next_buffer (int count, int key)
|
|
||||||
{
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
buffer_activate (ctx, buffer_next (ctx, count));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_switch_buffer (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
if (ctx->last_buffer)
|
|
||||||
buffer_activate (ctx, ctx->last_buffer);
|
|
||||||
else
|
|
||||||
input_ding (&ctx->input);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_display_backlog (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
display_backlog (ctx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_display_full_log (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
display_full_log (ctx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_edit_input (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
launch_input_editor (ctx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_redraw_screen (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
redraw_screen (ctx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_insert_attribute (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
ctx->awaiting_mirc_escape = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
on_readline_start_paste_mode (int count, int key)
|
|
||||||
{
|
|
||||||
(void) count;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
ctx->in_bracketed_paste = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
on_readline_return (int count, int key)
|
on_readline_return (int count, int key)
|
||||||
{
|
{
|
||||||
(void) count;
|
(void) count;
|
||||||
(void) key;
|
(void) key;
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
|
|
||||||
// Let readline pass the line to our input handler
|
// Let readline pass the line to our input handler
|
||||||
rl_done = 1;
|
rl_done = 1;
|
||||||
|
|
||||||
// Hide the line, don't redisplay it
|
// Hide the line, don't redisplay it
|
||||||
|
struct app_context *ctx = g_ctx;
|
||||||
input_hide (&ctx->input);
|
input_hide (&ctx->input);
|
||||||
input_restore (&ctx->input);
|
input_restore (&ctx->input);
|
||||||
return 0;
|
return 0;
|
||||||
@ -11602,18 +11696,7 @@ app_readline_init (void)
|
|||||||
// our dear user could potentionally rig things up in a way that might
|
// our dear user could potentionally rig things up in a way that might
|
||||||
// result in some funny unspecified behaviour
|
// result in some funny unspecified behaviour
|
||||||
|
|
||||||
rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
|
|
||||||
rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
|
|
||||||
rl_add_defun ("goto-buffer", on_readline_goto_buffer, -1);
|
|
||||||
rl_add_defun ("switch-buffer", on_readline_switch_buffer, -1);
|
|
||||||
rl_add_defun ("display-backlog", on_readline_display_backlog, -1);
|
|
||||||
rl_add_defun ("display-full-log", on_readline_display_full_log, -1);
|
|
||||||
rl_add_defun ("edit-input", on_readline_edit_input, -1);
|
|
||||||
rl_add_defun ("redraw-screen", on_readline_redraw_screen, -1);
|
|
||||||
rl_add_defun ("insert-attribute", on_readline_insert_attribute, -1);
|
|
||||||
rl_add_defun ("start-paste-mode", on_readline_start_paste_mode, -1);
|
|
||||||
rl_add_defun ("send-line", on_readline_return, -1);
|
rl_add_defun ("send-line", on_readline_return, -1);
|
||||||
|
|
||||||
bind_common_keys (ctx);
|
bind_common_keys (ctx);
|
||||||
|
|
||||||
// Move native history commands
|
// Move native history commands
|
||||||
@ -11636,113 +11719,6 @@ app_readline_init (void)
|
|||||||
|
|
||||||
#ifdef HAVE_EDITLINE
|
#ifdef HAVE_EDITLINE
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
on_editline_goto_buffer (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
if (!jump_to_buffer (ctx, key - '0'))
|
|
||||||
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_switch_buffer (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
struct app_context *ctx = g_ctx;
|
|
||||||
if (ctx->last_buffer)
|
|
||||||
buffer_activate (ctx, ctx->last_buffer);
|
|
||||||
else
|
|
||||||
input_ding (&ctx->input);
|
|
||||||
return CC_NORM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
on_editline_display_backlog (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
display_backlog (g_ctx);
|
|
||||||
return CC_NORM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
on_editline_display_full_log (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
display_full_log (g_ctx);
|
|
||||||
return CC_NORM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
on_editline_edit_input (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
launch_input_editor (g_ctx);
|
|
||||||
return CC_NORM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
on_editline_redraw_screen (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
redraw_screen (g_ctx);
|
|
||||||
return CC_NORM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
on_editline_insert_attribute (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
g_ctx->awaiting_mirc_escape = true;
|
|
||||||
return CC_NORM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
on_editline_start_paste_mode (EditLine *editline, int key)
|
|
||||||
{
|
|
||||||
(void) editline;
|
|
||||||
(void) key;
|
|
||||||
|
|
||||||
g_ctx->in_bracketed_paste = true;
|
|
||||||
return CC_NORM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
static unsigned char
|
||||||
on_editline_complete (EditLine *editline, int key)
|
on_editline_complete (EditLine *editline, int key)
|
||||||
{
|
{
|
||||||
@ -11831,29 +11807,11 @@ on_editline_return (EditLine *editline, int key)
|
|||||||
static void
|
static void
|
||||||
app_editline_init (struct input *self)
|
app_editline_init (struct input *self)
|
||||||
{
|
{
|
||||||
#define XX(name, help, fn) { name, help, on_editline_ ## fn },
|
|
||||||
|
|
||||||
// el_set() leaks memory in 20150325 and other versions, we need wchar_t
|
// el_set() leaks memory in 20150325 and other versions, we need wchar_t
|
||||||
static const struct { const wchar_t *name; const wchar_t *help;
|
el_wset (self->editline, EL_ADDFN,
|
||||||
unsigned char (*func) (EditLine *, int); } x[] =
|
L"send-line", L"Send line", on_editline_return);
|
||||||
{
|
el_wset (self->editline, EL_ADDFN,
|
||||||
XX( L"goto-buffer", L"Go to buffer", goto_buffer )
|
L"complete", L"Complete word", on_editline_complete);
|
||||||
XX( L"previous-buffer", L"Previous buffer", previous_buffer )
|
|
||||||
XX( L"next-buffer", L"Next buffer", next_buffer )
|
|
||||||
XX( L"switch-buffer", L"Switch buffer", switch_buffer )
|
|
||||||
XX( L"display-backlog", L"Show backlog", display_backlog )
|
|
||||||
XX( L"display-full-log", L"Show full log", display_full_log )
|
|
||||||
XX( L"edit-input", L"Edit input", edit_input )
|
|
||||||
XX( L"redraw-screen", L"Redraw screen", redraw_screen )
|
|
||||||
XX( L"insert-attribute", L"mIRC formatting", insert_attribute )
|
|
||||||
XX( L"start-paste-mode", L"Bracketed paste", start_paste_mode )
|
|
||||||
XX( L"send-line", L"Send line", return )
|
|
||||||
XX( L"complete", L"Complete word", complete )
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < N_ELEMENTS (x); i++)
|
|
||||||
el_wset (self->editline, EL_ADDFN, x[i].name, x[i].help, x[i].func);
|
|
||||||
|
|
||||||
#undef XX
|
|
||||||
|
|
||||||
bind_common_keys (g_ctx);
|
bind_common_keys (g_ctx);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user