aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/tty.c
diff options
context:
space:
mode:
authornishi <nishi@vegaa.systems>2023-07-07 23:40:27 +0000
committernishi <nishi@vegaa.systems>2023-07-07 23:40:27 +0000
commitd963772c6633a0610898aaba2ae90d461e8f2de8 (patch)
tree64d9e0a7b09b205d5f42011aa2bfe88e321f706d /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.c387
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 */
+}