Some major refactoring including moving more stuff into bterm.h (renamed
from keys.h), a custom readline (`read` was buggy), better customization, and improved renaming bindings.
This commit is contained in:
parent
92a198d6b6
commit
8d4e4aeba4
2
Makefile
2
Makefile
@ -13,7 +13,7 @@ clean:
|
||||
config.h:
|
||||
cp config.def.h config.h
|
||||
|
||||
$(NAME): $(NAME).c keys.h config.h
|
||||
$(NAME): $(NAME).c bterm.h config.h
|
||||
$(CC) $(NAME).c $(LIBS) $(CFLAGS) $(G) -o $(NAME)
|
||||
|
||||
test: $(NAME)
|
||||
|
247
bb.c
247
bb.c
@ -22,9 +22,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "keys.h"
|
||||
#include "bterm.h"
|
||||
|
||||
#define BB_VERSION "0.9.1"
|
||||
#define BB_VERSION "0.10.0"
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
@ -34,23 +34,10 @@
|
||||
#define MIN(a,b) ((a) > (b) ? (b) : (a))
|
||||
#define IS_SELECTED(p) (((p)->atme) != NULL)
|
||||
|
||||
// Terminal escape sequences:
|
||||
#define CSI "\033["
|
||||
#define T_WRAP "7"
|
||||
#define T_SHOW_CURSOR "25"
|
||||
#define T_MOUSE_XY "1000"
|
||||
#define T_MOUSE_CELL "1002"
|
||||
#define T_MOUSE_SGR "1006"
|
||||
#define T_ALT_SCREEN "1049"
|
||||
#define T_ON(opt) CSI "?" opt "h"
|
||||
#define T_OFF(opt) CSI "?" opt "l"
|
||||
|
||||
static const char *T_ENTER_BBMODE = T_OFF(T_SHOW_CURSOR) T_ON(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR);
|
||||
static const char *T_LEAVE_BBMODE = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR ";" T_ALT_SCREEN);
|
||||
static const char *T_LEAVE_BBMODE_PARTIAL = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR);
|
||||
|
||||
#define move_cursor(f, x, y) fprintf((f), CSI "%d;%dH", (int)(y)+1, (int)(x)+1)
|
||||
|
||||
#define err(...) do { \
|
||||
close_term(); \
|
||||
fputs(T_OFF(T_ALT_SCREEN), stdout); \
|
||||
@ -92,11 +79,10 @@ typedef struct entry_s {
|
||||
char *name, *linkname;
|
||||
// TODO: inline only the relevant fields:
|
||||
struct stat info;
|
||||
mode_t linkedmode;
|
||||
int refcount : 2; // Should only be between 0-2
|
||||
int isdir : 1;
|
||||
int islink : 1;
|
||||
int needs_esc : 1;
|
||||
int link_needs_esc : 1;
|
||||
int no_esc : 1;
|
||||
int link_no_esc : 1;
|
||||
char fullname[1]; // Must be last
|
||||
} entry_t;
|
||||
|
||||
@ -111,6 +97,8 @@ typedef struct bb_s {
|
||||
int colwidths[10];
|
||||
} bb_t;
|
||||
|
||||
typedef enum { BB_NOP = 0, BB_INVALID, BB_REFRESH, BB_QUIT } bb_result_t;
|
||||
|
||||
// Functions
|
||||
static void update_term_size(int sig);
|
||||
static void init_term(void);
|
||||
@ -118,6 +106,8 @@ static void cleanup_and_exit(int sig);
|
||||
static void close_term(void);
|
||||
static void* memcheck(void *p);
|
||||
static int run_cmd_on_selection(bb_t *bb, const char *cmd);
|
||||
static int fputs_escaped(FILE *f, const char *str, const char *color);
|
||||
static const char *color_of(mode_t mode);
|
||||
static void render(bb_t *bb, int lazy);
|
||||
static int compare_files(void *r, const void *v1, const void *v2);
|
||||
static int find_file(bb_t *bb, const char *name);
|
||||
@ -126,8 +116,10 @@ static int select_file(bb_t *bb, entry_t *e);
|
||||
static int deselect_file(bb_t *bb, entry_t *e);
|
||||
static void set_cursor(bb_t *bb, int i);
|
||||
static void set_scroll(bb_t *bb, int i);
|
||||
static entry_t *load_entry(const char *path);
|
||||
static void populate_files(bb_t *bb, const char *path);
|
||||
static void sort_files(bb_t *bb);
|
||||
static bb_result_t execute_cmd(bb_t *bb, const char *cmd);
|
||||
static void explore(bb_t *bb, const char *path);
|
||||
static void print_bindings(int verbose);
|
||||
|
||||
@ -169,7 +161,8 @@ void init_term(void)
|
||||
bb_termios.c_cflag |= (unsigned long)CS8;
|
||||
bb_termios.c_cc[VMIN] = 0;
|
||||
bb_termios.c_cc[VTIME] = 0;
|
||||
tcsetattr(fileno(tty_out), TCSAFLUSH, &bb_termios);
|
||||
if (tcsetattr(fileno(tty_out), TCSAFLUSH, &bb_termios) == -1)
|
||||
err("Couldn't tcsetattr");
|
||||
update_term_size(0);
|
||||
signal(SIGWINCH, update_term_size);
|
||||
// Initiate mouse tracking and disable text wrapping:
|
||||
@ -181,9 +174,6 @@ void cleanup_and_exit(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
close_term();
|
||||
fputs(T_OFF(T_ALT_SCREEN), stdout);
|
||||
fputs(T_ON(T_WRAP), stdout);
|
||||
fflush(stdout);
|
||||
unlink(cmdfilename);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@ -191,17 +181,20 @@ void cleanup_and_exit(int sig)
|
||||
void close_term(void)
|
||||
{
|
||||
if (tty_out) {
|
||||
fflush(tty_out);
|
||||
tcsetattr(fileno(tty_out), TCSAFLUSH, &orig_termios);
|
||||
fputs(T_LEAVE_BBMODE_PARTIAL, tty_out);
|
||||
fputs(T_ON(T_WRAP), tty_out);
|
||||
fflush(tty_out);
|
||||
fclose(tty_out);
|
||||
tty_out = NULL;
|
||||
fclose(tty_in);
|
||||
tty_in = NULL;
|
||||
}
|
||||
} else {
|
||||
fputs(T_LEAVE_BBMODE_PARTIAL, stdout);
|
||||
fputs(T_ON(T_WRAP), stdout);
|
||||
fflush(stdout);
|
||||
signal(SIGWINCH, SIG_IGN);
|
||||
}
|
||||
signal(SIGWINCH, SIG_DFL);
|
||||
}
|
||||
|
||||
void* memcheck(void *p)
|
||||
@ -237,8 +230,8 @@ int run_cmd_on_selection(bb_t *bb, const char *cmd)
|
||||
}
|
||||
args[i] = NULL;
|
||||
|
||||
setenv("BBCURSOR", bb->files[bb->cursor]->name, 1);
|
||||
setenv("BBFULLCURSOR", bb->files[bb->cursor]->fullname, 1);
|
||||
setenv("BBSELECTED", bb->firstselected ? "1" : "", 1);
|
||||
setenv("BBCURSOR", bb->files[bb->cursor]->fullname, 1);
|
||||
|
||||
execvp("sh", args);
|
||||
err("Failed to execute command: '%s'", cmd);
|
||||
@ -255,7 +248,7 @@ int run_cmd_on_selection(bb_t *bb, const char *cmd)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int fputs_escaped(FILE *f, const char *str, const char *color)
|
||||
int fputs_escaped(FILE *f, const char *str, const char *color)
|
||||
{
|
||||
static const char *escapes = " abtnvfr e";
|
||||
int escaped = 0;
|
||||
@ -273,6 +266,18 @@ static int fputs_escaped(FILE *f, const char *str, const char *color)
|
||||
return escaped;
|
||||
}
|
||||
|
||||
const char *color_of(mode_t mode)
|
||||
{
|
||||
if (S_ISDIR(mode))
|
||||
return DIR_COLOR;
|
||||
else if (S_ISLNK(mode))
|
||||
return LINK_COLOR;
|
||||
else if (mode & (S_IXUSR | S_IXGRP | S_IXOTH))
|
||||
return EXECUTABLE_COLOR;
|
||||
else
|
||||
return NORMAL_COLOR;
|
||||
}
|
||||
|
||||
void render(bb_t *bb, int lazy)
|
||||
{
|
||||
static int lastcursor = -1, lastscroll = -1;
|
||||
@ -330,13 +335,14 @@ void render(bb_t *bb, int lazy)
|
||||
if (!lazy) {
|
||||
// Path
|
||||
move_cursor(tty_out, 0, 0);
|
||||
const char *color = "\033[0;1;37m";
|
||||
const char *color = TITLE_COLOR;
|
||||
fputs(color, tty_out);
|
||||
fputs_escaped(tty_out, bb->path, color);
|
||||
fputs(" \033[K\033[0m", tty_out);
|
||||
|
||||
// Columns
|
||||
move_cursor(tty_out, 0, 1);
|
||||
fputs("\033[41m \033[0;44;30m\033[K", tty_out);
|
||||
fputs(" \033[0;44;30m\033[K", tty_out);
|
||||
int x = 1;
|
||||
for (int col = 0; col < cols; col++) {
|
||||
move_cursor(tty_out, x, 1);
|
||||
@ -391,20 +397,9 @@ void render(bb_t *bb, int lazy)
|
||||
|
||||
entry_t *entry = files[i];
|
||||
|
||||
if (IS_SELECTED(entry))
|
||||
fputs("\033[41m \033[0m", tty_out);
|
||||
else
|
||||
fputs(" ", tty_out);
|
||||
fputs(IS_SELECTED(entry) ? SELECTED_INDICATOR : NOT_SELECTED_INDICATOR, tty_out);
|
||||
|
||||
const char *color;
|
||||
if (i == bb->cursor)
|
||||
color = CURSOR_COLOR;
|
||||
else if (entry->islink)
|
||||
color = entry->isdir ? LINKDIR_COLOR : LINK_COLOR;
|
||||
else if (entry->isdir)
|
||||
color = DIR_COLOR;
|
||||
else
|
||||
color = NORMAL_COLOR;
|
||||
const char *color = (i == bb->cursor) ? CURSOR_COLOR : color_of(entry->info.st_mode);
|
||||
fputs(color, tty_out);
|
||||
|
||||
int x = 1;
|
||||
@ -459,20 +454,22 @@ void render(bb_t *bb, int lazy)
|
||||
break;
|
||||
|
||||
case 'n': {
|
||||
if (entry->needs_esc)
|
||||
entry->needs_esc &= fputs_escaped(tty_out, entry->name, color) > 0;
|
||||
else fputs(entry->name, tty_out);
|
||||
if (entry->no_esc) fputs(entry->name, tty_out);
|
||||
else entry->no_esc |= !fputs_escaped(tty_out, entry->name, color);
|
||||
|
||||
if (entry->isdir)
|
||||
if (S_ISDIR(S_ISLNK(entry->info.st_mode) ? entry->linkedmode : entry->info.st_mode))
|
||||
fputs("/", tty_out);
|
||||
|
||||
if (entry->linkname) {
|
||||
fputs("\033[2m -> ", tty_out);
|
||||
if (entry->link_needs_esc)
|
||||
entry->link_needs_esc &= fputs_escaped(tty_out, entry->linkname, color) > 0;
|
||||
else fputs(entry->linkname, tty_out);
|
||||
if (i != bb->cursor)
|
||||
fputs("\033[37m", tty_out);
|
||||
fputs("\033[2m -> \033[22;3m", tty_out);
|
||||
color = i == bb->cursor ? CURSOR_COLOR : color_of(entry->linkedmode);
|
||||
fputs(color, tty_out);
|
||||
if (entry->link_no_esc) fputs(entry->linkname, tty_out);
|
||||
else entry->link_no_esc |= !fputs_escaped(tty_out, entry->linkname, color);
|
||||
|
||||
if (entry->isdir)
|
||||
if (S_ISDIR(entry->linkedmode))
|
||||
fputs("/", tty_out);
|
||||
|
||||
fputs("\033[22m", tty_out);
|
||||
@ -527,8 +524,10 @@ int compare_files(void *v, const void *v1, const void *v2)
|
||||
const entry_t *f1 = *((const entry_t**)v1), *f2 = *((const entry_t**)v2);
|
||||
int diff = 0;
|
||||
if (!bb->options['i']) {
|
||||
diff = -(f1->isdir - f2->isdir);
|
||||
if (diff) return -diff;
|
||||
// Unless interleave mode is on, sort dirs before files
|
||||
int d1 = S_ISDIR(f1->info.st_mode) || (S_ISLNK(f1->info.st_mode) && S_ISDIR(f1->linkedmode));
|
||||
int d2 = S_ISDIR(f2->info.st_mode) || (S_ISLNK(f2->info.st_mode) && S_ISDIR(f2->linkedmode));
|
||||
if (d1 != d2) return d2 - d1;
|
||||
}
|
||||
if (sort == SORT_NAME) {
|
||||
const char *p1 = f1->name, *p2 = f2->name;
|
||||
@ -591,9 +590,10 @@ void clear_selection(bb_t *bb)
|
||||
{
|
||||
for (entry_t *next, *e = bb->firstselected; e; e = next) {
|
||||
next = e->next;
|
||||
*(e->atme) = NULL;
|
||||
e->atme = NULL;
|
||||
if (--e->refcount <= 0) free(e);
|
||||
}
|
||||
bb->firstselected = NULL;
|
||||
}
|
||||
|
||||
int select_file(bb_t *bb, entry_t *e)
|
||||
@ -663,6 +663,32 @@ void set_scroll(bb_t *bb, int newscroll)
|
||||
if (bb->cursor < 0) bb->cursor = 0;
|
||||
}
|
||||
|
||||
entry_t *load_entry(const char *path)
|
||||
{
|
||||
ssize_t linkpathlen = -1;
|
||||
char linkbuf[PATH_MAX];
|
||||
struct stat linkedstat, filestat;
|
||||
if (lstat(path, &filestat) == -1) return NULL;
|
||||
if (S_ISLNK(filestat.st_mode)) {
|
||||
linkpathlen = readlink(path, linkbuf, sizeof(linkbuf));
|
||||
if (linkpathlen < 0) err("Couldn't read link: '%s'", path);
|
||||
linkbuf[linkpathlen] = 0;
|
||||
if (stat(path, &linkedstat) == -1) memset(&linkedstat, 0, sizeof(linkedstat));
|
||||
}
|
||||
size_t entry_size = sizeof(entry_t) + (strlen(path) + 1) + (size_t)(linkpathlen + 1);
|
||||
entry_t *entry = memcheck(calloc(entry_size, 1));
|
||||
char *end = stpcpy(entry->fullname, path);
|
||||
if (linkpathlen >= 0)
|
||||
entry->linkname = strcpy(end + 1, linkbuf);
|
||||
entry->name = strrchr(entry->fullname, '/');
|
||||
if (!entry->name) err("No slash found in '%s'", entry->fullname);
|
||||
++entry->name;
|
||||
if (S_ISLNK(filestat.st_mode))
|
||||
entry->linkedmode = linkedstat.st_mode;
|
||||
entry->info = filestat;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void populate_files(bb_t *bb, const char *path)
|
||||
{
|
||||
ino_t old_inode = 0;
|
||||
@ -707,7 +733,9 @@ void populate_files(bb_t *bb, const char *path)
|
||||
err("Couldn't open dir: %s", bb->path);
|
||||
size_t pathlen = strlen(bb->path);
|
||||
size_t filecap = 0;
|
||||
char linkbuf[PATH_MAX+1];
|
||||
char pathbuf[PATH_MAX];
|
||||
strcpy(pathbuf, path);
|
||||
pathbuf[pathlen] = '/';
|
||||
for (struct dirent *dp; (dp = readdir(dir)) != NULL; ) {
|
||||
if (dp->d_name[0] == '.' && dp->d_name[1] == '\0')
|
||||
continue;
|
||||
@ -729,35 +757,10 @@ void populate_files(bb_t *bb, const char *path)
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t linkpathlen = -1;
|
||||
linkbuf[0] = 0;
|
||||
if (dp->d_type == DT_LNK) {
|
||||
linkpathlen = readlink(dp->d_name, linkbuf, sizeof(linkbuf));
|
||||
linkbuf[linkpathlen] = 0;
|
||||
}
|
||||
|
||||
entry_t *entry = memcheck(calloc(sizeof(entry_t) + pathlen + strlen(dp->d_name) + 2 + (size_t)(linkpathlen + 1), 1));
|
||||
if (pathlen > PATH_MAX) err("Path is too big");
|
||||
strncpy(entry->fullname, bb->path, pathlen);
|
||||
entry->fullname[pathlen] = '/';
|
||||
entry->name = &entry->fullname[pathlen + 1];
|
||||
strcpy(entry->name, dp->d_name);
|
||||
if (linkpathlen >= 0) {
|
||||
entry->linkname = entry->name + strlen(dp->d_name) + 2;
|
||||
strncpy(entry->linkname, linkbuf, linkpathlen+1);
|
||||
}
|
||||
entry->needs_esc = 1; // populate on-the-fly
|
||||
entry->link_needs_esc = 1; // populate on-the-fly
|
||||
entry->isdir = dp->d_type == DT_DIR;
|
||||
entry->islink = dp->d_type == DT_LNK;
|
||||
strcpy(&pathbuf[pathlen+1], dp->d_name);
|
||||
entry_t *entry = load_entry(pathbuf);
|
||||
if (!entry) err("Failed to load entry: '%s'", pathbuf);
|
||||
++entry->refcount;
|
||||
if (!entry->isdir && entry->islink) {
|
||||
struct stat statbuf;
|
||||
if (stat(entry->fullname, &statbuf) == 0)
|
||||
entry->isdir = S_ISDIR(statbuf.st_mode);
|
||||
}
|
||||
entry->next = NULL; entry->atme = NULL;
|
||||
lstat(entry->fullname, &entry->info);
|
||||
bb->files[bb->nfiles++] = entry;
|
||||
next_file:
|
||||
continue;
|
||||
@ -791,9 +794,12 @@ void sort_files(bb_t *bb)
|
||||
if (bb->options['s'] == SORT_RANDOM) {
|
||||
entry_t **files = &bb->files[1];
|
||||
int ndirs = 0, nents = bb->nfiles - 1;
|
||||
if (!bb->options['i']) {
|
||||
for (int i = 0; i < nents; i++) {
|
||||
if (bb->files[i]->isdir) ++ndirs;
|
||||
else break;
|
||||
if (S_ISDIR(bb->files[i]->info.st_mode)
|
||||
|| (S_ISLNK(bb->files[i]->info.st_mode) && S_ISDIR(bb->files[i]->linkedmode)))
|
||||
++ndirs;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < ndirs - 1; i++) {
|
||||
int j = i + rand() / (RAND_MAX / (ndirs - 1 - i));
|
||||
@ -816,8 +822,7 @@ void sort_files(bb_t *bb)
|
||||
}
|
||||
}
|
||||
|
||||
static enum { BB_NOP = 0, BB_INVALID, BB_REFRESH, BB_DIRTY, BB_QUIT }
|
||||
execute_cmd(bb_t *bb, const char *cmd)
|
||||
bb_result_t execute_cmd(bb_t *bb, const char *cmd)
|
||||
{
|
||||
char *value = strchr(cmd, ':');
|
||||
if (value) ++value;
|
||||
@ -857,7 +862,7 @@ execute_cmd(bb_t *bb, const char *cmd)
|
||||
if (f >= 0) select_file(bb, bb->files[f]);
|
||||
// TODO: support selecting files in other directories
|
||||
}
|
||||
return BB_DIRTY;
|
||||
return BB_REFRESH;
|
||||
}
|
||||
case 'c': { // cd:
|
||||
char pbuf[PATH_MAX];
|
||||
@ -898,7 +903,7 @@ execute_cmd(bb_t *bb, const char *cmd)
|
||||
entry_t *e = bb->files[f];
|
||||
if (IS_SELECTED(e)) deselect_file(bb, e);
|
||||
else select_file(bb, e);
|
||||
return f == bb->cursor ? BB_NOP : BB_DIRTY;
|
||||
return f == bb->cursor ? BB_NOP : BB_REFRESH;
|
||||
}
|
||||
case 'o': { // options:
|
||||
if (!value) return BB_INVALID;
|
||||
@ -923,12 +928,12 @@ execute_cmd(bb_t *bb, const char *cmd)
|
||||
if (!value) value = bb->files[bb->cursor]->name;
|
||||
if (strcmp(value, "*") == 0) {
|
||||
clear_selection(bb);
|
||||
return BB_DIRTY;
|
||||
return BB_REFRESH;
|
||||
} else {
|
||||
int f = find_file(bb, value);
|
||||
if (f >= 0)
|
||||
select_file(bb, bb->files[f]);
|
||||
return f == bb->cursor ? BB_NOP : BB_DIRTY;
|
||||
return f == bb->cursor ? BB_NOP : BB_REFRESH;
|
||||
}
|
||||
|
||||
case 'g': { // goto:
|
||||
@ -987,7 +992,7 @@ execute_cmd(bb_t *bb, const char *cmd)
|
||||
deselect_file(bb, bb->files[i]);
|
||||
}
|
||||
if (abs(oldcur - bb->cursor) > 1)
|
||||
return BB_DIRTY;
|
||||
return BB_REFRESH;
|
||||
}
|
||||
return BB_NOP;
|
||||
}
|
||||
@ -1002,7 +1007,7 @@ execute_cmd(bb_t *bb, const char *cmd)
|
||||
}
|
||||
return BB_INVALID;
|
||||
}
|
||||
default: break;
|
||||
default: err("UNKNOWN COMMAND: '%s'", cmd); break;
|
||||
}
|
||||
return BB_INVALID;
|
||||
}
|
||||
@ -1066,9 +1071,9 @@ void explore(bb_t *bb, const char *path)
|
||||
switch (execute_cmd(bb, cmd)) {
|
||||
case BB_INVALID:
|
||||
break;
|
||||
case BB_DIRTY:
|
||||
lazy = 0;
|
||||
case BB_NOP:
|
||||
free(cmd);
|
||||
fclose(cmdfile);
|
||||
goto redraw;
|
||||
case BB_REFRESH:
|
||||
free(cmd);
|
||||
@ -1090,7 +1095,7 @@ void explore(bb_t *bb, const char *path)
|
||||
|
||||
int key;
|
||||
get_keyboard_input:
|
||||
key = term_getkey(fileno(tty_in), &mouse_x, &mouse_y, KEY_DELAY);
|
||||
key = bgetkey(tty_in, &mouse_x, &mouse_y, KEY_DELAY);
|
||||
switch (key) {
|
||||
case KEY_MOUSE_LEFT: {
|
||||
struct timespec clicktime;
|
||||
@ -1137,10 +1142,8 @@ void explore(bb_t *bb, const char *path)
|
||||
goto quit; // Unreachable
|
||||
|
||||
case KEY_CTRL_Z:
|
||||
fputs(T_OFF(T_ALT_SCREEN), tty_out);
|
||||
close_term();
|
||||
fputs(T_OFF(T_ALT_SCREEN), stdout);
|
||||
fputs(T_ON(T_WRAP), stdout);
|
||||
fflush(stdout);
|
||||
raise(SIGTSTP);
|
||||
init_term();
|
||||
fputs(T_ON(T_ALT_SCREEN), tty_out);
|
||||
@ -1150,11 +1153,11 @@ void explore(bb_t *bb, const char *path)
|
||||
case KEY_CTRL_H: {
|
||||
move_cursor(tty_out, 0,termheight-1);
|
||||
fputs("\033[K\033[33;1mPress any key...\033[0m", tty_out);
|
||||
while ((key = term_getkey(fileno(tty_in), &mouse_x, &mouse_y, 1000)) == -1)
|
||||
while ((key = bgetkey(tty_in, &mouse_x, &mouse_y, 1000)) == -1)
|
||||
;
|
||||
move_cursor(tty_out, 0,termheight-1);
|
||||
fputs("\033[K\033[1m<\033[33m", tty_out);
|
||||
const char *name = keyname(key);
|
||||
const char *name = bkeyname(key);
|
||||
if (name) fputs(name, tty_out);
|
||||
else if (' ' <= key && key <= '~')
|
||||
fputc((char)key, tty_out);
|
||||
@ -1207,7 +1210,6 @@ void explore(bb_t *bb, const char *path)
|
||||
if (binding->command[0] == '+') {
|
||||
switch (execute_cmd(bb, binding->command + 1)) {
|
||||
case BB_INVALID: break;
|
||||
case BB_DIRTY: lazy = 0;
|
||||
case BB_NOP: goto redraw;
|
||||
case BB_REFRESH: goto refresh;
|
||||
case BB_QUIT: goto quit;
|
||||
@ -1215,14 +1217,15 @@ void explore(bb_t *bb, const char *path)
|
||||
goto get_keyboard_input;
|
||||
}
|
||||
move_cursor(tty_out, 0, termheight-1);
|
||||
close_term();
|
||||
if (binding->flags & NORMAL_TERM) {
|
||||
fputs(T_OFF(T_ALT_SCREEN), stdout);
|
||||
fputs(T_ON(T_WRAP), stdout);
|
||||
fflush(stdout);
|
||||
fputs(T_OFF(T_ALT_SCREEN), tty_out);
|
||||
fputs(T_ON(T_WRAP), tty_out);
|
||||
}
|
||||
if (binding->flags & SHOW_CURSOR)
|
||||
fputs(T_ON(T_SHOW_CURSOR), stdout);
|
||||
if (binding->flags & AT_CURSOR && !bb->firstselected) {
|
||||
move_cursor(tty_out, 0, 3 + bb->cursor - bb->scroll);
|
||||
fputs("\033[K", tty_out);
|
||||
}
|
||||
close_term();
|
||||
run_cmd_on_selection(bb, binding->command);
|
||||
init_term();
|
||||
if (binding->flags & NORMAL_TERM)
|
||||
@ -1239,9 +1242,6 @@ void explore(bb_t *bb, const char *path)
|
||||
populate_files(bb, NULL);
|
||||
fputs(T_LEAVE_BBMODE, tty_out);
|
||||
close_term();
|
||||
fputs(T_OFF(T_ALT_SCREEN), stdout);
|
||||
fputs(T_ON(T_WRAP), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void print_bindings(int verbose)
|
||||
@ -1259,7 +1259,7 @@ void print_bindings(int verbose)
|
||||
for (int j = 0; bindings[i].keys[j]; j++) {
|
||||
if (j > 0) *(p++) = ',';
|
||||
int key = bindings[i].keys[j];
|
||||
const char *name = keyname(key);
|
||||
const char *name = bkeyname(key);
|
||||
if (name)
|
||||
p = stpcpy(p, name);
|
||||
else if (' ' <= key && key <= '~')
|
||||
@ -1327,6 +1327,17 @@ int main(int argc, char *argv[])
|
||||
|
||||
int i;
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '?') {
|
||||
fclose(cmdfile);
|
||||
init_term();
|
||||
char *line = breadline(tty_in, tty_out, argv[i] + 1, argv[i+1]);
|
||||
close_term();
|
||||
if (!line) return 1;
|
||||
fputs(line, stdout);
|
||||
free(line);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
if (argv[i][0] == '+') {
|
||||
fwrite(argv[i]+1, sizeof(char), strlen(argv[i]+1)+1, cmdfile);
|
||||
continue;
|
||||
@ -1371,9 +1382,13 @@ int main(int argc, char *argv[])
|
||||
if (cmdfile)
|
||||
fclose(cmdfile);
|
||||
|
||||
if (!initial_path) {
|
||||
if (!initial_path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Default values
|
||||
setenv("SHELL", "bash", 0);
|
||||
setenv("EDITOR", "nano", 0);
|
||||
setenv("PAGER", "less", 0);
|
||||
|
||||
signal(SIGTERM, cleanup_and_exit);
|
||||
signal(SIGINT, cleanup_and_exit);
|
||||
|
431
bterm.h
Normal file
431
bterm.h
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* bterm.h
|
||||
* Copyright 2019 Bruce Hill
|
||||
* Released under the MIT License
|
||||
*
|
||||
* Definitions of some basic terminal stuff, like reading keys
|
||||
* and some terminal escape sequences.
|
||||
*/
|
||||
|
||||
#ifndef FILE__BTERM_H
|
||||
#define FILE__BTERM_H
|
||||
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define KEY_F1 (0xFFFF-0)
|
||||
#define KEY_F2 (0xFFFF-1)
|
||||
#define KEY_F3 (0xFFFF-2)
|
||||
#define KEY_F4 (0xFFFF-3)
|
||||
#define KEY_F5 (0xFFFF-4)
|
||||
#define KEY_F6 (0xFFFF-5)
|
||||
#define KEY_F7 (0xFFFF-6)
|
||||
#define KEY_F8 (0xFFFF-7)
|
||||
#define KEY_F9 (0xFFFF-8)
|
||||
#define KEY_F10 (0xFFFF-9)
|
||||
#define KEY_F11 (0xFFFF-10)
|
||||
#define KEY_F12 (0xFFFF-11)
|
||||
#define KEY_INSERT (0xFFFF-12)
|
||||
#define KEY_DELETE (0xFFFF-13)
|
||||
#define KEY_HOME (0xFFFF-14)
|
||||
#define KEY_END (0xFFFF-15)
|
||||
#define KEY_PGUP (0xFFFF-16)
|
||||
#define KEY_PGDN (0xFFFF-17)
|
||||
#define KEY_ARROW_UP (0xFFFF-18)
|
||||
#define KEY_ARROW_DOWN (0xFFFF-19)
|
||||
#define KEY_ARROW_LEFT (0xFFFF-20)
|
||||
#define KEY_ARROW_RIGHT (0xFFFF-21)
|
||||
#define KEY_MOUSE_LEFT (0xFFFF-22)
|
||||
#define KEY_MOUSE_RIGHT (0xFFFF-23)
|
||||
#define KEY_MOUSE_MIDDLE (0xFFFF-24)
|
||||
#define KEY_MOUSE_RELEASE (0xFFFF-25)
|
||||
#define KEY_MOUSE_WHEEL_UP (0xFFFF-26)
|
||||
#define KEY_MOUSE_WHEEL_DOWN (0xFFFF-27)
|
||||
#define KEY_MOUSE_DOUBLE_LEFT (0xFFFF-28)
|
||||
|
||||
/* These are all ASCII code points below SPACE character and a BACKSPACE key. */
|
||||
#define KEY_CTRL_TILDE 0x00
|
||||
#define KEY_CTRL_2 0x00 /* clash with 'CTRL_TILDE' */
|
||||
#define KEY_CTRL_A 0x01
|
||||
#define KEY_CTRL_B 0x02
|
||||
#define KEY_CTRL_C 0x03
|
||||
#define KEY_CTRL_D 0x04
|
||||
#define KEY_CTRL_E 0x05
|
||||
#define KEY_CTRL_F 0x06
|
||||
#define KEY_CTRL_G 0x07
|
||||
#define KEY_BACKSPACE 0x08
|
||||
#define KEY_CTRL_H 0x08 /* clash with 'CTRL_BACKSPACE' */
|
||||
#define KEY_TAB 0x09
|
||||
#define KEY_CTRL_I 0x09 /* clash with 'TAB' */
|
||||
#define KEY_CTRL_J 0x0A
|
||||
#define KEY_CTRL_K 0x0B
|
||||
#define KEY_CTRL_L 0x0C
|
||||
#define KEY_ENTER 0x0D
|
||||
#define KEY_CTRL_M 0x0D /* clash with 'ENTER' */
|
||||
#define KEY_CTRL_N 0x0E
|
||||
#define KEY_CTRL_O 0x0F
|
||||
#define KEY_CTRL_P 0x10
|
||||
#define KEY_CTRL_Q 0x11
|
||||
#define KEY_CTRL_R 0x12
|
||||
#define KEY_CTRL_S 0x13
|
||||
#define KEY_CTRL_T 0x14
|
||||
#define KEY_CTRL_U 0x15
|
||||
#define KEY_CTRL_V 0x16
|
||||
#define KEY_CTRL_W 0x17
|
||||
#define KEY_CTRL_X 0x18
|
||||
#define KEY_CTRL_Y 0x19
|
||||
#define KEY_CTRL_Z 0x1A
|
||||
#define KEY_ESC 0x1B
|
||||
#define KEY_CTRL_LSQ_BRACKET 0x1B /* clash with 'ESC' */
|
||||
#define KEY_CTRL_3 0x1B /* clash with 'ESC' */
|
||||
#define KEY_CTRL_4 0x1C
|
||||
#define KEY_CTRL_BACKSLASH 0x1C /* clash with 'CTRL_4' */
|
||||
#define KEY_CTRL_5 0x1D
|
||||
#define KEY_CTRL_RSQ_BRACKET 0x1D /* clash with 'CTRL_5' */
|
||||
#define KEY_CTRL_6 0x1E
|
||||
#define KEY_CTRL_7 0x1F
|
||||
#define KEY_CTRL_SLASH 0x1F /* clash with 'CTRL_7' */
|
||||
#define KEY_CTRL_UNDERSCORE 0x1F /* clash with 'CTRL_7' */
|
||||
#define KEY_SPACE 0x20
|
||||
#define KEY_BACKSPACE2 0x7F
|
||||
#define KEY_CTRL_8 0x7F /* clash with 'BACKSPACE2' */
|
||||
|
||||
|
||||
// Terminal escape sequences:
|
||||
#define CSI "\033["
|
||||
#define T_WRAP "7"
|
||||
#define T_SHOW_CURSOR "25"
|
||||
#define T_MOUSE_XY "1000"
|
||||
#define T_MOUSE_CELL "1002"
|
||||
#define T_MOUSE_SGR "1006"
|
||||
#define T_ALT_SCREEN "1049"
|
||||
#define T_ON(opt) CSI "?" opt "h"
|
||||
#define T_OFF(opt) CSI "?" opt "l"
|
||||
|
||||
#define move_cursor(f, x, y) fprintf((f), CSI "%d;%dH", (int)(y)+1, (int)(x)+1)
|
||||
|
||||
|
||||
#define ESC_DELAY 10
|
||||
|
||||
int bgetkey(FILE *in, int *mouse_x, int *mouse_y, int timeout);
|
||||
const char *bkeyname(int key);
|
||||
char *breadline(FILE *in, FILE *out, const char *prompt, const char *initial);
|
||||
|
||||
static inline int nextchar(int fd, int timeout)
|
||||
{
|
||||
char c;
|
||||
struct pollfd pfd = {fd, POLLIN, 0};
|
||||
if (poll(&pfd, 1, timeout) > 0)
|
||||
return read(fd, &c, 1) == 1 ? c : -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one key of input from the given file. Returns -1 on failure.
|
||||
*
|
||||
* If mouse_x or mouse_y are non-null and a mouse event occurs, they will be
|
||||
* set to the position of the mouse (0-indexed).
|
||||
*/
|
||||
int bgetkey(FILE *in, int *mouse_x, int *mouse_y, int timeout)
|
||||
{
|
||||
int fd = fileno(in);
|
||||
int numcode = 0, super = 0;
|
||||
int c = nextchar(fd, timeout);
|
||||
if (c == '\x1b')
|
||||
goto escape;
|
||||
|
||||
return c;
|
||||
|
||||
escape:
|
||||
c = nextchar(fd, ESC_DELAY);
|
||||
// Actual escape key:
|
||||
if (c < 0)
|
||||
return KEY_ESC;
|
||||
|
||||
switch (c) {
|
||||
case '\x1b': ++super; goto escape;
|
||||
case '[': goto CSI_start;
|
||||
case 'P': goto DCS;
|
||||
case 'O': goto SS3;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
CSI_start:
|
||||
c = nextchar(fd, ESC_DELAY);
|
||||
if (c == -1)
|
||||
return -1;
|
||||
|
||||
switch (c) {
|
||||
case 'A': return KEY_ARROW_UP;
|
||||
case 'B': return KEY_ARROW_DOWN;
|
||||
case 'C': return KEY_ARROW_RIGHT;
|
||||
case 'D': return KEY_ARROW_LEFT;
|
||||
case 'F': return KEY_END;
|
||||
case 'H': return KEY_HOME;
|
||||
case '~':
|
||||
switch (numcode) {
|
||||
case 3: return KEY_DELETE;
|
||||
case 5: return KEY_PGUP;
|
||||
case 6: return KEY_PGDN;
|
||||
case 15: return KEY_F5;
|
||||
case 17: return KEY_F6;
|
||||
case 18: return KEY_F7;
|
||||
case 19: return KEY_F8;
|
||||
case 20: return KEY_F9;
|
||||
case 21: return KEY_F10;
|
||||
case 23: return KEY_F11;
|
||||
case 24: return KEY_F12;
|
||||
}
|
||||
return -1;
|
||||
case '<': { // Mouse clicks
|
||||
int buttons = 0, x = 0, y = 0;
|
||||
char buf;
|
||||
while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||
buttons = buttons * 10 + (buf - '0');
|
||||
if (buf != ';') return -1;
|
||||
while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||
x = x * 10 + (buf - '0');
|
||||
if (buf != ';') return -1;
|
||||
while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||
y = y * 10 + (buf - '0');
|
||||
if (buf != 'm' && buf != 'M') return -1;
|
||||
|
||||
if (mouse_x) *mouse_x = x - 1;
|
||||
if (mouse_y) *mouse_y = y - 1;
|
||||
|
||||
if (buf == 'm')
|
||||
return KEY_MOUSE_RELEASE;
|
||||
switch (buttons) {
|
||||
case 64: return KEY_MOUSE_WHEEL_UP;
|
||||
case 65: return KEY_MOUSE_WHEEL_DOWN;
|
||||
case 0: return KEY_MOUSE_LEFT;
|
||||
case 1: return KEY_MOUSE_RIGHT;
|
||||
case 2: return KEY_MOUSE_MIDDLE;
|
||||
default: return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if ('0' <= c && c <= '9') {
|
||||
// Ps prefix
|
||||
numcode = 10*numcode + (c - '0');
|
||||
goto CSI_start;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
DCS:
|
||||
return -1;
|
||||
|
||||
SS3:
|
||||
switch (nextchar(fd, ESC_DELAY)) {
|
||||
case 'P': return KEY_F1;
|
||||
case 'Q': return KEY_F2;
|
||||
case 'R': return KEY_F3;
|
||||
case 'S': return KEY_F4;
|
||||
default: break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of a key, if one exists and is different from the key itself
|
||||
* (i.e. bkeyname('c') == NULL, bkeyname(' ') == "Space")
|
||||
*/
|
||||
const char *bkeyname(int key)
|
||||
{
|
||||
// TODO: currently only the keys I'm using are named
|
||||
switch (key) {
|
||||
case KEY_F1: return "F1";
|
||||
case KEY_F2: return "F2";
|
||||
case KEY_F3: return "F3";
|
||||
case KEY_F4: return "F4";
|
||||
case KEY_F5: return "F5";
|
||||
case KEY_F6: return "F6";
|
||||
case KEY_F7: return "F7";
|
||||
case KEY_F8: return "F8";
|
||||
case KEY_F9: return "F9";
|
||||
case KEY_F10: return "F10";
|
||||
case KEY_F11: return "F11";
|
||||
case KEY_F12: return "F12";
|
||||
case KEY_INSERT: return "Insert";
|
||||
case KEY_DELETE: return "Delete";
|
||||
case KEY_HOME: return "Home";
|
||||
case KEY_END: return "End";
|
||||
case KEY_PGUP: return "PgUp";
|
||||
case KEY_PGDN: return "PgDn";
|
||||
case KEY_ARROW_UP: return "Up";
|
||||
case KEY_ARROW_DOWN: return "Down";
|
||||
case KEY_ARROW_LEFT: return "Left";
|
||||
case KEY_ARROW_RIGHT: return "Right";
|
||||
case KEY_MOUSE_LEFT: return "Left click";
|
||||
case KEY_MOUSE_RIGHT: return "Right click";
|
||||
case KEY_MOUSE_MIDDLE: return "Middle click";
|
||||
case KEY_MOUSE_RELEASE: return "Mouse release";
|
||||
case KEY_MOUSE_WHEEL_UP: return "Mouse wheel up";
|
||||
case KEY_MOUSE_WHEEL_DOWN: return "Mouse wheel down";
|
||||
case KEY_MOUSE_DOUBLE_LEFT: return "Mouse double left";
|
||||
case KEY_CTRL_TILDE: return "Ctrl-[2~]";
|
||||
case KEY_CTRL_A: return "Ctrl-a";
|
||||
case KEY_CTRL_B: return "Ctrl-b";
|
||||
case KEY_CTRL_C: return "Ctrl-c";
|
||||
case KEY_CTRL_D: return "Ctrl-d";
|
||||
case KEY_CTRL_E: return "Ctrl-e";
|
||||
case KEY_CTRL_F: return "Ctrl-f";
|
||||
case KEY_CTRL_G: return "Ctrl-g";
|
||||
case KEY_BACKSPACE: return "Backspace";
|
||||
case KEY_TAB: return "Tab";
|
||||
case KEY_CTRL_J: return "Ctrl-j";
|
||||
case KEY_CTRL_K: return "Ctrl-k";
|
||||
case KEY_CTRL_L: return "Ctrl-l";
|
||||
case KEY_ENTER: return "Enter";
|
||||
case KEY_CTRL_N: return "Ctrl-n";
|
||||
case KEY_CTRL_O: return "Ctrl-o";
|
||||
case KEY_CTRL_P: return "Ctrl-p";
|
||||
case KEY_CTRL_Q: return "Ctrl-q";
|
||||
case KEY_CTRL_R: return "Ctrl-r";
|
||||
case KEY_CTRL_S: return "Ctrl-s";
|
||||
case KEY_CTRL_T: return "Ctrl-t";
|
||||
case KEY_CTRL_U: return "Ctrl-u";
|
||||
case KEY_CTRL_V: return "Ctrl-v";
|
||||
case KEY_CTRL_W: return "Ctrl-w";
|
||||
case KEY_CTRL_X: return "Ctrl-x";
|
||||
case KEY_CTRL_Y: return "Ctrl-y";
|
||||
case KEY_CTRL_Z: return "Ctrl-z";
|
||||
case KEY_ESC: return "Esc";
|
||||
case KEY_CTRL_BACKSLASH: return "Ctrl-[4\\]";
|
||||
case KEY_CTRL_RSQ_BRACKET: return "Ctrl-[5]]";
|
||||
case KEY_CTRL_6: return "Ctrl-6";
|
||||
case KEY_CTRL_SLASH: return "Ctrl-[7/_]";
|
||||
case KEY_SPACE: return "Space";
|
||||
case KEY_BACKSPACE2: return "Backspace";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic readline implementation. No history, but all the normal editing
|
||||
* key bindings like Ctrl-U to clear to the beginning of the line, backspace,
|
||||
* arrow keys, etc.
|
||||
*
|
||||
* Takes an input file and output file, and an optional prompt and returns
|
||||
* a malloc'd string containing the user's input or NULL if an error occurred
|
||||
* or if the user hit Escape or Ctrl-c.
|
||||
*/
|
||||
char *breadline(FILE *in, FILE *out, const char *prompt, const char *initial)
|
||||
{
|
||||
size_t cap = initial ? strlen(initial) + 100 : 100;
|
||||
char *buf = calloc(cap, 1);
|
||||
if (!buf) return NULL;
|
||||
int i = 0, len = 0;
|
||||
if (prompt) {
|
||||
fputs("\033[1G\033[K\033[37;1m", out);
|
||||
fputs(prompt, out);
|
||||
fputs("\033[0m", out);
|
||||
}
|
||||
if (initial) {
|
||||
strcpy(buf, initial);
|
||||
len = (int)strlen(initial);
|
||||
i = len;
|
||||
fputs(initial, out);
|
||||
}
|
||||
// Show cursor and set to blinking line:
|
||||
fputs("\033[?25h\033[5 q", out);
|
||||
while (1) {
|
||||
fflush(out);
|
||||
int key = bgetkey(in, NULL, NULL, -1);
|
||||
switch (key) {
|
||||
case -1: case -2: case -3: continue;
|
||||
case '\r':
|
||||
// TODO: support backslash-enter
|
||||
goto finished;
|
||||
case KEY_CTRL_C: case KEY_ESC:
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
goto finished;
|
||||
case KEY_CTRL_A:
|
||||
if (i > 0) {
|
||||
fprintf(out, "\033[%dD", i);
|
||||
i = 0;
|
||||
}
|
||||
break;
|
||||
case KEY_CTRL_E:
|
||||
if (i < len) {
|
||||
fprintf(out, "\033[%dC", len-i);
|
||||
i = len;
|
||||
}
|
||||
break;
|
||||
case KEY_CTRL_U: {
|
||||
int to_clear = i;
|
||||
if (to_clear) {
|
||||
memmove(buf, buf+i, len-i);
|
||||
buf[len -= i] = 0;
|
||||
i = 0;
|
||||
fprintf(out, "\033[%dD\033[K", to_clear);
|
||||
if (len > 0)
|
||||
fprintf(out, "%s\033[%dD", buf, len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KEY_CTRL_K:
|
||||
if (i < len) {
|
||||
buf[len = i] = 0;
|
||||
fputs("\033[K", out);
|
||||
}
|
||||
break;
|
||||
case KEY_BACKSPACE: case KEY_BACKSPACE2:
|
||||
if (i > 0) {
|
||||
memmove(buf+i, buf+i+1, len-i);
|
||||
--i;
|
||||
buf[--len] = 0;
|
||||
if (i == len) fputs("\033[D \033[D", out);
|
||||
else fprintf(out, "\033[D%s\033[K\033[%dD", buf+i, len-i);
|
||||
}
|
||||
break;
|
||||
case KEY_DELETE: case KEY_CTRL_D:
|
||||
if (i < len) {
|
||||
memmove(buf+i, buf+i+1, len-i);
|
||||
buf[--len] = 0;
|
||||
if (i == len) fputs(" \033[D", out);
|
||||
else fprintf(out, "%s\033[K\033[%dD", buf+i, len-i);
|
||||
}
|
||||
break;
|
||||
case KEY_ARROW_LEFT: case KEY_CTRL_B:
|
||||
if (i > 0) {
|
||||
--i;
|
||||
fputs("\033[D", out);
|
||||
}
|
||||
break;
|
||||
case KEY_ARROW_RIGHT: case KEY_CTRL_F:
|
||||
if (i < len) {
|
||||
++i;
|
||||
fputs("\033[C", out);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (' ' <= key && key <= '~') {
|
||||
if (len + 1 >= (int)cap) {
|
||||
cap += 100;
|
||||
buf = realloc(buf, cap);
|
||||
if (!buf) goto finished;
|
||||
}
|
||||
if (i < len)
|
||||
memmove(buf+i+1, buf+i, len-i+1);
|
||||
buf[i++] = (char)key;
|
||||
buf[++len] = 0;
|
||||
if (i == len)
|
||||
fputc(key, out);
|
||||
else
|
||||
fprintf(out, "%c%s\033[%dD", (char)key, buf+i, len-i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
finished:
|
||||
// Reset cursor to block
|
||||
fputs("\033[1G\033[1 q\033[2K", out);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1
|
108
config.def.h
108
config.def.h
@ -13,8 +13,7 @@
|
||||
When the scripts are run, the following values are provided as environment variables:
|
||||
|
||||
$@ (the list of arguments): the full paths of the selected files
|
||||
$BBCURSOR: the (short) name of the file under the cursor
|
||||
$BBFULLCURSOR: the full path name of the file under the cursor
|
||||
$BBCURSOR: the full name of the file under the cursor
|
||||
$BB_DEPTH: the number of `bb` instances deep (in case you want to run a
|
||||
shell and have that shell print something special in the prompt)
|
||||
$BBCMD: a file to which `bb` commands can be written (used internally)
|
||||
@ -62,28 +61,33 @@
|
||||
half a screen height down, and 100%n means the last file)
|
||||
|
||||
*/
|
||||
#include "keys.h"
|
||||
#include "bterm.h"
|
||||
|
||||
// Configurable options:
|
||||
#define KEY_DELAY 50
|
||||
#define SCROLLOFF MIN(5, (termheight-4)/2)
|
||||
|
||||
#define QUOTE(s) #s
|
||||
|
||||
#define CMDFILE_FORMAT "/tmp/bb.XXXXXX"
|
||||
|
||||
#define SORT_INDICATOR "↓"
|
||||
#define RSORT_INDICATOR "↑"
|
||||
#define SELECTED_INDICATOR "\033[33;1m★\033[0m"
|
||||
#define NOT_SELECTED_INDICATOR "\033[1m \033[0m"
|
||||
|
||||
#define NORMAL_COLOR "\033[0m"
|
||||
#define CURSOR_COLOR "\033[0;30;43m"
|
||||
#define LINKDIR_COLOR "\033[0;36m"
|
||||
#define DIR_COLOR "\033[0;34m"
|
||||
#define LINK_COLOR "\033[0;33m"
|
||||
#define TITLE_COLOR "\033[32;1m"
|
||||
#define NORMAL_COLOR "\033[37m"
|
||||
#define CURSOR_COLOR "\033[1;30;43m"
|
||||
#define LINK_COLOR "\033[35m"
|
||||
#define DIR_COLOR "\033[34m"
|
||||
#define EXECUTABLE_COLOR "\033[31m"
|
||||
|
||||
#define PIPE_SELECTION_TO " printf '%s\\n' \"$@\" | "
|
||||
#define PAUSE " read -n1 -p '\033[2m...press any key to continue...\033[0m\033[?25l'"
|
||||
#define PAUSE " read -n1 -p '\033[2mPress any key to continue...\033[0m\033[?25l'"
|
||||
|
||||
#define NORMAL_TERM (1<<0)
|
||||
#define SHOW_CURSOR (1<<1)
|
||||
#define AT_CURSOR (1<<1)
|
||||
|
||||
#define MAX_REBINDINGS 8
|
||||
|
||||
@ -108,6 +112,8 @@ const char *startupcmds[] = {
|
||||
NULL, // NULL-terminated array
|
||||
};
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
|
||||
extern binding_t bindings[];
|
||||
binding_t bindings[] = {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -115,7 +121,7 @@ binding_t bindings[] = {
|
||||
// Please note that these are sh scripts, not bash scripts, so bash-isms
|
||||
// won't work unless you make your script use `bash -c "<your script>"`
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
{{'?'}, "bb -b | less -r", "Show the help menu", NORMAL_TERM},
|
||||
{{'?'}, "bb -b | $PAGER -r", "Show the help menu", NORMAL_TERM},
|
||||
{{KEY_CTRL_H}, "<placeholder>", "Figure out what key does"},
|
||||
{{'q', 'Q'}, "+quit", "Quit"},
|
||||
{{'k', KEY_ARROW_UP}, "+move:-1", "Move up"},
|
||||
@ -126,42 +132,60 @@ binding_t bindings[] = {
|
||||
{{'e'}, "$EDITOR \"$@\"", "Edit file in $EDITOR", NORMAL_TERM},
|
||||
{{'\r', KEY_MOUSE_DOUBLE_LEFT},
|
||||
#ifdef __APPLE__
|
||||
"if test -d \"$BBCURSOR\"; then bb \"+cd:$BBCURSOR\";\n\
|
||||
elif test -x \"$BBCURSOR\"; then \"$BBCURSOR\";\n\
|
||||
read -n1 -p '\n\033[2m...press any key to continue...\033[0m';\n\
|
||||
elif file -bI \"$BBCURSOR\" | grep '^\\(text/\\|inode/empty\\)' >/dev/null; then $EDITOR \"$BBCURSOR\";\n\
|
||||
else open \"$BBCURSOR\"; fi",
|
||||
QUOTE(
|
||||
if test -d "$BBCURSOR"; then bb "+cd:$BBCURSOR";
|
||||
elif test -x "$BBCURSOR"; then "$BBCURSOR"; read -p 'Press any key to continue...' -n1;
|
||||
elif file -bI "$BBCURSOR" | grep '^\(text/\|inode/empty\)' >/dev/null; then $EDITOR "$BBCURSOR";
|
||||
else open "$BBCURSOR"; fi
|
||||
)/*ENDQUOTE*/,
|
||||
#else
|
||||
"if test -d \"$BBCURSOR\"; then bb \"+cd:$BBCURSOR\";\n\
|
||||
elif file -bi \"$BBCURSOR\" | grep '^\\(text/\\|inode/empty\\)' >/dev/null; then $EDITOR \"$BBCURSOR\";\n\
|
||||
else xdg-open \"$BBCURSOR\"; fi",
|
||||
QUOTE(
|
||||
if test -d "$BBCURSOR"; then bb "+cd:$BBCURSOR";
|
||||
elif file -bi "$BBCURSOR" | grep '^\(text/\|inode/empty\)' >/dev/null; then $EDITOR "$BBCURSOR";
|
||||
else xdg-open "$BBCURSOR"; fi
|
||||
)/*ENDQUOTE*/,
|
||||
#endif
|
||||
"Open file", NORMAL_TERM},
|
||||
{{'f'}, "bb \"+g:`fzf`\"", "Fuzzy search for file", NORMAL_TERM},
|
||||
{{'/'}, "bb \"+g:`ls -a|fzf`\"", "Fuzzy select file", NORMAL_TERM},
|
||||
{{'L'}, PIPE_SELECTION_TO "less", "List all selected files", NORMAL_TERM},
|
||||
{{'d', KEY_DELETE}, "rm -rfi \"$@\"; bb '+d:*' +r", "Delete files", SHOW_CURSOR},
|
||||
{{'L'}, PIPE_SELECTION_TO "$PAGER", "List all selected files", NORMAL_TERM},
|
||||
{{'d', KEY_DELETE}, "rm -rfi \"$@\"; bb '+d:*' +r", "Delete files", AT_CURSOR},
|
||||
{{'D'}, "rm -rf \"$@\"; bb '+d:*' +r", "Delete files without confirmation"},
|
||||
{{'M'}, "mv -i \"$@\" .; bb '+d:*' +r", "Move files to current folder", SHOW_CURSOR},
|
||||
{{'c'}, "cp -i \"$@\" .; bb +r", "Copy files to current folder", SHOW_CURSOR},
|
||||
{{'M'}, "mv -i \"$@\" .; bb '+d:*' +r", "Move files to current folder"},
|
||||
{{'c'}, "cp -i \"$@\" .; bb +r", "Copy files to current folder"},
|
||||
{{'C'}, "for f; do cp \"$f\" \"$f.copy\"; done; bb +r", "Clone files"},
|
||||
{{'n'}, "read -p 'New file: ' name && touch \"$name\"; bb +r \"+goto:$name\"", "New file", SHOW_CURSOR},
|
||||
{{'N'}, "read -p 'New dir: ' name && mkdir \"$name\"; bb +r \"+goto:$name\"", "New folder", SHOW_CURSOR},
|
||||
{{'|'}, "read -p '|' cmd && " PIPE_SELECTION_TO "sh -c \"$cmd\" && " PAUSE "; bb +r",
|
||||
"Pipe selected files to a command", SHOW_CURSOR},
|
||||
{{':'}, "read -p ':' cmd && sh -c \"$cmd\" -- \"$@\"; " PAUSE "; bb +r",
|
||||
"Run a command", SHOW_CURSOR},
|
||||
{{'>'}, "sh", "Open a shell", NORMAL_TERM},
|
||||
{{'m'}, "read -n1 -p 'Mark: ' m && bb \"+mark:$m;$PWD\"", "Set mark", SHOW_CURSOR},
|
||||
{{'\''}, "read -n1 -p 'Jump: ' j && bb \"+jump:$j\"", "Jump to mark", SHOW_CURSOR},
|
||||
{{'r'}, "for f; do read -p \"Rename $f: \" renamed && mv \"$f\" \"$renamed\"; done; "
|
||||
"bb '+d:*' +r",
|
||||
"Rename files", SHOW_CURSOR},
|
||||
{{'R'}, "read -p 'Rename pattern: ' patt && "
|
||||
"for f; do mv -i \"$f\" \"`echo \"$f\" | sed \"$patt\"`\"; done &&" PAUSE,
|
||||
"Regex rename files", SHOW_CURSOR},
|
||||
{{'n'}, "name=`bb '?New file: '` && touch \"$name\"; bb +r \"+goto:$name\"", "New file"},
|
||||
{{'N'}, "name=`bb '?New dir: '` && mkdir \"$name\"; bb +r \"+goto:$name\"", "New folder"},
|
||||
{{'|'}, "cmd=`bb '?|'` && " PIPE_SELECTION_TO "sh -c \"$cmd\" && " PAUSE "; bb +r",
|
||||
"Pipe selected files to a command"},
|
||||
{{':'}, "$SHELL -c \"`bb '?:'`\" -- \"$@\"; " PAUSE "; bb +refresh",
|
||||
"Run a command"},
|
||||
{{'>'}, "$SHELL", "Open a shell", NORMAL_TERM},
|
||||
{{'m'}, "read -n1 -p 'Mark: ' m && bb \"+mark:$m;$PWD\"", "Set mark"},
|
||||
{{'\''}, "read -n1 -p 'Jump: ' j && bb \"+jump:$j\"", "Jump to mark"},
|
||||
|
||||
{{'r'}, QUOTE(
|
||||
bb '+deselect:*' +refresh;
|
||||
for f; do
|
||||
if renamed="$(dirname "$f")/$(bb '?Rename: ' "$(basename "$f")")" &&
|
||||
test "$f" != "$renamed" && mv -i "$f" "$renamed"; then
|
||||
test $BBSELECTED && bb "+select:$renamed";
|
||||
elif test $BBSELECTED; then bb "+select:$f"; fi
|
||||
done)/*ENDQUOTE*/, "Rename files", AT_CURSOR},
|
||||
|
||||
{{'R'}, QUOTE(
|
||||
if patt="`bb '?Rename pattern: ' 's/'`"; then true; else bb +r; exit; fi;
|
||||
if sed -E "$patt" </dev/null; then true; else read -p 'Press any key to continue...' -n1; bb +r; exit; fi;
|
||||
bb '+deselect:*' +refresh;
|
||||
for f; do
|
||||
renamed="`dirname "$f"`/`basename "$f" | sed -E "$patt"`";
|
||||
if test "$f" != "$renamed" && mv -i "$f" "$renamed"; then
|
||||
test $BBSELECTED && bb "+select:$renamed";
|
||||
elif test $BBSELECTED; then bb "+select:$f"; fi
|
||||
done)/*ENDQUOTE*/, "Regex rename files", AT_CURSOR},
|
||||
|
||||
// TODO debug:
|
||||
{{'P'}, "read -p 'Select pattern: ' patt && "
|
||||
{{'P'}, "patt=`bb '?Select pattern: '` && "
|
||||
"for f; do echo \"$f\" | grep \"$patt\" >/dev/null 2>/dev/null && bb \"+sel:$f\"; done",
|
||||
"Regex select files"},
|
||||
{{'J'}, "+spread:+1", "Spread selection down"},
|
||||
@ -170,7 +194,8 @@ else xdg-open \"$BBCURSOR\"; fi",
|
||||
"\033[1m(s)\033[22mize \033[1m(m)\033[22modification \033[1m(c)\033[22mcreation "
|
||||
"\033[1m(a)\033[22maccess \033[1m(r)\033[22mandom \033[1m(p)\033[22mermissions:\033[0m ' sort "
|
||||
"&& bb \"+opt:s=$sort\"", "Sort by..."},
|
||||
{{'#'}, "read -p 'Set columns: ' cols && bb \"+opt:`echo $cols 0 | fold -w1 | sed 10q | nl -v0 -s= -w1 | paste -sd '\\0' -`\"",
|
||||
{{'#'}, "cols=`bb '?Set columns: '` && "
|
||||
"bb \"+opt:`echo $cols 0 | fold -w1 | sed 10q | nl -v0 -s= -w1 | paste -sd '\\0' -`\"",
|
||||
"Set columns"},
|
||||
{{'.'}, "+opt:.~", "Toggle dotfiles"},
|
||||
{{'g', KEY_HOME}, "+move:0", "Go to first file"},
|
||||
@ -186,3 +211,6 @@ else xdg-open \"$BBCURSOR\"; fi",
|
||||
{{KEY_MOUSE_WHEEL_UP}, "+scroll:-3", "Scroll up"},
|
||||
{0}, // Array must be 0-terminated
|
||||
};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1
|
||||
|
244
keys.h
244
keys.h
@ -1,244 +0,0 @@
|
||||
/*
|
||||
* Definitions of some key character constants
|
||||
*/
|
||||
|
||||
#ifndef FILE__KEYS_H
|
||||
#define FILE__KEYS_H
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#define KEY_F1 (0xFFFF-0)
|
||||
#define KEY_F2 (0xFFFF-1)
|
||||
#define KEY_F3 (0xFFFF-2)
|
||||
#define KEY_F4 (0xFFFF-3)
|
||||
#define KEY_F5 (0xFFFF-4)
|
||||
#define KEY_F6 (0xFFFF-5)
|
||||
#define KEY_F7 (0xFFFF-6)
|
||||
#define KEY_F8 (0xFFFF-7)
|
||||
#define KEY_F9 (0xFFFF-8)
|
||||
#define KEY_F10 (0xFFFF-9)
|
||||
#define KEY_F11 (0xFFFF-10)
|
||||
#define KEY_F12 (0xFFFF-11)
|
||||
#define KEY_INSERT (0xFFFF-12)
|
||||
#define KEY_DELETE (0xFFFF-13)
|
||||
#define KEY_HOME (0xFFFF-14)
|
||||
#define KEY_END (0xFFFF-15)
|
||||
#define KEY_PGUP (0xFFFF-16)
|
||||
#define KEY_PGDN (0xFFFF-17)
|
||||
#define KEY_ARROW_UP (0xFFFF-18)
|
||||
#define KEY_ARROW_DOWN (0xFFFF-19)
|
||||
#define KEY_ARROW_LEFT (0xFFFF-20)
|
||||
#define KEY_ARROW_RIGHT (0xFFFF-21)
|
||||
#define KEY_MOUSE_LEFT (0xFFFF-22)
|
||||
#define KEY_MOUSE_RIGHT (0xFFFF-23)
|
||||
#define KEY_MOUSE_MIDDLE (0xFFFF-24)
|
||||
#define KEY_MOUSE_RELEASE (0xFFFF-25)
|
||||
#define KEY_MOUSE_WHEEL_UP (0xFFFF-26)
|
||||
#define KEY_MOUSE_WHEEL_DOWN (0xFFFF-27)
|
||||
#define KEY_MOUSE_DOUBLE_LEFT (0xFFFF-28)
|
||||
|
||||
/* These are all ASCII code points below SPACE character and a BACKSPACE key. */
|
||||
#define KEY_CTRL_TILDE 0x00
|
||||
#define KEY_CTRL_2 0x00 /* clash with 'CTRL_TILDE' */
|
||||
#define KEY_CTRL_A 0x01
|
||||
#define KEY_CTRL_B 0x02
|
||||
#define KEY_CTRL_C 0x03
|
||||
#define KEY_CTRL_D 0x04
|
||||
#define KEY_CTRL_E 0x05
|
||||
#define KEY_CTRL_F 0x06
|
||||
#define KEY_CTRL_G 0x07
|
||||
#define KEY_BACKSPACE 0x08
|
||||
#define KEY_CTRL_H 0x08 /* clash with 'CTRL_BACKSPACE' */
|
||||
#define KEY_TAB 0x09
|
||||
#define KEY_CTRL_I 0x09 /* clash with 'TAB' */
|
||||
#define KEY_CTRL_J 0x0A
|
||||
#define KEY_CTRL_K 0x0B
|
||||
#define KEY_CTRL_L 0x0C
|
||||
#define KEY_ENTER 0x0D
|
||||
#define KEY_CTRL_M 0x0D /* clash with 'ENTER' */
|
||||
#define KEY_CTRL_N 0x0E
|
||||
#define KEY_CTRL_O 0x0F
|
||||
#define KEY_CTRL_P 0x10
|
||||
#define KEY_CTRL_Q 0x11
|
||||
#define KEY_CTRL_R 0x12
|
||||
#define KEY_CTRL_S 0x13
|
||||
#define KEY_CTRL_T 0x14
|
||||
#define KEY_CTRL_U 0x15
|
||||
#define KEY_CTRL_V 0x16
|
||||
#define KEY_CTRL_W 0x17
|
||||
#define KEY_CTRL_X 0x18
|
||||
#define KEY_CTRL_Y 0x19
|
||||
#define KEY_CTRL_Z 0x1A
|
||||
#define KEY_ESC 0x1B
|
||||
#define KEY_CTRL_LSQ_BRACKET 0x1B /* clash with 'ESC' */
|
||||
#define KEY_CTRL_3 0x1B /* clash with 'ESC' */
|
||||
#define KEY_CTRL_4 0x1C
|
||||
#define KEY_CTRL_BACKSLASH 0x1C /* clash with 'CTRL_4' */
|
||||
#define KEY_CTRL_5 0x1D
|
||||
#define KEY_CTRL_RSQ_BRACKET 0x1D /* clash with 'CTRL_5' */
|
||||
#define KEY_CTRL_6 0x1E
|
||||
#define KEY_CTRL_7 0x1F
|
||||
#define KEY_CTRL_SLASH 0x1F /* clash with 'CTRL_7' */
|
||||
#define KEY_CTRL_UNDERSCORE 0x1F /* clash with 'CTRL_7' */
|
||||
#define KEY_SPACE 0x20
|
||||
#define KEY_BACKSPACE2 0x7F
|
||||
#define KEY_CTRL_8 0x7F /* clash with 'BACKSPACE2' */
|
||||
|
||||
|
||||
#define ESC_DELAY 10
|
||||
|
||||
int term_getkey(int fd, int *mouse_x, int *mouse_y, int timeout);
|
||||
const char *keyname(int key);
|
||||
|
||||
static inline int nextchar(int fd, int timeout)
|
||||
{
|
||||
char c;
|
||||
struct pollfd pfd = {fd, POLLIN, 0};
|
||||
if (poll(&pfd, 1, timeout) > 0)
|
||||
return read(fd, &c, 1) == 1 ? c : -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int term_getkey(int fd, int *mouse_x, int *mouse_y, int timeout)
|
||||
{
|
||||
int c = nextchar(fd, timeout);
|
||||
if (c == '\x1b')
|
||||
goto escape;
|
||||
|
||||
return c;
|
||||
|
||||
escape:
|
||||
c = nextchar(fd, ESC_DELAY);
|
||||
// Actual escape key:
|
||||
if (c == -1)
|
||||
return KEY_ESC;
|
||||
|
||||
switch (c) {
|
||||
case '[': goto CSI;
|
||||
case 'P': goto DCS;
|
||||
case 'O': goto SS3;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
CSI:
|
||||
c = nextchar(fd, ESC_DELAY);
|
||||
if (c == -1)
|
||||
return -1;
|
||||
|
||||
switch (c) {
|
||||
case 'A': return KEY_ARROW_UP;
|
||||
case 'B': return KEY_ARROW_DOWN;
|
||||
case 'C': return KEY_ARROW_RIGHT;
|
||||
case 'D': return KEY_ARROW_LEFT;
|
||||
case 'F': return KEY_END;
|
||||
case 'H': return KEY_HOME;
|
||||
case '1':
|
||||
switch (nextchar(fd, ESC_DELAY)) {
|
||||
case '5':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F5 : -1;
|
||||
case '7':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F6 : -1;
|
||||
case '8':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F7 : -1;
|
||||
case '9':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F8 : -1;
|
||||
default: return -1;
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
switch (nextchar(fd, ESC_DELAY)) {
|
||||
case '0':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F9 : -1;
|
||||
case '1':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F10 : -1;
|
||||
case '3':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F11 : -1;
|
||||
case '4':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_F12 : -1;
|
||||
default: return -1;
|
||||
}
|
||||
break;
|
||||
case '3':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_DELETE : -1;
|
||||
case '5':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_PGUP : -1;
|
||||
case '6':
|
||||
return nextchar(fd, ESC_DELAY) == '~' ? KEY_PGDN : -1;
|
||||
case '<': { // Mouse clicks
|
||||
int buttons = 0, x = 0, y = 0;
|
||||
char buf;
|
||||
while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||
buttons = buttons * 10 + (buf - '0');
|
||||
if (buf != ';') return -1;
|
||||
while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||
x = x * 10 + (buf - '0');
|
||||
if (buf != ';') return -1;
|
||||
while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||
y = y * 10 + (buf - '0');
|
||||
if (buf != 'm' && buf != 'M') return -1;
|
||||
|
||||
*mouse_x = x - 1;
|
||||
*mouse_y = y - 1;
|
||||
|
||||
if (buf == 'm')
|
||||
return KEY_MOUSE_RELEASE;
|
||||
switch (buttons) {
|
||||
case 64: return KEY_MOUSE_WHEEL_UP;
|
||||
case 65: return KEY_MOUSE_WHEEL_DOWN;
|
||||
case 0: return KEY_MOUSE_LEFT;
|
||||
case 1: return KEY_MOUSE_RIGHT;
|
||||
case 2: return KEY_MOUSE_MIDDLE;
|
||||
default: return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
DCS:
|
||||
return -1;
|
||||
|
||||
SS3:
|
||||
switch (nextchar(fd, ESC_DELAY)) {
|
||||
case 'P': return KEY_F1;
|
||||
case 'Q': return KEY_F2;
|
||||
case 'R': return KEY_F3;
|
||||
case 'S': return KEY_F4;
|
||||
default: break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *keyname(int key)
|
||||
{
|
||||
// TODO: currently only the keys I'm using are named
|
||||
switch (key) {
|
||||
case ' ': return "Space";
|
||||
case '\r': return "Enter";
|
||||
case KEY_MOUSE_DOUBLE_LEFT: return "Doubleclick";
|
||||
case KEY_ARROW_UP: return "Up";
|
||||
case KEY_ARROW_DOWN: return "Down";
|
||||
case KEY_ARROW_LEFT: return "Left";
|
||||
case KEY_ARROW_RIGHT: return "Right";
|
||||
case KEY_HOME: return "Home";
|
||||
case KEY_END: return "End";
|
||||
case KEY_DELETE: return "Delete";
|
||||
case KEY_ESC: return "Escape";
|
||||
case KEY_F5: return "F5";
|
||||
case KEY_CTRL_A: return "Ctrl-a";
|
||||
case KEY_CTRL_D: return "Ctrl-d";
|
||||
case KEY_CTRL_H: return "Ctrl-h";
|
||||
case KEY_CTRL_R: return "Ctrl-r";
|
||||
case KEY_CTRL_U: return "Ctrl-u";
|
||||
case KEY_CTRL_T: return "Ctrl-t";
|
||||
case KEY_CTRL_W: return "Ctrl-w";
|
||||
case KEY_PGDN: return "PgDn";
|
||||
case KEY_PGUP: return "PgUp";
|
||||
case KEY_MOUSE_WHEEL_DOWN: return "Scroll down";
|
||||
case KEY_MOUSE_WHEEL_UP: return "Scroll up";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1
|
Loading…
Reference in New Issue
Block a user