steps towards an ncurses API

This commit is contained in:
2019-06-26 10:48:55 +02:00
parent 523725f375
commit e9bf4d0986
12 changed files with 384 additions and 49 deletions

View File

@@ -1,8 +1,15 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
PKG_CHECK_MODULES(NCURSES REQUIRED ncurses)
PKG_CHECK_MODULES(PANEL REQUIRED panel)
FIND_PACKAGE(Readline)
SET(TARGET "ncdc")
SET(SOURCES
"include/ncdc/mainwindow.h"
"include/ncdc/ncdc.h"
"src/mainwindow.c"
"src/ncdc.c"
)
@@ -13,10 +20,16 @@ INCLUDE_DIRECTORIES(
${CURL_INCLUDE_DIRS}
${EVENT_INCLUDE_DIRS}
${GLIB2_INCLUDE_DIRS}
${NCURSES_INCLUDE_DIRS}
${PANEL_INCLUDE_DIRS}
${Readline_INCLUDE_DIR}
)
ADD_EXECUTABLE(${TARGET} ${SOURCES})
TARGET_LINK_LIBRARIES(${TARGET}
${DC_LIBRARIES}
${GLIB2_LIBRARIES}
${NCURSES_LIBRARIES}
${PANEL_LIBRARIES}
${Readline_LIBRARY}
)

View File

@@ -0,0 +1,14 @@
#ifndef NCDC_MAINWINDOW_H
#define NCDC_MAINWINDOW_H
#include <ncdc/ncdc.h>
struct ncdc_mainwindow_;
typedef struct ncdc_mainwindow_ *ncdc_mainwindow_t;
bool ncdc_mainwindow_init(void);
void ncdc_mainwindow_feed(int ch);
void ncdc_mainwindow_refresh(void);
#endif

View File

@@ -21,7 +21,14 @@
#include <event.h>
#include <event2/thread.h>
//#define DEBUG
#include <curses.h>
#include <panel.h>
#include <readline/history.h>
#include <readline/readline.h>
#include <dc/refable.h>
#include <dc/api.h>
#include <dc/loop.h>
#define return_if_true(v,r) do { if (v) return r; } while(0)
#define goto_if_true(v,l) do { if (v) goto l; } while(0)

213
ncdc/src/mainwindow.c Normal file
View File

@@ -0,0 +1,213 @@
#include <ncdc/mainwindow.h>
#include <ncdc/ncdc.h>
typedef enum {
FOCUS_GUILDS = 0,
FOCUS_CHAT,
FOCUS_INPUT,
} focus_t;
struct ncdc_mainwindow_
{
dc_refable_t ref;
WINDOW *guilds;
int guilds_w;
int guilds_h;
int guilds_y;
int guilds_x;
WINDOW *chat;
int chat_h;
int chat_w;
int chat_y;
int chat_x;
WINDOW *input;
int input_w;
int input_h;
int input_y;
int input_x;
char *cmd;
int cin;
bool cin_ready;
int focus;
};
static ncdc_mainwindow_t mainwin = NULL;
static void mainwin_resize(void);
static void mainwin_update_focus(void);
static void mainwin_command(char *s);
static int readline_input_avail(void)
{
return mainwin->cin_ready;
}
static int readline_getc(FILE *dummy)
{
mainwin->cin_ready = false;
return mainwin->cin;
}
static int measure(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;
}
static void readline_redisplay(void)
{
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) {
case FOCUS_GUILDS:
{
} break;
case FOCUS_CHAT:
{
} break;
case FOCUS_INPUT:
{
int x = 1, y = 1;
wmove(mainwin->input, y, x);
} break;
}
}
void ncdc_mainwindow_feed(int ch)
{
switch (ch) {
case KEY_RESIZE: mainwin_resize(); break;
}
switch (mainwin->focus) {
case FOCUS_INPUT:
{
mainwin->cin = ch;
mainwin->cin_ready = true;
rl_callback_read_char();
} break;
}
}
void ncdc_mainwindow_refresh(void)
{
/* move windows
*/
box(mainwin->guilds, 0, 0);
box(mainwin->chat, 0, 0);
box(mainwin->input, 0, 0);
wrefresh(mainwin->guilds);
wrefresh(mainwin->chat);
wrefresh(mainwin->input);
doupdate();
}

View File

@@ -1,8 +1,5 @@
#include <ncdc/ncdc.h>
#include <dc/refable.h>
#include <dc/api.h>
#include <dc/loop.h>
#include <ncdc/mainwindow.h>
#include <stdio.h>
#include <unistd.h>
@@ -11,10 +8,13 @@
*/
struct event *stdin_ev = NULL;
/* main window
*/
ncdc_mainwindow_t mainwin = NULL;
/* we loop in a different thread
*/
static bool done = false;
static pthread_t looper;
char *dc_private_dir = NULL;
char *dc_config_file = NULL;
@@ -26,11 +26,14 @@ dc_api_t api = NULL;
static void sighandler(int sig)
{
endwin();
exit(3);
}
static void cleanup(void)
{
endwin();
if (stdin_ev != NULL) {
event_del(stdin_ev);
event_free(stdin_ev);
@@ -38,7 +41,6 @@ static void cleanup(void)
}
done = true;
pthread_join(looper, NULL);
dc_unref(api);
dc_unref(loop);
@@ -46,6 +48,12 @@ 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);
}
}
static bool init_everything(void)
@@ -79,20 +87,7 @@ static bool init_everything(void)
return true;
}
static void *loop_thread(void *arg)
{
while (!done) {
if (!dc_loop_once(loop)) {
break;
}
usleep(10 * 1000);
}
return NULL;
}
static dc_account_t account_from_config(void)
dc_account_t account_from_config(void)
{
char const *email = NULL;
char const *password = NULL;
@@ -138,25 +133,32 @@ int main(int ac, char **av)
}
done = false;
if (pthread_create(&looper, NULL, loop_thread, &done)) {
initscr();
cbreak();
noecho();
nonl();
intrflush(NULL, FALSE);
if (has_colors()) {
start_color();
use_default_colors();
}
if (!ncdc_mainwindow_init()) {
fprintf(stderr, "failed to init ncurses\n");
return 3;
}
dc_account_t a = account_from_config();
if (a == NULL) {
fprintf(stderr, "no account specified in config file; sho-sho!\n");
return 3;
while (!done) {
ncdc_mainwindow_refresh();
if (!dc_loop_once(loop)) {
break;
}
}
if (!dc_api_authenticate(api, a)) {
fprintf(stderr, "authentication failed, wrong password?\n");
return 3;
}
if (!dc_api_userinfo(api, a, a)) {
fprintf(stderr, "failed to get user information\n");
return 3;
}
endwin();
return 0;
}