1 /*
2  * Copyright (c) 2008-2010, 2015 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 /**
10  * @file
11  * @brief  Manage graphics console
12  *
13  * This file contains functions to provide stdout to the graphics console.
14  *
15  * @ingroup graphics
16  */
17 
18 #include <lk/debug.h>
19 #include <assert.h>
20 #include <lib/io.h>
21 #include <lk/init.h>
22 #include <lib/gfx.h>
23 #include <lib/gfxconsole.h>
24 #include <lib/font.h>
25 #include <dev/display.h>
26 
27 /** @addtogroup graphics
28  * @{
29  */
30 
31 /**
32  * @brief  Represent state of graphics console
33  */
34 static struct {
35     gfx_surface *surface;
36     uint rows, columns;
37     uint extray; // extra pixels left over if the rows doesn't fit precisely
38 
39     uint x, y;
40 
41     uint32_t front_color;
42     uint32_t back_color;
43 } gfxconsole;
44 
gfxconsole_putc(char c)45 static void gfxconsole_putc(char c) {
46     static enum { NORMAL, ESCAPE } state = NORMAL;
47     static uint32_t p_num = 0;
48 
49     switch (state) {
50         case NORMAL: {
51             if (c == '\n' || c == '\r') {
52                 gfxconsole.x = 0;
53                 gfxconsole.y++;
54             } else if (c == 0x1b) {
55                 p_num = 0;
56                 state = ESCAPE;
57             } else {
58                 font_draw_char(gfxconsole.surface, c, gfxconsole.x * FONT_X, gfxconsole.y * FONT_Y, gfxconsole.front_color);
59                 gfxconsole.x++;
60             }
61             break;
62         }
63 
64         case ESCAPE: {
65             if (c >= '0' && c <= '9') {
66                 p_num = (p_num * 10) + (c - '0');
67             } else if (c == 'D') {
68                 if (p_num <= gfxconsole.x)
69                     gfxconsole.x -= p_num;
70                 state = NORMAL;
71             } else if (c == '[') {
72                 // eat this character
73             } else {
74                 font_draw_char(gfxconsole.surface, c, gfxconsole.x * FONT_X, gfxconsole.y * FONT_Y, gfxconsole.front_color);
75                 gfxconsole.x++;
76                 state = NORMAL;
77             }
78             break;
79         }
80     }
81 
82     if (gfxconsole.x >= gfxconsole.columns) {
83         gfxconsole.x = 0;
84         gfxconsole.y++;
85     }
86     if (gfxconsole.y >= gfxconsole.rows) {
87         // scroll up
88         gfx_copyrect(gfxconsole.surface, 0, FONT_Y, gfxconsole.surface->width, gfxconsole.surface->height - FONT_Y - gfxconsole.extray, 0, 0);
89         gfxconsole.y--;
90         gfx_fillrect(gfxconsole.surface, 0, gfxconsole.surface->height - FONT_Y - gfxconsole.extray, gfxconsole.surface->width, FONT_Y, gfxconsole.back_color);
91         gfx_flush(gfxconsole.surface);
92     }
93 }
94 
gfxconsole_print_callback(print_callback_t * cb,const char * str,size_t len)95 void gfxconsole_print_callback(print_callback_t *cb, const char *str, size_t len) {
96     for (size_t i = 0; i < len; i++) {
97         gfxconsole_putc(str[i]);
98     }
99 }
100 
101 static print_callback_t cb = {
102     .entry = { 0 },
103     .print = gfxconsole_print_callback,
104     .context = NULL
105 };
106 
107 /**
108  * @brief  Initialize graphics console on given drawing surface.
109  *
110  * The graphics console subsystem is initialized, and registered as
111  * an output device for debug output.
112  */
gfxconsole_start(gfx_surface * surface)113 void gfxconsole_start(gfx_surface *surface) {
114     DEBUG_ASSERT(gfxconsole.surface == NULL);
115 
116     // set up the surface
117     gfxconsole.surface = surface;
118 
119     // calculate how many rows/columns we have
120     gfxconsole.rows = surface->height / FONT_Y;
121     gfxconsole.columns = surface->width / FONT_X;
122     gfxconsole.extray = surface->height - (gfxconsole.rows * FONT_Y);
123 
124     dprintf(SPEW, "gfxconsole: rows %d, columns %d, extray %d\n", gfxconsole.rows, gfxconsole.columns, gfxconsole.extray);
125 
126     // start in the upper left
127     gfxconsole.x = 0;
128     gfxconsole.y = 0;
129 
130     // colors are white and black for now
131     gfxconsole.front_color = 0xffffffff;
132     gfxconsole.back_color = 0;
133 
134     // register for debug callbacks
135     register_print_callback(&cb);
136 }
137 
138 /**
139  * @brief  Initialize graphics console on default display
140  */
gfxconsole_start_on_display(void)141 void gfxconsole_start_on_display(void) {
142     static bool started = false;
143 
144     if (started)
145         return;
146 
147     /* pop up the console */
148     struct display_framebuffer fb;
149     if (display_get_framebuffer(&fb) < 0)
150         return;
151 
152     gfx_surface *s = gfx_create_surface_from_display(&fb);
153     gfxconsole_start(s);
154     started = true;
155 }
156 
gfxconsole_init_hook(uint level)157 static void gfxconsole_init_hook(uint level) {
158     gfxconsole_start_on_display();
159 }
160 
161 LK_INIT_HOOK(gfxconsole, &gfxconsole_init_hook, LK_INIT_LEVEL_PLATFORM);
162