Files
Astral/src/video/consoledev.c
T
2023-01-05 23:29:03 -03:00

313 lines
5.8 KiB
C

#include <kernel/consoledev.h>
#include <kernel/devman.h>
#include <kernel/liminetty.h>
#include <errno.h>
#include <arch/panic.h>
#include <kernel/keyboard.h>
#include <ringbuffer.h>
#include <arch/interrupt.h>
#include <arch/spinlock.h>
#include <termios.h>
static ringbuffer_t input;
static thread_t* thread;
static event_t outputevent;
static event_t inputevent;
static int outputlock;
static termios tty;
void console_write(char* str, size_t count){
while(!spinlock_trytoacquire(&outputlock)){
event_wait(&outputevent, false);
}
liminetty_writeuser(str, count);
spinlock_release(&outputlock);
event_signal(&outputevent, true);
}
#define THREAD_BUFF_MAX 2048
#define BACKSPACE_STR "\b \b"
// taken from https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_input_sequences
#define HOME_STR "\e[1~"
#define INSERT_STR "\e[2~"
#define DELETE_STR "\e[3~"
#define END_STR "\e[4~"
#define PGUP_STR "\e[5~"
#define PGDN_STR "\e[6~"
#define UP_STR "\e[A"
#define DOWN_STR "\e[B"
#define RIGHT_STR "\e[C"
#define LEFT_STR "\e[D"
__attribute__((noreturn)) static void console_thread(){
char buff[THREAD_BUFF_MAX];
int buffpos = 0;
for(;;){
kbpacket_t packet;
keyboard_getandwait(0, &packet);
if(KBPACKET_FLAGS_RELEASED & packet.flags)
continue;
char* strbufptr;
size_t strbuflen;
char thingy[2];
if(packet.ascii){
if(packet.ascii == '\n'){
if(IGNCR & tty.c_iflag)
continue;
if((ICRNL & tty.c_iflag) == 0)
packet.ascii = '\r';
}
strbufptr = &packet.ascii;
strbuflen = 1;
if(packet.flags & (KBPACKET_FLAGS_LEFTCTRL | KBPACKET_FLAGS_RIGHTCTRL)){
if(!((packet.ascii > 'a' && packet.ascii < 'z') || (packet.ascii > 'A' && packet.ascii < 'Z')))
continue;
strbufptr[0] = packet.ascii > 'a' ? packet.ascii - 0x60 : packet.ascii - 0x40;
}
}
else{
switch(packet.keycode){
case KEYCODE_HOME:
strbufptr = HOME_STR;
break;
case KEYCODE_INSERT:
strbufptr = INSERT_STR;
break;
case KEYCODE_DELETE:
strbufptr = DELETE_STR;
break;
case KEYCODE_END:
strbufptr = END_STR;
break;
case KEYCODE_PAGEUP:
strbufptr = PGUP_STR;
break;
case KEYCODE_PAGEDOWN:
strbufptr = PGDN_STR;
break;
case KEYCODE_UP:
strbufptr = UP_STR;
break;
case KEYCODE_DOWN:
strbufptr = DOWN_STR;
break;
case KEYCODE_RIGHT:
strbufptr = RIGHT_STR;
break;
case KEYCODE_LEFT:
strbufptr = LEFT_STR;
break;
default:
continue;
}
strbuflen = strlen(strbufptr);
}
if(tty.c_lflag & ICANON){
bool flush = false;
switch(packet.ascii){
case '\b':
if(buffpos){
--buffpos;
buff[buffpos] = '\0';
if(tty.c_lflag & ECHO)
console_write(BACKSPACE_STR, strlen(BACKSPACE_STR));
}
continue;
case '\n':
flush = true;
break;
}
if(packet.ascii && tty.c_lflag & ECHO){
console_write(strbufptr, strbuflen);
}
for(size_t i = 0; i < strbuflen; ++i){
buff[buffpos++] = strbufptr[i];
if(buffpos == THREAD_BUFF_MAX){
flush = true;
break;
}
}
if(flush){
arch_interrupt_disable();
ringbuffer_write(&input, buff, buffpos);
arch_interrupt_enable();
buffpos = 0;
event_signal(&inputevent, true);
}
}
else{ // not canon
if(tty.c_lflag & ECHO){
console_write(strbufptr, strbuflen);
}
arch_interrupt_disable();
ringbuffer_write(&input, strbufptr, strbuflen);
arch_interrupt_enable();
event_signal(&inputevent, true);
}
}
}
static int isatty(int minor){
return 0;
}
static int read(int *error, int dev, void* buff, size_t count, size_t offset){
int readc = 0;
*error = 0;
while(readc < count){
arch_interrupt_disable();
readc += ringbuffer_read(&input, buff+readc, count-readc);
arch_interrupt_enable();
if(readc < count && readc < tty.c_cc[VMIN])
*error = event_wait(&inputevent, true);
if(*error)
return -1;
if(readc >= tty.c_cc[VMIN])
break;
}
return readc;
}
static int write(int *error, int dev, void* buff, size_t count, size_t offset){
*error = 0;
console_write(buff, count);
return count;
}
static int isseekable(){
return ESPIPE;
}
#define TCGETS 0x5401
#define TCSETS 0x5402
#define TIOCGWINSZ 0x5413
typedef struct{
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel;
unsigned short ws_ypixel;
} winsize;
#include <arch/cls.h>
static int ioctl(int minor, unsigned long req, void* arg){
switch(req){
case TIOCGWINSZ:
struct limine_terminal* ltty = liminetty_get(0);
winsize* w = arg;
w->ws_row = ltty->rows;
w->ws_col = ltty->columns;
break;
case TCGETS:
*(termios*)arg = tty;
break;
case TCSETS:
tty = *(termios*)arg;
break;
default:
return ENOTTY;
}
return 0;
}
#include <poll.h>
static int poll(int minor, pollfd* fd){
if((fd->events & POLLIN) && input.write != input.read)
fd->revents |= POLLIN;
if((fd->events & POLLOUT) && outputlock == 0)
fd->revents |= POLLOUT;
return 0;
}
static devcalls calls = {
read, write, isatty, isseekable, ioctl, poll
};
static void callback(struct limine_terminal *terminal, uint64_t type, uint64_t arg1, uint64_t arg2, uint64_t arg3){
asm("cli;hlt");
}
void consoledev_init(){
if(devman_newdevice("console", TYPE_CHARDEV, MAJOR_CONSOLE, 0, &calls))
_panic("Failed to create console device", 0);
if(ringbuffer_init(&input, THREAD_BUFF_MAX))
_panic("Failed to initialise console ringbuffer", 0);
thread = sched_newkthread(console_thread, THREAD_DEFAULT_KSTACK_SIZE, true, THREAD_PRIORITY_KERNEL);
if(!thread)
_panic("Failed to initialise console thread", 0);
tty.c_iflag = ICRNL;
tty.c_lflag = ECHO | ICANON | ISIG;
tty.c_cc[VINTR] = 0x03;
tty.ibaud = 38400;
tty.obaud = 38400;
tty.c_cc[VMIN] = 1;
liminetty_setcallback(0, callback);
}