Files
profanity/src/ui/inputwin.c
Michael Vetter 36ec2b0ae1 feat: implement asynchronous external editor support
Move from blocking fork/wait logic to nonblocking fork and
g_child_watch_add.

This ensures that the Profanity main loop continues to run while an
external editor is open. So we don't loose connection and react to
pings.

We change editor handling also in vcard and muc subject editing.
In the new implementation we are launching the editor and passing a
callback which we will use once the editor exited.

We use the recently added ui_susped() and ui_resume().

To not clutter the UI we need to check whether Profanity UI is suspended
and omit drawing in this case.

Fixes: https://github.com/profanity-im/profanity/issues/1888
Ref: 9b112904a9bc7250dc013d901187ca8622580d98
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
2026-04-02 22:47:26 +02:00

1033 lines
26 KiB
C

/*
* inputwin.c
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#define _XOPEN_SOURCE_EXTENDED
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <sys/select.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>
#include <readline/readline.h>
#include <readline/history.h>
#ifdef HAVE_NCURSESW_NCURSES_H
#include <ncursesw/ncurses.h>
#elif HAVE_NCURSES_H
#include <ncurses.h>
#elif HAVE_CURSES_H
#include <curses.h>
#endif
#include "profanity.h"
#include "log.h"
#include "common.h"
#include "command/cmd_ac.h"
#include "config/files.h"
#include "config/accounts.h"
#include "config/preferences.h"
#include "config/theme.h"
#include "ui/ui.h"
#include "ui/screen.h"
#include "ui/statusbar.h"
#include "ui/inputwin.h"
#include "ui/window.h"
#include "ui/window_list.h"
#include "xmpp/xmpp.h"
#include "xmpp/muc.h"
#include "xmpp/roster_list.h"
#include "xmpp/chat_state.h"
#include "tools/editor.h"
#include "tools/spellcheck.h"
static WINDOW* inp_win;
static int pad_start = 0;
static struct timeval p_rl_timeout;
/* Timeout in ms. Shows how long select() may block. */
static gint inp_timeout = 0;
static gint no_input_count = 0;
static FILE* discard;
static fd_set fds;
static int r;
static char* inp_line = NULL;
static gboolean get_password = FALSE;
static void _inp_win_update_virtual(void);
static int _inp_edited(const wint_t ch);
static void _inp_win_handle_scroll(void);
static int _inp_offset_to_col(char* str, int offset);
static void _inp_write(char* line, int offset);
static void _inp_redisplay(void);
static void _inp_rl_addfuncs(void);
static int _inp_rl_getc(FILE* stream);
static void _inp_rl_linehandler(char* line);
static int _inp_rl_tab_handler(int count, int key);
static int _inp_rl_shift_tab_handler(int count, int key);
static int _inp_rl_win_clear_handler(int count, int key);
static int _inp_rl_win_close_handler(int count, int key);
static int _inp_rl_win_1_handler(int count, int key);
static int _inp_rl_win_2_handler(int count, int key);
static int _inp_rl_win_3_handler(int count, int key);
static int _inp_rl_win_4_handler(int count, int key);
static int _inp_rl_win_5_handler(int count, int key);
static int _inp_rl_win_6_handler(int count, int key);
static int _inp_rl_win_7_handler(int count, int key);
static int _inp_rl_win_8_handler(int count, int key);
static int _inp_rl_win_9_handler(int count, int key);
static int _inp_rl_win_0_handler(int count, int key);
static int _inp_rl_win_11_handler(int count, int key);
static int _inp_rl_win_12_handler(int count, int key);
static int _inp_rl_win_13_handler(int count, int key);
static int _inp_rl_win_14_handler(int count, int key);
static int _inp_rl_win_15_handler(int count, int key);
static int _inp_rl_win_16_handler(int count, int key);
static int _inp_rl_win_17_handler(int count, int key);
static int _inp_rl_win_18_handler(int count, int key);
static int _inp_rl_win_19_handler(int count, int key);
static int _inp_rl_win_20_handler(int count, int key);
static int _inp_rl_win_prev_handler(int count, int key);
static int _inp_rl_win_next_handler(int count, int key);
static int _inp_rl_win_next_unread_handler(int count, int key);
static int _inp_rl_win_attention_handler(int count, int key);
static int _inp_rl_win_attention_next_handler(int count, int key);
static int _inp_rl_win_pageup_handler(int count, int key);
static int _inp_rl_win_pagedown_handler(int count, int key);
static int _inp_rl_subwin_pageup_handler(int count, int key);
static int _inp_rl_subwin_pagedown_handler(int count, int key);
static int _inp_rl_startup_hook(void);
static int _inp_rl_down_arrow_handler(int count, int key);
static int _inp_rl_scroll_handler(int count, int key);
static int _inp_rl_send_to_editor(int count, int key);
static int _inp_rl_print_newline_symbol(int count, int key);
void
create_input_window(void)
{
/* MB_CUR_MAX is evaluated at runtime depending on the current
* locale, therefore we check that our own version is big enough
* and bail out if it isn't.
*/
assert(MB_CUR_MAX <= PROF_MB_CUR_MAX);
#ifdef NCURSES_REENTRANT
set_escdelay(25);
#else
ESCDELAY = 25;
#endif
discard = fopen("/dev/null", "a");
rl_outstream = discard;
rl_readline_name = "profanity";
_inp_rl_addfuncs();
rl_getc_function = _inp_rl_getc;
rl_redisplay_function = _inp_redisplay;
rl_startup_hook = _inp_rl_startup_hook;
rl_callback_handler_install(NULL, _inp_rl_linehandler);
inp_win = newpad(1, INP_WIN_MAX);
wbkgd(inp_win, theme_attrs(THEME_INPUT_TEXT));
keypad(inp_win, TRUE);
wmove(inp_win, 0, 0);
_inp_win_update_virtual();
}
static gboolean
_inp_slashguard_check(void)
{
if (get_password)
return false;
/* ignore empty and quoted messages */
if (inp_line == NULL || inp_line[0] == '\0' || inp_line[0] == '>')
return false;
if (!prefs_get_boolean(PREF_SLASH_GUARD))
return false;
size_t n = 1;
while (inp_line[n] != '\0' && n < 4) {
if (inp_line[n] == '/') {
cons_show("Your text contains a slash in the first 4 characters");
free(inp_line);
inp_line = NULL;
return true;
}
n++;
}
return false;
}
char*
inp_readline(void)
{
// UI is suspended
if (isendwin()) {
g_usleep(100000); // 100ms
return NULL;
}
p_rl_timeout.tv_sec = inp_timeout / 1000;
p_rl_timeout.tv_usec = inp_timeout % 1000 * 1000;
FD_ZERO(&fds);
FD_SET(fileno(rl_instream), &fds);
errno = 0;
pthread_mutex_unlock(&lock);
r = select(FD_SETSIZE, &fds, NULL, NULL, &p_rl_timeout);
pthread_mutex_lock(&lock);
if (r < 0) {
if (errno != EINTR) {
const char* err_msg = strerror(errno);
log_error("Readline failed: %s", err_msg);
}
return NULL;
}
if (FD_ISSET(fileno(rl_instream), &fds)) {
rl_callback_read_char();
if (rl_line_buffer && rl_line_buffer[0] != '/' && rl_line_buffer[0] != '\0' && rl_line_buffer[0] != '\n') {
chat_state_activity();
}
ui_reset_idle_time();
inp_nonblocking(TRUE);
} else {
inp_nonblocking(FALSE);
chat_state_idle();
}
if (inp_line && !_inp_slashguard_check()) {
char* ret = inp_line;
inp_line = NULL;
return ret;
} else {
return NULL;
}
}
void
inp_win_resize(void)
{
int col = getcurx(inp_win);
int wcols = getmaxx(stdscr);
// if lost cursor off screen, move contents to show it
if (col >= pad_start + wcols) {
pad_start = col - (wcols / 2);
if (pad_start < 0) {
pad_start = 0;
}
}
wbkgd(inp_win, theme_attrs(THEME_INPUT_TEXT));
_inp_win_update_virtual();
}
void
inp_nonblocking(gboolean reset)
{
gint inpblock = prefs_get_inpblock();
if (!prefs_get_boolean(PREF_INPBLOCK_DYNAMIC)) {
inp_timeout = inpblock;
return;
}
if (reset) {
inp_timeout = 0;
no_input_count = 0;
}
if (inp_timeout < inpblock) {
no_input_count++;
if (no_input_count % 10 == 0) {
inp_timeout += no_input_count;
if (inp_timeout > inpblock) {
inp_timeout = inpblock;
}
}
}
}
void
inp_close(void)
{
rl_callback_handler_remove();
delwin(inp_win);
inp_win = NULL;
fclose(discard);
discard = NULL;
}
char*
inp_get_line(void)
{
werase(inp_win);
wmove(inp_win, 0, 0);
_inp_win_update_virtual();
doupdate();
char* line = NULL;
while (!line) {
line = inp_readline();
ui_update();
}
status_bar_clear_prompt();
return line;
}
char*
inp_get_password(void)
{
werase(inp_win);
wmove(inp_win, 0, 0);
_inp_win_update_virtual();
doupdate();
char* password = NULL;
get_password = TRUE;
while (!password) {
password = inp_readline();
ui_update();
}
get_password = FALSE;
status_bar_clear_prompt();
return password;
}
void
inp_put_back(void)
{
_inp_win_update_virtual();
}
static void
_inp_win_update_virtual(void)
{
int wcols = getmaxx(stdscr);
int row = screen_inputwin_row();
if (inp_win != NULL) {
pnoutrefresh(inp_win, 0, pad_start, row, 0, row, wcols - 1);
}
}
static void
_inp_write(char* line, int offset)
{
int x;
int y __attribute__((unused));
int col = _inp_offset_to_col(line, offset);
werase(inp_win);
waddstr(inp_win, rl_display_prompt);
getyx(inp_win, y, x);
col += x;
gboolean do_spell = prefs_get_boolean(PREF_SPELLCHECK_ENABLE) && (line[0] != '/');
for (size_t i = 0; line[i] != '\0'; i++) {
char* c = &line[i];
char retc[PROF_MB_CUR_MAX] = { 0 };
size_t ch_len = mbrlen(c, MB_CUR_MAX, NULL);
if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1)) {
waddch(inp_win, ' ');
continue;
}
if (line[i] == '\n') {
c = retc;
ch_len = wctomb(retc, L'\u23ce'); /* return symbol */
if (ch_len == (size_t)-1) { /* not representable */
retc[0] = '\\';
ch_len = 1;
}
} else {
i += ch_len - 1;
}
if (do_spell) {
gunichar uc = g_utf8_get_char(c);
if (g_unichar_isalnum(uc) || uc == '\'') {
// start of a word
size_t start = i - (ch_len - 1);
size_t end = start + ch_len;
while (line[end] != '\0') {
size_t next_ch_len = mbrlen(&line[end], MB_CUR_MAX, NULL);
if (next_ch_len == (size_t)-2 || next_ch_len == (size_t)-1)
break;
gunichar next_uc = g_utf8_get_char(&line[end]);
if (!g_unichar_isalnum(next_uc) && next_uc != '\'')
break;
end += next_ch_len;
}
char* word = g_strndup(&line[start], end - start);
gboolean misspelled = spellcheck_is_misspelled(word);
g_free(word);
if (misspelled) {
wattron(inp_win, theme_attrs(THEME_INPUT_MISSPELLED));
}
// add the word
size_t word_pos = start;
while (word_pos < end) {
size_t cur_ch_len = mbrlen(&line[word_pos], MB_CUR_MAX, NULL);
waddnstr(inp_win, &line[word_pos], cur_ch_len);
word_pos += cur_ch_len;
}
if (misspelled) {
wattroff(inp_win, theme_attrs(THEME_INPUT_MISSPELLED));
}
i = end - 1;
continue;
}
}
waddnstr(inp_win, c, ch_len);
}
wmove(inp_win, 0, col);
_inp_win_handle_scroll();
_inp_win_update_virtual();
doupdate();
}
static int
_inp_edited(const wint_t ch)
{
// Use own state to be thread-safe with possible pthreads. C standard
// guarantees that initial value of the state will be zeroed buffer.
static mbstate_t mbstate;
// backspace
if (ch == 127) {
return 1;
}
// ctrl-w
if (ch == 23) {
return 1;
}
// enter
if (ch == 13) {
return 1;
}
// printable
char bytes[MB_CUR_MAX + 1];
size_t utf_len = wcrtomb(bytes, ch, &mbstate);
if (utf_len == (size_t)-1) {
return 0;
}
bytes[utf_len] = '\0';
gunichar unichar = g_utf8_get_char(bytes);
return g_unichar_isprint(unichar);
}
static int
_inp_offset_to_col(char* str, int offset)
{
int i = 0;
int col = 0;
while (i < offset && str[i] != '\0') {
gunichar uni = g_utf8_get_char(&str[i]);
size_t ch_len = mbrlen(&str[i], MB_CUR_MAX, NULL);
if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1)) {
i++;
continue;
}
i += ch_len;
col++;
if (g_unichar_iswide(uni)) {
col++;
}
}
return col;
}
static void
_inp_win_handle_scroll(void)
{
int col = getcurx(inp_win);
int wcols = getmaxx(stdscr);
if (col == 0) {
pad_start = 0;
} else if (col >= pad_start + (wcols - 1)) {
pad_start = col - (wcols / 2);
if (pad_start < 0) {
pad_start = 0;
}
} else if (col <= pad_start) {
pad_start = pad_start - (wcols / 2);
if (pad_start < 0) {
pad_start = 0;
}
}
}
static void
_inp_rl_addfuncs(void)
{
rl_add_funmap_entry("prof_win_1", _inp_rl_win_1_handler);
rl_add_funmap_entry("prof_win_2", _inp_rl_win_2_handler);
rl_add_funmap_entry("prof_win_3", _inp_rl_win_3_handler);
rl_add_funmap_entry("prof_win_4", _inp_rl_win_4_handler);
rl_add_funmap_entry("prof_win_5", _inp_rl_win_5_handler);
rl_add_funmap_entry("prof_win_6", _inp_rl_win_6_handler);
rl_add_funmap_entry("prof_win_7", _inp_rl_win_7_handler);
rl_add_funmap_entry("prof_win_8", _inp_rl_win_8_handler);
rl_add_funmap_entry("prof_win_9", _inp_rl_win_9_handler);
rl_add_funmap_entry("prof_win_0", _inp_rl_win_0_handler);
rl_add_funmap_entry("prof_win_11", _inp_rl_win_11_handler);
rl_add_funmap_entry("prof_win_12", _inp_rl_win_12_handler);
rl_add_funmap_entry("prof_win_13", _inp_rl_win_13_handler);
rl_add_funmap_entry("prof_win_14", _inp_rl_win_14_handler);
rl_add_funmap_entry("prof_win_15", _inp_rl_win_15_handler);
rl_add_funmap_entry("prof_win_16", _inp_rl_win_16_handler);
rl_add_funmap_entry("prof_win_17", _inp_rl_win_17_handler);
rl_add_funmap_entry("prof_win_18", _inp_rl_win_18_handler);
rl_add_funmap_entry("prof_win_19", _inp_rl_win_19_handler);
rl_add_funmap_entry("prof_win_20", _inp_rl_win_20_handler);
rl_add_funmap_entry("prof_win_prev", _inp_rl_win_prev_handler);
rl_add_funmap_entry("prof_win_next", _inp_rl_win_next_handler);
rl_add_funmap_entry("prof_win_next_unread", _inp_rl_win_next_unread_handler);
rl_add_funmap_entry("prof_win_set_attention", _inp_rl_win_attention_handler);
rl_add_funmap_entry("prof_win_attention_next", _inp_rl_win_attention_next_handler);
rl_add_funmap_entry("prof_win_pageup", _inp_rl_win_pageup_handler);
rl_add_funmap_entry("prof_win_pagedown", _inp_rl_win_pagedown_handler);
rl_add_funmap_entry("prof_subwin_pageup", _inp_rl_subwin_pageup_handler);
rl_add_funmap_entry("prof_subwin_pagedown", _inp_rl_subwin_pagedown_handler);
rl_add_funmap_entry("prof_complete_next", _inp_rl_tab_handler);
rl_add_funmap_entry("prof_complete_prev", _inp_rl_shift_tab_handler);
rl_add_funmap_entry("prof_win_clear", _inp_rl_win_clear_handler);
rl_add_funmap_entry("prof_win_close", _inp_rl_win_close_handler);
rl_add_funmap_entry("prof_send_to_editor", _inp_rl_send_to_editor);
rl_add_funmap_entry("prof_cut_to_history", _inp_rl_down_arrow_handler);
rl_add_funmap_entry("prof_print_newline_symbol", _inp_rl_print_newline_symbol);
}
// Readline callbacks
static int
_inp_rl_startup_hook(void)
{
rl_bind_keyseq("\\e1", _inp_rl_win_1_handler);
rl_bind_keyseq("\\e2", _inp_rl_win_2_handler);
rl_bind_keyseq("\\e3", _inp_rl_win_3_handler);
rl_bind_keyseq("\\e4", _inp_rl_win_4_handler);
rl_bind_keyseq("\\e5", _inp_rl_win_5_handler);
rl_bind_keyseq("\\e6", _inp_rl_win_6_handler);
rl_bind_keyseq("\\e7", _inp_rl_win_7_handler);
rl_bind_keyseq("\\e8", _inp_rl_win_8_handler);
rl_bind_keyseq("\\e9", _inp_rl_win_9_handler);
rl_bind_keyseq("\\e0", _inp_rl_win_0_handler);
rl_bind_keyseq("\\eq", _inp_rl_win_11_handler);
rl_bind_keyseq("\\ew", _inp_rl_win_12_handler);
rl_bind_keyseq("\\ee", _inp_rl_win_13_handler);
rl_bind_keyseq("\\er", _inp_rl_win_14_handler);
rl_bind_keyseq("\\et", _inp_rl_win_15_handler);
rl_bind_keyseq("\\ey", _inp_rl_win_16_handler);
rl_bind_keyseq("\\eu", _inp_rl_win_17_handler);
rl_bind_keyseq("\\ei", _inp_rl_win_18_handler);
rl_bind_keyseq("\\eo", _inp_rl_win_19_handler);
rl_bind_keyseq("\\ep", _inp_rl_win_20_handler);
rl_bind_keyseq("\\eOP", _inp_rl_win_1_handler);
rl_bind_keyseq("\\eOQ", _inp_rl_win_2_handler);
rl_bind_keyseq("\\eOR", _inp_rl_win_3_handler);
rl_bind_keyseq("\\eOS", _inp_rl_win_4_handler);
rl_bind_keyseq("\\e[15~", _inp_rl_win_5_handler);
rl_bind_keyseq("\\e[17~", _inp_rl_win_6_handler);
rl_bind_keyseq("\\e[18~", _inp_rl_win_7_handler);
rl_bind_keyseq("\\e[19~", _inp_rl_win_8_handler);
rl_bind_keyseq("\\e[20~", _inp_rl_win_9_handler);
rl_bind_keyseq("\\e[21~", _inp_rl_win_0_handler);
rl_bind_keyseq("\\e[1;9D", _inp_rl_win_prev_handler);
rl_bind_keyseq("\\e[1;3D", _inp_rl_win_prev_handler);
rl_bind_keyseq("\\e\\e[D", _inp_rl_win_prev_handler);
rl_bind_keyseq("\\e\\eOD", _inp_rl_win_prev_handler);
rl_bind_keyseq("\\e[1;9C", _inp_rl_win_next_handler);
rl_bind_keyseq("\\e[1;3C", _inp_rl_win_next_handler);
rl_bind_keyseq("\\e\\e[C", _inp_rl_win_next_handler);
rl_bind_keyseq("\\e\\eOC", _inp_rl_win_next_handler);
rl_bind_keyseq("\\ea", _inp_rl_win_next_unread_handler);
rl_bind_keyseq("\\ev", _inp_rl_win_attention_handler);
rl_bind_keyseq("\\em", _inp_rl_win_attention_next_handler);
rl_bind_keyseq("\\ec", _inp_rl_send_to_editor);
rl_bind_keyseq("\\e\\e[5~", _inp_rl_subwin_pageup_handler);
rl_bind_keyseq("\\e[5;3~", _inp_rl_subwin_pageup_handler);
rl_bind_keyseq("\\e\\eOy", _inp_rl_subwin_pageup_handler);
rl_bind_keyseq("\\e\\e[6~", _inp_rl_subwin_pagedown_handler);
rl_bind_keyseq("\\e[6;3~", _inp_rl_subwin_pagedown_handler);
rl_bind_keyseq("\\e\\eOs", _inp_rl_subwin_pagedown_handler);
rl_bind_keyseq("\\e[5~", _inp_rl_win_pageup_handler);
rl_bind_keyseq("\\eOy", _inp_rl_win_pageup_handler);
rl_bind_keyseq("\\e[6~", _inp_rl_win_pagedown_handler);
rl_bind_keyseq("\\eOs", _inp_rl_win_pagedown_handler);
rl_bind_key('\t', _inp_rl_tab_handler);
rl_bind_keyseq("\\e[Z", _inp_rl_shift_tab_handler);
rl_bind_keyseq("\\e[1;3A", _inp_rl_scroll_handler); // alt + scroll/arrow up
rl_bind_keyseq("\\e[1;3B", _inp_rl_scroll_handler); // alt + scroll/arrow down
rl_bind_keyseq("\\e[1;5B", _inp_rl_down_arrow_handler); // ctrl+arrow down
rl_bind_keyseq("\\eOb", _inp_rl_down_arrow_handler);
rl_bind_keyseq("\\e\\C-\r", _inp_rl_print_newline_symbol); // alt+enter
// unbind unwanted mappings
rl_bind_keyseq("\\e=", NULL);
// disable readline completion
rl_variable_bind("disable-completion", "on");
// check for and load ~/.config/profanity/inputrc
auto_gchar gchar* inputrc = files_get_inputrc_file();
if (inputrc) {
rl_read_init_file(inputrc);
}
return 0;
}
static void
_inp_rl_linehandler(char* line)
{
inp_line = line;
if (!line || !*line || get_password) {
return;
}
HISTORY_STATE* history = history_get_history_state();
HIST_ENTRY* last = history->length > 0 ? history->entries[history->length - 1] : NULL;
if (last == NULL || strcmp(last->line, line) != 0) {
add_history(line);
}
free(history);
}
static gboolean shift_tab = FALSE;
static int
_inp_rl_getc(FILE* stream)
{
int ch = rl_getc(stream);
// 27, 91, 90 = Shift tab
if (ch == 27) {
shift_tab = TRUE;
return ch;
}
if (shift_tab && ch == 91) {
return ch;
}
if (shift_tab && ch == 90) {
return ch;
}
shift_tab = FALSE;
if (_inp_edited(ch)) {
ProfWin* window = wins_get_current();
cmd_ac_reset(window);
if ((window->type == WIN_CHAT || window->type == WIN_MUC || window->type == WIN_PRIVATE) && window->quotes_ac != NULL) {
autocomplete_reset(window->quotes_ac);
}
}
return ch;
}
static void
_inp_redisplay(void)
{
if (!get_password) {
_inp_write(rl_line_buffer, rl_point);
}
}
static int
_inp_rl_win_clear_handler(int count, int key)
{
ProfWin* win = wins_get_current();
win_clear(win);
return 0;
}
static int
_inp_rl_win_close_handler(int count, int key)
{
ProfWin* win = wins_get_current();
gchar* args = 0;
cmd_close(win, 0, &args);
return 0;
}
static int
_inp_rl_tab_com_handler(int count, int key, gboolean previous)
{
if (rl_point != rl_end || !rl_line_buffer) {
return 0;
}
if (strncmp(rl_line_buffer, "/", 1) == 0) {
ProfWin* window = wins_get_current();
auto_char char* result = cmd_ac_complete(window, rl_line_buffer, previous);
if (result) {
rl_replace_line(result, 1);
rl_point = rl_end;
return 0;
}
}
if (strncmp(rl_line_buffer, ">", 1) == 0) {
ProfWin* window = wins_get_current();
auto_char char* result = win_quote_autocomplete(window, rl_line_buffer, previous);
if (result) {
rl_replace_line(result, 1);
rl_point = rl_end;
return 0;
}
}
ProfWin* current = wins_get_current();
if (current->type == WIN_MUC) {
auto_char char* result = muc_autocomplete(current, rl_line_buffer, previous);
if (result) {
rl_replace_line(result, 1);
rl_point = rl_end;
}
}
return 0;
}
static int
_inp_rl_tab_handler(int count, int key)
{
return _inp_rl_tab_com_handler(count, key, FALSE);
}
static int
_inp_rl_shift_tab_handler(int count, int key)
{
return _inp_rl_tab_com_handler(count, key, TRUE);
}
static void
_go_to_win(int i)
{
ProfWin* window = wins_get_by_num(i);
if (window) {
ui_focus_win(window);
}
}
static int
_inp_rl_win_1_handler(int count, int key)
{
_go_to_win(1);
return 0;
}
static int
_inp_rl_win_2_handler(int count, int key)
{
_go_to_win(2);
return 0;
}
static int
_inp_rl_win_3_handler(int count, int key)
{
_go_to_win(3);
return 0;
}
static int
_inp_rl_win_4_handler(int count, int key)
{
_go_to_win(4);
return 0;
}
static int
_inp_rl_win_5_handler(int count, int key)
{
_go_to_win(5);
return 0;
}
static int
_inp_rl_win_6_handler(int count, int key)
{
_go_to_win(6);
return 0;
}
static int
_inp_rl_win_7_handler(int count, int key)
{
_go_to_win(7);
return 0;
}
static int
_inp_rl_win_8_handler(int count, int key)
{
_go_to_win(8);
return 0;
}
static int
_inp_rl_win_9_handler(int count, int key)
{
_go_to_win(9);
return 0;
}
static int
_inp_rl_win_0_handler(int count, int key)
{
_go_to_win(0);
return 0;
}
static int
_inp_rl_win_11_handler(int count, int key)
{
_go_to_win(11);
return 0;
}
static int
_inp_rl_win_12_handler(int count, int key)
{
_go_to_win(12);
return 0;
}
static int
_inp_rl_win_13_handler(int count, int key)
{
_go_to_win(13);
return 0;
}
static int
_inp_rl_win_14_handler(int count, int key)
{
_go_to_win(14);
return 0;
}
static int
_inp_rl_win_15_handler(int count, int key)
{
_go_to_win(15);
return 0;
}
static int
_inp_rl_win_16_handler(int count, int key)
{
_go_to_win(16);
return 0;
}
static int
_inp_rl_win_17_handler(int count, int key)
{
_go_to_win(17);
return 0;
}
static int
_inp_rl_win_18_handler(int count, int key)
{
_go_to_win(18);
return 0;
}
static int
_inp_rl_win_19_handler(int count, int key)
{
_go_to_win(19);
return 0;
}
static int
_inp_rl_win_20_handler(int count, int key)
{
_go_to_win(20);
return 0;
}
static int
_inp_rl_win_prev_handler(int count, int key)
{
ProfWin* window = wins_get_previous();
if (window) {
ui_focus_win(window);
}
return 0;
}
static int
_inp_rl_win_next_handler(int count, int key)
{
ProfWin* window = wins_get_next();
if (window) {
ui_focus_win(window);
}
return 0;
}
static int
_inp_rl_win_next_unread_handler(int count, int key)
{
ProfWin* window = wins_get_next_unread();
if (window) {
ui_focus_win(window);
}
return 0;
}
static int
_inp_rl_win_attention_handler(int count, int key)
{
ProfWin* current = wins_get_current();
if (current) {
gboolean attention = win_toggle_attention(current);
if (attention) {
win_println(current, THEME_DEFAULT, "!", "Attention flag has been activated");
} else {
win_println(current, THEME_DEFAULT, "!", "Attention flag has been deactivated");
}
win_redraw(current);
}
return 0;
}
static int
_inp_rl_win_attention_next_handler(int count, int key)
{
ProfWin* window = wins_get_next_attention();
if (window) {
ui_focus_win(window);
}
return 0;
}
static int
_inp_rl_win_pageup_handler(int count, int key)
{
ProfWin* current = wins_get_current();
win_page_up(current, 0);
return 0;
}
static int
_inp_rl_win_pagedown_handler(int count, int key)
{
ProfWin* current = wins_get_current();
win_page_down(current, 0);
return 0;
}
static int
_inp_rl_subwin_pageup_handler(int count, int key)
{
ProfWin* current = wins_get_current();
win_sub_page_up(current);
return 0;
}
static int
_inp_rl_subwin_pagedown_handler(int count, int key)
{
ProfWin* current = wins_get_current();
win_sub_page_down(current);
return 0;
}
static int
_inp_rl_scroll_handler(int count, int key)
{
ProfWin* window = wins_get_current();
if (key == 'B') {
// mouse wheel down
win_page_down(window, 4);
} else if (key == 'A') {
// mouse wheel up
win_page_up(window, 4);
}
return 0;
}
static int
_inp_rl_down_arrow_handler(int count, int key)
{
add_history(rl_line_buffer);
using_history();
rl_replace_line("", 0);
rl_redisplay();
return 0;
}
static void
_editor_finished_cb(gchar* message, void* user_data)
{
if (message) {
rl_replace_line(message, 0);
rl_point = rl_end;
rl_forced_update_display();
}
}
static int
_inp_rl_send_to_editor(int count, int key)
{
if (!rl_line_buffer) {
return 0;
}
launch_editor(rl_line_buffer, _editor_finished_cb, NULL);
return 0;
}
static int
_inp_rl_print_newline_symbol(int count, int key)
{
rl_insert_text("\n");
return 0;
}