Break out rline, add ^W support

This commit is contained in:
Kevin Lange 2015-04-04 12:33:30 -07:00
parent b54e2d1b54
commit 8a1430879a
5 changed files with 286 additions and 213 deletions

View File

@ -32,6 +32,7 @@
#include "lib/list.h"
#include "lib/kbd.h"
#include "lib/rline.h"
#define PIPE_TOKEN "\xFF\xFFPIPE\xFF\xFF"
@ -219,219 +220,6 @@ void sig_pass(int sig) {
}
}
struct rline_callback;
typedef struct {
char * buffer;
struct rline_callback * callbacks;
int collected;
int requested;
int newline;
int cancel;
int offset;
int tabbed;
} rline_context_t;
typedef void (*rline_callback_t)(rline_context_t * context);
typedef struct rline_callback {
rline_callback_t tab_complete;
rline_callback_t redraw_prompt;
rline_callback_t special_key;
rline_callback_t key_up;
rline_callback_t key_down;
rline_callback_t key_left;
rline_callback_t key_right;
rline_callback_t rev_search;
} rline_callbacks_t;
void rline_redraw(rline_context_t * context) {
printf("\033[u%s\033[K", context->buffer);
for (int i = context->offset; i < context->collected; ++i) {
printf("\033[D");
}
fflush(stdout);
}
void rline_redraw_clean(rline_context_t * context) {
printf("\033[u%s", context->buffer);
for (int i = context->offset; i < context->collected; ++i) {
printf("\033[D");
}
fflush(stdout);
}
int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks) {
/* Initialize context */
rline_context_t context = {
buffer,
callbacks,
0,
buf_size,
0,
0,
0,
0,
};
printf("\033[s");
fflush(stdout);
key_event_state_t kbd_state = {0};
/* Read keys */
while ((context.collected < context.requested) && (!context.newline)) {
uint32_t key_sym = kbd_key(&kbd_state, fgetc(stdin));
if (key_sym == KEY_NONE) continue;
switch (key_sym) {
case KEY_CTRL_C:
printf("^C\n");
context.buffer[0] = '\0';
return 0;
case KEY_CTRL_D:
if (context.collected == 0) {
printf("exit\n");
sprintf(context.buffer, "exit\n");
return strlen(context.buffer);
}
continue;
case KEY_CTRL_R:
if (callbacks->rev_search) {
callbacks->rev_search(&context);
return context.collected;
}
continue;
case KEY_ARROW_UP:
if (callbacks->key_up) {
callbacks->key_up(&context);
}
continue;
case KEY_ARROW_DOWN:
if (callbacks->key_down) {
callbacks->key_down(&context);
}
continue;
case KEY_ARROW_RIGHT:
if (callbacks->key_right) {
callbacks->key_right(&context);
} else {
if (context.offset < context.collected) {
printf("\033[C");
fflush(stdout);
context.offset++;
}
}
continue;
case KEY_ARROW_LEFT:
if (callbacks->key_left) {
callbacks->key_left(&context);
} else {
if (context.offset > 0) {
printf("\033[D");
fflush(stdout);
context.offset--;
}
}
continue;
case KEY_BACKSPACE:
if (context.collected) {
if (!context.offset) {
continue;
}
printf("\010 \010");
if (context.offset != context.collected) {
int remaining = context.collected - context.offset;
for (int i = 0; i < remaining; ++i) {
printf("%c", context.buffer[context.offset + i]);
context.buffer[context.offset + i - 1] = context.buffer[context.offset + i];
}
printf(" ");
for (int i = 0; i < remaining + 1; ++i) {
printf("\033[D");
}
context.offset--;
context.collected--;
} else {
context.buffer[--context.collected] = '\0';
context.offset--;
}
fflush(stdout);
}
continue;
case KEY_CTRL_A:
while (context.offset > 0) {
printf("\033[D");
context.offset--;
}
fflush(stdout);
continue;
case KEY_CTRL_E:
while (context.offset < context.collected) {
printf("\033[C");
context.offset++;
}
fflush(stdout);
continue;
case KEY_CTRL_L: /* ^L: Clear Screen, redraw prompt and buffer */
printf("\033[H\033[2J");
if (callbacks->redraw_prompt) {
callbacks->redraw_prompt(&context);
}
printf("\033[s");
rline_redraw_clean(&context);
continue;
case '\t':
if (callbacks->tab_complete) {
callbacks->tab_complete(&context);
}
continue;
case '\n':
while (context.offset < context.collected) {
printf("\033[C");
context.offset++;
}
if (context.collected < context.requested) {
context.buffer[context.collected] = '\n';
context.buffer[++context.collected] = '\0';
context.offset++;
}
printf("\n");
fflush(stdout);
context.newline = 1;
continue;
}
if (context.offset != context.collected) {
for (int i = context.collected; i > context.offset; --i) {
context.buffer[i] = context.buffer[i-1];
}
if (context.collected < context.requested) {
context.buffer[context.offset] = (char)key_sym;
context.buffer[++context.collected] = '\0';
context.offset++;
}
for (int i = context.offset - 1; i < context.collected; ++i) {
printf("%c", context.buffer[i]);
}
for (int i = context.offset; i < context.collected; ++i) {
printf("\033[D");
}
fflush(stdout);
} else {
printf("%c", (char)key_sym);
if (context.collected < context.requested) {
context.buffer[context.collected] = (char)key_sym;
context.buffer[++context.collected] = '\0';
context.offset++;
}
fflush(stdout);
}
}
/* Cap that with a null */
context.buffer[context.collected] = '\0';
return context.collected;
}
void redraw_prompt_func(rline_context_t * context) {
draw_prompt(0);
}

