#include #include #include #include #include #include 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; } 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; } } tty_t* tty_get_active(void) { return active_tty; } int tty_fread(struct file* f, char* b, size_t s) { return (int) tty_read_active(b, s); } 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; } void tty_and_flags(tty_t* tty, uint32_t flags) { tty->flags &= flags; } uint32_t tty_get_flags(tty_t* tty) { return tty->flags; } ssize_t tty_read_active(char* buf, size_t count) { return tty_read(active_tty, buf, count); } /* 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) { tty_t* tty = get_active_tty(); if (!tty) { return 0; } while (1) { IRQ_DISABLE(); spin_lock(&tty->lock); if (tty->char_available) { char c = tty->last_char; tty->char_available = false; spin_unlock(&tty->lock); IRQ_ENABLE(); return c; } spin_unlock(&tty->lock); IRQ_ENABLE(); } }*/ 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(); } } ssize_t tty_read(tty_t* tty, char* buf, size_t count) { if (!tty || !buf) { return -1; } 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; t->f_ops.read = tty_fread; t->line_ready = false; 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('*'); } } } /*void tty_input_char(tty_t* tty, char c) { if (!tty || c == 0) { return; } IRQ_DISABLE(); spin_lock(&tty->lock); raw_put(tty, c); if (c == '\b') { if (tty->head != tty->tail) { tty->head = (tty->head + TTY_BUFFER_SIZE - 1) % TTY_BUFFER_SIZE; if (tty->flags & TTY_ECHO) { putc('\b'); } } 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(); 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(); 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; }