lchat

A line oriented chat front end for ii.
git clone git://git.suckless.org/lchat
Log | Files | Refs | README

commit a2e85fdc57c1322c16d120d1865acc8f13f4c8bd
parent 19b4d99293ad470c98406b94935565750339b716
Author: Tom Schwindl <schwindl@posteo.de>
Date:   Thu, 29 Dec 2022 13:17:48 +0100

Introduce modes

A mode determines how keyborad input is interpreted and which keybindings
are available. Currently, there is a default- and an emacs-mode from which
the user can choose.

Diffstat:
MMakefile | 7+++++--
Mlchat.c | 9++++++---
Mslackline.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Aslackline_emacs.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 139 insertions(+), 42 deletions(-)

diff --git a/Makefile b/Makefile @@ -22,8 +22,8 @@ dist: tar -czf lchat-$(VERSION).tar.gz lchat-$(VERSION) rm -fr lchat-$(VERSION) -lchat: lchat.o slackline.o util.o - $(CC) -o $@ lchat.o slackline.o util.o $(LIBS) +lchat: lchat.o slackline.o util.o slackline_emacs.o + $(CC) -o $@ lchat.o slackline.o slackline_emacs.o util.o $(LIBS) lchat.o: lchat.c $(CC) -c $(CFLAGS) -D_BSD_SOURCE -D_XOPEN_SOURCE -D_GNU_SOURCE \ @@ -42,5 +42,8 @@ sl_test: sl_test.o slackline.o slackline.h slackline.o: slackline.c slackline.h $(CC) -c $(CFLAGS) -o $@ slackline.c +slackline_emacs.o: slackline_emacs.c slackline.h + $(CC) -c $(CFLAGS) -o $@ slackline_emacs.c + util.o: util.c util.h $(CC) -c $(CFLAGS) -D_BSD_SOURCE -o $@ util.c diff --git a/lchat.c b/lchat.c @@ -23,6 +23,7 @@ #include <limits.h> #include <poll.h> #include <signal.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -183,7 +184,7 @@ main(int argc, char *argv[]) char *in_file = NULL; char *out_file = NULL; - while ((ch = getopt(argc, argv, "an:i:eo:p:t:uh")) != -1) { + while ((ch = getopt(argc, argv, "an:i:eo:p:t:uhm:")) != -1) { switch (ch) { case 'a': bell_flag = false; @@ -217,6 +218,10 @@ main(int argc, char *argv[]) case 'u': ucspi = true; break; + case 'm': + if (strcmp(optarg, "emacs") == 0) + sl_mode(sl, SL_EMACS); + break; case 'h': default: usage(); @@ -342,8 +347,6 @@ main(int argc, char *argv[]) return EXIT_SUCCESS; switch (c) { - case 4: /* eot */ - return EXIT_SUCCESS; case 13: /* return */ if (sl->rlen == 0 && empty_line == false) goto out; diff --git a/slackline.c b/slackline.c @@ -22,13 +22,14 @@ #include <grapheme.h> +#include "slackline_internals.h" #include "slackline.h" - -enum direction {LEFT, RIGHT, HOME, END}; +#include "util.h" struct slackline * sl_init(void) { + char *mode = getenv("EDITOR"); struct slackline *sl = malloc(sizeof *sl); if (sl == NULL) @@ -45,6 +46,14 @@ sl_init(void) sl_reset(sl); + sl->mode = SL_DEFAULT; + if (mode != NULL) { + if (strcmp(mode, "emacs") == 0) + sl->mode = SL_EMACS; + else if (strcmp(mode, "vi") == 0) + sl->mode = SL_VI; + } + return sl; } @@ -71,7 +80,13 @@ sl_reset(struct slackline *sl) sl->ubuf_len = 0; } -static size_t +void +sl_mode(struct slackline *sl, enum mode mode) +{ + sl->mode = mode; +} + +size_t sl_postobyte(struct slackline *sl, size_t pos) { char *ptr = &sl->buf[0]; @@ -84,13 +99,13 @@ sl_postobyte(struct slackline *sl, size_t pos) return byte; } -static char * +char * sl_postoptr(struct slackline *sl, size_t pos) { return &sl->buf[sl_postobyte(sl, pos)]; } -static void +void sl_backspace(struct slackline *sl) { char *ncur; @@ -114,7 +129,7 @@ sl_backspace(struct slackline *sl) sl->ptr = ncur; } -static void +void sl_move(struct slackline *sl, enum direction dir) { switch (dir) { @@ -139,21 +154,41 @@ sl_move(struct slackline *sl, enum direction dir) sl->ptr = sl->buf + sl->bcur; } -int -sl_keystroke(struct slackline *sl, int key) +static void +sl_default(struct slackline *sl, int key) { - uint_least32_t cp; - - if (sl == NULL || sl->rlen < sl->rcur) - return -1; + switch (key) { + case 27: /* Escape */ + sl->esc = ESC; + break; + case 21: + sl_reset(sl); + break; + case 23: /* ctrl+w -- erase previous word */ + while (sl->rcur != 0 && isspace((unsigned char) *(sl->ptr-1))) + sl_backspace(sl); + while (sl->rcur != 0 && !isspace((unsigned char) *(sl->ptr-1))) + sl_backspace(sl); + break; + case 127: /* backspace */ + case 8: /* backspace */ + sl_backspace(sl); + break; + default: + break; + } +} +static int +sl_esc(struct slackline *sl, int key) +{ /* handle escape sequences */ switch (sl->esc) { case ESC_NONE: break; case ESC: sl->esc = key == '[' ? ESC_BRACKET : ESC_NONE; - return 0; + return 1; case ESC_BRACKET: switch (key) { case 'A': /* up */ @@ -189,10 +224,10 @@ sl_keystroke(struct slackline *sl, int key) case '9': sl->nummod = key; sl->esc = ESC_BRACKET_NUM; - return 0; + return 1; } sl->esc = ESC_NONE; - return 0; + return 1; case ESC_BRACKET_NUM: switch(key) { case '~': @@ -213,35 +248,38 @@ sl_keystroke(struct slackline *sl, int key) break; } sl->esc = ESC_NONE; - return 0; + return 1; } } - if (!iscntrl((unsigned char) key)) - goto compose; + return 0; +} - /* handle ctl keys */ - switch (key) { - case 27: /* Escape */ - sl->esc = ESC; - return 0; - case 127: /* backspace */ - case 8: /* backspace */ - sl_backspace(sl); - return 0; - case 21: /* ctrl+u -- clearline */ - sl_reset(sl); - return 0; - case 23: /* ctrl+w -- erase previous word */ - while (sl->rcur != 0 && isspace((unsigned char) *(sl->ptr-1))) - sl_backspace(sl); +int +sl_keystroke(struct slackline *sl, int key) +{ + uint_least32_t cp; - while (sl->rcur != 0 && !isspace((unsigned char) *(sl->ptr-1))) - sl_backspace(sl); - return 0; - default: + if (sl == NULL || sl->rlen < sl->rcur) + return -1; + if (sl_esc(sl, key)) return 0; + if (!iscntrl((unsigned char) key)) + goto compose; + + switch (sl->mode) { + case SL_DEFAULT: + sl_default(sl, key); + break; + case SL_EMACS: + sl_default(sl, key); + sl_emacs(sl, key); + break; + case SL_VI: + /* TODO: implement vi-mode */ + break; } + return 0; compose: /* byte-wise composing of UTF-8 runes */ diff --git a/slackline_emacs.c b/slackline_emacs.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <ctype.h> +#include <stddef.h> +#include <stdlib.h> + +#include "slackline_internals.h" + +void +sl_emacs(struct slackline *sl, int key) +{ + char tmp; + + switch (key) { + case 27: /* Escape */ + sl->esc = ESC; + break; + case 1: /* ctrl+a -- start of line */ + sl_move(sl, HOME); + break; + case 2: /* ctrl+b -- previous char */ + sl_move(sl, LEFT); + break; + case 4: /* ctrl+d -- delete char in front of the cursor or exit */ + if (sl->rcur < sl->rlen) { + sl_move(sl, RIGHT); + sl_backspace(sl); + } else { + exit(EXIT_SUCCESS); + } + break; + case 5: /* ctrl+e -- end of line */ + sl_move(sl, END); + break; + case 6: /* ctrl+f -- next char */ + sl_move(sl, RIGHT); + break; + case 11: /* ctrl+k -- delete line from cursor to end */ + for (int i = sl->rlen - sl->rcur; i > 0; --i) { + sl_move(sl, RIGHT); + sl_backspace(sl); + } + break; + case 20: /* ctrl+t -- swap last two chars */ + if (sl->rcur >= 2) { + tmp = *sl_postoptr(sl, sl->rcur-1); + sl->buf[sl->rcur-1] = *sl_postoptr(sl, sl->rcur-2); + sl->buf[sl->rcur-2] = tmp; + } + break; + default: + break; + } +}