add exit and printf tests
This commit is contained in:
parent
40349e99b7
commit
287d9fb695
|
@ -20,6 +20,7 @@ set(SOURCES
|
|||
src/widgets/flist.c
|
||||
src/widgets/flog.c
|
||||
src/widgets/gscreen.c
|
||||
src/widgets/gtable.c
|
||||
# ctype
|
||||
src/ctype/charprops.c
|
||||
src/ctype/classes.c
|
||||
|
@ -33,8 +34,11 @@ set(SOURCES
|
|||
src/setjmp/simple.c
|
||||
# signal
|
||||
src/signal/signal.c
|
||||
# stdio
|
||||
src/stdio/printf.c
|
||||
# stdlib
|
||||
src/stdlib/arith.c
|
||||
src/stdlib/exit.c
|
||||
src/stdlib/fpconv.c
|
||||
src/stdlib/intconv.c
|
||||
src/stdlib/sizes.c
|
||||
|
|
|
@ -23,12 +23,20 @@ extern ft_test ft_setjmp_interrupt;
|
|||
/* signal */
|
||||
extern ft_test ft_signal_signal;
|
||||
|
||||
/* stdio */
|
||||
extern ft_test ft_stdio_printf_basics;
|
||||
extern ft_test ft_stdio_printf_formats;
|
||||
extern ft_test ft_stdio_printf_fp;
|
||||
|
||||
/* stdlib */
|
||||
extern ft_test ft_stdlib_arith;
|
||||
extern ft_test ft_stdlib_sizes;
|
||||
extern ft_test ft_stdlib_llconv;
|
||||
extern ft_test ft_stdlib_lconv;
|
||||
extern ft_test ft_stdlib_fpconv;
|
||||
extern ft_test ft_stdlib_exit;
|
||||
extern ft_test ft_stdlib__Exit;
|
||||
extern ft_test ft_stdlib_abort;
|
||||
|
||||
/* string */
|
||||
extern ft_test ft_string_memset;
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
//---
|
||||
// gintctl.widgets.gtable: gintctl's scrolling tables
|
||||
//---
|
||||
|
||||
#ifndef _GINTCTL_WIDGETS_GTABLE
|
||||
#define _GINTCTL_WIDGETS_GTABLE
|
||||
|
||||
#include <justui/jwidget.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
/* gtable: A dynamic scrolling table
|
||||
|
||||
This widget is a table with a header and a set of rows, that scrolls
|
||||
vertically to show more rows than there is space available on-screen.
|
||||
|
||||
The columns all have their own width, label, and font. During rendering, the
|
||||
data is provided to the table through a user-provided function that
|
||||
generates strings for every column in a chosen row. */
|
||||
typedef struct gtable {
|
||||
jwidget widget;
|
||||
/* Column details */
|
||||
struct gtable_column *meta;
|
||||
/* Function to generate the strings for a row */
|
||||
void (*generator)(struct gtable *g, int row, j_arg_t arg);
|
||||
j_arg_t arg;
|
||||
/* Number of columns, number of rows visible on-screen, number of rows,
|
||||
and current top row */
|
||||
uint8_t columns;
|
||||
uint8_t visible;
|
||||
uint16_t rows;
|
||||
uint16_t offset;
|
||||
/* Row height */
|
||||
uint8_t row_height;
|
||||
/* Additional row spacing */
|
||||
uint8_t row_spacing;
|
||||
|
||||
/* Coordinates during rendering */
|
||||
int16_t x, y;
|
||||
|
||||
} gtable;
|
||||
|
||||
/* gtable_create(): Create a scrolling table
|
||||
|
||||
The number of columns of the table must be fixed, while the number of rows
|
||||
can be set and changed later with gtable_set_rows().
|
||||
|
||||
The generator is a function that will be called during rendering to generate
|
||||
strings for each row. It should have the following prototype, where the last
|
||||
argument is an optional object of a type listed in j_arg_t (essentially an
|
||||
integer or a pointer).
|
||||
|
||||
void generator(gtable *g, int row [, <an type of j_arg_t>])
|
||||
|
||||
The generator should return all the strings for the row by a call to
|
||||
gtable_provide(). */
|
||||
gtable *gtable_create(int columns, void *generator, j_arg_t arg, void *parent);
|
||||
|
||||
/* gtable_provide(): Pass strings to display on a given row
|
||||
|
||||
The strings should be passed as variable arguments. A NULL terminator is
|
||||
added automatically. The strings will not be used after the call finishes,
|
||||
so they can be allocated statically or on the stack of the caller. */
|
||||
void gtable_provide(gtable *g, ...);
|
||||
#define gtable_provide(...) gtable_provide(__VA_ARGS__, NULL)
|
||||
|
||||
//---
|
||||
// Configuration of columns
|
||||
//---
|
||||
|
||||
/* gtable_set_column_title(): Set the title of a column */
|
||||
void gtable_set_column_title(gtable *t, int column, char const *title);
|
||||
|
||||
/* gtable_set_column_font(): Set the font of a column */
|
||||
void gtable_set_column_font(gtable *t, int column, font_t const *font);
|
||||
|
||||
/* gtable_set_column_size(): Set the size of a column
|
||||
This function sets the size of a column in relative units. During layout,
|
||||
the width of the table will be split, and each column will receive space
|
||||
proportional to their size setting. The default is 1 for all columns. */
|
||||
void gtable_set_column_size(gtable *t, int column, uint size);
|
||||
|
||||
/* The previous functions have group-setting functions to set properties for
|
||||
all columns at once. Simply list the headers/fonts/widths all at once;
|
||||
columns 0 through the number of args will be assigned. */
|
||||
|
||||
void gtable_set_column_titles(gtable *t, ...);
|
||||
#define gtable_set_column_titles(...) \
|
||||
gtable_set_column_titles(__VA_ARGS__, NULL)
|
||||
|
||||
void gtable_set_column_sizes(gtable *t, ...);
|
||||
#define gtable_set_column_sizes(...) \
|
||||
gtable_set_column_sizes(__VA_ARGS__, 0)
|
||||
|
||||
void gtable_set_column_fonts(gtable *t, ...);
|
||||
#define gtable_set_column_fonts(...) \
|
||||
gtable_set_column_fonts(__VA_ARGS__, NULL)
|
||||
|
||||
/* gtable_set_font(): Set the font for all columns */
|
||||
void gtable_set_font(gtable *t, font_t const *font);
|
||||
|
||||
//---
|
||||
// Configuration of rows
|
||||
//---
|
||||
|
||||
/* gtable_set_rows(): Set the number of rows */
|
||||
void gtable_set_rows(gtable *t, int rows);
|
||||
|
||||
/* gtable_set_row_height(): Fix row height instead of guessing from font
|
||||
Setting 0 will reset to the default behavior. */
|
||||
void gtable_set_row_height(gtable *t, int row_height);
|
||||
|
||||
/* gtable_set_row_spacing(): Set additional row spacing */
|
||||
void gtable_set_row_spacing(gtable *t, int row_spacing);
|
||||
|
||||
//---
|
||||
// Movement
|
||||
//---
|
||||
|
||||
/* gtable_scroll_to(): Scroll to the specified offset (if acceptable) */
|
||||
void gtable_scroll_to(gtable *t, int offset);
|
||||
|
||||
/* gtable_end(): Offset of the end of the table */
|
||||
int gtable_end(gtable *t);
|
||||
|
||||
#endif /* _GINTCTL_WIDGETS_GTABLE */
|
19
src/main.c
19
src/main.c
|
@ -1,6 +1,5 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/kprint.h>
|
||||
|
||||
#include <ft/widgets/gscreen.h>
|
||||
#include <ft/widgets/fbrowser.h>
|
||||
|
@ -9,10 +8,12 @@
|
|||
#include <ft/test.h>
|
||||
#include <ft/all-tests.h>
|
||||
|
||||
#include <fxlibc/printf.h>
|
||||
|
||||
#include <gint/hardware.h>
|
||||
#include <bits/cpucap.h>
|
||||
|
||||
/* We don't initialize the test result fields below */
|
||||
/* We don't initialize the test result fields in the ft_list objects below */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
|
||||
|
@ -38,13 +39,21 @@ ft_list headers_libc[] = {
|
|||
&ft_signal_signal,
|
||||
NULL,
|
||||
}},
|
||||
{ "<stdio.h>", NULL },
|
||||
{ "<stdio.h>", (ft_test*[]){
|
||||
&ft_stdio_printf_basics,
|
||||
&ft_stdio_printf_formats,
|
||||
&ft_stdio_printf_fp,
|
||||
NULL,
|
||||
}},
|
||||
{ "<stdlib.h>", (ft_test*[]){
|
||||
&ft_stdlib_arith,
|
||||
&ft_stdlib_sizes,
|
||||
&ft_stdlib_llconv,
|
||||
&ft_stdlib_lconv,
|
||||
&ft_stdlib_fpconv,
|
||||
&ft_stdlib_exit,
|
||||
&ft_stdlib__Exit,
|
||||
&ft_stdlib_abort,
|
||||
NULL,
|
||||
}},
|
||||
{ "<string.h>", (ft_test*[]){
|
||||
|
@ -78,7 +87,8 @@ int main(void)
|
|||
if(isSH4())
|
||||
__cpucap |= __CPUCAP_SH4ALDSP;
|
||||
|
||||
kprint_enable_fp();
|
||||
__printf_enable_fp();
|
||||
__printf_enable_fixed();
|
||||
|
||||
// Initialize test results
|
||||
for(int i = 0; headers_libc[i].name; i++)
|
||||
|
@ -96,6 +106,7 @@ int main(void)
|
|||
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_fkeys_visible(scr, 1, false);
|
||||
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
#include <ft/test.h>
|
||||
#include <ft/all-tests.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fxlibc/printf.h>
|
||||
|
||||
#include <ft/widgets/gtable.h>
|
||||
|
||||
#define NAN __builtin_nan("")
|
||||
#define INFINITY __builtin_inf()
|
||||
|
||||
struct printf_test {
|
||||
char const *format;
|
||||
|
||||
/* Argument information is set by macros below */
|
||||
enum {
|
||||
TYPE_I32, TYPE_U32, TYPE_STR, TYPE_PTR, TYPE_U64, TYPE_DBL
|
||||
} type;
|
||||
union {
|
||||
int i32;
|
||||
uint32_t u32;
|
||||
char const *str;
|
||||
void *ptr;
|
||||
uint64_t u64;
|
||||
double dbl;
|
||||
};
|
||||
char const *argument_as_string;
|
||||
char const *solution;
|
||||
};
|
||||
|
||||
struct printf_result {
|
||||
char answer[15];
|
||||
uint8_t passed;
|
||||
};
|
||||
|
||||
#define I32(i) TYPE_I32, { .i32 = i }, #i
|
||||
#define U32(u) TYPE_U32, { .u32 = u }, #u
|
||||
#define STR(s) TYPE_STR, { .str = s }, #s
|
||||
#define PTR(p) TYPE_PTR, { .ptr = (void *)p }, #p
|
||||
#define U64(u) TYPE_U64, { .u64 = u }, #u
|
||||
#define DBL(d) TYPE_DBL, { .dbl = d }, #d
|
||||
|
||||
static void run_tests(ft_test *t0, struct printf_test const *tests,
|
||||
struct printf_result *results)
|
||||
{
|
||||
for(int i = 0; tests[i].format; i++)
|
||||
{
|
||||
struct printf_test const *t = &tests[i];
|
||||
struct printf_result *r = &results[i];
|
||||
int c = -1;
|
||||
|
||||
#define run(TYPE, field) \
|
||||
case TYPE: \
|
||||
c = snprintf(r->answer, 16, t->format, t->field); \
|
||||
break;
|
||||
|
||||
switch(t->type)
|
||||
{
|
||||
run(TYPE_I32, i32)
|
||||
run(TYPE_U32, u32)
|
||||
run(TYPE_STR, str)
|
||||
run(TYPE_PTR, ptr)
|
||||
run(TYPE_U64, u64)
|
||||
run(TYPE_DBL, dbl)
|
||||
}
|
||||
|
||||
r->passed = (!strcmp(r->answer, t->solution) &&
|
||||
c == (int)strlen(t->solution));
|
||||
ft_assert(t0, r->passed == 1);
|
||||
}
|
||||
}
|
||||
|
||||
struct table_params {
|
||||
struct printf_test const *test;
|
||||
struct printf_result const *result;
|
||||
};
|
||||
|
||||
static void table_gen(gtable *t0, int row, void *params0)
|
||||
{
|
||||
struct table_params *params = params0;
|
||||
struct printf_test const *t = params->test;
|
||||
struct printf_result const *r = params->result;
|
||||
|
||||
char c1[16];
|
||||
sprintf(c1, "%d", row+1);
|
||||
gtable_provide(t0, c1, t[row].format, t[row].solution, r[row].answer);
|
||||
}
|
||||
|
||||
static jwidget *widget_gen(struct printf_test const *tests,
|
||||
struct printf_result const *results)
|
||||
{
|
||||
if(!results) return NULL;
|
||||
static struct table_params p;
|
||||
p.test = tests;
|
||||
p.result = results;
|
||||
|
||||
int test_count = 0;
|
||||
while(tests[test_count].format) test_count++;
|
||||
|
||||
gtable *t = gtable_create(4, table_gen, &p, NULL);
|
||||
gtable_set_rows(t, test_count);
|
||||
gtable_set_column_titles(t, "ID", "Format", "Expected", "Got");
|
||||
gtable_set_column_sizes(t, 3, 8, 16, 16);
|
||||
gtable_set_row_spacing(t, 2);
|
||||
jwidget_set_stretch(t, 1, 1, false);
|
||||
|
||||
return (void *)t;
|
||||
}
|
||||
|
||||
//---
|
||||
// General features
|
||||
//---
|
||||
|
||||
static void _ft_stdio_printf_basics(ft_test *t)
|
||||
{
|
||||
char str[32];
|
||||
int n;
|
||||
|
||||
/* Basic output */
|
||||
n = snprintf(str, 32, "test");
|
||||
ft_assert(t, n == 4 && !strcmp(str, "test"));
|
||||
|
||||
/* Truncated output */
|
||||
n = snprintf(str, 8, "12345678901234");
|
||||
ft_assert(t, n == 14 && !strcmp(str, "1234567"));
|
||||
|
||||
/* Parameter size */
|
||||
n = snprintf(str, 32, "%hhx", 0x123);
|
||||
ft_assert(t, n == 2 && !strcmp(str, "23"));
|
||||
|
||||
/* Truncated output in the middle of a format */
|
||||
n = snprintf(str, 5, "%hd", 100000);
|
||||
ft_assert(t, n == 6 && !strcmp(str, "-310"));
|
||||
|
||||
/* Percent format */
|
||||
n = snprintf(str, 32, "%%d");
|
||||
ft_assert(t, n == 2 && !strcmp(str, "%d"));
|
||||
|
||||
/* Nonexistent formats */
|
||||
n = snprintf(str, 32, "a%.7Z");
|
||||
ft_assert(t, n == 1 && !strcmp(str, "a"));
|
||||
|
||||
/* Extension */
|
||||
n = snprintf(str, 32, "%.4D", 1234567);
|
||||
ft_assert(t, n == 8 && !strcmp(str, "123.4567"));
|
||||
}
|
||||
|
||||
ft_test ft_stdio_printf_basics = {
|
||||
.name = "printf: Basics options",
|
||||
.function = _ft_stdio_printf_basics,
|
||||
};
|
||||
|
||||
//---
|
||||
// Usual formats
|
||||
//---
|
||||
|
||||
static struct printf_test const usual_tests[] = {
|
||||
/* Base cases with length and precision */
|
||||
{ "%d", I32(-849), "-849" },
|
||||
{ "%7i", I32(78372), " 78372" },
|
||||
{ "%3d", I32(65536), "65536" },
|
||||
{ "%6.4d", I32(17), " 0017" },
|
||||
{ "%6.3d", I32(-1876), " -1876" },
|
||||
{ "%.0d", I32(0), "" },
|
||||
{ "%.d", I32(0), "" },
|
||||
/* Sign */
|
||||
{ "%+i", I32(15), "+15" },
|
||||
{ "% 7i", I32(78372), " 78372" },
|
||||
{ "% d", I32(65536), " 65536" },
|
||||
/* Alignment */
|
||||
{ "%08d", I32(-839), "-0000839" },
|
||||
{ "%-6.4d", I32(17), "0017 " },
|
||||
{ "%-+6.4i", I32(17), "+0017 " },
|
||||
/* Bases */
|
||||
{ "%d", I32(0xcb7), "3255" },
|
||||
{ "%x", U32(0xcb7), "cb7" },
|
||||
{ "%X", U32(0xcb7), "CB7" },
|
||||
{ "%o", U32(0xcb7), "6267" },
|
||||
/* Argument size */
|
||||
{ "%zu", U32(1000000000), "1000000000" },
|
||||
{ "%td", I32(99999), "99999" },
|
||||
{ "%lld", U64(10000000000ll), "10000000000" },
|
||||
{ "%llx", U64(0x123456789abull), "123456789ab" },
|
||||
{ "%jx", U64(0x123456789abull), "123456789ab" },
|
||||
/* Alternative prefixes */
|
||||
{ "%#x", U32(0x7b), "0x7b" },
|
||||
{ "%#X", U32(0x7b), "0X7B" },
|
||||
{ "%#o", U32(255), "0377" },
|
||||
/* Pointers */
|
||||
{ "%p", PTR(0xa44b0000), "0xa44b0000" },
|
||||
/* Characters and strings */
|
||||
{ "%s", STR("HellWrld!"), "HellWrld!" },
|
||||
{ "%-8.5s", STR("Hello, World!"), "Hello " },
|
||||
{ "%c", I32(100), "d" },
|
||||
{ "%6c", I32('#'), " #", },
|
||||
/* NULL terminator */
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct printf_result *usual_results = NULL;
|
||||
|
||||
static void _ft_stdio_printf_formats(ft_test *t)
|
||||
{
|
||||
int test_count = sizeof usual_tests / sizeof usual_tests[0] - 1;
|
||||
|
||||
if(usual_results) free(usual_results);
|
||||
usual_results = malloc(test_count * sizeof *usual_results);
|
||||
if(!ft_assert(t, usual_results != NULL)) return;
|
||||
|
||||
run_tests(t, usual_tests, usual_results);
|
||||
}
|
||||
|
||||
|
||||
static jwidget *_ft_stdio_printf_formats_widget(ft_test *test)
|
||||
{
|
||||
(void)test;
|
||||
return widget_gen(usual_tests, usual_results);
|
||||
}
|
||||
|
||||
ft_test ft_stdio_printf_formats = {
|
||||
.name = "printf: Usual formats",
|
||||
.function = _ft_stdio_printf_formats,
|
||||
.widget = _ft_stdio_printf_formats_widget,
|
||||
};
|
||||
|
||||
//---
|
||||
// Floating-point formats
|
||||
//---
|
||||
|
||||
static struct printf_test const fp_tests[] = {
|
||||
/* Floating-point special values */
|
||||
{ "%f", DBL(NAN), "nan" },
|
||||
{ "%3.5F", DBL(NAN), "NAN" },
|
||||
{ "%+2F", DBL(-INFINITY), "-INF" },
|
||||
{ "%10G", DBL(INFINITY), "INF" },
|
||||
{ "%+g", DBL(INFINITY), "inf" },
|
||||
{ "%7.3e", DBL(-INFINITY), "-inf" },
|
||||
{ "%8E", DBL(NAN), "NAN" },
|
||||
/* Simple floating-point cases */
|
||||
{ "%f", DBL(13e3), "13000.000000" },
|
||||
{ "%.3f", DBL(-13e3), "-13000.000" },
|
||||
{ "%.F", DBL(13e3), "13000" },
|
||||
{ "%f", DBL(-12.42), "-12.420000" },
|
||||
{ "%.0f", DBL(-12.42), "-12", },
|
||||
{ "%.1f", DBL(12.42), "12.4", },
|
||||
{ "%.8F", DBL(12.42), "12.42000000" },
|
||||
{ "%F", DBL(0.0312), "0.031200" },
|
||||
{ "%.4f", DBL(0.0312), "0.0312" },
|
||||
{ "%.2f", DBL(-0.0312), "-0.03", },
|
||||
{ "%.0f", DBL(0.0312), "0", },
|
||||
/* Floating-point rounding */
|
||||
{ "%.f", DBL(1.75), "2", },
|
||||
{ "%.1f", DBL(1.75), "1.8", },
|
||||
{ "%.3F", DBL(0.0625), "0.063", },
|
||||
{ "%.1F", DBL(-99.99), "-100.0" },
|
||||
{ "%.2F", DBL(12999.992), "12999.99" },
|
||||
{ "%.2f", DBL(12999.995), "13000.00" },
|
||||
/* General options with floating-point */
|
||||
{ "%09.3F", DBL(123.4567), "00123.457" },
|
||||
{ "%05.0f", DBL(99.99), "00100" },
|
||||
{ "%+11f", DBL(0.0035678), " +0.003568" },
|
||||
{ "%- 11F", DBL(0.0035678), " 0.003568 " },
|
||||
/* Simple exponent cases */
|
||||
{ "%e", DBL(3.876), "3.876000e+00" },
|
||||
{ "%E", DBL(-38473.34254), "-3.847334E+04" },
|
||||
{ "%.2e", DBL(187.2), "1.87e+02" },
|
||||
{ "%.1e", DBL(-18.27), "-1.8e+01" },
|
||||
{ "%e", DBL(1e-10), "1.000000e-10" },
|
||||
{ "%E", DBL(3.873e180), "3.873000E+180" },
|
||||
{ "%.e", DBL(0.0005), "5e-04" },
|
||||
{ "%.E", DBL(128.37), "1E+02" },
|
||||
{ "%.5e", DBL(912.3), "9.12300e+02" },
|
||||
/* Exponent with rounding and general options */
|
||||
{ "%11.3e", DBL(12.499), " 1.250e+01" },
|
||||
{ "% -11.E", DBL(358.7), " 4E+02 " },
|
||||
{ "%+e", DBL(14.99999), "+1.499999e+01" },
|
||||
{ "%+e", DBL(14.999999), "+1.500000e+01" },
|
||||
/* Exponent sizes */
|
||||
{ "%.2e", DBL(1e10), "1.00e+10" },
|
||||
{ "%.2e", DBL(1e100), "1.00e+100" },
|
||||
{ "%.2e", DBL(1e-10), "1.00e-10" },
|
||||
{ "%.2e", DBL(1e-100), "1.00e-100" },
|
||||
/* Format selection and trailing zero elimination in %g */
|
||||
{ "%g", DBL(124), "124" },
|
||||
{ "%g", DBL(2.3), "2.3" },
|
||||
{ "%g", DBL(0.0001), "0.0001" },
|
||||
{ "%G", DBL(0.00001), "1E-05" },
|
||||
{ "%g", DBL(834e-93), "8.34e-91" },
|
||||
{ "%g", DBL(32842914732), "3.28429e+10" },
|
||||
{ "%g", DBL(123456), "123456" },
|
||||
{ "%G", DBL(1234567), "1.23457E+06" },
|
||||
{ "%G", DBL(123000), "123000" },
|
||||
/* Rounding and general options in %g */
|
||||
{ "%.3g", DBL(1278), "1.28e+03" },
|
||||
{ "%+.8g", DBL(1.23e5), "+123000" },
|
||||
{ "%- 12.8g", DBL(123000.01), " 123000.01 " },
|
||||
{ "%0.8g", DBL(123000.001), "123000" },
|
||||
{ "%08.8g", DBL(123000.001), "00123000" },
|
||||
{ "%g", DBL(1.234567), "1.23457" },
|
||||
{ "%.1g", DBL(1.8), "2" },
|
||||
/* Edge cases of significant digit count in %g */
|
||||
{ "%.4g", DBL(999.93), "999.9" },
|
||||
{ "%.4g", DBL(999.97), "1000" },
|
||||
{ "%.5g", DBL(999.97), "999.97" },
|
||||
{ "%.8G", DBL(999.97), "999.97" },
|
||||
{ "%.4g", DBL(1.0001), "1", },
|
||||
/* Elimination of trailing zeros in %g */
|
||||
{ "%.3g", DBL(3002), "3e+03" },
|
||||
{ "%.5g", DBL(0.00000034), "3.4e-07" },
|
||||
{ "%.6g", DBL(999.9997), "1000" },
|
||||
/* NULL terminator */
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct printf_result *fp_results = NULL;
|
||||
|
||||
static void _ft_stdio_printf_fp(ft_test *t)
|
||||
{
|
||||
int test_count = sizeof fp_tests / sizeof fp_tests[0] - 1;
|
||||
|
||||
if(fp_results) free(fp_results);
|
||||
fp_results = malloc(test_count * sizeof *fp_results);
|
||||
if(!ft_assert(t, fp_results != NULL)) return;
|
||||
|
||||
run_tests(t, fp_tests, fp_results);
|
||||
}
|
||||
|
||||
|
||||
static jwidget *_ft_stdio_printf_fp_widget(ft_test *test)
|
||||
{
|
||||
(void)test;
|
||||
return widget_gen(fp_tests, fp_results);
|
||||
}
|
||||
|
||||
ft_test ft_stdio_printf_fp = {
|
||||
.name = "printf: Floating-point formats",
|
||||
.function = _ft_stdio_printf_fp,
|
||||
.widget = _ft_stdio_printf_fp_widget,
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
#include <ft/test.h>
|
||||
#include <ft/all-tests.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static jwidget *_ft_stdlib_exit(GUNUSED ft_test *t)
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ft_test ft_stdlib_exit = {
|
||||
.name = "Press F5: exit()",
|
||||
.function = NULL,
|
||||
.widget = _ft_stdlib_exit,
|
||||
};
|
||||
|
||||
static jwidget *_ft_stdlib__Exit(GUNUSED ft_test *t)
|
||||
{
|
||||
_Exit(EXIT_SUCCESS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ft_test ft_stdlib__Exit = {
|
||||
.name = "Press F5: _Exit()",
|
||||
.function = NULL,
|
||||
.widget = _ft_stdlib__Exit,
|
||||
};
|
||||
|
||||
static jwidget *_ft_stdlib_abort(GUNUSED ft_test *t)
|
||||
{
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ft_test ft_stdlib_abort = {
|
||||
.name = "Press F5: abort()",
|
||||
.function = NULL,
|
||||
.widget = _ft_stdlib_abort,
|
||||
};
|
|
@ -115,9 +115,15 @@ static void fbar_poly_render(void *b0, int x, int y)
|
|||
bool guaranteed = (w > guaranteeable_min);
|
||||
|
||||
if(guaranteed) w -= guaranteeable_min;
|
||||
px.done = (st.done * w) / main;
|
||||
px.pending = (st.pending * w) / main;
|
||||
px.empty = (st.empty * w) / main;
|
||||
px.done = w - px.pending - px.empty;
|
||||
int rest = w - px.done - px.pending - px.empty;
|
||||
if(rest > 0) {
|
||||
if(st.done) px.done += rest;
|
||||
else if(st.pending) px.pending += rest;
|
||||
else if(st.empty) px.empty += rest;
|
||||
}
|
||||
if(guaranteed) {
|
||||
if(st.done) px.done += 10;
|
||||
if(st.pending) px.pending += 10;
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
#include <ft/widgets/gtable.h>
|
||||
#include <justui/jwidget-api.h>
|
||||
#include <gint/std/stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct gtable_column {
|
||||
char const *title;
|
||||
font_t const *font;
|
||||
uint16_t size;
|
||||
uint16_t width;
|
||||
};
|
||||
|
||||
/* Type identifier for gtable */
|
||||
static int gtable_type_id = -1;
|
||||
|
||||
void update_visible(gtable *t);
|
||||
|
||||
gtable *gtable_create(int columns, void *generator, j_arg_t arg, void *parent)
|
||||
{
|
||||
if(gtable_type_id < 0) return NULL;
|
||||
|
||||
gtable *t = malloc(sizeof *t);
|
||||
if(!t) return NULL;
|
||||
|
||||
t->meta = malloc(columns * sizeof *t->meta);
|
||||
if(!t->meta) { free(t); return NULL; }
|
||||
|
||||
jwidget_init(&t->widget, gtable_type_id, parent);
|
||||
|
||||
t->generator = generator;
|
||||
t->arg = arg;
|
||||
|
||||
t->columns = columns;
|
||||
t->rows = 0;
|
||||
t->offset = 0;
|
||||
t->visible = 0;
|
||||
|
||||
t->row_height = 0;
|
||||
t->row_spacing = 1;
|
||||
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
t->meta[i].title = NULL;
|
||||
t->meta[i].font = dfont_default();
|
||||
t->meta[i].width = 1;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
//---
|
||||
// Configuration of columns
|
||||
//---
|
||||
|
||||
void gtable_set_column_title(gtable *t, int column, char const *title)
|
||||
{
|
||||
if(column < 0 || column >= t->columns) return;
|
||||
t->meta[column].title = title;
|
||||
t->widget.update = 1;
|
||||
}
|
||||
|
||||
void gtable_set_column_font(gtable *t, int column, font_t const *font)
|
||||
{
|
||||
if(column < 0 || column >= t->columns) return;
|
||||
t->meta[column].font = (font ? font : dfont_default());
|
||||
t->widget.update = 1;
|
||||
}
|
||||
|
||||
void gtable_set_column_size(gtable *t, int column, uint size)
|
||||
{
|
||||
if(column < 0 || column >= t->columns) return;
|
||||
t->meta[column].size = size;
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
#undef gtable_set_column_titles
|
||||
void gtable_set_column_titles(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++)
|
||||
{
|
||||
char const *title = va_arg(args, char const *);
|
||||
if(!title) break;
|
||||
gtable_set_column_title(t, i, title);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#undef gtable_set_column_fonts
|
||||
void gtable_set_column_fonts(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++)
|
||||
{
|
||||
font_t const *font = va_arg(args, font_t const *);
|
||||
if(!font) break;
|
||||
gtable_set_column_font(t, i, font);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#undef gtable_set_column_sizes
|
||||
void gtable_set_column_sizes(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++)
|
||||
{
|
||||
uint size = va_arg(args, uint);
|
||||
if(!size) break;
|
||||
gtable_set_column_size(t, i, size);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void gtable_set_font(gtable *t, font_t const *font)
|
||||
{
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
gtable_set_column_font(t, i, font);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Configuration of rows
|
||||
//---
|
||||
|
||||
void gtable_set_rows(gtable *t, int rows)
|
||||
{
|
||||
/* The re-layout will make sure we stay in the visible range */
|
||||
t->rows = max(rows, 0);
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
void gtable_set_row_height(gtable *t, int row_height)
|
||||
{
|
||||
t->row_height = row_height;
|
||||
t->visible = 0;
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
void gtable_set_row_spacing(gtable *t, int row_spacing)
|
||||
{
|
||||
t->row_spacing = row_spacing;
|
||||
t->visible = 0;
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Movement
|
||||
//---
|
||||
|
||||
/* Guarantee that the current positioning is valid */
|
||||
static void bound(gtable *t)
|
||||
{
|
||||
t->offset = min(t->offset, gtable_end(t));
|
||||
}
|
||||
|
||||
void gtable_scroll_to(gtable *t, int offset)
|
||||
{
|
||||
t->offset = offset;
|
||||
bound(t);
|
||||
}
|
||||
|
||||
int gtable_end(gtable *t)
|
||||
{
|
||||
update_visible(t);
|
||||
return max((int)t->rows - (int)t->visible, 0);
|
||||
}
|
||||
|
||||
//---
|
||||
// Polymorphic widget operations
|
||||
//---
|
||||
|
||||
int compute_row_height(gtable *t)
|
||||
{
|
||||
int row_height = t->row_height;
|
||||
if(row_height == 0) {
|
||||
for(int i = 0; i < t->columns; i++) {
|
||||
row_height = max(row_height, t->meta[i].font->line_height);
|
||||
}
|
||||
}
|
||||
return row_height;
|
||||
}
|
||||
|
||||
/* Recompute (visible) based on the current size */
|
||||
void update_visible(gtable *t)
|
||||
{
|
||||
if(t->visible == 0) {
|
||||
int row_height = compute_row_height(t);
|
||||
int header_height = row_height + t->row_spacing + 1;
|
||||
|
||||
int space = jwidget_content_height(t) - header_height;
|
||||
t->visible = max(0, space / (row_height + t->row_spacing));
|
||||
}
|
||||
}
|
||||
|
||||
static void gtable_poly_csize(void *t0)
|
||||
{
|
||||
gtable *t = t0;
|
||||
t->widget.w = 64;
|
||||
t->widget.h = 32;
|
||||
}
|
||||
|
||||
static void gtable_poly_layout(void *t0)
|
||||
{
|
||||
gtable *t = t0;
|
||||
t->visible = 0;
|
||||
update_visible(t);
|
||||
bound(t);
|
||||
|
||||
int cw = jwidget_content_width(t);
|
||||
/* Leave space for a scrollbar */
|
||||
if(t->visible < t->rows) cw -= 4;
|
||||
|
||||
/* Compute the width of each column */
|
||||
uint total_size = 0;
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
total_size += t->meta[i].size;
|
||||
}
|
||||
|
||||
uint space_left = cw;
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
t->meta[i].width = (t->meta[i].size * cw) / total_size;
|
||||
space_left -= t->meta[i].width;
|
||||
}
|
||||
|
||||
/* Grant space left to the first columns */
|
||||
for(uint i = 0; i < t->columns && i < space_left; i++) {
|
||||
t->meta[i].width++;
|
||||
}
|
||||
}
|
||||
|
||||
static void gtable_poly_render(void *t0, int base_x, int base_y)
|
||||
{
|
||||
gtable *t = t0;
|
||||
int row_height = compute_row_height(t);
|
||||
int cw = jwidget_content_width(t);
|
||||
int y = base_y;
|
||||
|
||||
for(uint i=0, x=base_x; i < t->columns; i++) {
|
||||
font_t const *old_font = dfont(t->meta[i].font);
|
||||
dtext(x, y, C_BLACK, t->meta[i].title);
|
||||
dfont(old_font);
|
||||
|
||||
x += t->meta[i].width;
|
||||
}
|
||||
|
||||
y += row_height + t->row_spacing;
|
||||
dline(base_x, y, base_x + cw - 1, y, C_BLACK);
|
||||
y += t->row_spacing + 1;
|
||||
int rows_y = y;
|
||||
|
||||
for(uint i = 0; t->offset + i < t->rows && i < t->visible; i++) {
|
||||
t->x = base_x;
|
||||
t->y = y;
|
||||
|
||||
t->generator(t, t->offset + i, t->arg);
|
||||
y += row_height + t->row_spacing;
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
if(t->visible < t->rows) {
|
||||
/* Area where the scroll bar lives */
|
||||
int area_w = 2;
|
||||
int area_x = base_x + cw - area_w;
|
||||
int area_y = rows_y;
|
||||
int area_h = jwidget_content_height(t) - (area_y - base_y);
|
||||
|
||||
/* Position and size of scroll bar within the area */
|
||||
int bar_y = (t->offset * area_h + t->rows/2) / t->rows;
|
||||
int bar_h = (t->visible * area_h + t->rows/2) / t->rows;
|
||||
|
||||
drect(area_x, area_y + bar_y, area_x + area_w - 1,
|
||||
area_y + bar_y + bar_h, C_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
#undef gtable_provide
|
||||
void gtable_provide(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
char const *str = va_arg(args, char const *);
|
||||
if(!str) break;
|
||||
|
||||
font_t const *old_font = dfont(t->meta[i].font);
|
||||
dtext(t->x, t->y, C_BLACK, str);
|
||||
dfont(old_font);
|
||||
|
||||
t->x += t->meta[i].width;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static bool gtable_poly_event(void *t0, jevent e)
|
||||
{
|
||||
gtable *t = t0;
|
||||
int end = gtable_end(t);
|
||||
update_visible(t);
|
||||
|
||||
if(e.type != JWIDGET_KEY) return false;
|
||||
|
||||
if(e.key.type == KEYEV_UP) return false;
|
||||
|
||||
if(e.key.key == KEY_DOWN && t->offset < end) {
|
||||
if(e.key.shift) t->offset = end;
|
||||
else t->offset++;
|
||||
t->widget.update = 1;
|
||||
return true;
|
||||
}
|
||||
if(e.key.key == KEY_UP && t->offset > 0) {
|
||||
if(e.key.shift) t->offset = 0;
|
||||
else t->offset--;
|
||||
t->widget.update = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gtable_poly_destroy(void *t0)
|
||||
{
|
||||
gtable *t = t0;
|
||||
free(t->meta);
|
||||
}
|
||||
|
||||
/* gtable type definition */
|
||||
static jwidget_poly type_gtable = {
|
||||
.name = "gtable",
|
||||
.csize = gtable_poly_csize,
|
||||
.layout = gtable_poly_layout,
|
||||
.render = gtable_poly_render,
|
||||
.event = gtable_poly_event,
|
||||
.destroy = gtable_poly_destroy,
|
||||
};
|
||||
|
||||
/* Type registration */
|
||||
__attribute__((constructor(2000)))
|
||||
static void j_register_gtable(void)
|
||||
{
|
||||
gtable_type_id = j_register_widget(&type_gtable, "jwidget");
|
||||
}
|
Loading…
Reference in New Issue