mirror of
https://github.com/vim/vim.git
synced 2025-07-04 23:07:33 -04:00
Problem: Comments in libvterm are inconsistent. Solution: Use // comments. Als update the table of combining characters.
367 lines
7.5 KiB
C
367 lines
7.5 KiB
C
#define _XOPEN_SOURCE 500 // strdup
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#define streq(a,b) (strcmp(a,b)==0)
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#include <termios.h>
|
|
|
|
static char *getvalue(int *argip, int argc, char *argv[])
|
|
{
|
|
if(*argip >= argc) {
|
|
fprintf(stderr, "Expected an option value\n");
|
|
exit(1);
|
|
}
|
|
|
|
return argv[(*argip)++];
|
|
}
|
|
|
|
static int getchoice(int *argip, int argc, char *argv[], const char *options[])
|
|
{
|
|
const char *arg = getvalue(argip, argc, argv);
|
|
|
|
int value = -1;
|
|
while(options[++value])
|
|
if(streq(arg, options[value]))
|
|
return value;
|
|
|
|
fprintf(stderr, "Unrecognised option value %s\n", arg);
|
|
exit(1);
|
|
}
|
|
|
|
typedef enum {
|
|
OFF,
|
|
ON,
|
|
QUERY,
|
|
} BoolQuery;
|
|
|
|
static BoolQuery getboolq(int *argip, int argc, char *argv[])
|
|
{
|
|
const char *choices[] = {"off", "on", "query", NULL};
|
|
return getchoice(argip, argc, argv, choices);
|
|
}
|
|
|
|
static char *helptext[] = {
|
|
"reset",
|
|
"s8c1t [off|on]",
|
|
"keypad [app|num]",
|
|
"screen [off|on|query]",
|
|
"cursor [off|on|query]",
|
|
"curblink [off|on|query]",
|
|
"curshape [block|under|bar|query]",
|
|
"mouse [off|click|clickdrag|motion]",
|
|
"reportfocus [off|on|query]",
|
|
"altscreen [off|on|query]",
|
|
"bracketpaste [off|on|query]",
|
|
"icontitle [STR]",
|
|
"icon [STR]",
|
|
"title [STR]",
|
|
NULL
|
|
};
|
|
|
|
static int seticanon(int icanon, int echo)
|
|
{
|
|
struct termios termios;
|
|
int ret;
|
|
|
|
tcgetattr(0, &termios);
|
|
|
|
ret = (termios.c_lflag & ICANON);
|
|
|
|
if(icanon) termios.c_lflag |= ICANON;
|
|
else termios.c_lflag &= ~ICANON;
|
|
|
|
if(echo) termios.c_lflag |= ECHO;
|
|
else termios.c_lflag &= ~ECHO;
|
|
|
|
tcsetattr(0, TCSANOW, &termios);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void await_c1(unsigned char c1)
|
|
{
|
|
unsigned char c;
|
|
|
|
// await CSI - 8bit or 2byte 7bit form
|
|
int in_esc = FALSE;
|
|
while((c = getchar())) {
|
|
if(c == c1)
|
|
break;
|
|
if(in_esc && c == (char)(c1 - 0x40))
|
|
break;
|
|
if(!in_esc && c == 0x1b)
|
|
in_esc = TRUE;
|
|
else
|
|
in_esc = FALSE;
|
|
}
|
|
}
|
|
|
|
static char *read_csi()
|
|
{
|
|
unsigned char csi[32];
|
|
int i = 0;
|
|
|
|
await_c1(0x9B); // CSI
|
|
|
|
// TODO: This really should be a more robust CSI parser
|
|
for(; i < sizeof(csi)-1; i++) {
|
|
int c = csi[i] = getchar();
|
|
if(c >= 0x40 && c <= 0x7e)
|
|
break;
|
|
}
|
|
csi[++i] = 0;
|
|
|
|
// TODO: returns longer than 32?
|
|
|
|
return strdup((char *)csi);
|
|
}
|
|
|
|
static char *read_dcs()
|
|
{
|
|
unsigned char dcs[32];
|
|
int in_esc = FALSE;
|
|
int i;
|
|
|
|
await_c1(0x90);
|
|
|
|
for(i = 0; i < sizeof(dcs)-1; ) {
|
|
char c = getchar();
|
|
if(c == 0x9c) // ST
|
|
break;
|
|
if(in_esc && c == 0x5c)
|
|
break;
|
|
if(!in_esc && c == 0x1b)
|
|
in_esc = TRUE;
|
|
else {
|
|
dcs[i++] = c;
|
|
in_esc = FALSE;
|
|
}
|
|
}
|
|
dcs[++i] = 0;
|
|
|
|
return strdup((char *)dcs);
|
|
}
|
|
|
|
static void usage(int exitcode)
|
|
{
|
|
char **p;
|
|
|
|
fprintf(stderr, "Control a libvterm-based terminal\n"
|
|
"\n"
|
|
"Options:\n");
|
|
|
|
for(p = helptext; *p; p++)
|
|
fprintf(stderr, " %s\n", *p);
|
|
|
|
exit(exitcode);
|
|
}
|
|
|
|
static int query_dec_mode(int mode)
|
|
{
|
|
char *s = NULL;
|
|
|
|
printf("\x1b[?%d$p", mode);
|
|
|
|
do {
|
|
int reply_mode, reply_value;
|
|
char reply_cmd;
|
|
|
|
if(s)
|
|
free(s);
|
|
s = read_csi();
|
|
|
|
// expect "?" mode ";" value "$y"
|
|
|
|
// If the sscanf format string ends in a literal, we can't tell from
|
|
// its return value if it matches. Hence we'll %c the cmd and check it
|
|
// explicitly
|
|
if(sscanf(s, "?%d;%d$%c", &reply_mode, &reply_value, &reply_cmd) < 3)
|
|
continue;
|
|
if(reply_cmd != 'y')
|
|
continue;
|
|
|
|
if(reply_mode != mode)
|
|
continue;
|
|
|
|
free(s);
|
|
|
|
if(reply_value == 1 || reply_value == 3)
|
|
return TRUE;
|
|
if(reply_value == 2 || reply_value == 4)
|
|
return FALSE;
|
|
|
|
printf("Unrecognised reply to DECRQM: %d\n", reply_value);
|
|
return FALSE;
|
|
} while(1);
|
|
}
|
|
|
|
static void do_dec_mode(int mode, BoolQuery val, const char *name)
|
|
{
|
|
switch(val) {
|
|
case OFF:
|
|
case ON:
|
|
printf("\x1b[?%d%c", mode, val == ON ? 'h' : 'l');
|
|
break;
|
|
|
|
case QUERY:
|
|
if(query_dec_mode(mode))
|
|
printf("%s on\n", name);
|
|
else
|
|
printf("%s off\n", name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int query_rqss_numeric(char *cmd)
|
|
{
|
|
char *s = NULL;
|
|
|
|
printf("\x1bP$q%s\x1b\\", cmd);
|
|
|
|
do {
|
|
int num;
|
|
|
|
if(s)
|
|
free(s);
|
|
s = read_dcs();
|
|
|
|
if(!s)
|
|
return -1;
|
|
if(strlen(s) < strlen(cmd))
|
|
return -1;
|
|
if(strcmp(s + strlen(s) - strlen(cmd), cmd) != 0) {
|
|
printf("No match\n");
|
|
continue;
|
|
}
|
|
|
|
if(s[0] != '1' || s[1] != '$' || s[2] != 'r')
|
|
return -1;
|
|
|
|
if(sscanf(s + 3, "%d", &num) != 1)
|
|
return -1;
|
|
|
|
return num;
|
|
} while(1);
|
|
}
|
|
|
|
int wasicanon;
|
|
|
|
void restoreicanon(void)
|
|
{
|
|
seticanon(wasicanon, TRUE);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int argi = 1;
|
|
|
|
if(argc == 1)
|
|
usage(0);
|
|
|
|
wasicanon = seticanon(FALSE, FALSE);
|
|
atexit(restoreicanon);
|
|
|
|
while(argi < argc) {
|
|
const char *arg = argv[argi++];
|
|
|
|
if(streq(arg, "reset")) {
|
|
printf("\x1b" "c");
|
|
}
|
|
else if(streq(arg, "s8c1t")) {
|
|
const char *choices[] = {"off", "on", NULL};
|
|
switch(getchoice(&argi, argc, argv, choices)) {
|
|
case 0:
|
|
printf("\x1b F"); break;
|
|
case 1:
|
|
printf("\x1b G"); break;
|
|
}
|
|
}
|
|
else if(streq(arg, "keypad")) {
|
|
const char *choices[] = {"app", "num", NULL};
|
|
switch(getchoice(&argi, argc, argv, choices)) {
|
|
case 0:
|
|
printf("\x1b="); break;
|
|
case 1:
|
|
printf("\x1b>"); break;
|
|
}
|
|
}
|
|
else if(streq(arg, "screen")) {
|
|
do_dec_mode(5, getboolq(&argi, argc, argv), "screen");
|
|
}
|
|
else if(streq(arg, "cursor")) {
|
|
do_dec_mode(25, getboolq(&argi, argc, argv), "cursor");
|
|
}
|
|
else if(streq(arg, "curblink")) {
|
|
do_dec_mode(12, getboolq(&argi, argc, argv), "curblink");
|
|
}
|
|
else if(streq(arg, "curshape")) {
|
|
// TODO: This ought to query the current value of DECSCUSR because it
|
|
// may need blinking on or off
|
|
const char *choices[] = {"block", "under", "bar", "query", NULL};
|
|
int shape = getchoice(&argi, argc, argv, choices);
|
|
switch(shape) {
|
|
case 3: // query
|
|
shape = query_rqss_numeric(" q");
|
|
switch(shape) {
|
|
case 1: case 2:
|
|
printf("curshape block\n");
|
|
break;
|
|
case 3: case 4:
|
|
printf("curshape under\n");
|
|
break;
|
|
case 5: case 6:
|
|
printf("curshape bar\n");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
printf("\x1b[%d q", 1 + (shape * 2));
|
|
break;
|
|
}
|
|
}
|
|
else if(streq(arg, "mouse")) {
|
|
const char *choices[] = {"off", "click", "clickdrag", "motion", NULL};
|
|
switch(getchoice(&argi, argc, argv, choices)) {
|
|
case 0:
|
|
printf("\x1b[?1000l"); break;
|
|
case 1:
|
|
printf("\x1b[?1000h"); break;
|
|
case 2:
|
|
printf("\x1b[?1002h"); break;
|
|
case 3:
|
|
printf("\x1b[?1003h"); break;
|
|
}
|
|
}
|
|
else if(streq(arg, "reportfocus")) {
|
|
do_dec_mode(1004, getboolq(&argi, argc, argv), "reportfocus");
|
|
}
|
|
else if(streq(arg, "altscreen")) {
|
|
do_dec_mode(1049, getboolq(&argi, argc, argv), "altscreen");
|
|
}
|
|
else if(streq(arg, "bracketpaste")) {
|
|
do_dec_mode(2004, getboolq(&argi, argc, argv), "bracketpaste");
|
|
}
|
|
else if(streq(arg, "icontitle")) {
|
|
printf("\x1b]0;%s\a", getvalue(&argi, argc, argv));
|
|
}
|
|
else if(streq(arg, "icon")) {
|
|
printf("\x1b]1;%s\a", getvalue(&argi, argc, argv));
|
|
}
|
|
else if(streq(arg, "title")) {
|
|
printf("\x1b]2;%s\a", getvalue(&argi, argc, argv));
|
|
}
|
|
else {
|
|
fprintf(stderr, "Unrecognised command %s\n", arg);
|
|
exit(1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|