From 7d52612b729a6037bc6c085d9eeb9d8be2dcc207 Mon Sep 17 00:00:00 2001 From: Florian Stinglmayr Date: Thu, 27 Jun 2019 20:25:30 +0200 Subject: [PATCH] abandon input libraries and do it ourselves --- CMakeLists.txt | 2 - cmake/FindReadline.cmake | 49 ------- ncdc/CMakeLists.txt | 12 +- ncdc/include/ncdc/input.h | 40 ++++++ ncdc/include/ncdc/mainwindow.h | 6 +- ncdc/include/ncdc/ncdc.h | 8 +- ncdc/src/emacs.c | 11 ++ ncdc/src/input.c | 151 ++++++++++++++++++++ ncdc/src/mainwindow.c | 242 +++++++++++++++------------------ ncdc/src/ncdc.c | 18 ++- ncdc/src/util.c | 39 ++++++ 11 files changed, 376 insertions(+), 202 deletions(-) delete mode 100644 cmake/FindReadline.cmake create mode 100644 ncdc/include/ncdc/input.h create mode 100644 ncdc/src/emacs.c create mode 100644 ncdc/src/input.c create mode 100644 ncdc/src/util.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3068991..628e905 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,5 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.0) -LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - FIND_PACKAGE(PkgConfig) FIND_PACKAGE(Threads) diff --git a/cmake/FindReadline.cmake b/cmake/FindReadline.cmake deleted file mode 100644 index 6708ba4..0000000 --- a/cmake/FindReadline.cmake +++ /dev/null @@ -1,49 +0,0 @@ -# Code copied from sethhall@github -# -# - Try to find readline include dirs and libraries -# -# Usage of this module as follows: -# -# find_package(Readline) -# -# Variables used by this module, they can change the default behaviour and need -# to be set before calling find_package: -# -# Readline_ROOT_DIR Set this variable to the root installation of -# readline if the module has problems finding the -# proper installation path. -# -# Variables defined by this module: -# -# READLINE_FOUND System has readline, include and lib dirs found -# Readline_INCLUDE_DIR The readline include directories. -# Readline_LIBRARY The readline library. - -find_path(Readline_ROOT_DIR - NAMES include/readline/readline.h -) - -find_path(Readline_INCLUDE_DIR - NAMES readline/readline.h - HINTS ${Readline_ROOT_DIR}/include -) - -find_library(Readline_LIBRARY - NAMES readline - HINTS ${Readline_ROOT_DIR}/lib -) - -if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) - set(READLINE_FOUND TRUE) -else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) - FIND_LIBRARY(Readline_LIBRARY NAMES readline) - include(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY ) - MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY) -endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) - -mark_as_advanced( - Readline_ROOT_DIR - Readline_INCLUDE_DIR - Readline_LIBRARY -) diff --git a/ncdc/CMakeLists.txt b/ncdc/CMakeLists.txt index fd42499..2f7e921 100644 --- a/ncdc/CMakeLists.txt +++ b/ncdc/CMakeLists.txt @@ -1,16 +1,20 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.0) -PKG_CHECK_MODULES(NCURSES REQUIRED ncurses) +PKG_CHECK_MODULES(NCURSES REQUIRED ncursesw) PKG_CHECK_MODULES(PANEL REQUIRED panel) -FIND_PACKAGE(Readline) +PKG_CHECK_MODULES(EDIT REQUIRED libedit) SET(TARGET "ncdc") SET(SOURCES + "include/ncdc/input.h" "include/ncdc/mainwindow.h" "include/ncdc/ncdc.h" + "src/emacs.c" + "src/input.c" "src/mainwindow.c" "src/ncdc.c" + "src/util.c" ) INCLUDE_DIRECTORIES( @@ -22,7 +26,7 @@ INCLUDE_DIRECTORIES( ${GLIB2_INCLUDE_DIRS} ${NCURSES_INCLUDE_DIRS} ${PANEL_INCLUDE_DIRS} - ${Readline_INCLUDE_DIR} + ${EDIT_INCLUDE_DIRS} ) ADD_EXECUTABLE(${TARGET} ${SOURCES}) @@ -31,5 +35,5 @@ TARGET_LINK_LIBRARIES(${TARGET} ${GLIB2_LIBRARIES} ${NCURSES_LIBRARIES} ${PANEL_LIBRARIES} - ${Readline_LIBRARY} + ${EDIT_LIBRARIES} ) diff --git a/ncdc/include/ncdc/input.h b/ncdc/include/ncdc/input.h new file mode 100644 index 0000000..766956d --- /dev/null +++ b/ncdc/include/ncdc/input.h @@ -0,0 +1,40 @@ +#ifndef NCDC_INPUT_H +#define NCDC_INPUT_H + +#include + +struct ncdc_input_; +typedef struct ncdc_input_ *ncdc_input_t; + +typedef void (*ncdc_keybinding_t)(ncdc_input_t p); + +typedef bool (*ncdc_input_callback_t)(ncdc_input_t p, wchar_t const *str, + size_t len, void *data); + +typedef struct { + char const *key; + char const *name; + ncdc_keybinding_t handler; +} ncdc_input_keybinding_t; + +#define NCDC_BINDING(k, n, f) { k, n, f } + +extern ncdc_input_keybinding_t emacs[]; + +ncdc_input_t ncdc_input_new(void); + +void ncdc_input_feed(ncdc_input_t input, wchar_t c); +int ncdc_input_cursor(ncdc_input_t input); +char const *ncdc_input_buffer(ncdc_input_t input); +void ncdc_input_draw(ncdc_input_t input, WINDOW *win); + +void ncdc_input_set_callback(ncdc_input_t i, ncdc_input_callback_t c, void *a); + +/* keybinding functions + */ +void ncdc_input_backward(ncdc_input_t i); +void ncdc_input_forward(ncdc_input_t i); +void ncdc_input_delete(ncdc_input_t input); +void ncdc_input_delete_backward(ncdc_input_t input); + +#endif diff --git a/ncdc/include/ncdc/mainwindow.h b/ncdc/include/ncdc/mainwindow.h index 16a44af..f86ffd4 100644 --- a/ncdc/include/ncdc/mainwindow.h +++ b/ncdc/include/ncdc/mainwindow.h @@ -6,9 +6,9 @@ struct ncdc_mainwindow_; typedef struct ncdc_mainwindow_ *ncdc_mainwindow_t; -bool ncdc_mainwindow_init(void); +ncdc_mainwindow_t ncdc_mainwindow_new(void); -void ncdc_mainwindow_feed(int ch); -void ncdc_mainwindow_refresh(void); +void ncdc_mainwindow_refresh(ncdc_mainwindow_t n); +void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n); #endif diff --git a/ncdc/include/ncdc/ncdc.h b/ncdc/include/ncdc/ncdc.h index 9395f2d..eb656ae 100644 --- a/ncdc/include/ncdc/ncdc.h +++ b/ncdc/include/ncdc/ncdc.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -23,8 +24,8 @@ #include #include -#include -#include + +#include #include #include @@ -35,4 +36,7 @@ extern char *ncdc_private_dir; +int strwidth(char const *string); +char *read_char(FILE *stream); + #endif diff --git a/ncdc/src/emacs.c b/ncdc/src/emacs.c new file mode 100644 index 0000000..334a9a0 --- /dev/null +++ b/ncdc/src/emacs.c @@ -0,0 +1,11 @@ +#include + +ncdc_input_keybinding_t emacs[] = { + NCDC_BINDING("KEY_RIGHT", "forward", ncdc_input_forward), + NCDC_BINDING("KEY_LEFT", "backward", ncdc_input_backward), + NCDC_BINDING("^F", "forward", ncdc_input_forward), + NCDC_BINDING("^B", "backward", ncdc_input_backward), + NCDC_BINDING("^D", "delete", ncdc_input_delete), + NCDC_BINDING("KEY_BACKSPACE", "delete", ncdc_input_delete_backward), + NCDC_BINDING(NULL, NULL, NULL) +}; diff --git a/ncdc/src/input.c b/ncdc/src/input.c new file mode 100644 index 0000000..c7030ab --- /dev/null +++ b/ncdc/src/input.c @@ -0,0 +1,151 @@ +#include +#include + +struct ncdc_input_ +{ + dc_refable_t ref; + + GArray *buffer; + int cursor; + ncdc_input_keybinding_t *keys; + + ncdc_input_callback_t callback; + void *callback_arg; +}; + +static void ncdc_input_enter(ncdc_input_t p); + +static void ncdc_input_free(ncdc_input_t p) +{ + g_array_unref(p->buffer); + + free(p); +} + +ncdc_input_t ncdc_input_new(void) +{ + ncdc_input_t p = calloc(1, sizeof(struct ncdc_input_)); + return_if_true(p == NULL, NULL); + + p->ref.cleanup = (dc_cleanup_t)ncdc_input_free; + + p->buffer = g_array_new(TRUE, TRUE, sizeof(wchar_t)); + p->cursor = 0; + + p->keys = emacs; + + return p; +} + +static ncdc_keybinding_t has_binding(ncdc_input_t in, wchar_t key) +{ + char const *k = keyname(key); + size_t i = 0; + + for (; in->keys[i].name != NULL; i++) { + if (strcmp(k, in->keys[i].key) == 0) { + return in->keys[i].handler; + } + } + + return NULL; +} + +void ncdc_input_feed(ncdc_input_t input, wchar_t c) +{ + return_if_true(input == NULL,); + ncdc_keybinding_t handler = NULL; + + if (c == '\r') { + ncdc_input_enter(input); + } else if ((handler = has_binding(input, c)) != NULL) { + handler(input); + } else if (iswprint(c)) { + g_array_insert_vals(input->buffer, input->cursor, &c, 1); + input->cursor += wcswidth(&c, 1); + } +} + +void ncdc_input_set_callback(ncdc_input_t i, ncdc_input_callback_t c, void *a) +{ + return_if_true(i == NULL,); + + i->callback = c; + i->callback_arg = a; +} + +int ncdc_input_cursor(ncdc_input_t input) +{ + return_if_true(input == NULL, 0); + return input->cursor; +} + +char const *ncdc_input_buffer(ncdc_input_t input) +{ + return_if_true(input == NULL, NULL); + return (char const *)input->buffer->data; +} + +void ncdc_input_draw(ncdc_input_t input, WINDOW *win) +{ + werase(win); + mvwaddwstr(win, 0, 0, (wchar_t const *)input->buffer->data); + wmove(win, 0, input->cursor); + wrefresh(win); +} + +static void ncdc_input_enter(ncdc_input_t input) +{ + if (input->buffer->len == 0) { + return; + } + + if (input->callback != NULL) { + bool ret = false; + + ret = input->callback(input, + (wchar_t const *)input->buffer->data, + input->buffer->len, + input->callback_arg + ); + if (ret) { + /* TODO: add to history + */ + } + } + + g_array_remove_range(input->buffer, 0, input->buffer->len); + input->cursor = 0; +} + +void ncdc_input_delete(ncdc_input_t input) +{ + return_if_true(input->cursor == input->buffer->len,); + g_array_remove_index(input->buffer, input->cursor); +} + +void ncdc_input_delete_backward(ncdc_input_t input) +{ + return_if_true(input->cursor == 0,); + + g_array_remove_index(input->buffer, input->cursor-1); + --input->cursor; +} + +void ncdc_input_backward(ncdc_input_t input) +{ + return_if_true(input->cursor == 0,); + + wchar_t c = g_array_index(input->buffer, wchar_t, input->cursor-1); + int len = wcswidth(&c, 1); + input->cursor -= len; +} + +void ncdc_input_forward(ncdc_input_t input) +{ + return_if_true(input->cursor >= input->buffer->len,); + + wchar_t c = g_array_index(input->buffer, wchar_t, input->cursor); + int len = wcswidth(&c, 1); + input->cursor += len; +} diff --git a/ncdc/src/mainwindow.c b/ncdc/src/mainwindow.c index b923cb0..8a6edb3 100644 --- a/ncdc/src/mainwindow.c +++ b/ncdc/src/mainwindow.c @@ -1,4 +1,5 @@ #include +#include #include typedef enum { @@ -28,141 +29,105 @@ struct ncdc_mainwindow_ int input_h; int input_y; int input_x; + int input_curs_x; - char *cmd; - int cin; - bool cin_ready; + WINDOW *sep1; + WINDOW *sep2; + + ncdc_input_t in; int focus; }; -static ncdc_mainwindow_t mainwin = NULL; +static void ncdc_mainwindow_resize(ncdc_mainwindow_t n); +static void ncdc_mainwindow_update_focus(ncdc_mainwindow_t n); -static void mainwin_resize(void); -static void mainwin_update_focus(void); -static void mainwin_command(char *s); - -static int readline_input_avail(void) +static void ncdc_mainwindow_free(ncdc_mainwindow_t n) { - return mainwin->cin_ready; + return_if_true(n == NULL,); + + delwin(n->guilds); + delwin(n->chat); + delwin(n->input); + + delwin(n->sep1); + delwin(n->sep2); + + dc_unref(n->in); + + free(n); } -static int readline_getc(FILE *dummy) +ncdc_mainwindow_t ncdc_mainwindow_new(void) { - mainwin->cin_ready = false; - return mainwin->cin; + ncdc_mainwindow_t ptr = calloc(1, sizeof(struct ncdc_mainwindow_)); + return_if_true(ptr == NULL, NULL); + + ptr->ref.cleanup = (dc_cleanup_t)ncdc_mainwindow_free; + + ptr->in = ncdc_input_new(); + + ptr->guilds = newwin(5, 5, 1, 1); + ptr->chat = newwin(5, 5, 4, 4); + + ptr->input = newwin(5, 5, 8, 8); + keypad(ptr->input, TRUE); + + ptr->sep1 = newwin(5, 5, 10, 10); + ptr->sep2 = newwin(5, 5, 12, 12); + + ncdc_mainwindow_resize(ptr); + + ptr->focus = FOCUS_INPUT; + ncdc_mainwindow_update_focus(ptr); + + return ptr; } -static int measure(char const *string) +static void ncdc_mainwindow_resize(ncdc_mainwindow_t n) { - size_t needed = mbstowcs(NULL, string, 0) + 1; - wchar_t *wcstring = calloc(needed, sizeof(wchar_t)); - size_t ret = 0; + n->guilds_h = LINES - 2; + n->guilds_w = (COLS / 5); + n->guilds_y = 0; + n->guilds_x = 0; - return_if_true(wcstring == NULL, -1); + wresize(n->guilds, n->guilds_h, n->guilds_w); + mvwin(n->guilds, n->guilds_y, n->guilds_x); + wnoutrefresh(n->guilds); - ret = mbstowcs(wcstring, string, needed); - if (ret == (size_t)-1) { - free(wcstring); - return -1; - } + n->input_h = 1; + n->input_w = COLS; + n->input_y = LINES - n->input_h; + n->input_x = 0; - int width = wcswidth(wcstring, needed); - free(wcstring); + wresize(n->input, n->input_h, n->input_w); + mvwin(n->input, n->input_y, n->input_x); + wnoutrefresh(n->input); - return width; + wresize(n->sep1, 1, COLS); + mvwin(n->sep1, LINES - 2, 0); + wbkgd(n->sep1, COLOR_PAIR(1)); + wnoutrefresh(n->sep1); + + n->chat_h = LINES - n->input_h - 1; + n->chat_w = COLS - n->guilds_w - 1; + n->chat_y = 0; + n->chat_x = n->guilds_w + 1; + + wresize(n->chat, n->chat_h, n->chat_w); + mvwin(n->chat, n->chat_y, n->chat_x); + wnoutrefresh(n->chat); + + wresize(n->sep2, LINES - 2, 1); + mvwin(n->sep2, 0, n->guilds_w + 1); + wnoutrefresh(n->sep2); + + ncdc_mainwindow_update_focus(n); } -static void readline_redisplay(void) +static void ncdc_mainwindow_update_focus(ncdc_mainwindow_t n) { - int len = 0; - char *line = NULL; - int diff = 0; - - asprintf(&line, "%s%s", - (rl_display_prompt != NULL ? rl_display_prompt : ""), - (rl_line_buffer != NULL ? rl_line_buffer : "") - ); - len = measure(line); - - diff = len - mainwin->input_w + 3; - if (diff > 0) { - memmove(line, line + diff, len - diff); - line[len-diff] = '\0'; - } - - werase(mainwin->input); - mvwprintw(mainwin->input, 1, 1, "%s", line); - free(line); - - wrefresh(mainwin->input); -} - -bool ncdc_mainwindow_init(void) -{ - mainwin = calloc(1, sizeof(struct ncdc_mainwindow_)); - return_if_true(mainwin == NULL, false); - - mainwin->guilds = newwin(5, 5, 1, 1); - mainwin->chat = newwin(5, 5, 4, 4); - mainwin->input = newwin(5, 5, 8, 8); - mainwin_resize(); - - mainwin->focus = FOCUS_INPUT; - mainwin_update_focus(); - - rl_getc_function = readline_getc; - rl_input_available_hook = readline_input_avail; - rl_redisplay_function = readline_redisplay; - rl_callback_handler_install("", mainwin_command); - - rl_catch_signals = 0; - rl_catch_sigwinch = 0; - rl_deprep_term_function = NULL; - rl_prep_term_function = NULL; - - return true; -} - -static void mainwin_resize(void) -{ - mainwin->guilds_h = LINES; - mainwin->guilds_w = (COLS / 5); - mainwin->guilds_y = 0; - mainwin->guilds_x = 0; - - wresize(mainwin->guilds, mainwin->guilds_h, mainwin->guilds_w); - mvwin(mainwin->guilds, mainwin->guilds_y, mainwin->guilds_x); - wnoutrefresh(mainwin->guilds); - - mainwin->input_h = 3; - mainwin->input_w = COLS - mainwin->guilds_w; - mainwin->input_y = LINES - mainwin->input_h; - mainwin->input_x = mainwin->guilds_w; - - wresize(mainwin->input, mainwin->input_h, mainwin->input_w); - mvwin(mainwin->input, mainwin->input_y, mainwin->input_x); - wnoutrefresh(mainwin->input); - - mainwin->chat_h = LINES - mainwin->input_h; - mainwin->chat_w = COLS - mainwin->guilds_w; - mainwin->chat_y = 0; - mainwin->chat_x = mainwin->guilds_w; - - wresize(mainwin->chat, mainwin->chat_h, mainwin->chat_w); - mvwin(mainwin->chat, mainwin->chat_y, mainwin->chat_x); - wnoutrefresh(mainwin->chat); -} - -static void mainwin_command(char *s) -{ - free(mainwin->cmd); - mainwin->cmd = s; -} - -static void mainwin_update_focus(void) -{ - switch (mainwin->focus) { + switch (n->focus) { case FOCUS_GUILDS: { } break; @@ -173,41 +138,48 @@ static void mainwin_update_focus(void) case FOCUS_INPUT: { - int x = 1, y = 1; - wmove(mainwin->input, y, x); + wmove(n->input, 0, ncdc_input_cursor(n->in)); + wrefresh(n->input); } break; } } -void ncdc_mainwindow_feed(int ch) +void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n) { - switch (ch) { - case KEY_RESIZE: mainwin_resize(); break; - } - - switch (mainwin->focus) { + switch (n->focus) { case FOCUS_INPUT: { - mainwin->cin = ch; - mainwin->cin_ready = true; - rl_callback_read_char(); + wint_t i = 0; + + if (wget_wch(n->input, &i) == ERR) { + return; + } + + if (i == KEY_RESIZE) { + ncdc_mainwindow_resize(n); + } else { + ncdc_input_feed(n->in, (wchar_t)i); + } } break; } } -void ncdc_mainwindow_refresh(void) +void ncdc_mainwindow_refresh(ncdc_mainwindow_t n) { - /* move windows - */ - box(mainwin->guilds, 0, 0); - box(mainwin->chat, 0, 0); - box(mainwin->input, 0, 0); + wnoutrefresh(n->guilds); + wnoutrefresh(n->chat); - wrefresh(mainwin->guilds); - wrefresh(mainwin->chat); - wrefresh(mainwin->input); + ncdc_input_draw(n->in, n->input); + wnoutrefresh(n->input); + + wbkgd(n->sep1, COLOR_PAIR(1)); + wnoutrefresh(n->sep1); + wbkgd(n->sep2, COLOR_PAIR(1)); + wnoutrefresh(n->sep2); + + ncdc_mainwindow_update_focus(n); doupdate(); } diff --git a/ncdc/src/ncdc.c b/ncdc/src/ncdc.c index 5e03984..b39d3e0 100644 --- a/ncdc/src/ncdc.c +++ b/ncdc/src/ncdc.c @@ -48,11 +48,8 @@ static void cleanup(void) static void stdin_handler(int sock, short what, void *data) { - int ch = 0; - if ((what & EV_READ) == EV_READ) { - ch = getch(); - ncdc_mainwindow_feed(ch); + ncdc_mainwindow_input_ready(mainwin); } } @@ -60,6 +57,8 @@ static bool init_everything(void) { evthread_use_pthreads(); + setlocale(LC_CTYPE, ""); + loop = dc_loop_new(); return_if_true(loop == NULL, false); @@ -135,29 +134,34 @@ int main(int ac, char **av) done = false; initscr(); - cbreak(); noecho(); nonl(); + keypad(stdscr, TRUE); intrflush(NULL, FALSE); if (has_colors()) { start_color(); use_default_colors(); + + init_pair(1, COLOR_WHITE, COLOR_BLUE); } - if (!ncdc_mainwindow_init()) { + mainwin = ncdc_mainwindow_new(); + if (mainwin == NULL) { fprintf(stderr, "failed to init ncurses\n"); return 3; } while (!done) { - ncdc_mainwindow_refresh(); + ncdc_mainwindow_refresh(mainwin); + doupdate(); if (!dc_loop_once(loop)) { break; } } + dc_unref(mainwin); endwin(); return 0; diff --git a/ncdc/src/util.c b/ncdc/src/util.c new file mode 100644 index 0000000..0b836b7 --- /dev/null +++ b/ncdc/src/util.c @@ -0,0 +1,39 @@ +#include + +char *read_char(FILE *stream) +{ + uint8_t str[7] = {0}; + int len = 0, i = 0; + + /* check if we need more + */ + str[0] = (uint8_t)fgetc(stream); + len = g_utf8_skip[str[0]]; + + for (i = 1; i < len; i++) { + str[i] = (uint8_t)fgetc(stream); + } + str[len] = '\0'; + + return strdup((char const *)str); +} + +int strwidth(char const *string) +{ + size_t needed = mbstowcs(NULL, string, 0) + 1; + wchar_t *wcstring = calloc(needed, sizeof(wchar_t)); + size_t ret = 0; + + return_if_true(wcstring == NULL, -1); + + ret = mbstowcs(wcstring, string, needed); + if (ret == (size_t)-1) { + free(wcstring); + return -1; + } + + int width = wcswidth(wcstring, needed); + free(wcstring); + + return width; +}