basic fx-9860G support and some filesystem tests (WIP)

This commit is contained in:
Lephenixnoir 2021-12-04 21:20:37 +01:00
parent c712416d12
commit 02fe82d903
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
19 changed files with 323 additions and 57 deletions

View File

@ -48,9 +48,17 @@ set(SOURCES
src/string/memarray.c
src/string/misc.c
src/string/naive.c
# unistd
src/unistd/files.c
# fcntl
src/fcntl/open.c
)
# fx-9860G-only assets and fx-CG-50-only assets
set(ASSETS_fx
assets-fx/status.png
assets-fx/status2.png
assets-fx/font_mini.png
assets-fx/fbar_main.png
)
set(ASSETS_cg
)

BIN
assets-fx/fbar_main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
assets-fx/font_mini.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,12 @@
*.png:
type: bopti-image
name_regex: (.*)\.png img_\1
font_mini.png:
type: font
name: font_mini
charset: ascii
proportional: true
height: 5
grid.size: 5x6
grid.padding: 1

BIN
assets-fx/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
assets-fx/status.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

BIN
assets-fx/status2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

View File

@ -49,4 +49,11 @@ extern ft_test ft_string_strlen;
extern ft_test ft_string_naive;
extern ft_test ft_string_strerror;
/* unistd */
extern ft_test ft_unistd_simple_write;
extern ft_test ft_unistd_write_odd;
/* fcntl */
extern ft_test ft_fcntl_open;
#endif /* _FT_ALL_TESTS_H_ */

View File

@ -7,6 +7,7 @@
#include <gint/display.h>
#include <justui/jwidget.h>
#include <ft/util.h>
//---
// Test framework
@ -91,11 +92,11 @@ void ft_list_aggregate(ft_list *l);
//---
/* Colors for each test category */
#define FT_TEST_EMPTY C_RGB(20,20,20)
#define FT_TEST_PASSED C_RGB(6,25,8)
#define FT_TEST_SKIPPED C_RGB(31,20,9)
#define FT_TEST_PENDING C_RGB(15,23,27)
#define FT_TEST_FAILED C_RGB(31,10,7)
#define FT_TEST_EMPTY _(1, C_RGB(20,20,20))
#define FT_TEST_PASSED _(2, C_RGB(6,25,8))
#define FT_TEST_SKIPPED _(3, C_RGB(31,20,9))
#define FT_TEST_PENDING _(4, C_RGB(15,23,27))
#define FT_TEST_FAILED _(5, C_RGB(31,10,7))
/* ft_test_color(): Color for the summary of a test */
int ft_test_color(ft_test const *t);

View File

@ -5,12 +5,33 @@
#ifndef _FT_UTIL_H_
#define _FT_UTIL_H_
#include <errno.h>
#include <string.h>
#ifdef FX9860G
#define _(fx,cg) (fx)
extern font_t font_mini;
extern bopti_image_t img_fbar_main;
#endif
#ifdef FXCG50
#define _(fx, cg) (cg)
#endif
/* Macro to make a call, get the return value, and log it immediately. This
does *not* work well under context, keep calls simple. */
#define DO(var, expr, t, fmt) { \
var = expr; \
ft_log(t, #expr " = " fmt "\n", var); \
}
/* Macro to print errno when non-zero */
#define SHOW_ERRNO(t) \
if(errno != 0) ft_log(t, "errno %d: %s\n", errno, strerror(errno))
/* Combination */
#define DO_E(var, expr, t, fmt) { \
var = expr; \
ft_log(t, #expr " = " fmt "\n", var); \
SHOW_ERRNO(t); \
}
#endif /* _FT_UTIL_H_ */

49
src/fcntl/open.c Normal file
View File

