New input parser via state machine.

This commit is contained in:
Nicholas Marriott 2007-09-28 22:47:22 +00:00
parent d2e035f892
commit aafee17de6
9 changed files with 1088 additions and 956 deletions

14
CHANGES
View File

@ -1,5 +1,15 @@
28 September 2007
* (mxey) Added window remaming, like "tmux rename [-s session] [-i index] name"
* (nicm) Major rewrite of input parser:
- Lose the old weirdness in favour of a state machine.
- Merge in parsing from screen.c.
- Split key parsing off into a separate file.
This is step one towards hopefully allowing a status line. It requires
that we output data as if the terminal had one line less than it really does -
a serious problem when it comes to things like scrolling. This change
consolidates all the range checking and limiting together which should make
it easier.
* (mxey) Added window remaming, like "tmux rename [-s session] [-i index] name"
27 September 2007
@ -58,5 +68,5 @@
(including mutt, emacs). No status bar yet and no key remapping or other
customisation.
$Id: CHANGES,v 1.12 2007-09-28 21:41:51 mxey Exp $
$Id: CHANGES,v 1.13 2007-09-28 22:47:21 nicm Exp $

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.6 2007-09-26 18:32:16 nicm Exp $
# $Id: Makefile,v 1.7 2007-09-28 22:47:21 nicm Exp $
.SUFFIXES: .c .o .y .h
.PHONY: clean
@ -17,8 +17,8 @@ DEBUG=
META?= \002 # C-b
SRCS= tmux.c server.c server-msg.c server-fn.c buffer.c buffer-poll.c \
xmalloc.c xmalloc-debug.c input.c screen.c window.c session.c local.c \
log.c client.c client-msg.c client-cmd.c op.c op-list.c
xmalloc.c xmalloc-debug.c input.c input-keys.c screen.c window.c \
session.c local.c log.c client.c client-msg.c client-cmd.c op.c op-list.c
YACC= yacc -d

78
input-keys.c Normal file
View File

@ -0,0 +1,78 @@
/* $Id: input-keys.c,v 1.1 2007-09-28 22:47:21 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct {
int key;
const char *data;
} input_keys[] = {
/* { KEYC_BACKSPACE, "\010" }, */
{ KEYC_DC, "\e[3~" },
{ KEYC_DOWN, "\eOB" },
{ KEYC_F1, "\eOP" },
{ KEYC_F10, "\e[21~" },
{ KEYC_F11, "\e[23~" },
{ KEYC_F12, "\e[24~" },
{ KEYC_F2, "\eOQ" },
{ KEYC_F3, "\eOR" },
{ KEYC_F4, "\eOS" },
{ KEYC_F5, "\e[15~" },
{ KEYC_F6, "\e[17~" },
{ KEYC_F7, "\e[18~" },
{ KEYC_F8, "\e[19~" },
{ KEYC_F9, "\e[20~" },
{ KEYC_HOME, "\e[1~" },
{ KEYC_IC, "\e[2~" },
{ KEYC_LEFT, "\eOD" },
{ KEYC_LL, "\e[4~" },
{ KEYC_NPAGE, "\e[6~" },
{ KEYC_PPAGE, "\e[5~" },
{ KEYC_RIGHT, "\eOC" },
{ KEYC_UP, "\eOA" }
};
#define NINPUTKEYS (sizeof input_keys / sizeof input_keys[0])
/* Translate a key code from client into an output key sequence. */
void
input_translate_key(struct buffer *b, int key)
{
u_int i;
log_debug2("writing key %d", key);
if (key != KEYC_NONE && key >= 0) {
input_store8(b, key);
return;
}
for (i = 0; i < NINPUTKEYS; i++) {
if (input_keys[i].key == key) {
log_debug2(
"found key %d: \"%s\"", key, input_keys[i].data);
buffer_write(
b, input_keys[i].data, strlen(input_keys[i].data));
return;
}
}
}

1556
input.c

File diff suppressed because it is too large Load Diff

32
local.c
View File