View File

@ -9,11 +9,31 @@
#define KEY_NONE 0
#define KEY_BACKSPACE 8
#define KEY_CTRL_A 1
#define KEY_CTRL_B 2
#define KEY_CTRL_C 3
#define KEY_CTRL_D 4
#define KEY_CTRL_E 5
#define KEY_CTRL_F 6
#define KEY_CTRL_G 7
#define KEY_CTRL_H 8
#define KEY_CTRL_I 9
#define KEY_CTRL_J 10
#define KEY_CTRL_K 11
#define KEY_CTRL_L 12
#define KEY_CTRL_M 13
#define KEY_CTRL_N 14
#define KEY_CTRL_O 15
#define KEY_CTRL_P 16
#define KEY_CTRL_Q 17
#define KEY_CTRL_R 18
#define KEY_CTRL_S 19
#define KEY_CTRL_T 20
#define KEY_CTRL_U 21
#define KEY_CTRL_V 22
#define KEY_CTRL_W 23
#define KEY_CTRL_X 24
#define KEY_CTRL_Y 25
#define KEY_CTRL_Z 26
#define KEY_ESCAPE 27
#define KEY_NORMAL_MAX 256
#define KEY_ARROW_UP 257

229
userspace/lib/rline.c Normal file
View File

@ -0,0 +1,229 @@
/*
* rline - a line reading library.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "kbd.h"
#include "rline.h"
void rline_redraw(rline_context_t * context) {
printf("\033[u%s\033[K", context->buffer);
for (int i = context->offset; i < context->collected; ++i) {
printf("\033[D");
}
fflush(stdout);
}
void rline_redraw_clean(rline_context_t * context) {
printf("\033[u%s", context->buffer);
for (int i = context->offset; i < context->collected; ++i) {
printf("\033[D");
}
fflush(stdout);
}
int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks) {
/* Initialize context */
rline_context_t context = {
buffer,
callbacks,
0,
buf_size,
0,
0,
0,
0,
};
printf("\033[s");
fflush(stdout);
key_event_state_t kbd_state = {0};
/* Read keys */
while ((context.collected < context.requested) && (!context.newline)) {
uint32_t key_sym = kbd_key(&kbd_state, fgetc(stdin));
if (key_sym == KEY_NONE) continue;
switch (key_sym) {
case KEY_CTRL_C:
printf("^C\n");
context.buffer[0] = '\0';
return 0;
case KEY_CTRL_D:
if (context.collected == 0) {
printf("exit\n");
sprintf(context.buffer, "exit\n");
return strlen(context.buffer);
}
continue;
case KEY_CTRL_R:
if (callbacks->rev_search) {
callbacks->rev_search(&context);
return context.collected;
}
continue;
case KEY_ARROW_UP:
if (callbacks->key_up) {
callbacks->key_up(&context);
}
continue;
case KEY_ARROW_DOWN:
if (callbacks->key_down) {
callbacks->key_down(&context);
}
continue;
case KEY_ARROW_RIGHT:
if (callbacks->key_right) {
callbacks->key_right(&context);
} else {
if (context.offset < context.collected) {
printf("\033[C");
fflush(stdout);
context.offset++;
}
}
continue;
case KEY_ARROW_LEFT:
if (callbacks->key_left) {
callbacks->key_left(&context);
} else {
if (context.offset > 0) {
printf("\033[D");
fflush(stdout);
context.offset--;
}
}
continue;
case KEY_BACKSPACE:
if (context.collected) {
if (!context.offset) {
continue;
}
printf("\010 \010");
if (context.offset != context.collected) {
int remaining = context.collected - context.offset;
for (int i = 0; i < remaining; ++i) {
printf("%c", context.buffer[context.offset + i]);
context.buffer[context.offset + i - 1] = context.buffer[context.offset + i];
}
printf(" ");
for (int i = 0; i < remaining + 1; ++i) {
printf("\033[D");
}
context.offset--;
context.collected--;
} else {
context.buffer[--context.collected] = '\0';
context.offset--;
}
fflush(stdout);
}
continue;
case KEY_CTRL_A:
while (context.offset > 0) {
printf("\033[D");
context.offset--;
}
fflush(stdout);
continue;
case KEY_CTRL_E:
while (context.offset < context.collected) {
printf("\033[C");
context.offset++;
}
fflush(stdout);
continue;
case KEY_CTRL_L: /* ^L: Clear Screen, redraw prompt and buffer */
printf("\033[H\033[2J");
if (callbacks->redraw_prompt) {
callbacks->redraw_prompt(&context);
}
printf("\033[s");
rline_redraw_clean(&context);
continue;
case KEY_CTRL_W:
/*
* Erase word before cursor.
* If the character before the cursor is a space, delete it.
* Continue deleting until the previous character is a space.
*/
if (context.collected) {
if (!context.offset) {
continue;
}
do {
printf("\010 \010");
if (context.offset != context.collected) {
int remaining = context.collected - context.offset;
for (int i = 0; i < remaining; ++i) {
printf("%c", context.buffer[context.offset + i]);
context.buffer[context.offset + i - 1] = context.buffer[context.offset + i];
}
printf(" ");
for (int i = 0; i < remaining + 1; ++i) {
printf("\033[D");
}
context.offset--;
context.collected--;
} else {
context.buffer[--context.collected] = '\0';
context.offset--;
}
} while ((context.offset) && (context.buffer[context.offset-1] != ' '));
fflush(stdout);
}
continue;
case '\t':
if (callbacks->tab_complete) {
callbacks->tab_complete(&context);
}
continue;
case '\n':
while (context.offset < context.collected) {
printf("\033[C");
context.offset++;
}
if (context.collected < context.requested) {
context.buffer[context.collected] = '\n';
context.buffer[++context.collected] = '\0';
context.offset++;
}
printf("\n");
fflush(stdout);
context.newline = 1;
continue;
}
if (context.offset != context.collected) {
for (int i = context.collected; i > context.offset; --i) {
context.buffer[i] = context.buffer[i-1];
}
if (context.collected < context.requested) {
context.buffer[context.offset] = (char)key_sym;
context.buffer[++context.collected] = '\0';
context.offset++;
}
for (int i = context.offset - 1; i < context.collected; ++i) {
printf("%c", context.buffer[i]);
}
for (int i = context.offset; i < context.collected; ++i) {
printf("\033[D");
}
fflush(stdout);
} else {
printf("%c", (char)key_sym);
if (context.collected < context.requested) {
context.buffer[context.collected] = (char)key_sym;
context.buffer[++context.collected] = '\0';
context.offset++;
}
fflush(stdout);
}
}
/* Cap that with a null */
context.buffer[context.collected] = '\0';
return context.collected;
}