@ -0,0 +1,49 @@
#include <ft/test.h>
#include <ft/all-tests.h>
#include <gint/gint.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
static void _ft_fcntl_open_switch(ft_test *t)
{
char const *filename = "ft_open.txt";
int fd, rc;
errno = 0;
fd = open(filename, O_RDONLY);
ft_log(t, "open(\"%s\", O_RDONLY) = %d\n", filename, fd);
if(fd == -1)
ft_log(t, "errno %d: %s\n", errno, strerror(errno));
ft_assert(t, fd == -1 && errno == ENOENT);
if(fd != -1)
close(fd);
fd = open(filename, O_RDWR | O_CREAT);
ft_log(t, "open(\"%s\", O_RDWR | O_CREAT) = %d\n", filename, fd);
if(fd == -1)
ft_log(t, "errno %d: %s\n", errno, strerror(errno));
ft_assert(t, fd >= 0);
if(fd != -1)
close(fd);
rc = unlink(filename);
ft_log(t, "unlink(\"%s\") = %d\n", filename, rc);
if(rc == -1)
ft_log(t, "errno %d: %s\n", errno, strerror(errno));
ft_assert(t, rc == 0);
}
static void _ft_fcntl_open(ft_test *t)
{
gint_world_switch(GINT_CALL(_ft_fcntl_open_switch, (void *)t));
}
ft_test ft_fcntl_open = {
.name = "Opening files",
.function = _ft_fcntl_open,
};
// TODO: Truncation

View File

