#include <pty.h>
#include <sys/ioctl.h>
#include <termios.h>
+#include <langinfo.h>
#include "madtty.h"
#define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
-static int has_default = 0;
+static int has_default, is_utf8;
enum {
C0_NUL = 0x00,
unsigned escaped : 1;
unsigned graphmode : 1;
unsigned curshid : 1;
+ unsigned curskeymode: 1;
/* geometry */
int rows, cols;
char rbuf[BUFSIZ];
char ebuf[BUFSIZ];
int rlen, elen;
+
+ /* custom escape sequence handler */
+ madtty_handler_t handler;
+ void *data;
};
typedef struct t_row_t {
static char const * const keytable[KEY_MAX+1] = {
['\n'] = "\r",
- [KEY_UP] = "\e[A",
- [KEY_DOWN] = "\e[B",
- [KEY_RIGHT] = "\e[C",
- [KEY_LEFT] = "\e[D",
+ /* for the arrow keys the CSI / SS3 sequences are not stored here
+ * because they depend on the current cursor terminal mode
+ */
+ [KEY_UP] = "A",
+ [KEY_DOWN] = "B",
+ [KEY_RIGHT] = "C",
+ [KEY_LEFT] = "D",
[KEY_BACKSPACE] = "\177",
[KEY_HOME] = "\e[1~",
[KEY_IC] = "\e[2~",
case 'l':
if (csiparam[0] == 25)
t->curshid = true;
+ if (csiparam[0] == 1) /* DECCKM: reset ANSI cursor (normal) key mode */
+ t->curskeymode = 0;
break;
case 'h':
if (csiparam[0] == 25)
t->curshid = false;
+ if (csiparam[0] == 1) /* DECCKM: set ANSI cursor (application) key mode */
+ t->curskeymode = 1;
break;
}
}
static void try_interpret_escape_seq(madtty_t *t)
{
char lastchar = t->ebuf[t->elen-1];
-
- switch (*t->ebuf) {
- case '\0':
+ if(!*t->ebuf)
return;
-
+ if(t->handler){
+ switch((*(t->handler))(t, t->ebuf)){
+ case MADTTY_HANDLER_OK:
+ goto cancel;
+ case MADTTY_HANDLER_NOTYET:
+ return;
+ }
+ }
+ switch (*t->ebuf) {
case 'M':
interpret_csi_SR(t);
cancel_escape_sequence(t);
}
}
+static void is_utf8_locale(void)
+{
+ const char *cset = nl_langinfo(CODESET) ?: "ANSI_X3.4-1968";
+ is_utf8 = !strcmp(cset, "UTF-8");
+}
+
// vt100 special graphics and line drawing
// 5f-7e standard vt100
// 40-5e rxvt extension for extra curses acs chars
-static uint16_t const vt100_0[62] = { // 41 .. 7e
+static uint16_t const vt100_utf8[62] = { // 41 .. 7e
0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47 hi mr. snowman!
0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
0, 0, 0, 0, 0, 0, 0, 0, // 50-57
0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
};
+static uint32_t vt100[62];
+
+void madtty_init_vt100_graphics(void)
+{
+ vt100['l' - 0x41] = ACS_ULCORNER;
+ vt100['m' - 0x41] = ACS_LLCORNER;
+ vt100['k' - 0x41] = ACS_URCORNER;
+ vt100['j' - 0x41] = ACS_LRCORNER;
+ vt100['u' - 0x41] = ACS_RTEE;
+ vt100['t' - 0x41] = ACS_LTEE;
+ vt100['v' - 0x41] = ACS_TTEE;
+ vt100['w' - 0x41] = ACS_BTEE;
+ vt100['q' - 0x41] = ACS_HLINE;
+ vt100['x' - 0x41] = ACS_VLINE;
+ vt100['n' - 0x41] = ACS_PLUS;
+ vt100['o' - 0x41] = ACS_S1;
+ vt100['s' - 0x41] = ACS_S9;
+ vt100['`' - 0x41] = ACS_DIAMOND;
+ vt100['a' - 0x41] = ACS_CKBOARD;
+ vt100['f' - 0x41] = ACS_DEGREE;
+ vt100['g' - 0x41] = ACS_PLMINUS;
+ vt100['~' - 0x41] = ACS_BULLET;
+#if 0 /* out of bounds */
+ vt100[',' - 0x41] = ACS_LARROW;
+ vt100['+' - 0x41] = ACS_RARROW;
+ vt100['.' - 0x41] = ACS_DARROW;
+ vt100['-' - 0x41] = ACS_UARROW;
+ vt100['0' - 0x41] = ACS_BLOCK;
+#endif
+ vt100['h' - 0x41] = ACS_BOARD;
+ vt100['i' - 0x41] = ACS_LANTERN;
+ /* these defaults were invented for ncurses */
+ vt100['p' - 0x41] = ACS_S3;
+ vt100['r' - 0x41] = ACS_S7;
+ vt100['y' - 0x41] = ACS_LEQUAL;
+ vt100['z' - 0x41] = ACS_GEQUAL;
+ vt100['{' - 0x41] = ACS_PI;
+ vt100['|' - 0x41] = ACS_NEQUAL;
+ vt100['}' - 0x41] = ACS_STERLING;
+ is_utf8_locale();
+}
+
static void madtty_putc(madtty_t *t, wchar_t wc)
{
int width = 0;
t_row_t *tmp;
if (t->graphmode) {
- if (wc >= 0x41 && wc <= 0x7e && vt100_0[wc - 0x41]) {
- wc = vt100_0[wc - 0x41];
+ if (wc >= 0x41 && wc <= 0x7e) {
+ wchar_t gc = is_utf8 ? vt100_utf8[wc - 0x41] : vt100[wc - 0x41];
+ if (gc)
+ wc = gc;
}
width = 1;
} else {
lines[row].attr = realloc(lines[row].attr, sizeof(uint16_t) * cols);
if (t->cols < cols)
t_row_set(lines + row, t->cols, cols - t->cols, 0);
+ else
+ lines[row].dirty = true;
}
t->cols = cols;
}
while (t->rows < rows) {
lines[t->rows].text = (wchar_t *)calloc(sizeof(wchar_t), cols);
lines[t->rows].attr = (uint16_t *)calloc(sizeof(uint16_t), cols);
+ t_row_set(lines + t->rows, 0, t->cols, 0);
t->rows++;
}
t->curs_row += lines - t->lines;
- t->scroll_top += lines - t->lines;
- t->scroll_bot += lines - t->lines;
- if (t->scroll_bot > lines + t->rows)
- t->scroll_bot = lines + t->rows;
+ t->scroll_top = lines;
+ t->scroll_bot = lines + rows;
t->lines = lines;
clamp_cursor_to_bounds(t);
ioctl(t->pty, TIOCSWINSZ, &ws);
free(t);
}
+void madtty_dirty(madtty_t *t)
+{
+ for (int i = 0; i < t->rows; i++)
+ t->lines[i].dirty = true;
+}
+
void madtty_draw(madtty_t *t, WINDOW *win, int srow, int scol)
{
curs_set(0);
for (int j = 0; j < t->cols; j++) {
if (!j || row->attr[j] != row->attr[j - 1])
wattrset(win, (attr_t)row->attr[j] << NCURSES_ATTR_SHIFT);
- if (row->text[j] >= 128) {
+ if (is_utf8 && row->text[j] >= 128) {
char buf[MB_CUR_MAX + 1];
int len;
return t->pty;
}
-void madtty_keypress(madtty_t *t, int keycode)
+static void term_write(madtty_t *t, const char *buf, int len)
{
- char c = (char)keycode;
- const char *buf;
- int len;
-
- if (keycode >= 0 && keycode < KEY_MAX && keytable[keycode]) {
- buf = keytable[keycode];
- len = strlen(keytable[keycode]);
- } else {
- buf = &c;
- len = 1;
- }
-
while (len > 0) {
int res = write(t->pty, buf, len);
if (res < 0 && errno != EAGAIN && errno != EINTR)
}
}
+void madtty_keypress(madtty_t *t, int keycode)
+{
+ char c = (char)keycode;
+
+ if (keycode >= 0 && keycode < KEY_MAX && keytable[keycode]) {
+ switch(keycode) {
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_RIGHT:
+ case KEY_LEFT: {
+ char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] };
+ term_write(t, keyseq, 3);
+ break;
+ }
+ default:
+ term_write(t, keytable[keycode], strlen(keytable[keycode]));
+ }
+ } else
+ term_write(t, &c, 1);
+}
+
+void madtty_keypress_sequence(madtty_t *t, const char *seq)
+{
+ int key, len = strlen(seq);
+ /* check for function keys from putty, this makes the
+ * keypad work but it's probably not the right way to
+ * do it. the sequence we look for is \eO + a character
+ * representing the number.
+ */
+ if(len == 3 && seq[0] == '\e' && seq[1] == 'O') {
+ key = seq[2] - 64;
+ if(key >= '0' && key <= '9')
+ madtty_keypress(t, key);
+ } else
+ term_write(t, seq, len);
+}
+
void madtty_init_colors(void)
{
if (COLOR_PAIRS > 64) {
bg = COLOR_BLACK;
return COLOR_PAIR((7 - fg) * 8 + bg);
}
+
+void madtty_set_handler(madtty_t *t, madtty_handler_t handler)
+{
+ t->handler = handler;
+}
+
+void madtty_set_data(madtty_t *t, void *data)
+{
+ t->data = data;
+}
+
+void *madtty_get_data(madtty_t *t)
+{
+ return t->data;
+}
+
+unsigned madtty_cursor(madtty_t *t)
+{
+ return !t->curshid;
+}
#include <sys/types.h>
-void madtty_init_colors(void);
-int madtty_color_pair(int fg, int bg);
+enum {
+ /* means escape sequence was handled */
+ MADTTY_HANDLER_OK,
+ /* means the escape sequence was not recognized yet, but
+ * there is hope that it still will once more characters
+ * arrive (i.e. it is not yet complete).
+ *
+ * The library will thus continue collecting characters
+ * and calling the handler as each character arrives until
+ * either OK or NOWAY is returned.
+ */
+ MADTTY_HANDLER_NOTYET,
+ /* means the escape sequence was not recognized, and there
+ * is no chance that it will even if more characters are
+ * added to it.
+ */
+ MADTTY_HANDLER_NOWAY
+};
typedef struct madtty_t madtty_t;
+typedef int (*madtty_handler_t)(madtty_t *, char *es);
+
+void madtty_init_colors(void);
+void madtty_init_vt100_graphics(void);
+void madtty_set_handler(madtty_t *, madtty_handler_t);
+void madtty_set_data(madtty_t *, void *);
+void *madtty_get_data(madtty_t *);
+int madtty_color_pair(int fg, int bg);
madtty_t *madtty_create(int rows, int cols);
void madtty_resize(madtty_t *, int rows, int cols);
void madtty_destroy(madtty_t *);
pid_t madtty_forkpty(madtty_t *, const char *, const char *argv[], int *pty);
int madtty_getpty(madtty_t *);
+unsigned madtty_cursor(madtty_t *t);
int madtty_process(madtty_t *);
void madtty_keypress(madtty_t *, int keycode);
+void madtty_keypress_sequence(madtty_t *, const char *seq);
+void madtty_dirty(madtty_t *t);
void madtty_draw(madtty_t *, WINDOW *win, int startrow, int startcol);
-
#endif /* MADTTY_MADTTY_H */