1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <gfx/gfx.h>
6#include <string.h>
7
8#include <zircon/process.h>
9#include <zircon/syscalls.h>
10
11#include "vc.h"
12
13gfx_surface* vc_gfx;
14gfx_surface* vc_tb_gfx;
15
16const gfx_font* vc_font;
17
18void vc_gfx_draw_char(vc_t* vc, vc_char_t ch, unsigned x, unsigned y,
19                      bool invert) {
20    uint8_t fg_color = vc_char_get_fg_color(ch);
21    uint8_t bg_color = vc_char_get_bg_color(ch);
22    if (invert) {
23        // Swap the colors.
24        uint8_t temp = fg_color;
25        fg_color = bg_color;
26        bg_color = temp;
27    }
28    gfx_putchar(vc_gfx, vc->font, vc_char_get_char(ch),
29                x * vc->charw, y * vc->charh,
30                palette_to_color(vc, fg_color),
31                palette_to_color(vc, bg_color));
32}
33
34#if BUILD_FOR_TEST
35static gfx_surface* vc_test_gfx;
36
37zx_status_t vc_init_gfx(gfx_surface* test) {
38    const gfx_font* font = vc_get_font();
39    vc_font = font;
40
41    vc_test_gfx = test;
42
43    // init the status bar
44    vc_tb_gfx = gfx_create_surface(NULL, test->width, font->height,
45                                   test->stride, test->format, 0);
46    if (!vc_tb_gfx) {
47        return ZX_ERR_NO_MEMORY;
48    }
49
50    // init the main surface
51    vc_gfx = gfx_create_surface(NULL, test->width, test->height,
52                                test->stride, test->format, 0);
53    if (!vc_gfx) {
54        gfx_surface_destroy(vc_tb_gfx);
55        vc_tb_gfx = NULL;
56        return ZX_ERR_NO_MEMORY;
57    }
58
59    g_status_width = vc_gfx->width / font->width;
60
61    return ZX_OK;
62}
63
64void vc_gfx_invalidate_all(vc_t* vc) {
65    gfx_copylines(vc_test_gfx, vc_tb_gfx, 0, 0, vc_tb_gfx->height);
66    gfx_copylines(vc_test_gfx, vc_gfx, 0, vc_tb_gfx->height, vc_gfx->height - vc_tb_gfx->height);
67}
68
69void vc_gfx_invalidate_status() {
70    gfx_copylines(vc_test_gfx, vc_tb_gfx, 0, 0, vc_tb_gfx->height);
71}
72
73void vc_gfx_invalidate(vc_t* vc, unsigned x, unsigned y, unsigned w, unsigned h) {
74    unsigned desty = vc_tb_gfx->height + y * vc->charh;
75    if ((x == 0) && (w == vc->columns)) {
76        gfx_copylines(vc_test_gfx, vc_gfx, y * vc->charh, desty, h * vc->charh);
77    } else {
78        gfx_blend(vc_test_gfx, vc_gfx, x * vc->charw, y * vc->charh,
79                  w * vc->charw, h * vc->charh, x * vc->charw, desty);
80    }
81}
82
83void vc_gfx_invalidate_region(vc_t* vc, unsigned x, unsigned y, unsigned w, unsigned h) {
84    unsigned desty = vc_tb_gfx->height + y;
85    if ((x == 0) && (w == vc->columns)) {
86        gfx_copylines(vc_test_gfx, vc_gfx, y, desty, h);
87    } else {
88        gfx_blend(vc_test_gfx, vc_gfx, x, y, w, h, x, desty);
89    }
90}
91#else
92static zx_handle_t vc_gfx_vmo = ZX_HANDLE_INVALID;
93static uintptr_t vc_gfx_mem = 0;
94static size_t vc_gfx_size = 0;
95
96static zx_handle_t vc_hw_gfx_vmo = ZX_HANDLE_INVALID;
97static gfx_surface* vc_hw_gfx = 0;
98static uintptr_t vc_hw_gfx_mem = 0;
99
100void vc_free_gfx() {
101    if (vc_gfx) {
102        gfx_surface_destroy(vc_gfx);
103        vc_gfx = NULL;
104    }
105    if (vc_tb_gfx) {
106        gfx_surface_destroy(vc_tb_gfx);
107        vc_tb_gfx = NULL;
108    }
109    if (vc_gfx_mem) {
110        zx_vmar_unmap(zx_vmar_root_self(), vc_gfx_mem, vc_gfx_size);
111        vc_gfx_mem = 0;
112    }
113    if (vc_gfx_vmo) {
114        zx_handle_close(vc_gfx_vmo);
115        vc_gfx_vmo = ZX_HANDLE_INVALID;
116    }
117    if (vc_hw_gfx_mem) {
118        zx_vmar_unmap(zx_vmar_root_self(), vc_hw_gfx_mem, vc_gfx_size);
119        vc_hw_gfx_mem = 0;
120    }
121    if (vc_hw_gfx_vmo) {
122        zx_handle_close(vc_hw_gfx_vmo);
123        vc_hw_gfx_vmo = ZX_HANDLE_INVALID;
124    }
125}
126
127zx_status_t vc_init_gfx(zx_handle_t fb_vmo, int32_t width, int32_t height,
128                        zx_pixel_format_t format, int32_t stride) {
129    const gfx_font* font = vc_get_font();
130    vc_font = font;
131
132    vc_gfx_size = stride * ZX_PIXEL_FORMAT_BYTES(format) * height;
133
134    zx_status_t r;
135    // If we can't efficiently read from the framebuffer VMO, create a secondary
136    // surface using a regular VMO and blit contents between the two.
137    if ((r = zx_vmo_set_cache_policy(fb_vmo, ZX_CACHE_POLICY_CACHED)) == ZX_ERR_BAD_STATE) {
138        if ((r = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
139                             0, fb_vmo, 0, vc_gfx_size, &vc_hw_gfx_mem)) < 0) {
140            goto fail;
141        }
142
143        if ((vc_hw_gfx = gfx_create_surface((void*) vc_hw_gfx_mem, width, height,
144                                            stride, format, 0)) == NULL) {
145            r = ZX_ERR_INTERNAL;
146            goto fail;
147        }
148
149        vc_hw_gfx_vmo = fb_vmo;
150
151        if ((r = zx_vmo_create(vc_gfx_size, 0, &fb_vmo)) < 0) {
152            goto fail;
153        }
154    } else if (r != ZX_OK) {
155        goto fail;
156    }
157
158    uintptr_t ptr;
159    vc_gfx_vmo = fb_vmo;
160    if ((r = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
161                         0, vc_gfx_vmo, 0, vc_gfx_size, &vc_gfx_mem)) < 0) {
162        goto fail;
163    }
164
165    r = ZX_ERR_NO_MEMORY;
166    // init the status bar
167    if ((vc_tb_gfx = gfx_create_surface((void*) vc_gfx_mem, width, font->height,
168                                        stride, format, 0)) == NULL) {
169        goto fail;
170    }
171
172    // init the main surface
173    ptr = vc_gfx_mem + stride * font->height * ZX_PIXEL_FORMAT_BYTES(format);
174    if ((vc_gfx = gfx_create_surface((void*) ptr, width, height - font->height,
175                                     stride, format, 0)) == NULL) {
176        goto fail;
177    }
178
179    g_status_width = vc_gfx->width / font->width;
180
181    return ZX_OK;
182
183fail:
184    vc_free_gfx();
185    return r;
186}
187
188static void vc_gfx_invalidate_mem(size_t offset, size_t size) {
189    void* ptr;
190    if (vc_hw_gfx_mem) {
191        void* data_ptr = reinterpret_cast<void*>(vc_gfx_mem + offset);
192        ptr = reinterpret_cast<void*>(vc_hw_gfx_mem + offset);
193        memcpy(ptr, data_ptr, size);
194    } else {
195        ptr = reinterpret_cast<void*>(vc_gfx_mem + offset);
196    }
197    zx_cache_flush(ptr, size, ZX_CACHE_FLUSH_DATA);
198}
199
200void vc_gfx_invalidate_all(vc_t* vc) {
201    if (g_vc_owns_display || vc->active) {
202        vc_gfx_invalidate_mem(0, vc_gfx_size);
203    }
204}
205
206void vc_gfx_invalidate_status() {
207    vc_gfx_invalidate_mem(0, vc_tb_gfx->stride * vc_tb_gfx->height * vc_tb_gfx->pixelsize);
208}
209
210// pixel coords
211void vc_gfx_invalidate_region(vc_t* vc, unsigned x, unsigned y, unsigned w, unsigned h) {
212    if (!g_vc_owns_display || !vc->active) {
213        return;
214    }
215    uint32_t flush_size = w * vc_gfx->pixelsize;
216    size_t offset = vc_gfx->stride * (vc->charh + y) * vc_gfx->pixelsize;
217    for (unsigned i = 0; i < h; i++, offset += (vc_gfx->stride * vc_gfx->pixelsize)) {
218        vc_gfx_invalidate_mem(offset, flush_size);
219    }
220}
221
222// text coords
223void vc_gfx_invalidate(vc_t* vc, unsigned x, unsigned y, unsigned w, unsigned h) {
224    vc_gfx_invalidate_region(vc, x * vc->charw, y * vc->charh, w * vc->charw, h * vc->charh);
225}
226#endif
227