Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/kbd.c
- This file was added.
/* $OpenBSD: kbd.c,v 1.35 2021/03/01 10:51:14 lum Exp $ */ | |||||
/* This file is in the public domain. */ | |||||
/* | |||||
* Terminal independent keyboard handling. | |||||
*/ | |||||
#include <sys/queue.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | |||||
#include "def.h" | |||||
#include "kbd.h" | |||||
#include "key.h" | |||||
#include "macro.h" | |||||
#ifdef MGLOG | |||||
#include "log.h" | |||||
#endif | |||||
#define METABIT 0x80 | |||||
#define PROMPTL 80 | |||||
char prompt[PROMPTL] = "", *promptp = prompt; | |||||
static int mgwrap(PF, int, int); | |||||
static int use_metakey = TRUE; | |||||
static int pushed = FALSE; | |||||
static int pushedc; | |||||
struct map_element *ele; | |||||
struct key key; | |||||
int rptcount; | |||||
/* | |||||
* Toggle the value of use_metakey | |||||
*/ | |||||
int | |||||
do_meta(int f, int n) | |||||
{ | |||||
if (f & FFARG) | |||||
use_metakey = n > 0; | |||||
else | |||||
use_metakey = !use_metakey; | |||||
ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis"); | |||||
return (TRUE); | |||||
} | |||||
static int bs_map = 0; | |||||
/* | |||||
* Toggle backspace mapping | |||||
*/ | |||||
int | |||||
bsmap(int f, int n) | |||||
{ | |||||
if (f & FFARG) | |||||
bs_map = n > 0; | |||||
else | |||||
bs_map = !bs_map; | |||||
ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis"); | |||||
return (TRUE); | |||||
} | |||||
void | |||||
ungetkey(int c) | |||||
{ | |||||
if (use_metakey && pushed && c == CCHR('[')) | |||||
pushedc |= METABIT; | |||||
else | |||||
pushedc = c; | |||||
pushed = TRUE; | |||||
} | |||||
int | |||||
getkey(int flag) | |||||
{ | |||||
int c; | |||||
if (flag && !pushed) { | |||||
if (prompt[0] != '\0' && ttwait(2000)) { | |||||
/* avoid problems with % */ | |||||
ewprintf("%s", prompt); | |||||
/* put the cursor back */ | |||||
update(CMODE); | |||||
epresf = KCLEAR; | |||||
} | |||||
if (promptp > prompt) | |||||
*(promptp - 1) = ' '; | |||||
} | |||||
if (pushed) { | |||||
c = pushedc; | |||||
pushed = FALSE; | |||||
} else | |||||
c = ttgetc(); | |||||
if (bs_map) { | |||||
if (c == CCHR('H')) | |||||
c = CCHR('?'); | |||||
else if (c == CCHR('?')) | |||||
c = CCHR('H'); | |||||
} | |||||
if (use_metakey && (c & METABIT)) { | |||||
pushedc = c & ~METABIT; | |||||
pushed = TRUE; | |||||
c = CCHR('['); | |||||
} | |||||
if (flag && promptp < &prompt[PROMPTL - 5]) { | |||||
promptp = getkeyname(promptp, | |||||
sizeof(prompt) - (promptp - prompt) - 1, c); | |||||
*promptp++ = '-'; | |||||
*promptp = '\0'; | |||||
} | |||||
return (c); | |||||
} | |||||
/* | |||||
* doscan scans a keymap for a keyboard character and returns a pointer | |||||
* to the function associated with that character. Sets ele to the | |||||
* keymap element the keyboard was found in as a side effect. | |||||
*/ | |||||
PF | |||||
doscan(KEYMAP *map, int c, KEYMAP **newmap) | |||||
{ | |||||
struct map_element *elec = &map->map_element[0]; | |||||
struct map_element *last = &map->map_element[map->map_num]; | |||||
PF ret; | |||||
while (elec < last && c > elec->k_num) | |||||
elec++; | |||||
/* used by prefix and binding code */ | |||||
ele = elec; | |||||
if (elec >= last || c < elec->k_base) | |||||
ret = map->map_default; | |||||
else | |||||
ret = elec->k_funcp[c - elec->k_base]; | |||||
if (ret == NULL && newmap != NULL) | |||||
*newmap = elec->k_prefmap; | |||||
return (ret); | |||||
} | |||||
int | |||||
doin(void) | |||||
{ | |||||
KEYMAP *curmap; | |||||
PF funct; | |||||
*(promptp = prompt) = '\0'; | |||||
curmap = curbp->b_modes[curbp->b_nmodes]->p_map; | |||||
key.k_count = 0; | |||||
while ((funct = doscan(curmap, (key.k_chars[key.k_count++] = | |||||
getkey(TRUE)), &curmap)) == NULL) | |||||
/* nothing */; | |||||
#ifdef MGLOG | |||||
if (!mglog(funct, curmap)) | |||||
ewprintf("Problem with logging"); | |||||
#endif | |||||
if (macrodef && macrocount < MAXMACRO) | |||||
macro[macrocount++].m_funct = funct; | |||||
return (mgwrap(funct, 0, 1)); | |||||
} | |||||
int | |||||
rescan(int f, int n) | |||||
{ | |||||
int c; | |||||
KEYMAP *curmap; | |||||
int i; | |||||
PF fp = NULL; | |||||
int md = curbp->b_nmodes; | |||||
for (;;) { | |||||
if (ISUPPER(key.k_chars[key.k_count - 1])) { | |||||
c = TOLOWER(key.k_chars[key.k_count - 1]); | |||||
curmap = curbp->b_modes[md]->p_map; | |||||
for (i = 0; i < key.k_count - 1; i++) { | |||||
if ((fp = doscan(curmap, (key.k_chars[i]), | |||||
&curmap)) != NULL) | |||||
break; | |||||
} | |||||
if (fp == NULL) { | |||||
if ((fp = doscan(curmap, c, NULL)) == NULL) | |||||
while ((fp = doscan(curmap, | |||||
key.k_chars[key.k_count++] = | |||||
getkey(TRUE), &curmap)) == NULL) | |||||
/* nothing */; | |||||
if (fp != rescan) { | |||||
if (macrodef && macrocount <= MAXMACRO) | |||||
macro[macrocount - 1].m_funct | |||||
= fp; | |||||
return (mgwrap(fp, f, n)); | |||||
} | |||||
} | |||||
} | |||||
/* try previous mode */ | |||||
if (--md < 0) | |||||
return (ABORT); | |||||
curmap = curbp->b_modes[md]->p_map; | |||||
for (i = 0; i < key.k_count; i++) { | |||||
if ((fp = doscan(curmap, (key.k_chars[i]), &curmap)) != NULL) | |||||
break; | |||||
} | |||||
if (fp == NULL) { | |||||
while ((fp = doscan(curmap, key.k_chars[i++] = | |||||
getkey(TRUE), &curmap)) == NULL) | |||||
/* nothing */; | |||||
key.k_count = i; | |||||
} | |||||
if (fp != rescan && i >= key.k_count - 1) { | |||||
if (macrodef && macrocount <= MAXMACRO) | |||||
macro[macrocount - 1].m_funct = fp; | |||||
return (mgwrap(fp, f, n)); | |||||
} | |||||
} | |||||
} | |||||
int | |||||
universal_argument(int f, int n) | |||||
{ | |||||
KEYMAP *curmap; | |||||
PF funct; | |||||
int c, nn = 4; | |||||
if (f & FFUNIV) | |||||
nn *= n; | |||||
for (;;) { | |||||
key.k_chars[0] = c = getkey(TRUE); | |||||
key.k_count = 1; | |||||
if (c == '-') | |||||
return (negative_argument(f, nn)); | |||||
if (c >= '0' && c <= '9') | |||||
return (digit_argument(f, nn)); | |||||
curmap = curbp->b_modes[curbp->b_nmodes]->p_map; | |||||
while ((funct = doscan(curmap, c, &curmap)) == NULL) { | |||||
key.k_chars[key.k_count++] = c = getkey(TRUE); | |||||
} | |||||
if (funct != universal_argument) { | |||||
if (macrodef && macrocount < MAXMACRO - 1) { | |||||
if (f & FFARG) | |||||
macrocount--; | |||||
macro[macrocount++].m_count = nn; | |||||
macro[macrocount++].m_funct = funct; | |||||
} | |||||
return (mgwrap(funct, FFUNIV, nn)); | |||||
} | |||||
nn <<= 2; | |||||
} | |||||
} | |||||
/* ARGSUSED */ | |||||
int | |||||
digit_argument(int f, int n) | |||||
{ | |||||
KEYMAP *curmap; | |||||
PF funct; | |||||
int nn, c; | |||||
nn = key.k_chars[key.k_count - 1] - '0'; | |||||
for (;;) { | |||||
c = getkey(TRUE); | |||||
if (c < '0' || c > '9') | |||||
break; | |||||
nn *= 10; | |||||
nn += c - '0'; | |||||
} | |||||
key.k_chars[0] = c; | |||||
key.k_count = 1; | |||||
curmap = curbp->b_modes[curbp->b_nmodes]->p_map; | |||||
while ((funct = doscan(curmap, c, &curmap)) == NULL) { | |||||
key.k_chars[key.k_count++] = c = getkey(TRUE); | |||||
} | |||||
if (macrodef && macrocount < MAXMACRO - 1) { | |||||
if (f & FFARG) | |||||
macrocount--; | |||||
else | |||||
macro[macrocount - 1].m_funct = universal_argument; | |||||
macro[macrocount++].m_count = nn; | |||||
macro[macrocount++].m_funct = funct; | |||||
} | |||||
return (mgwrap(funct, FFOTHARG, nn)); | |||||
} | |||||
int | |||||
negative_argument(int f, int n) | |||||
{ | |||||
KEYMAP *curmap; | |||||
PF funct; | |||||
int c; | |||||
int nn = 0; | |||||
for (;;) { | |||||
c = getkey(TRUE); | |||||
if (c < '0' || c > '9') | |||||
break; | |||||
nn *= 10; | |||||
nn += c - '0'; | |||||
} | |||||
if (nn) | |||||
nn = -nn; | |||||
else | |||||
nn = -n; | |||||
key.k_chars[0] = c; | |||||
key.k_count = 1; | |||||
curmap = curbp->b_modes[curbp->b_nmodes]->p_map; | |||||
while ((funct = doscan(curmap, c, &curmap)) == NULL) { | |||||
key.k_chars[key.k_count++] = c = getkey(TRUE); | |||||
} | |||||
if (macrodef && macrocount < MAXMACRO - 1) { | |||||
if (f & FFARG) | |||||
macrocount--; | |||||
else | |||||
macro[macrocount - 1].m_funct = universal_argument; | |||||
macro[macrocount++].m_count = nn; | |||||
macro[macrocount++].m_funct = funct; | |||||
} | |||||
return (mgwrap(funct, FFNEGARG, nn)); | |||||
} | |||||
/* | |||||
* Insert a character. While defining a macro, create a "LINE" containing | |||||
* all inserted characters. | |||||
*/ | |||||
int | |||||
selfinsert(int f, int n) | |||||
{ | |||||
struct line *lp; | |||||
int c; | |||||
int count; | |||||
if (n < 0) | |||||
return (FALSE); | |||||
if (n == 0) | |||||
return (TRUE); | |||||
c = key.k_chars[key.k_count - 1]; | |||||
if (macrodef && macrocount < MAXMACRO) { | |||||
if (f & FFARG) | |||||
macrocount -= 2; | |||||
/* last command was insert -- tack on the end */ | |||||
if (lastflag & CFINS) { | |||||
macrocount--; | |||||
/* Ensure the line can handle the new characters */ | |||||
if (maclcur->l_size < maclcur->l_used + n) { | |||||
if (lrealloc(maclcur, maclcur->l_used + n) == | |||||
FALSE) | |||||
return (FALSE); | |||||
} | |||||
maclcur->l_used += n; | |||||
/* Copy in the new data */ | |||||
for (count = maclcur->l_used - n; | |||||
count < maclcur->l_used; count++) | |||||
maclcur->l_text[count] = c; | |||||
} else { | |||||
macro[macrocount - 1].m_funct = insert; | |||||
if ((lp = lalloc(n)) == NULL) | |||||
return (FALSE); | |||||
lp->l_bp = maclcur; | |||||
lp->l_fp = maclcur->l_fp; | |||||
maclcur->l_fp = lp; | |||||
maclcur = lp; | |||||
for (count = 0; count < n; count++) | |||||
lp->l_text[count] = c; | |||||
} | |||||
thisflag |= CFINS; | |||||
} | |||||
if (c == *curbp->b_nlchr) { | |||||
do { | |||||
count = lnewline(); | |||||
} while (--n && count == TRUE); | |||||
return (count); | |||||
} | |||||
/* overwrite mode */ | |||||
if (curbp->b_flag & BFOVERWRITE) { | |||||
lchange(WFEDIT); | |||||
while (curwp->w_doto < llength(curwp->w_dotp) && n--) | |||||
lputc(curwp->w_dotp, curwp->w_doto++, c); | |||||
if (n <= 0) | |||||
return (TRUE); | |||||
} | |||||
return (linsert(n, c)); | |||||
} | |||||
/* | |||||
* selfinsert() can't be called directly from a startup file or by | |||||
* 'eval-current-buffer' since it is by design, meant to be called interactively | |||||
* as characters are typed in a buffer. ask_selfinsert() allows selfinsert() to | |||||
* be used by excline(). Having ask_selfinsert() helps with regression testing. | |||||
* No manual page entry since use case is a bit obscure. See 'insert' command. | |||||
*/ | |||||
int | |||||
ask_selfinsert(int f, int n) | |||||
{ | |||||
char *c, cbuf[2]; | |||||
if ((c = eread("Insert a character: ", cbuf, sizeof(cbuf), | |||||
EFNEW)) == NULL || (c[0] == '\0')) | |||||
return (ABORT); | |||||
key.k_chars[0] = *c; | |||||
key.k_chars[1] = '\0'; | |||||
key.k_count = 1; | |||||
return (selfinsert(FFRAND, 1)); | |||||
} | |||||
/* | |||||
* This could be implemented as a keymap with everything defined as self-insert. | |||||
*/ | |||||
int | |||||
quote(int f, int n) | |||||
{ | |||||
int c; | |||||
key.k_count = 1; | |||||
if ((key.k_chars[0] = getkey(TRUE)) >= '0' && key.k_chars[0] <= '7') { | |||||
key.k_chars[0] -= '0'; | |||||
if ((c = getkey(TRUE)) >= '0' && c <= '7') { | |||||
key.k_chars[0] <<= 3; | |||||
key.k_chars[0] += c - '0'; | |||||
if ((c = getkey(TRUE)) >= '0' && c <= '7') { | |||||
key.k_chars[0] <<= 3; | |||||
key.k_chars[0] += c - '0'; | |||||
} else | |||||
ungetkey(c); | |||||
} else | |||||
ungetkey(c); | |||||
} | |||||
return (selfinsert(f, n)); | |||||
} | |||||
/* | |||||
* Wraper function to count invocation repeats. | |||||
* We ignore any function whose sole purpose is to get us | |||||
* to the intended function. | |||||
*/ | |||||
static int | |||||
mgwrap(PF funct, int f, int n) | |||||
{ | |||||
static PF ofp; | |||||
if (funct != rescan && | |||||
funct != negative_argument && | |||||
funct != digit_argument && | |||||
funct != universal_argument) { | |||||
if (funct == ofp) | |||||
rptcount++; | |||||
else | |||||
rptcount = 0; | |||||
ofp = funct; | |||||
} | |||||
return ((*funct)(f, n)); | |||||
} |