Files
Espresso/drivers/new_tty.c

500 lines
7.8 KiB
C
Raw Permalink Normal View History

2026-03-20 16:57:08 -05:00
#include <stdio.h>
#include <stdlib.h>
#include <processes.h>
#include <sync.h>
#include <types.h>
#include <new_tty.h>
static tty_t ttys[MAX_TTYS];
static uint8_t num_ttys = 0;
static tty_t* active_tty = NULL;
static inline size_t next_index(size_t index)
{
return (index + 1) % TTY_BUFFER_SIZE;
}
static inline bool tty_empty(tty_t* tty)
{
return tty->head == tty->tail;
}
static inline bool tty_full(tty_t* tty)
{
return ((tty->head + 1) % TTY_BUFFER_SIZE) == tty->tail;
}
static inline bool is_canonical(tty_t* tty)
{
return (tty->flags & TTY_CANONICAL) != 0;
}
2026-06-05 16:14:14 -05:00
static void raw_put(tty_t* tty, char c)
{
size_t next = (tty->raw_head + 1) % TTY_RAW_BUFFER_SIZE;
if (next != tty->raw_tail) // avoid overflow
{
tty->raw_buf[tty->raw_head] = c;
tty->raw_head = next;
}
}
2026-03-20 16:57:08 -05:00
tty_t* tty_get_active(void)
{
return active_tty;
}
2026-06-05 16:14:14 -05:00
int tty_fread(struct file* f, char* b, size_t s)
{
return (int) tty_read_active(b, s);
}
2026-03-20 16:57:08 -05:00
void tty_backspace(tty_t* tty)
{
if (tty->head == tty->tail)
{
return; /* buffer empty */
}
//tty->head = (tty->head + TTY_BUFFER_SIZE - 1) % TTY_BUFFER_SIZE;
tty->head = (tty->head + TTY_BUFFER_SIZE - 1) / TTY_BUFFER_SIZE;
}
2026-06-05 16:14:14 -05:00
void tty_and_flags(tty_t* tty, uint32_t flags)
{
tty->flags &= flags;
}
uint32_t tty_get_flags(tty_t* tty)
{
return tty->flags;
}
2026-03-20 16:57:08 -05:00
ssize_t tty_read_active(char* buf, size_t count)
{
return tty_read(active_tty, buf, count);
}
2026-06-05 16:14:14 -05:00
/*
this is special, because when this is called the char returned by this function
is NOT added to the TTY buffer.
This can be changed however by setting flag TTY_ADDCHAR_ALWAYS.
*/
/*char tty_get_char(void)
2026-03-20 16:57:08 -05:00
{
2026-06-05 16:14:14 -05:00
tty_t* tty = get_active_tty();
if (!tty)
{
return 0;
}
2026-03-20 16:57:08 -05:00
2026-06-05 16:14:14 -05:00
while (1)
2026-03-20 16:57:08 -05:00
{
IRQ_DISABLE();
spin_lock(&tty->lock);
2026-06-05 16:14:14 -05:00
if (tty->char_available)
2026-03-20 16:57:08 -05:00
{
2026-06-05 16:14:14 -05:00
char c = tty->last_char;
tty->char_available = false;
2026-03-20 16:57:08 -05:00
spin_unlock(&tty->lock);
IRQ_ENABLE();
2026-06-05 16:14:14 -05:00
return c;
2026-03-20 16:57:08 -05:00
}
2026-06-05 16:14:14 -05:00
spin_unlock(&tty->lock);
IRQ_ENABLE();
}
2026-03-20 16:57:08 -05:00
}*/
2026-06-05 16:14:14 -05:00
char tty_get_char(void)
{
tty_t* tty = get_active_tty();
if (!tty)
return 0;
while (1)
{
IRQ_DISABLE();
spin_lock(&tty->lock);
if (tty->raw_head != tty->raw_tail)
{
char c = tty->raw_buf[tty->raw_tail];
tty->raw_tail = (tty->raw_tail + 1) % TTY_RAW_BUFFER_SIZE;
spin_unlock(&tty->lock);
IRQ_ENABLE();
return c;
}
spin_unlock(&tty->lock);
IRQ_ENABLE();
}
}
2026-03-20 16:57:08 -05:00
ssize_t tty_read(tty_t* tty, char* buf, size_t count)
{
if (!tty || !buf)
2026-06-05 16:14:14 -05:00
{
return -1;
}
2026-03-20 16:57:08 -05:00
size_t bytes = 0;
while (1)
{
IRQ_DISABLE();
spin_lock(&tty->lock);
if (!tty->line_ready)
{
spin_unlock(&tty->lock);
IRQ_ENABLE();
continue; /* spin until newline */
}
while (bytes < count && tty->tail != tty->head)
{
char c = tty->input_buffer[tty->tail];
tty->tail = (tty->tail + 1) % TTY_BUFFER_SIZE;
buf[bytes++] = c;
if (c == '\n')
{
tty->line_ready = false;
break;
}
}
spin_unlock(&tty->lock);
IRQ_ENABLE();
break;
}
return bytes;
}
int input_buffer_put(tty_t* tty, char c)
{
IRQ_DISABLE();
spin_lock(&tty->lock);
size_t next = next_index(tty->head);
if (next == tty->tail)
{
spin_unlock(&tty->lock);
IRQ_ENABLE();
return -1;
}
tty->input_buffer[tty->head] = c;
tty->head = next;
spin_unlock(&tty->lock);
IRQ_ENABLE();
return 0;
}
int init_tty(void)
{
if (num_ttys >= MAX_TTYS)
{
return -1;
}
tty_t* t = &ttys[0];
memset(t, 0, sizeof(tty_t));
t->flags = TTY_NORMAL;
active_tty = t;
num_ttys = 1;
2026-06-05 16:14:14 -05:00
t->f_ops.read = tty_fread;
t->line_ready = false;
2026-03-20 16:57:08 -05:00
return 0;
}
tty_t* get_active_tty(void)
{
return active_tty;
}
tty_t* make_tty(uint32_t flags)
{
if (num_ttys >= MAX_TTYS)
{
return NULL;
}
tty_t* t = &ttys[num_ttys];
memset(t, 0, sizeof(tty_t));
t->flags = flags;
num_ttys++;
return t;
}
void tty_receive_char(char c, uint32_t kbd_mod)
{
if (!active_tty || c == 0)
{
return;
}
c = tty_translate_char(c, kbd_mod);
/* Handle backspace */
if (c == '\b')
{
tty_backspace(active_tty);
if (active_tty->head == active_tty->tail)
{
extern void terminal_putcharat(char c, uint16_t row, uint16_t column);
terminal_putcharat('Z', 20, 20);
}
if (active_tty->flags & TTY_ECHO)
{
/* erase character visually */
putc('\b');
}
return;
}
input_buffer_put(active_tty, c);
if (active_tty->flags & TTY_ECHO)
{
if ((active_tty->flags & TTY_PASSWORD) == 0)
{
putc(c);
}
else
{
putc('*');
}
}
}
2026-06-05 16:14:14 -05:00
/*void tty_input_char(tty_t* tty, char c)
2026-03-20 16:57:08 -05:00
{
if (!tty || c == 0)
2026-06-05 16:14:14 -05:00
{
return;
}
2026-03-20 16:57:08 -05:00
IRQ_DISABLE();
spin_lock(&tty->lock);
2026-06-05 16:14:14 -05:00
raw_put(tty, c);
2026-03-20 16:57:08 -05:00
if (c == '\b')
{
if (tty->head != tty->tail)
{
tty->head = (tty->head + TTY_BUFFER_SIZE - 1) % TTY_BUFFER_SIZE;
if (tty->flags & TTY_ECHO)
{
2026-06-05 16:14:14 -05:00
putc('\b');
2026-03-20 16:57:08 -05:00
}
}
spin_unlock(&tty->lock);
IRQ_ENABLE();
return;
}
size_t next = (tty->head + 1) % TTY_BUFFER_SIZE;
if (next != tty->tail)
{
tty->input_buffer[tty->head] = c;
tty->head = next;
if (c == '\n')
{
tty->line_ready = true;
}
}
spin_unlock(&tty->lock);
IRQ_ENABLE();
2026-06-05 16:14:14 -05:00
if (tty->flags & TTY_ECHO)
{
putc(c);
}
}*/
void tty_input_char(tty_t* tty, char c)
{
if (!tty || c == 0)
{
return;
}
IRQ_DISABLE();
spin_lock(&tty->lock);
/* RAW queue always gets input */
raw_put(tty, c);
if (!(tty->flags & TTY_RAW))
{
/* canonical behavior */
if (c == '\b')
{
if (tty->head != tty->tail)
{
tty->head = (tty->head + TTY_BUFFER_SIZE - 1) % TTY_BUFFER_SIZE;
putc('\b');
putc(' ');
putc('\b');
spin_unlock(&tty->lock);
IRQ_ENABLE();
return;
}
}
else
{
size_t next = next_index(tty->head);
if (next != tty->tail)
{
tty->input_buffer[tty->head] = c;
tty->head = next;
if (c == '\n')
{
tty->line_ready = true;
}
}
}
}
spin_unlock(&tty->lock);
IRQ_ENABLE();
2026-03-20 16:57:08 -05:00
if (tty->flags & TTY_ECHO)
{
putc(c);
}
}
char tty_translate_char(char c, uint32_t modifiers)
{
bool caps = (modifiers & MODIFIER_CAPS) != 0;
bool shift = (modifiers & MODIFIER_SHIFT) != 0;
bool ctrl = (modifiers & (MODIFIER_LCTRL | MODIFIER_RCTRL)) != 0;
/* handle alphabetic characters */
if (c >= 'a' && c <= 'z')
{
if (caps ^ shift) /* XOR logic */
{
return c - 32; /* to uppercase */
}
return c;
}
if (c >= 'A' && c <= 'Z')
{
if (caps ^ shift)
{
return c;
}
return c + 32; /* to lowercase */
}
/* handle ctrl (should later include control mapping/sending messages to processes via these) */
if (ctrl)
{
if (c >= 'a' && c <= 'z')
{
return c - 'a' + 1; /* Ctrl+A → 1 */
}
if (c >= 'A' && c <= 'Z')
{
return c - 'A' + 1;
}
}
/* shifted symbols (US layout) */
if (shift)
{
switch (c)
{
case '1':
return '!';
case '2':
return '@';
case '3':
return '#';
case '4':
return '$';
case '5':
return '%';
case '6':
return '^';
case '7':
return '&';
case '8':
return '*';
case '9':
return '(';
case '0':
return ')';
case '-':
return '_';
case '=':
return '+';
case '[':
return '{';
case ']':
return '}';
case ';':
return ':';
case '\'':
return '"';
case ',':
return '<';
case '.':
return '>';
case '/':
return '?';
case '\\':
return '|';
case '`':
return '~';
}
}
return c;
}