diff --git a/maple/maple_bus_ft6_key_scan_codes.hpp b/maple/maple_bus_ft6_key_scan_codes.hpp index 70df769..9a8f919 100644 --- a/maple/maple_bus_ft6_key_scan_codes.hpp +++ b/maple/maple_bus_ft6_key_scan_codes.hpp @@ -91,6 +91,7 @@ namespace ft6 { } namespace ft6 { namespace scan_code { + constexpr uint32_t first_printable = 0x4; constexpr uint32_t last_printable = 0x38; const uint8_t code_point[last_printable + 1][2] = { @@ -154,4 +155,3 @@ namespace ft6 { }; } } - diff --git a/text_editor/gap_buffer.cpp b/text_editor/gap_buffer.cpp index 3edcf2a..8151289 100644 --- a/text_editor/gap_buffer.cpp +++ b/text_editor/gap_buffer.cpp @@ -127,7 +127,7 @@ void gap_cursor_pos_abs(struct gap_buffer& gb, int32_t pos) gap_cursor_pos(gb, pos - gb.gap_start); } -int32_t gap_column_number(struct gap_buffer& gb) +int32_t gap_column_number(const struct gap_buffer& gb) { int32_t line_start = 0; if (gb.line.gap > 0) diff --git a/text_editor/gap_buffer.hpp b/text_editor/gap_buffer.hpp index 17e12d9..8fa2ab7 100644 --- a/text_editor/gap_buffer.hpp +++ b/text_editor/gap_buffer.hpp @@ -32,5 +32,5 @@ void gap_resize(struct gap_buffer& gb); void gap_append(struct gap_buffer& gb, char_type c); void gap_pop(struct gap_buffer& gb); void gap_cursor_pos(struct gap_buffer& gb, int32_t delta); -int32_t gap_column_number(struct gap_buffer& gb); +int32_t gap_column_number(const struct gap_buffer& gb); void gap_cursor_pos_line(struct gap_buffer& gb, int32_t delta); diff --git a/text_editor/keyboard.cpp b/text_editor/keyboard.cpp index 95a0297..ac953f4 100644 --- a/text_editor/keyboard.cpp +++ b/text_editor/keyboard.cpp @@ -119,14 +119,36 @@ void keyboard_debug(ft6::data_transfer::data_format * keyboards, uint32_t frame_ } } -static inline bool is_shifted(const uint8_t modifier_key) +constexpr inline bool is_modified(uint32_t modifier, uint32_t bit) { - return - (ft6::data_transfer::modifier_key::right_shift() & modifier_key) || - (ft6::data_transfer::modifier_key::left_shift() & modifier_key); + uint32_t mask = ~(bit); + return ((modifier & bit) != 0) && ((modifier & mask) == 0); } -void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb) +constexpr inline bool is_unmodified(uint32_t modifier) +{ + return modifier == 0; +} + +constexpr inline bool is_shifted(uint32_t modifier) +{ + uint32_t bit = ft6::data_transfer::modifier_key::right_shift() | ft6::data_transfer::modifier_key::left_shift(); + return is_modified(modifier, bit); +} + +constexpr inline bool is_left_alted(uint32_t modifier) +{ + uint32_t bit = (ft6::data_transfer::modifier_key::left_alt()); + return is_modified(modifier, bit); +} + +constexpr inline bool is_ctrled(uint32_t modifier) +{ + uint32_t bit = (ft6::data_transfer::modifier_key::left_control() | ft6::data_transfer::modifier_key::right_control()); + return is_modified(modifier, bit); +} + +void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb, viewport_window& vw) { uint32_t this_frame = (frame_ix + 0) & 1; uint32_t next_frame = (frame_ix + 1) & 1; @@ -144,26 +166,56 @@ void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame } if (make) { // make + int modifier_key = keyboards[this_frame].modifier_key; uint8_t scan_code = keyboards[this_frame].scan_code_array[i]; - if (scan_code <= ft6::scan_code::last_printable) { - bool shifted = is_shifted(keyboards[this_frame].modifier_key); - char_type code_point = ft6::scan_code::code_point[scan_code][shifted]; - if (code_point != 0) { - gap_append(gb, code_point); - continue; - } - } - switch (scan_code) { - case ft6::scan_code::_return: gap_append(gb, '\n'); break; - case ft6::scan_code::backspace: gap_pop(gb); break; - case ft6::scan_code::spacebar: gap_append(gb, ' '); break; - case ft6::scan_code::left_arrow: gap_cursor_pos(gb, -1); break; - case ft6::scan_code::right_arrow: gap_cursor_pos(gb, 1); break; - case ft6::scan_code::up_arrow: gap_cursor_pos_line(gb, -1); break; - case ft6::scan_code::down_arrow: gap_cursor_pos_line(gb, 1); break; - default: - break; + if (is_shifted(modifier_key) || is_unmodified(modifier_key)) { + if (scan_code >= ft6::scan_code::first_printable && scan_code <= ft6::scan_code::last_printable) { + bool shifted = is_shifted(modifier_key); + char_type code_point = ft6::scan_code::code_point[scan_code][shifted]; + if (code_point != 0) { + gap_append(gb, code_point); + continue; + } + } + } + + if (is_unmodified(modifier_key)) { + switch (scan_code) { + case ft6::scan_code::_return: gap_append(gb, '\n'); break; + case ft6::scan_code::backspace: gap_pop(gb); break; + case ft6::scan_code::spacebar: gap_append(gb, ' '); break; + + case ft6::scan_code::left_arrow: gap_cursor_pos(gb, -1); break; + case ft6::scan_code::right_arrow: gap_cursor_pos(gb, 1); break; + case ft6::scan_code::up_arrow: gap_cursor_pos_line(gb, -1); break; + case ft6::scan_code::down_arrow: gap_cursor_pos_line(gb, 1); break; + + default: break; + } + } + + if (is_left_alted(modifier_key)) { + switch (scan_code) { + case ft6::scan_code::v_V: + vw.first_line -= 1; + if (vw.first_line < 0) + vw.first_line = 0; + break; + default: break; + } + } + + if (is_ctrled(modifier_key)) { + switch (scan_code) { + case ft6::scan_code::v_V: + vw.first_line += 1; + if ((vw.first_line - 1) >= gb.line.length) { + vw.first_line = gb.line.length; + } + break; + default: break; + } } } } diff --git a/text_editor/keyboard.hpp b/text_editor/keyboard.hpp index 3593c1b..de298c7 100644 --- a/text_editor/keyboard.hpp +++ b/text_editor/keyboard.hpp @@ -4,9 +4,10 @@ #include "maple/maple_bus_ft6.hpp" #include "gap_buffer.hpp" +#include "viewport_window.hpp" void keyboard_do_get_condition(ft6::data_transfer::data_format& data); void keyboard_debug(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix); -void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb); +void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb, viewport_window& vw); diff --git a/text_editor/parse.cpp b/text_editor/parse.cpp new file mode 100644 index 0000000..b47d706 --- /dev/null +++ b/text_editor/parse.cpp @@ -0,0 +1,70 @@ +#include + +#include "parse.hpp" + +int parse_base10_digit(char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + default: return -1; + } +} + +const char * parse_base10(const char * s, int * n) +{ + *n = 0; + int sign = 1; + + if (*s == '-') { + sign = -1; + s++; + } + + while (true) { + int digit = parse_base10_digit(*s); + if (digit == -1) + break; + + *n *= 10; + *n += digit; + s++; + } + + *n *= sign; + + return s; +} + +const char * parse_base10_64(const char * s, int64_t * n) +{ + *n = 0; + int sign = 1; + + if (*s == '-') { + sign = -1; + s++; + } + + while (true) { + int digit = parse_base10_digit(*s); + if (digit == -1) + break; + + *n *= 10; + *n += digit; + s++; + } + + *n *= sign; + + return s; +} diff --git a/text_editor/parse.hpp b/text_editor/parse.hpp new file mode 100644 index 0000000..88e1224 --- /dev/null +++ b/text_editor/parse.hpp @@ -0,0 +1,5 @@ +#include + +int parse_base10_digit(char c); +const char * parse_base10(const char * s, int * n); +const char * parse_base10_64(const char * s, int64_t * n); diff --git a/text_editor/printf.cpp b/text_editor/printf.cpp new file mode 100644 index 0000000..9e15ca9 --- /dev/null +++ b/text_editor/printf.cpp @@ -0,0 +1,173 @@ +#include +#include + +#include "parse.hpp" +#include "unparse.hpp" +#include "printf.hpp" + +enum format_type { + FORMAT_BASE10_UNSIGNED, + FORMAT_BASE10, + FORMAT_BASE10_64, + FORMAT_POINTER, + FORMAT_BASE16, + FORMAT_STRING, + FORMAT_CHAR, + FORMAT_PERCENT, +}; + +struct format { + enum format_type type; + int pad_length; + char fill_char; +}; + +static const char * parse_escape(const char * format, struct format * ft); + +static const char * parse_fill_pad(const char * format, struct format * ft) +{ + if (*format == 0) + return format; + if (*format >= '1' || *format <= '9') + ft->fill_char = ' '; + else + ft->fill_char = *format++; + format = parse_base10(format, &ft->pad_length); + return parse_escape(format, ft); +} + +static const char * parse_escape(const char * format, struct format * ft) +{ + switch (*format) { + case 0: + return format; + case 'u': + ft->type = FORMAT_BASE10_UNSIGNED; + return format + 1; + case 'd': + ft->type = FORMAT_BASE10; + return format + 1; + case 'l': + ft->type = FORMAT_BASE10_64; + return format + 1; + case 'p': + ft->type = FORMAT_POINTER; + return format + 1; + case 'x': + ft->type = FORMAT_BASE16; + return format + 1; + case 's': + ft->type = FORMAT_STRING; + return format + 1; + case 'c': + ft->type = FORMAT_CHAR; + return format + 1; + case '%': + ft->type = FORMAT_PERCENT; + return format + 1; + default: + return parse_fill_pad(format, ft); + } +} + +static inline int copy_string(const char * src, int src_len, char * dst, int dst_len, int dst_ix) +{ + int i; + for (i = 0; i < src_len; i++) { + if (dst_ix + i >= dst_len) + break; + dst[dst_ix + i] = src[i]; + } + return i; +} + +int snprintf(char * buf, int buf_len, const char * format, ...) +{ + va_list args; + va_start(args, format); + + int buf_ix = 0; + + while (buf_ix < buf_len) { + if (*format == 0) + break; + + switch (*format) { + case '%': + { + struct format ft; + ft.pad_length = 0; + ft.fill_char = 0; + format = parse_escape(format + 1, &ft); + switch (ft.type) { + case FORMAT_BASE10_UNSIGNED: + { + uint32_t num = va_arg(args, uint32_t); + char s[10]; + int offset = unparse_base10_unsigned(s, num, ft.pad_length, ft.fill_char); + buf_ix += copy_string(s, offset, buf, buf_len, buf_ix); + } + break; + case FORMAT_BASE10: + { + int32_t num = va_arg(args, int32_t); + char s[10]; + int offset = unparse_base10(s, num, ft.pad_length, ft.fill_char); + buf_ix += copy_string(s, offset, buf, buf_len, buf_ix); + } + break; + case FORMAT_BASE10_64: + { + int64_t num = va_arg(args, int64_t); + char s[20]; + int offset = unparse_base10_64(s, num, ft.pad_length, ft.fill_char); + buf_ix += copy_string(s, offset, buf, buf_len, buf_ix); + } + break; + case FORMAT_POINTER: + { + const char s[2] = {'0', 'x'}; + buf_ix += copy_string(s, 2, buf, buf_len, buf_ix); + } + /* fall through */; + case FORMAT_BASE16: + { + uint32_t num = va_arg(args, uint32_t); + char s[8]; + int offset = unparse_base16(s, num, ft.pad_length, ft.fill_char); + buf_ix += copy_string(s, offset, buf, buf_len, buf_ix); + } + break; + case FORMAT_STRING: + { + const char * s = va_arg(args, const char *); + while (*s != 0 && buf_ix < buf_len) { + buf[buf_ix++] = *s++; + } + } + break; + case FORMAT_CHAR: + { + const int c = va_arg(args, const int); + buf[buf_ix++] = c; + } + break; + case FORMAT_PERCENT: + buf[buf_ix++] = '%'; + break; + } + } + break; + default: + { + char c = *format++; + buf[buf_ix++] = c; + } + break; + } + } + + va_end(args); + + return buf_ix; +} diff --git a/text_editor/printf.hpp b/text_editor/printf.hpp new file mode 100644 index 0000000..090d264 --- /dev/null +++ b/text_editor/printf.hpp @@ -0,0 +1,3 @@ +#pragma once + +int snprintf(char * buf, int len, const char * format, ...); diff --git a/text_editor/render.cpp b/text_editor/render.cpp index 314f411..9e2e895 100644 --- a/text_editor/render.cpp +++ b/text_editor/render.cpp @@ -1,6 +1,7 @@ #include "render.hpp" #include "minmax.hpp" #include "transform.hpp" +#include "unparse.hpp" #include "holly/ta_global_parameter.hpp" #include "holly/ta_fifo_polygon_converter.hpp" @@ -36,9 +37,11 @@ cursor_advance render_primary_buffer(ta_parameter_writer& writer, const gap_buffer& gb, const viewport_window& window) { - int32_t first_line = min(window.first_line, gb.line.length - 1); + int32_t first_line = window.first_line; cursor_advance cursor = { 0 }; + cursor.x = -1; + cursor.y = -1; int32_t h_advance = 0; int32_t v_advance = 0; @@ -47,7 +50,16 @@ cursor_advance render_primary_buffer(ta_parameter_writer& writer, int row = first_line; - int32_t init_i = first_line >= 0 ? gb.line.offsets[first_line] + 1 : 0; + // line 0 is prior to the beginning of gb.line.offsets + // gb.line.offsets is the index of the newline character + int32_t init_i; + if (first_line == 0) { + init_i = 0; + } else if ((first_line - 1) >= 0 && (first_line - 1) < gb.line.length) { + init_i = gb.line.offsets[first_line - 1] + 1; + } else { + init_i = gb.size; + } for (int32_t i = init_i; i <= gb.size; i++) { if (i == gb.gap_start) { uint32_t ix = get_font_ix(font, ' '); @@ -97,7 +109,41 @@ cursor_advance render_primary_buffer(ta_parameter_writer& writer, return cursor; } -void render_cursor(ta_parameter_writer& parameter, +void render_text(ta_parameter_writer& writer, + const font * font, + const glyph * glyphs, + const char * buf, + int length, + int32_t x0, + int32_t y0) +{ + int32_t h_advance = 0; + int32_t v_advance = 0; + + const float r_texture_width = 1.0f / font->texture_width; + const float r_texture_height = 1.0f / font->texture_height; + + for (int i = 0; i < length; i++) { + int32_t x = x0 + int_26_6(h_advance); + int32_t y = y0 + int_26_6(v_advance); + + char_type c = buf[i]; + uint32_t ix = get_font_ix(font, c); + auto& glyph = glyphs[ix]; + + transform_glyph(writer, + r_texture_width, + r_texture_height, + glyph, + x, + y + ); + + h_advance += glyph.metrics.horiAdvance; + } +} + +void render_cursor(ta_parameter_writer& writer, const cursor_advance& cursor, const viewport_window& window) { @@ -106,7 +152,7 @@ void render_cursor(ta_parameter_writer& parameter, float width = int_26_6(cursor.width); float height = int_26_6(cursor.height); - transform_cursor(parameter, + transform_cursor(writer, x, // x y, // y width, @@ -114,6 +160,39 @@ void render_cursor(ta_parameter_writer& parameter, ); } +static inline int copy_str(const char * s, char * d, int d_ix) +{ + while (*s != 0) { + d[d_ix++] = *s++; + } + return d_ix; +} + +void render_status(ta_parameter_writer& writer, + const font * font, + const glyph * glyphs, + const gap_buffer& gb, + const viewport_window& window) +{ + char buf[256]; + int ix = 0; + + ix = copy_str("(", buf, ix); + ix += unparse_base10(&buf[ix], gb.line.gap, 0, 0); + ix = copy_str(",", buf, ix); + ix += unparse_base10(&buf[ix], gap_column_number(gb), 0, 0); + ix = copy_str(") ", buf, ix); + ix += unparse_base10(&buf[ix], window.first_line, 0, 0); + + render_text(writer, + font, + glyphs, + buf, + ix, + window.box.x0, + 480 - int_26_6(font->face_metrics.height) * 2); +} + void render(ta_parameter_writer& writer, const font * font, const glyph * glyphs, @@ -124,6 +203,8 @@ void render(ta_parameter_writer& writer, font->texture_width, font->texture_height); + render_status(writer, font, glyphs, gb, window); + cursor_advance cursor = render_primary_buffer(writer, font, glyphs, gb, window); render_cursor(writer, cursor, window); diff --git a/text_editor/text_editor.cpp b/text_editor/text_editor.cpp index 095d955..8de97bf 100644 --- a/text_editor/text_editor.cpp +++ b/text_editor/text_editor.cpp @@ -256,7 +256,7 @@ void main() struct editor_state state = { 0 }; gap_init_from_buf(state.gb, buf, buf_size, 0); line_init_from_buf(state.gb, offsets, offsets_size); - viewport_init_fullscreen(state.window); + viewport_init_fullscreen(state.window, font); ft6::data_transfer::data_format keyboards[2] = { 0 }; @@ -269,7 +269,7 @@ void main() while (true) { keyboard_do_get_condition(keyboards[frame_ix & 1]); keyboard_debug(keyboards, frame_ix); - keyboard_update(keyboards, frame_ix, state.gb); + keyboard_update(keyboards, frame_ix, state.gb, state.window); ta_polygon_converter_init2(texture_memory_alloc.isp_tsp_parameters[0].start, texture_memory_alloc.isp_tsp_parameters[0].end, diff --git a/text_editor/text_editor.mk b/text_editor/text_editor.mk index 8e7d879..70760e1 100644 --- a/text_editor/text_editor.mk +++ b/text_editor/text_editor.mk @@ -4,6 +4,7 @@ TEXT_EDITOR_OBJ = \ text_editor/render.o \ text_editor/keyboard.o \ text_editor/transform.o \ + text_editor/unparse.o \ holly/video_output.o \ holly/core.o \ holly/region_array.o \ diff --git a/text_editor/transform.cpp b/text_editor/transform.cpp index eb59572..cd337d9 100644 --- a/text_editor/transform.cpp +++ b/text_editor/transform.cpp @@ -204,6 +204,6 @@ void transform_glyph(ta_parameter_writer& writer, } const float z = 0.1f; - uint32_t base_color = 0xff0080ff; + uint32_t base_color = 0xffffff; transfer_quad_type_3(writer, out[0], out[1], out[2], out[3], z, base_color); } diff --git a/text_editor/unparse.cpp b/text_editor/unparse.cpp new file mode 100644 index 0000000..9157922 --- /dev/null +++ b/text_editor/unparse.cpp @@ -0,0 +1,165 @@ +#include "minmax.hpp" +#include "unparse.hpp" + +int digits_base10(uint32_t n) +{ + if (n >= 1000000000ul) return 10; + if (n >= 100000000ul) return 9; + if (n >= 10000000ul) return 8; + if (n >= 1000000ul) return 7; + if (n >= 100000ul) return 6; + if (n >= 10000ul) return 5; + if (n >= 1000ul) return 4; + if (n >= 100ul) return 3; + if (n >= 10ul) return 2; + return 1; +} + +int unparse_base10_unsigned(char * s, uint32_t n, int len, char fill) +{ + int digits = 0; + digits += digits_base10(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + while (len > 0) { + const uint32_t digit = n % 10; + n = n / 10; + s[--len] = digit + 48; + } + + return ret; +} + +int unparse_base10(char * s, int32_t n, int len, char fill) +{ + bool negative = false; + int digits = 0; + if (n < 0) { + digits += 1; + n = -n; + negative = true; + } + + digits += digits_base10(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + if (negative) { + *s++ = '-'; + len--; + } + + while (len > 0) { + const uint32_t digit = n % 10; + n = n / 10; + s[--len] = digit + 48; + } + + return ret; +} + +int digits_base10_64(uint64_t n) +{ + if (n >= 10000000000000000000ull) return 20; + if (n >= 1000000000000000000ull) return 19; + if (n >= 100000000000000000ull) return 18; + if (n >= 10000000000000000ull) return 17; + if (n >= 1000000000000000ull) return 16; + if (n >= 100000000000000ull) return 15; + if (n >= 10000000000000ull) return 14; + if (n >= 1000000000000ull) return 13; + if (n >= 100000000000ull) return 12; + if (n >= 10000000000ull) return 11; + if (n >= 1000000000ull) return 10; + if (n >= 100000000ull) return 9; + if (n >= 10000000ull) return 8; + if (n >= 1000000ull) return 7; + if (n >= 100000ull) return 6; + if (n >= 10000ull) return 5; + if (n >= 1000ull) return 4; + if (n >= 100ull) return 3; + if (n >= 10ull) return 2; + return 1; +} + +int unparse_base10_64(char * s, int64_t n, int len, char fill) +{ + bool negative = false; + int digits = 0; + if (n < 0) { + digits += 1; + n = -n; + negative = true; + } + + digits += digits_base10_64(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + if (negative) { + *s++ = '-'; + len--; + } + + while (len > 0) { + const uint32_t digit = n % 10; + n = n / 10; + s[--len] = digit + 48; + } + + return ret; +} + +static int digits_base16(uint32_t n) +{ + if (n <= 0xf) return 1; + if (n <= 0xff) return 2; + if (n <= 0xfff) return 3; + if (n <= 0xffff) return 4; + if (n <= 0xfffff) return 5; + if (n <= 0xffffff) return 6; + if (n <= 0xfffffff) return 7; + return 8; +} + +int unparse_base16(char * s, uint32_t n, int len, char fill) +{ + int digits = digits_base16(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + while (len > 0) { + uint32_t nib = n & 0xf; + n = n >> 4; + if (nib > 9) { + nib += (97 - 10); + } else { + nib += (48 - 0); + } + + s[--len] = nib; + } + + return ret; +} diff --git a/text_editor/unparse.hpp b/text_editor/unparse.hpp new file mode 100644 index 0000000..2a3785f --- /dev/null +++ b/text_editor/unparse.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +int unparse_base10_unsigned(char * s, uint32_t n, int len, char fill); +int unparse_base10(char * s, int32_t n, int len, char fill); +int unparse_base10_64(char * s, int64_t n, int len, char fill); +int unparse_base16(char * s, uint32_t n, int len, char fill); diff --git a/text_editor/viewport_window.hpp b/text_editor/viewport_window.hpp index 76970d8..18c1949 100644 --- a/text_editor/viewport_window.hpp +++ b/text_editor/viewport_window.hpp @@ -2,6 +2,8 @@ #include +#include "font/font.h" + struct viewport_window { int32_t first_line; struct { @@ -12,11 +14,11 @@ struct viewport_window { } box; }; -inline void viewport_init_fullscreen(viewport_window& window) +static inline void viewport_init_fullscreen(viewport_window& window, const font * font) { window.first_line = 0; window.box.x0 = 10; window.box.y0 = 20; window.box.x1 = 640 - 10; - window.box.y1 = 480 - 20; + window.box.y1 = 480 - (font->face_metrics.height >> 6) * 2; }