diff options
author | nishi <nishi@vegaa.systems> | 2023-07-07 23:40:27 +0000 |
---|---|---|
committer | nishi <nishi@vegaa.systems> | 2023-07-07 23:40:27 +0000 |
commit | d963772c6633a0610898aaba2ae90d461e8f2de8 (patch) | |
tree | 64d9e0a7b09b205d5f42011aa2bfe88e321f706d /sys/kern/tty.c |
should be working, should be
git-svn-id: https://svn.vegaa.systems/svn/vega-Vega/trunk@7 a8a8aea2-181d-ee11-89e8-15fd0e089fc4
Diffstat (limited to 'sys/kern/tty.c')
-rw-r--r-- | sys/kern/tty.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c new file mode 100644 index 0000000..f166302 --- /dev/null +++ b/sys/kern/tty.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2023 Ian Marco Moffett and the VegaOS team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of VegaOS nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* $Id$ */ + +#include <sys/tty.h> +#include <sys/cdefs.h> +#include <sys/errno.h> +#include <sys/ascii.h> +#include <string.h> +#include <tty_font.h> + +/* + * Default cursor color. + * + * TODO: The cursor color should + * be the invert of the color + * of whatever it is on top of. + */ +#define DEFAULT_CURSOR_BG 0x808080 + +#define CURSOR_WIDTH FONT_WIDTH +#define CURSOR_HEIGHT FONT_HEIGHT + +__KERNEL_META("$Vega$: tty.c, Ian Marco Moffett, " + "Core TTY implementation"); + +/* List of attached TTYs */ +static TAILQ_HEAD(, tty) tty_list; + +/* + * Renders a char onto the + * TTY specified by `tty`. + */ +static void +tty_draw_char(struct tty *tty, char c, uint32_t fg, uint32_t bg) +{ + uint32_t *fb_ptr; + uint32_t x, y; + size_t idx; + const uint8_t *glyph; + + /* Get a pointer to framebuffer memory */ + fb_ptr = tty->fbdev.mem; + + /* Get the specific glyph of `c` */ + glyph = &DEFAULT_FONT_DATA[(int)c*16]; + + x = tty->chpos_x; + y = tty->chpos_y; + + for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) { + for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) { + idx = fbdev_get_index(&tty->fbdev, x+FONT_WIDTH-cx, y+cy); + fb_ptr[idx] = __TEST(glyph[cy], __BIT(cx)) ? fg : bg; + } + } +} + +/* + * Draws a cursor onto + * the screen. + * + * Call with TTY locked. + */ +static void +tty_draw_cursor(struct tty *tty, bool hide) +{ + uint32_t x, y, idx; + uint32_t *fb_ptr; + uint32_t color; + + fb_ptr = tty->fbdev.mem; + color = hide ? tty->bg : DEFAULT_CURSOR_BG; + + for (size_t cy = 0; cy < CURSOR_HEIGHT; ++cy) { + for (size_t cx = 0; cx < CURSOR_WIDTH; ++cx) { + x = tty->curspos_x + cx; + y = tty->curspos_y + cy; + idx = fbdev_get_index(&tty->fbdev, x, y); + fb_ptr[idx] = color; + } + } +} + +static void +tty_scroll_single(struct tty *tty) +{ + uint32_t *fb_ptr; + size_t line_size, dest_idx, src_idx; + + fb_ptr = tty->fbdev.mem; + line_size = tty->fbdev.pitch/4; + + /* Copy each line up */ + for (size_t y = FONT_HEIGHT; y < tty->t_ws_ypixel; y += FONT_HEIGHT) { + dest_idx = fbdev_get_index(&tty->fbdev, 0, y - FONT_HEIGHT); + src_idx = fbdev_get_index(&tty->fbdev, 0, y); + memcpy32(&fb_ptr[dest_idx], &fb_ptr[src_idx], FONT_HEIGHT*line_size); + } + + /* + * Ensure we start at X position 0 + * after we scrolled down. + */ + tty->chpos_x = 0; + tty->curspos_x = 0; +} + +/* + * Handles a newline. + * + * Call with TTY locked. + */ +static inline void +tty_newline(struct tty *tty) +{ + uint32_t ypos; + const uint32_t MAX_YPOS = tty->t_ws_ypixel - (CURSOR_HEIGHT*2); + + /* Hide the cursor */ + tty_draw_cursor(tty, true); + + /* Reset X positions */ + tty->chpos_x = 0; + tty->curspos_x = 0; + + /* + * Get the value closest to end + * of the screen. + */ + ypos = __MAX(tty->chpos_y, tty->curspos_y); + + /* + * Check if we need to scroll + * instead of incrementing + * Y positions. + */ + if (ypos < MAX_YPOS) { + tty->chpos_y += FONT_HEIGHT; + tty->curspos_y += FONT_HEIGHT; + } else { + tty_scroll_single(tty); + } + + /* Redraw the cursor */ + tty_draw_cursor(tty, false); +} + +/* + * Appends a character to the TTY specified + * by `tty` as well as incrementing tty->chpos_x + * and making newlines as needed. + * + * Call with TTY locked. + */ +static inline void +tty_append_char(struct tty *tty, int c) +{ + const uint32_t MAX_XPOS = tty->t_ws_xpixel - FONT_WIDTH; + + /* Hide the cursor */ + tty_draw_cursor(tty, true); + + tty_draw_char(tty, c, tty->fg, tty->bg); + tty->chpos_x += FONT_WIDTH; + tty->curspos_x += FONT_WIDTH; + + if (tty->chpos_x >= MAX_XPOS) { + tty_newline(tty); + } + + /* Redraw the cursor */ + tty_draw_cursor(tty, false); +} + +/* + * Writes out a tab as `tty->tab_width` + * spaces. + * + * Call with TTY locked. + */ +static void +tty_expand_tab(struct tty *tty) +{ + for (size_t i = 0; i < tty->tab_width; ++i) { + tty_append_char(tty, ' '); + } +} + +/* + * Writes a char to the TTY. + * + * Returns 0 on success; non-zero value + * is a character to be resent. + * + * Call with TTY locked. + */ +static int +tty_putch(struct tty *tty, int c) +{ + if (!__TEST(tty->t_oflag, OPOST)) { + /* + * Just write out the char with + * no processing. + */ + tty_append_char(tty, c); + return 0; + } + + switch (c) { + case ASCII_HT: + /* Tab */ + tty_expand_tab(tty); + break; + case ASCII_LF: + /* Line feed ('\n') */ + tty_newline(tty); + break; + default: + tty_append_char(tty, c); + break; + } + + return 0; +} + +/* + * Flushes a TTY specified + * by `tty`. + * + * Call with TTY locked. + */ +void +tty_flush(struct tty *tty) +{ + struct tty_ring *ring; + + ring = &tty->ring; + + /* + * Write each byte from the buffer + * to the screen with output processing + * if possible. + * + * XXX: Perhaps there could be a faster + * way of doing this instead of + * byte by byte? + */ + for (size_t i = 0; i < ring->len; ++i) { + tty_putch(tty, ring->buf[i]); + } + + ring->len = 0; +} + +/* + * Writes to a TTY specified by `tty`. + * + * @buf: Buffer to write. + * @len: Length of buffer. + * + * Returns number of bytes written, and + * EXIT_FAILURE on error. + */ +ssize_t +tty_write(struct tty *tty, const char *buf, size_t len) +{ + if (len == 0) { + /* Bad value, don't even try */ + return EXIT_FAILURE; + } + + TTY_LOCK(tty); + for (size_t i = 0; i < len; ++i) { + tty_push_char(tty, buf[i]); + /* + * If we have a newline and we are + * buffering bytes, flush the ring. + */ + if (buf[i] == '\n' && __TEST(tty->t_oflag, ORBUF)) { + tty_flush(tty); + } + } + + /* + * If we aren't buffering bytes, don't + * keep the bytes within the ring and + * flush it right away per `tty_write()` + * call. + */ + if (!__TEST(tty->t_oflag, ORBUF)) { + tty_flush(tty); + } + + TTY_UNLOCK(tty); + return len; +} + +/* + * Sets TTY fields to their defaults. + */ +void +tty_set_defaults(struct tty *tty) +{ + /* Ensure everything is initially zero */ + memset(tty, 0, sizeof(*tty)); + + /* + * Now, initialize everything to their defaults. + * + * Some notes about the default framebuffer device: + * ------------------------------------------------ + * The default framebuffer device should be the + * front buffer. Later on during boot, all attached + * TTYs shall have their fbdev swapped out with a + * backbuffer to improve performace as reading directly + * from video memory is going to be slow. + * + * XXX: At some point we should be allocating a backbuffer + * instead when it's time for *all* TTYs to have them. + * + * A good idea would be to only allocate a backbuffer + * *if* we switched to some TTY and deallocate + * that backbuffer when switching away from that TTY. + * + * The first thing that comes to mind when thinking + * about this idea is loosing our text when we switch + * back out. To rectify this, we could buffer chars + * which would take less memory than keeping the whole + * backbuffer (holds pixels i.e uint32_t). + * + * This can perhaps be done by some internal flag which + * indicates that kmalloc() is usable and chars can + * be buffered. Once we switch back, just allocate + * a new backbuffer and copy the chars back. + */ + tty->fbdev = fbdev_get_front(); + tty->t_oflag = (OPOST | ORBUF); + tty->tab_width = DEFAULT_TAB_WIDTH; + tty->fg = 0x808080; + tty->bg = 0x000000; + tty->t_ws_xpixel = tty->fbdev.width; + tty->t_ws_ypixel = tty->fbdev.height; + tty->t_ws_row = tty->fbdev.height / FONT_HEIGHT; + tty->t_ws_col = tty->fbdev.width / FONT_WIDTH; + +} + +void +tty_attach(struct tty *tty) +{ + TAILQ_INSERT_TAIL(&tty_list, tty, link); + tty_draw_cursor(tty, false); +} + +void +tty_init(void) +{ + TAILQ_INIT(&tty_list); /* Ensure the TTY list is usable */ +} |