@ -1,4 +1,4 @@
/* $Id: local.c,v 1.7 2007-09-21 19:24:37 nicm Exp $ */
/* $Id: local.c,v 1.8 2007-09-28 22:47:21 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -310,7 +310,7 @@ local_putc(int c)
if (c < 0 || c > (int) UCHAR_MAX)
fatalx("invalid character");
if (debug_level > 1) {
if (debug_level > 2) {
f = fopen("tmux-out.log", "a+");
fprintf(f, "%c", ch);
fclose(f);
@ -416,7 +416,7 @@ local_output(struct buffer *b, size_t size)
switch (ch) {
case CODE_CURSORUP:
if (size < 2)
fatalx("underflow");
fatalx("CODE_CURSORUP underflow");
size -= 2;
ua = input_extract16(b);
if (parm_up_cursor == NULL) {
@ -427,7 +427,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_CURSORDOWN:
if (size < 2)
fatalx("underflow");
fatalx("CODE_CURSORDOWN underflow");
size -= 2;
ua = input_extract16(b);
if (parm_down_cursor == NULL) {
@ -438,7 +438,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_CURSORRIGHT:
if (size < 2)
fatalx("underflow");
fatalx("CODE_CURSORRIGHT underflow");
size -= 2;
ua = input_extract16(b);
if (parm_right_cursor == NULL) {
@ -449,7 +449,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_CURSORLEFT:
if (size < 2)
fatalx("underflow");
fatalx("CODE_CURSORLEFT underflow");
size -= 2;
ua = input_extract16(b);
if (parm_left_cursor == NULL) {
@ -460,7 +460,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_CURSORMOVE:
if (size < 4)
fatalx("underflow");
fatalx("CODE_CURSORMOVE underflow");
size -= 4;
ua = input_extract16(b);
ub = input_extract16(b);
@ -507,7 +507,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_INSERTLINE:
if (size < 2)
fatalx("underflow");
fatalx("CODE_INSERTLINE underflow");
size -= 2;
ua = input_extract16(b);
if (parm_insert_line == NULL) {
@ -518,7 +518,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_DELETELINE:
if (size < 2)
fatalx("underflow");
fatalx("CODE_DELETELINE underflow");
size -= 2;
ua = input_extract16(b);
if (parm_delete_line == NULL) {
@ -529,7 +529,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_INSERTCHARACTER:
if (size < 2)
fatalx("underflow");
fatalx("CODE_INSERTCHARACTER underflow");
size -= 2;
ua = input_extract16(b);
if (parm_ich == NULL) {
@ -540,7 +540,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_DELETECHARACTER:
if (size < 2)
fatalx("underflow");
fatalx("CODE_DELETECHARACTER underflow");
size -= 2;
ua = input_extract16(b);
if (parm_dch == NULL) {
@ -572,7 +572,7 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_SCROLLREGION:
if (size < 4)
fatalx("underflow");
fatalx("CODE_SCROLLREGION underflow");
size -= 4;
ua = input_extract16(b);
ub = input_extract16(b);
@ -630,18 +630,18 @@ local_output(struct buffer *b, size_t size)
break;
case CODE_TITLE:
if (size < 2)
fatalx("underflow");
fatalx("CODE_TITLE underflow");
size -= 2;
ua = input_extract16(b);
if (size < ua)
fatalx("underflow");
fatalx("CODE_TITLE underflow");
size -= ua;
buffer_remove(b, ua);
break;
case CODE_ATTRIBUTES:
if (size < 2)
fatalx("underflow");
fatalx("CODE_ATTRIBUTES underflow");
size -= 2;
ua = input_extract16(b);
@ -656,7 +656,7 @@ local_output(struct buffer *b, size_t size)
while (ua-- != 0) {
if (size < 2)
fatalx("underflow");
fatalx("CODE_ATTRIBUTES underflow");
size -= 2;
ub = input_extract16(b);

266
screen.c
View File

@ -1,4 +1,4 @@
/* $Id: screen.c,v 1.11 2007-09-21 19:24:37 nicm Exp $ */
/* $Id: screen.c,v 1.12 2007-09-28 22:47:21 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -35,30 +35,6 @@ void screen_make_lines(struct screen *, u_int, u_int);
void screen_move_lines(struct screen *, u_int, u_int, u_int);
void screen_fill_lines(
struct screen *, u_int, u_int, u_char, u_char, u_char);
uint16_t screen_extract(u_char *);
void screen_write_character(struct screen *, u_char);
void screen_cursor_up_scroll(struct screen *);
void screen_cursor_down_scroll(struct screen *);
void screen_scroll_region_up(struct screen *);
void screen_scroll_region_down(struct screen *);
void screen_scroll_up(struct screen *, u_int);
void screen_scroll_down(struct screen *, u_int);
void screen_fill_screen(struct screen *, u_char, u_char, u_char);
void screen_fill_line(struct screen *, u_int, u_char, u_char, u_char);
void screen_fill_end_of_screen(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_fill_end_of_line(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_fill_start_of_line(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_insert_lines(struct screen *, u_int, u_int);
void screen_delete_lines(struct screen *, u_int, u_int);
void screen_insert_characters(struct screen *, u_int, u_int, u_int);
void screen_delete_characters(struct screen *, u_int, u_int, u_int);
#define SCREEN_DEFDATA ' '
#define SCREEN_DEFATTR 0
#define SCREEN_DEFCOLR 0x88
#define screen_last_y(s) ((s)->sy - 1)
#define screen_last_x(s) ((s)->sx - 1)
@ -380,246 +356,6 @@ screen_fill_lines(
screen_fill_line(s, i, data, attr, colr);
}
/* Update screen with character. */
void
screen_character(struct screen *s, u_char ch)
{
switch (ch) {
case '\n': /* LF */
screen_cursor_down_scroll(s);
break;
case '\r': /* CR */
s->cx = 0;
break;
case '\010': /* BS */
if (s->cx > 0)
s->cx--;
break;
case '\177': /* DC */
/* XXX */
break;
default:
if (ch < ' ')
fatalx("bad control");
screen_write_character(s, ch);
break;
}
}
/* Extract 16-bit value from pointer. */
uint16_t
screen_extract(u_char *ptr)
{
uint16_t n;
memcpy(&n, ptr, sizeof n);
return (n);
}
/* Update screen with escape sequence. */
void
screen_sequence(struct screen *s, u_char *ptr)
{
uint16_t ua, ub;
ptr++;
log_debug("processing code %hhu", *ptr);
switch (*ptr++) {
case CODE_CURSORUP:
ua = screen_extract(ptr);
if (ua > s->cy)
ua = s->cy;
s->cy -= ua;
break;
case CODE_CURSORDOWN:
ua = screen_extract(ptr);
if (s->cy + ua > screen_last_y(s))
ua = screen_last_y(s) - s->cy;
s->cy += ua;
break;
case CODE_CURSORLEFT:
ua = screen_extract(ptr);
if (ua > s->cx)
ua = s->cx;
s->cx -= ua;
break;
case CODE_CURSORRIGHT:
ua = screen_extract(ptr);
if (s->cx + ua > screen_last_x(s))
ua = screen_last_x(s) - s->cx;
s->cx += ua;
break;
case CODE_CURSORMOVE:
ua = screen_extract(ptr);
ptr += 2;
ub = screen_extract(ptr);
if (ub > s->sx)
ub = s->sx;
s->cx = ub - 1;
if (ua > s->sy)
ua = s->sy;
s->cy = ua - 1;
break;
case CODE_CLEARENDOFSCREEN:
screen_fill_end_of_screen(
s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARSCREEN:
screen_fill_screen(s, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARENDOFLINE:
screen_fill_end_of_line(
s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARSTARTOFLINE:
screen_fill_start_of_line(
s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARLINE:
screen_fill_line(s, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_INSERTLINE:
ua = screen_extract(ptr);
screen_insert_lines(s, s->cy, ua);
break;
case CODE_DELETELINE:
ua = screen_extract(ptr);
screen_delete_lines(s, s->cy, ua);
break;
case CODE_INSERTCHARACTER:
ua = screen_extract(ptr);
screen_insert_characters(s, s->cx, s->cy, ua);
break;
case CODE_DELETECHARACTER:
ua = screen_extract(ptr);
screen_delete_characters(s, s->cx, s->cy, ua);
break;
case CODE_CURSORON:
s->mode |= MODE_CURSOR;
break;
case CODE_CURSOROFF:
s->mode &= ~MODE_CURSOR;
break;
case CODE_REVERSEINDEX:
screen_cursor_up_scroll(s);
break;
case CODE_SCROLLREGION:
ua = screen_extract(ptr);
ptr += 2;
ub = screen_extract(ptr);
if (ua > s->sy)
ua = s->sy;
s->ry_upper = ua - 1;
if (ub > s->sy)
ub = s->sy;
s->ry_lower = ub - 1;
break;
case CODE_INSERTOFF:
s->mode &= ~MODE_INSERT;
break;
case CODE_INSERTON:
s->mode |= MODE_INSERT;
break;
case CODE_KCURSOROFF:
s->mode &= ~MODE_KCURSOR;
break;
case CODE_KCURSORON:
s->mode |= MODE_KCURSOR;
break;
case CODE_KKEYPADOFF:
s->mode &= ~MODE_KKEYPAD;
break;
case CODE_KKEYPADON:
s->mode |= MODE_KKEYPAD;
break;
case CODE_TITLE:
ua = screen_extract(ptr);
ptr += 2;
log_debug("new title: %u:%.*s", ua, (int) ua, ptr);
if (ua > (sizeof s->title) - 1)
ua = (sizeof s->title) - 1;
memcpy(s->title, ptr, ua);
s->title[ua] = '\0';
break;
case CODE_ATTRIBUTES:
ua = screen_extract(ptr);
if (ua == 0) {
s->attr = 0;
s->colr = SCREEN_DEFCOLR;
break;
}
while (ua-- > 0) {
ptr += 2;
ub = screen_extract(ptr);
switch (ub) {
case 0:
case 10:
s->attr = 0;
s->colr = SCREEN_DEFCOLR;
break;
case 1:
s->attr |= ATTR_BRIGHT;
break;
case 2:
s->attr |= ATTR_DIM;
break;
case 3:
s->attr |= ATTR_ITALICS;
break;
case 4:
s->attr |= ATTR_UNDERSCORE;
break;
case 5:
s->attr |= ATTR_BLINK;
break;
case 7:
s->attr |= ATTR_REVERSE;
break;
case 8:
s->attr |= ATTR_HIDDEN;
break;
case 23:
s->attr &= ~ATTR_ITALICS;
break;
case 24:
s->attr &= ~ATTR_UNDERSCORE;
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
s->colr &= 0x0f;
s->colr |= (ub - 30) << 4;
break;
case 39:
s->colr &= 0x0f;
s->colr |= 0x80;
break;
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
s->colr &= 0xf0;
s->colr |= ub - 40;
break;
case 49:
s->colr &= 0xf0;
s->colr |= 0x08;
break;
}
}
}
}
/* Write a single character to the screen at the cursor and move forward. */
void
screen_write_character(struct screen *s, u_char ch)

View File

@ -1,4 +1,4 @@
/* $Id: server-msg.c,v 1.7 2007-09-28 21:41:52 mxey Exp $ */
/* $Id: server-msg.c,v 1.8 2007-09-28 22:47:21 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -389,14 +389,13 @@ server_msg_fn_rename(struct hdr *hdr, struct client *c)
char *cause;
struct window *w;
struct session *s;
if (hdr->size != sizeof data)
fatalx("bad MSG_RENAME size");
buffer_read(c->in, &data, hdr->size);
data.newname[sizeof data.newname] = '\0';
data.newname[(sizeof data.newname) - 1] = '\0';
if ((s = server_find_sessid(&data.sid, &cause)) == NULL) {
/* XXX: Send message to client */

77
tmux.h
View File

@ -1,4 +1,4 @@
/* $Id: tmux.h,v 1.20 2007-09-28 21:41:52 mxey Exp $ */
/* $Id: tmux.h,v 1.21 2007-09-28 22:47:21 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -391,6 +391,50 @@ struct screen {
int mode;
};
/* Screen default contents. */
#define SCREEN_DEFDATA ' '
#define SCREEN_DEFATTR 0
#define SCREEN_DEFCOLR 0x88
/* Input parser sequence argument. */
struct input_arg {
size_t off;
size_t len;
};
/* Input character classes. */
enum input_class {
INPUT_C0CONTROL,
INPUT_SPACE,
INPUT_INTERMEDIATE,
INPUT_PARAMETER,
INPUT_UPPERCASE,
INPUT_LOWERCASE,
INPUT_DELETE,
INPUT_C1CONTROL,
INPUT_G1DISPLAYABLE,
INPUT_SPECIAL
};
/* Input parser context. */
struct input_ctx {
u_char *buf;
size_t len;
size_t off;
struct buffer *b;
struct screen *s;
void *(*state)(u_char, enum input_class, struct input_ctx *);
size_t intoff;
size_t intlen;
size_t saved;
u_char private;
ARRAY_DECL(, struct input_arg) args;
};
/* Window structure. */
struct window {
char name[MAXNAMELEN];
@ -401,6 +445,8 @@ struct window {
u_int references;
struct input_ctx ictx;
struct screen screen;
};
ARRAY_DECL(windows, struct window *);
@ -492,8 +538,9 @@ void server_window_changed(struct client *);
void server_draw_client(struct client *, u_int, u_int);
/* input.c */
void input_key(struct buffer *, int);
size_t input_parse(u_char *, size_t, struct buffer *, struct screen *);
void input_init(struct input_ctx *, struct screen *);
void input_free(struct input_ctx *);
size_t input_parse(struct input_ctx *, u_char *, size_t, struct buffer *);
uint8_t input_extract8(struct buffer *);
uint16_t input_extract16(struct buffer *);
void input_store8(struct buffer *, uint8_t);
@ -502,12 +549,32 @@ void input_store_zero(struct buffer *, u_char);
void input_store_one(struct buffer *, u_char, uint16_t);
void input_store_two(struct buffer *, u_char, uint16_t, uint16_t);
/* input-key.c */
void input_translate_key(struct buffer *, int);
/* screen.c */
void screen_create(struct screen *, u_int, u_int);
void screen_resize(struct screen *, u_int, u_int);
void screen_draw(struct screen *, struct buffer *, u_int, u_int);
void screen_character(struct screen *, u_char);
void screen_sequence(struct screen *, u_char *);
void screen_write_character(struct screen *, u_char);
void screen_insert_lines(struct screen *, u_int, u_int);
void screen_delete_lines(struct screen *, u_int, u_int);
void screen_insert_characters(struct screen *, u_int, u_int, u_int);
void screen_delete_characters(struct screen *, u_int, u_int, u_int);
void screen_cursor_up_scroll(struct screen *);
void screen_cursor_down_scroll(struct screen *);
void screen_scroll_region_up(struct screen *);
void screen_scroll_region_down(struct screen *);
void screen_scroll_up(struct screen *, u_int);
void screen_scroll_down(struct screen *, u_int);
void screen_fill_screen(struct screen *, u_char, u_char, u_char);
void screen_fill_line(struct screen *, u_int, u_char, u_char, u_char);
void screen_fill_end_of_screen(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_fill_end_of_line(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_fill_start_of_line(
struct screen *, u_int, u_int, u_char, u_char, u_char);
/* local.c */
int local_init(struct buffer **, struct buffer **);

View File

@ -1,4 +1,4 @@
/* $Id: window.c,v 1.11 2007-09-27 09:15:58 nicm Exp $ */
/* $Id: window.c,v 1.12 2007-09-28 22:47:22 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -102,6 +102,7 @@ window_create(const char *cmd, const char **environ, u_int sx, u_int sy)
w->in = buffer_create(BUFSIZ);
w->out = buffer_create(BUFSIZ);
screen_create(&w->screen, sx, sy);
input_init(&w->ictx, &w->screen);
name = xstrdup(cmd);
if ((ptr = strchr(name, ' ')) != NULL) {
@ -175,6 +176,8 @@ window_destroy(struct window *w)
{
close(w->fd);
input_free(&w->ictx);
buffer_destroy(w->in);
buffer_destroy(w->out);
xfree(w);
@ -300,7 +303,7 @@ window_input(struct window *w, struct buffer *b, size_t size)
size -= 2;
key = (int16_t) input_extract16(b);
}
input_key(w->out, key);
input_translate_key(w->out, key);
}
}
@ -313,8 +316,7 @@ window_output(struct window *w, struct buffer *b)
{
size_t used;
used = input_parse(
BUFFER_OUT(w->in), BUFFER_USED(w->in), b, &w->screen);
used = input_parse(&w->ictx, BUFFER_OUT(w->in), BUFFER_USED(w->in), b);
if (used != 0)
buffer_remove(w->in, used);
}