| /* |
| * Copyright 2016 Google Inc. |
| * |
| * 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. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "base/container_of.h" |
| #include "base/graphics/font8x16.h" |
| #include "base/graphics/graphics.h" |
| #include "base/init_funcs.h" |
| #include "base/list.h" |
| #include "base/xalloc.h" |
| #include "drivers/board/board.h" |
| #include "drivers/console/console.h" |
| #include "drivers/console/display.h" |
| |
| static const DcColor display_console_background = { |
| // Black. |
| .red = 0, |
| .green = 0, |
| .blue = 0, |
| }; |
| |
| static const DcColor display_console_foreground = { |
| // Gray. |
| .red = 0xaa, |
| .green = 0xaa, |
| .blue = 0xaa, |
| }; |
| |
| typedef struct { |
| Console console; |
| |
| DisplayOps *display; |
| |
| int32_t cursor_x; |
| int32_t cursor_y; |
| |
| uint32_t display_height; |
| uint32_t display_width; |
| |
| DcFont *font; |
| |
| uint32_t height; |
| uint32_t width; |
| } DisplayConsole; |
| |
| static int display_console_initialize(DisplayConsole *console) |
| { |
| DisplayOps *display = board_display(); |
| |
| uint32_t height, width; |
| if (display_dimensions(display, &height, &width)) |
| return 1; |
| |
| console->cursor_x = 0; |
| console->cursor_y = 0; |
| |
| console->display_height = height; |
| console->display_width = width; |
| |
| console->font = &dc_font_8x16; |
| |
| console->height = console->display_height / console->font->height; |
| console->width = console->display_width / console->font->width; |
| |
| console->display = display; |
| |
| return 0; |
| } |
| |
| static int display_console_scroll_up(DisplayConsole *console) |
| { |
| uint32_t font_height = console->font->height; |
| |
| // Scroll the text up. |
| if (display_move(console->display, 0, font_height, 0, 0, |
| console->display_height - font_height, |
| console->display_width)) { |
| return 1; |
| } |
| |
| // Erase the last line. |
| if (display_fill(console->display, 0, |
| console->display_height - font_height, |
| font_height, console->display_width, |
| &display_console_background)) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int display_console_put_char_work(ConsoleOutputOps *me, unsigned int c) |
| { |
| DisplayConsole *console = |
| container_of(me, DisplayConsole, console.output); |
| |
| if (!console->display && display_console_initialize(console)) |
| return 1; |
| |
| static DcBitmap glyph; |
| |
| switch (c) { |
| case '\r': |
| console->cursor_x = 0; |
| break; |
| case '\n': |
| console->cursor_x = 0; |
| console->cursor_y++; |
| break; |
| case '\b': |
| console->cursor_x--; |
| if (console->cursor_x < 0) { |
| console->cursor_y--; |
| console->cursor_x = console->width; |
| } |
| break; |
| case '\t': |
| while (console->cursor_x % 8 && |
| console->cursor_x < console->width) { |
| display_console_put_char_work(me, ' '); |
| } |
| break; |
| default: |
| if (!glyph.pixels || |
| glyph.width != console->font->width || |
| glyph.height != console->font->height) { |
| free(glyph.pixels); |
| glyph.width = console->font->width; |
| glyph.height = console->font->height; |
| glyph.pixels = xmalloc(glyph.width * glyph.height * |
| sizeof(*glyph.pixels)); |
| } |
| graphics_render_char(&glyph, console->font, |
| &display_console_foreground, |
| &display_console_background, c); |
| uint32_t x = console->cursor_x * console->font->width; |
| uint32_t y = console->cursor_y * console->font->height; |
| display_draw(console->display, x, y, &glyph); |
| |
| console->cursor_x++; |
| } |
| |
| if (console->cursor_x < 0) |
| console->cursor_x = 0; |
| if (console->cursor_y < 0) |
| console->cursor_y = 0; |
| if (console->cursor_x >= console->width) { |
| console->cursor_x = 0; |
| console->cursor_y++; |
| } |
| |
| while (console->cursor_y >= console->height) { |
| display_console_scroll_up(console); |
| console->cursor_y--; |
| } |
| |
| return 0; |
| } |
| |
| static void display_console_put_char_cb(ConsoleOutputOps *me, unsigned int c) |
| { |
| display_console_put_char_work(me, c); |
| } |
| |
| static DisplayConsole display_console = { |
| .console = { |
| .output = { |
| .putchar = &display_console_put_char_cb, |
| }, |
| }, |
| }; |
| |
| int display_console_dimensions(uint32_t *rows, uint32_t *cols) |
| { |
| *rows = display_console.height; |
| *cols = display_console.width; |
| return 0; |
| } |
| |
| int display_console_set_cursor(uint32_t x, uint32_t y) |
| { |
| if (!display_console.display && |
| display_console_initialize(&display_console)) |
| return 1; |
| |
| if (x >= display_console.width || y >= display_console.height) { |
| printf("Cursor position (%d, %d) out of bounds.\n", x, y); |
| return 1; |
| } |
| |
| display_console.cursor_x = x; |
| display_console.cursor_y = y; |
| |
| return 0; |
| } |
| |
| int display_console_put_char(char c) |
| { |
| return display_console_put_char_work( |
| &display_console.console.output, c); |
| } |
| |
| void display_console_attach(void) |
| { |
| if (!display_console.console.list_node.next) { |
| list_insert_after(&display_console.console.list_node, |
| &console_list); |
| } |
| } |
| |
| void display_console_detach(void) |
| { |
| if (display_console.console.list_node.next) { |
| list_remove(&display_console.console.list_node); |
| display_console.console.list_node.next = NULL; |
| display_console.console.list_node.prev = NULL; |
| } |
| } |