@ -18,34 +18,34 @@
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
ft_list headers_libc[] = {
{ "<ctype.h>", (ft_test*[]){
{ _("ctype.h", "<ctype.h>"), (ft_test*[]){
&ft_ctype_classes,
&ft_ctype_conversion,
NULL,
}},
{ "<inttypes.h>", (ft_test*[]){
{ _("inttypes.h", "<inttypes.h>"), (ft_test*[]){
&ft_inttypes_sizes,
&ft_inttypes_functions,
NULL,
}},
{ "<locale.h>", NULL },
{ "<setjmp.h>", (ft_test*[]){
{ _("locale.h", "<locale.h>"), NULL },
{ _("setjmp.h", "<setjmp.h>"), (ft_test*[]){
&ft_setjmp_simple,
&ft_setjmp_massive,
&ft_setjmp_interrupt,
NULL,
}},
{ "<signal.h>", (ft_test*[]){
{ _("signal.h", "<signal.h>"), (ft_test*[]){
&ft_signal_signal,
NULL,
}},
{ "<stdio.h>", (ft_test*[]){
{ _("stdio.h", "<stdio.h>"), (ft_test*[]){
&ft_stdio_printf_basics,
&ft_stdio_printf_formats,
&ft_stdio_printf_fp,
NULL,
}},
{ "<stdlib.h>", (ft_test*[]){
{ _("stdlib.h", "<stdlib.h>"), (ft_test*[]){
&ft_stdlib_arith,
&ft_stdlib_sizes,
&ft_stdlib_llconv,
@ -57,7 +57,7 @@ ft_list headers_libc[] = {
&ft_stdlib_abort,
NULL,
}},
{ "<string.h>", (ft_test*[]){
{ _("string.h", "<string.h>"), (ft_test*[]){
&ft_string_memset,
&ft_string_memcpy,
&ft_string_memmove,
@ -68,14 +68,22 @@ ft_list headers_libc[] = {
&ft_string_strerror,
NULL,
}},
{ "<time.h>", NULL },
{ "<uchar.h>", NULL },
{ "<wchar.h>", NULL },
{ "<wctype.h>", NULL },
{ _("time.h", "<time.h>"), NULL },
{ _("uchar.h", "<uchar.h>"), NULL },
{ _("wchar.h", "<wchar.h>"), NULL },
{ _("wctype.h", "<wctype.h>"), NULL },
{ NULL }
};
ft_list headers_posix[] = {
{ "<unistd.h>", NULL },
{ "<fcntl.h>", (ft_test*[]){
&ft_fcntl_open,
NULL,
}},
{ "<unistd.h>", (ft_test*[]){
&ft_unistd_simple_write,
&ft_unistd_write_odd,
NULL,
}},
{ NULL }
};
@ -98,22 +106,26 @@ int main(void)
ft_list_init(&headers_posix[i]);
// Create GUI
gscreen *scr = gscreen_create2("FxLibc tests", NULL,
gscreen *scr = gscreen_create2("FxLibc tests", &img_fbar_main,
"FxLibc regression and performance tests", "/LIBC;/POSIX;;;;#RUN");
fbrowser *browser = fbrowser_create(NULL);
int selected_set = 0;
gscreen_add_tab(scr, browser, NULL);
gscreen_set_tab_title_visible(scr, 0, _(false, true));
fbrowser_set_headers(browser, headers_libc, false);
jwidget *results = jwidget_create(NULL);
jlayout_set_stack(results);
gscreen_add_tab(scr, results, NULL);
gscreen_set_tab_title_visible(scr, 1, _(false, true));
gscreen_set_tab_fkeys_visible(scr, 1, false);
flog *testlog = flog_create(NULL);
flog_set_line_spacing(testlog, 3);
flog_set_line_spacing(testlog, _(1,3));
flog_set_font(testlog, _(&font_mini, dfont_default()));
gscreen_add_tab(scr, testlog, testlog);
gscreen_set_tab_title_visible(scr, 2, _(false, true));
gscreen_set_tab_fkeys_visible(scr, 2, false);
// Event handling

View File

@ -186,8 +186,17 @@ static void paint_results(int x, int y, uint8_t *rc)
int value = -1;
if(row < 4*sizes) value = memarray_get(rc, 4*sizes * col + row);
#ifdef FX9860G
if(value >= 0) {
for(int y = y1; y <= y1+10; y++)
for(int x = x1; x <= x1+19; x++)
dpixel(x, y, (x&1)^(y&1) ? C_BLACK : (value != 0));
}
#else
int fg = (value == -1) ? C_WHITE : (value == 0) ? C_GREEN : C_RED;
drect_border(x1, y1, x1+19, y1+10, fg, 1, C_BLACK);
#endif
}
}

87
src/unistd/files.c Normal file
View File

@ -0,0 +1,87 @@
#include <ft/test.h>
#include <ft/all-tests.h>
#include <ft/util.h>
#include <gint/gint.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
static void _ft_unistd_simple_write_switch(ft_test *t)
{
int fd, rc;
errno = 0;
DO_E(fd, open("ft_write.txt", O_WRONLY | O_CREAT), t, "%d");
ft_assert(t, fd >= 0);
if(fd >= 0) {
DO_E(rc, write(fd, "write\n", 6), t, "%d");
ft_assert(t, rc == 6);
DO_E(rc, write(fd, "line #2\n", 8), t, "%d");
ft_assert(t, rc == 8);
DO_E(rc, write(fd, "line #3\n", 8), t, "%d");
ft_assert(t, rc == 8);
}
close(fd);
}
static void _ft_unistd_simple_write(ft_test *t)
{
gint_world_switch(GINT_CALL(_ft_unistd_simple_write_switch,(void *)t));
}
ft_test ft_unistd_simple_write = {
.name = "Write simple file",
.function = _ft_unistd_simple_write,
};
static void _ft_unistd_write_odd_switch_1(ft_test *t)
{
int fd, rc;
errno = 0;
DO_E(fd, open("ft_odd1.txt", O_WRONLY | O_CREAT), t, "%d");
ft_assert(t, fd >= 0);
if(fd >= 0) {
DO_E(rc, write(fd, "write odd byte count\n", 21), t, "%d");
ft_assert(t, rc == 21);
DO_E(rc, write(fd, "again, cancelling it\n", 21), t, "%d");
ft_assert(t, rc == 21);
}
close(fd);
}
static void _ft_unistd_write_odd_switch_2(ft_test *t)
{
int fd, rc;
errno = 0;
DO_E(fd, open("ft_odd2.txt", O_WRONLY | O_CREAT), t, "%d");
ft_assert(t, fd >= 0);
if(fd >= 0) {
DO_E(rc, write(fd, "write odd byte count\n", 21), t, "%d");
ft_assert(t, rc == 21);
DO_E(rc, write(fd, "then even, keeping it\n", 22), t, "%d");
ft_assert(t, rc == 22);
}
close(fd);
}
static void _ft_unistd_write_odd(ft_test *t)
{
gint_world_switch(GINT_CALL(_ft_unistd_write_odd_switch_1, (void *)t));
gint_world_switch(GINT_CALL(_ft_unistd_write_odd_switch_2, (void *)t));
}
ft_test ft_unistd_write_odd = {
.name = "Odd-length writes",
.function = _ft_unistd_write_odd,
};

View File

@ -18,7 +18,7 @@ fbar *fbar_create(void *parent)
b->tests = NULL;
b->lists = NULL;
jwidget_set_margin(b, 0, 8, 2, 8);
jwidget_set_margin(b, _(1,0), _(4,8), _(0,2), _(4,8));
return b;
}
@ -82,10 +82,38 @@ static void stats_add_list(struct stats *st, ft_list *l)
static void fbar_poly_csize(void *b0)
{
fbar *b = b0;
b->widget.w = 120;
b->widget.h = 13;
b->widget.w = _(75,120);
b->widget.h = _(5,13);
}
#ifdef FX9860G
static int block(int rx, int y, GUNUSED int h, int px, int val, int fg)
{
if(!px) return 0;
extern bopti_image_t img_status;
dsubimage(rx, y, &img_status, 6*(fg-1), 0, 5, 5, DIMAGE_NONE);
font_t const *old_font = dfont(&font_mini);
int w;
char str[16];
sprintf(str, "%d", val);
dsize(str, NULL, &w, NULL);
dtext(rx+6, y, C_BLACK, str);
dfont(old_font);
return w + 11;
}
#else
static int block(int rx, int y, int h, int px, int val, int fg)
{
drect(rx, y, rx+px-1, y+h-1, fg);
dprint_opt(rx+px/2, y+2, C_BLACK, C_NONE, DTEXT_CENTER, DTEXT_TOP,
"%d", val);
return px;
}
#endif
static void fbar_poly_render(void *b0, int x, int y)
{
fbar *b = b0;
@ -169,20 +197,14 @@ static void fbar_poly_render(void *b0, int x, int y)
}
/* Draw */
#define block(px, val, fg) if(px) { \
drect(rx, y, rx+px-1, y+h-1, fg); \
dprint_opt(rx+px/2, y+2, C_BLACK, C_NONE, DTEXT_CENTER, DTEXT_TOP, \
"%d", val); \
rx += px; \
}
int rx = x;
block(px.passed, st.passed, FT_TEST_PASSED);
block(px.skipped, st.skipped, FT_TEST_SKIPPED);
block(px.failed, st.failed, FT_TEST_FAILED);
block(px.pending, st.pending, FT_TEST_PENDING);
block(px.empty, st.empty, FT_TEST_EMPTY);
#undef block
rx += block(rx, y, h, px.passed, st.passed, FT_TEST_PASSED);
rx += block(rx, y, h, px.skipped, st.skipped, FT_TEST_SKIPPED);
rx += block(rx, y, h, px.failed, st.failed, FT_TEST_FAILED);
rx += block(rx, y, h, px.pending, st.pending, FT_TEST_PENDING);
rx += block(rx, y, h, px.empty, st.empty, FT_TEST_EMPTY);
#ifdef FXCG50
/* Darken the border for a cool effect */
for(int dx = 0; dx < w; dx++) {
int i1 = 396 * (y) + (x + dx);
@ -196,6 +218,7 @@ static void fbar_poly_render(void *b0, int x, int y)
gint_vram[i1] = (gint_vram[i1] & 0xf7de) >> 1;
gint_vram[i2] = (gint_vram[i2] & 0xf7de) >> 1;
}
#endif
}
/* fbar type definition */

View File

@ -27,15 +27,15 @@ fbrowser *fbrowser_create(void *parent)
b->data_tests = NULL;
jwidget_init(&b->widget, fbrowser_type_id, parent);
jlayout_set_hbox(b)->spacing = 4;
jlayout_set_hbox(b)->spacing = _(0,4);
// On the left, list of headers
b->headers = flist_create(GINT_CALL_NULL, b);
flist_set_rows(b->headers, 1);
flist_select(b->headers, 0);
flist_set_row_height(b->headers, 13);
jwidget_set_fixed_width(b->headers, 120);
flist_set_row_height(b->headers, _(7,13));
jwidget_set_fixed_width(b->headers, _(39,120));
jwidget_set_stretch(b->headers, 0, 1, false);
// On the right, either a summary of the whole application...
@ -52,6 +52,13 @@ fbrowser *fbrowser_create(void *parent)
jwidget_set_stretch(b->bar_top, 1, 0, false);
l = jlabel_create(
#ifdef FX9860G
"fxlibc unit tests!\n"
"Run with F6:\n"
"- Here: all\n"
"- On header\n"
"- On single test",
#else
"Summary here!\n\n"
"Green: Passed\n"
"Orange: Skipped\n"
@ -62,29 +69,34 @@ fbrowser *fbrowser_create(void *parent)
"- Here: runs all tests\n"
"- On a header: all tests in header\n"
"- On a test: just that test",
#endif
summary);
jlabel_set_block_alignment(l, J_ALIGN_LEFT, J_ALIGN_TOP);
jlabel_set_line_spacing(l, 3);
jlabel_set_line_spacing(l, _(1,3));
jlabel_set_font(l, _(&font_mini, dfont_default()));
jwidget_set_stretch(l, 1, 1, false);
jwidget_set_margin(l, 0, 0, 0, _(1,0));
// ... or a label for headers with no tests yet...
l = jlabel_create("No test for this header.", b->rstack);
l = jlabel_create(_("No test", "No test for this header."), b->rstack);
jlabel_set_block_alignment(l, J_ALIGN_LEFT, J_ALIGN_TOP);
jlabel_set_font(l, _(&font_mini, dfont_default()));
jwidget_set_margin(l, 2, 0, 0, 0);
jwidget_set_stretch(l, 1, 1, false);
jwidget_set_margin(l, 0, 0, 0, _(1,0));
// Or a header summary and a test list for the selected header
jwidget *rvbox = jwidget_create(b->rstack);
jwidget_set_stretch(rvbox, 1, 1, false);
jlayout_set_vbox(rvbox)->spacing = 4;
jlayout_set_vbox(rvbox)->spacing = _(1,4);
b->bar_right = fbar_create(rvbox);
jwidget_set_stretch(b->bar_right, 1, 0, false);
b->tests = flist_create(GINT_CALL_NULL, rvbox);
flist_set_row_height(b->tests, 13);
flist_set_row_height(b->tests, _(7,13));
jwidget_set_stretch(b->tests, 1, 1, false);
return b;
@ -150,9 +162,11 @@ static void render_entries(flist *l, void *data, int is_headers)
int w = jwidget_content_width(l);
int h = l->row_height;
#ifdef FXCG50
if(selected && !focused) {
drect(l->x, l->y, l->x + w - 1, l->y + h - 1, C_RGB(24, 24, 24));
}
#endif
char const *name = "(null)";
int color = C_BLACK;
@ -170,22 +184,35 @@ static void render_entries(flist *l, void *data, int is_headers)
name = test->name;
}
font_t const *old_font = dfont(_(&font_mini, dfont_default()));
if(is_headers && l->row == 0)
dtext(l->x + 2, l->y + 2, C_BLACK, "Summary");
dtext(l->x+_(1,2), l->y+_(1,2), C_BLACK, "Summary");
else if(is_headers)
dprint(l->x+_(1,16), l->y+_(1,2), C_BLACK, "%s", name);
else
dprint(l->x + 16, l->y + 2, C_BLACK, "%s", name);
dprint(l->x+_(7,16), l->y+_(1,2), C_BLACK, "%s", name);
if(selected && focused) {
drect(l->x, l->y, l->x + w - 1, l->y + h - 1, C_INVERT);
}
if(is_headers && l->row == 0) return;
for(int y = 0; y < 9; y++)
for(int x = 0; x < 9; x++)
{
if((x == 0 || x == 8) && (y == 0 || y == 8)) continue;
dpixel(l->x + 3 + x, l->y + 2 + y, color);
if(!is_headers || l->row != 0) {
#ifdef FX9860G
extern bopti_image_t img_status, img_status2;
bopti_image_t *i = (selected && focused) ? &img_status2 : &img_status;
if(!is_headers)
dsubimage(l->x+1, l->y+1, i, 6*(color-1), 0, 5, 5, DIMAGE_NONE);
#else
for(int y = 0; y < 9; y++)
for(int x = 0; x < 9; x++) {
if((x == 0 || x == 8) && (y == 0 || y == 8)) continue;
dpixel(l->x + 3 + x, l->y + 2 + y, color);
}
#endif
}
dfont(old_font);
}
//---

View File

@ -147,8 +147,10 @@ static void flist_poly_render(void *l0, int x, int base_y)
int bar_y = (l->top * area_h + l->rows/2) / l->rows;
int bar_h = (l->visible * area_h + l->rows/2) / l->rows;
#ifdef FXCG50
drect(area_x, area_y, area_x+area_w-1, area_y+area_h-1,
C_RGB(24,24,24));
#endif
drect(area_x, area_y + bar_y, area_x + area_w - 1,
area_y + bar_y + bar_h, C_BLACK);
}

View File

@ -73,7 +73,7 @@ static void flog_poly_layout(void *l0)
l->visible = total_height / (l->font->line_height + l->line_spacing);
/* Compute number of lines. Keep 3 pixels of room for the scrollbar */
int const w = max(0, jwidget_content_width(l) - 33);
int const w = max(0, jwidget_content_width(l) - _(3,33));
char const *log = l->log;
l->lines = 0;
@ -83,7 +83,7 @@ static void flog_poly_layout(void *l0)
l->lines++;
}
else {
char const *endscreen = drsize(log, NULL, w, NULL);
char const *endscreen = drsize(log, l->font, w, NULL);
char const *endline = strchrnul(log, '\n');
int len = max(1, min(endscreen-log, endline-log));
log += len + (log[len] == '\n');
@ -100,38 +100,42 @@ static void flog_poly_render(void *l0, int x, int y)
int const w = jwidget_content_width(l);
int const h = jwidget_content_height(l);
char const *log = l->log;
font_t const *old_font = dfont(l->font);
/* current_y is ignored until current_line >= top, at which point it
starts at y and increases each line */
int current_line = 0;
int current_y = y;
while(log < l->log + l->log_size)
{
#ifdef FXCG50
if(current_line >= l->top && current_line < l->top + l->visible)
dprint_opt(x+22, current_y, C_BLACK, C_NONE,
DTEXT_RIGHT, DTEXT_TOP, "%d", current_line+1);
#endif
if(log[0] == '\n') {
log++;
}
else {
char const *endscreen = drsize(log, NULL, w - 33, NULL);
char const *endscreen = drsize(log, NULL, w - _(3,33), NULL);
char const *endline = strchrnul(log, '\n');
int len = max(1, min(endscreen-log, endline-log));
if(current_line >= l->top && current_line < l->top + l->visible)
dtext_opt(x+30, current_y, C_BLACK, C_NONE, DTEXT_LEFT,
dtext_opt(x+_(0,30), current_y, C_BLACK, C_NONE, DTEXT_LEFT,
DTEXT_TOP, log, len);
log += len + (log[len] == '\n');
}
if(current_line >= l->top && current_line < l->top + l->visible) {
#ifdef FXCG50
int previous_y = current_y;
current_y += l->font->line_height + l->line_spacing;
dline(x+26, previous_y, x+26, current_y-1, C_BLACK);
#endif
current_y += l->font->line_height + l->line_spacing;
}
current_line++;
}
@ -147,11 +151,15 @@ static void flog_poly_render(void *l0, int x, int y)
int bar_y = (l->top * area_h) / current_line;
int bar_h = (l->visible * area_h) / current_line;
#ifdef FXCG50
drect(area_x, area_y, area_x+area_w-1, area_y+area_h-1,
C_RGB(24,24,24));
#endif
drect(area_x, area_y + bar_y, area_x + area_w - 1,
area_y + bar_y + bar_h, C_BLACK);
}
dfont(old_font);
}
static bool flog_poly_event(void *l0, jevent e)

View File

@ -43,7 +43,7 @@ gscreen *gscreen_create(char const *name, char const *labels)
if(title) {
jwidget_set_background(title, C_BLACK);
jlabel_set_text_color(title, C_WHITE);
jlabel_set_font(title, _(&font_title, dfont_default()));
jlabel_set_font(title, dfont_default());
jwidget_set_stretch(title, 1, 0, false);
#ifdef FX9860G