35
userspace/lib/rline.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef _RLINE_H
#define _RLINE_H
struct rline_callback;
typedef struct {
char * buffer;
struct rline_callback * callbacks;
int collected;
int requested;
int newline;
int cancel;
int offset;
int tabbed;
} rline_context_t;
typedef void (*rline_callback_t)(rline_context_t * context);
typedef struct rline_callback {
rline_callback_t tab_complete;
rline_callback_t redraw_prompt;
rline_callback_t special_key;
rline_callback_t key_up;
rline_callback_t key_down;
rline_callback_t key_left;
rline_callback_t key_right;
rline_callback_t rev_search;
} rline_callbacks_t;
void rline_redraw(rline_context_t * context);
void rline_redraw_clean(rline_context_t * context);
int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks);
#endif /* _RLINE_H */

View File

@ -37,6 +37,7 @@ class Classifier(object):
'"lib/pex.h"': (None, 'userspace/lib/pex.o', []),
'"lib/graphics.h"': (None, 'userspace/lib/graphics.o', ['<png.h>']),
'"lib/shmemfonts.h"': (None, 'userspace/lib/shmemfonts.o', ['"lib/graphics.h"', '<ft2build.h>']),
'"lib/rline.h"': (None, 'userspace/lib/rline.o', ['"lib/kbd.h"']),
# Yutani Libraries
'"lib/yutani.h"': (None, 'userspace/lib/yutani.o', ['"lib/list.h"', '"lib/pex.h"', '"lib/graphics.h"', '"lib/hashmap.h"']),
'"lib/decorations.h"': (None, 'userspace/lib/decorations.o', ['"lib/shmemfonts.h"', '"lib/graphics.h"', '"lib/yutani.h"']),