diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index e7ee15c6..7b7c2e6f 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -32,6 +32,11 @@ sources: # to: tasks: + - fcft: | + cd foot/subprojects + git clone https://codeberg.org/dnkl/fcft.git + cd fcft && git checkout 3.0-dev && cd .. + cd ../.. - debug: | mkdir -p bld/debug meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug diff --git a/.builds/freebsd-x64.yml b/.builds/freebsd-x64.yml index f52ae0c0..edd4c7ca 100644 --- a/.builds/freebsd-x64.yml +++ b/.builds/freebsd-x64.yml @@ -27,6 +27,11 @@ sources: # to: tasks: + - fcft: | + cd foot/subprojects + git clone https://codeberg.org/dnkl/fcft.git + cd fcft && git checkout 3.0-dev && cd .. + cd ../.. - debug: | mkdir -p bld/debug meson --buildtype=debug -Dterminfo=disabled -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 01baedfb..499ca038 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,10 @@ debug-x64: image: alpine:edge stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/debug - cd bld/debug - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ @@ -31,6 +35,10 @@ debug-x64-no-grapheme-clustering: stage: build script: - apk del harfbuzz harfbuzz-dev utf8proc utf8proc-dev + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/debug - cd bld/debug - meson --buildtype=debug -Dgrapheme-clustering=disabled -Dfcft:grapheme-shaping=disabled -Dfcft:run-shaping=disabled -Dfcft:test-text-shaping=false ../../ @@ -46,6 +54,10 @@ release-x64: image: alpine:edge stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/release - cd bld/release - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ @@ -61,6 +73,10 @@ debug-x86: image: i386/alpine:edge stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/debug - cd bld/debug - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ @@ -76,6 +92,10 @@ release-x86: image: i386/alpine:edge stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/release - cd bld/release - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ diff --git a/.woodpecker.yml b/.woodpecker.yml index b109aa0c..17f7a680 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -22,6 +22,7 @@ pipeline: - mkdir -p subprojects && cd subprojects - git clone https://codeberg.org/dnkl/tllist.git - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. - cd .. x64: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b6efe63..e209c37a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -334,6 +334,7 @@ * `dpi-aware=auto`: fonts are now scaled using the monitor’s DPI only when **all** monitors have a scaling factor of one (https://codeberg.org/dnkl/foot/issues/714). +* fcft >= 3.0.0 in now required. ### Fixed diff --git a/box-drawing.c b/box-drawing.c index 09f16d6a..c4b2819e 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1259,7 +1259,7 @@ set_a1_bit(uint8_t *data, size_t ofs, size_t bit_no) } static void NOINLINE -draw_box_drawings_light_arc(struct buf *buf, wchar_t wc) +draw_box_drawings_light_arc(struct buf *buf, char32_t wc) { const pixman_format_code_t fmt = buf->format; const int supersample = fmt == PIXMAN_a8 ? 4 : 1; @@ -1341,28 +1341,28 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc) * Hence the ‘thick % 2 ^ width % 2’ in the expressions below. */ switch (wc) { - case L'╭': + case U'╭': row_end = height - row - (thick_is_odd ^ height_is_odd); row_start = row_end - thick; col_end = width - col - (thick_is_odd ^ width_is_odd); col_start = col_end - thick; break; - case L'╮': + case U'╮': row_end = height - row - (thick_is_odd ^ height_is_odd); row_start = row_end - thick; col_start = col - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); col_end = col_start + thick; break; - case L'╰': + case U'╰': row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); row_end = row_start + thick; col_end = width - col - (thick_is_odd ^ width_is_odd); col_start = col_end - thick; break; - case L'╯': + case U'╯': row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); row_end = row_start + thick; col_start = col - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); @@ -1392,7 +1392,7 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc) * are. */ - if (wc == L'╭' || wc == L'╰') { + if (wc == U'╭' || wc == U'╰') { for (int y = 0; y < thick; y++) { int row = (height - thick) / 2 + y - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); for (int col = width - supersample; col < width; col++) { @@ -1408,7 +1408,7 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc) } } - if (wc == L'╭' || wc == L'╮') { + if (wc == U'╭' || wc == U'╮') { for (int x = 0; x < thick; x++) { int col = (width - thick) / 2 + x - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); for (int row = height - supersample; row < height; row++) { @@ -1924,7 +1924,7 @@ quad_lower_right(struct buf *buf) } static void NOINLINE -draw_quadrant(struct buf *buf, wchar_t wc) +draw_quadrant(struct buf *buf, char32_t wc) { enum { UPPER_LEFT = 1 << 0, @@ -1966,7 +1966,7 @@ draw_quadrant(struct buf *buf, wchar_t wc) } static void NOINLINE -draw_braille(struct buf *buf, wchar_t wc) +draw_braille(struct buf *buf, char32_t wc) { int w = min(buf->width / 4, buf->height / 8); int x_spacing = buf->width / 4; @@ -2091,7 +2091,7 @@ sextant_lower_right(struct buf *buf) } static void NOINLINE -draw_sextant(struct buf *buf, wchar_t wc) +draw_sextant(struct buf *buf, char32_t wc) { /* * Each byte encodes one sextant: @@ -2209,7 +2209,7 @@ draw_sextant(struct buf *buf, wchar_t wc) } static void NOINLINE -draw_wedge_triangle(struct buf *buf, wchar_t wc) +draw_wedge_triangle(struct buf *buf, char32_t wc) { const int width = buf->width; const int height = buf->height; @@ -2476,7 +2476,7 @@ draw_wedge_triangle(struct buf *buf, wchar_t wc) } static void NOINLINE -draw_wedge_triangle_inverted(struct buf *buf, wchar_t wc) +draw_wedge_triangle_inverted(struct buf *buf, char32_t wc) { draw_wedge_triangle(buf, wc); @@ -2486,7 +2486,7 @@ draw_wedge_triangle_inverted(struct buf *buf, wchar_t wc) } static void NOINLINE -draw_wedge_triangle_and_box(struct buf *buf, wchar_t wc) +draw_wedge_triangle_and_box(struct buf *buf, char32_t wc) { draw_wedge_triangle(buf, wc); @@ -2591,7 +2591,7 @@ draw_right_seven_eighths_block(struct buf *buf) } static void -draw_glyph(struct buf *buf, wchar_t wc) +draw_glyph(struct buf *buf, char32_t wc) { IGNORE_WARNING("-Wpedantic") @@ -2829,7 +2829,7 @@ draw_glyph(struct buf *buf, wchar_t wc) } struct fcft_glyph * COLD -box_drawing(const struct terminal *term, wchar_t wc) +box_drawing(const struct terminal *term, char32_t wc) { int width = term->cell_width; int height = term->cell_height; @@ -2907,7 +2907,7 @@ box_drawing(const struct terminal *term, wchar_t wc) struct fcft_glyph *glyph = xmalloc(sizeof(*glyph)); *glyph = (struct fcft_glyph){ - .wc = wc, + .cp = wc, .cols = 1, .pix = buf.pix, .x = -term->font_x_ofs, diff --git a/box-drawing.h b/box-drawing.h index e4633c7e..d7413bdb 100644 --- a/box-drawing.h +++ b/box-drawing.h @@ -1,6 +1,7 @@ #pragma once +#include #include struct terminal; -struct fcft_glyph *box_drawing(const struct terminal *term, wchar_t wc); +struct fcft_glyph *box_drawing(const struct terminal *term, char32_t wc); diff --git a/char32.c b/char32.c new file mode 100644 index 00000000..6e7ce0c0 --- /dev/null +++ b/char32.c @@ -0,0 +1,505 @@ +#include "char32.h" + +#include +#include +#include + +#include +#include + + +#define LOG_MODULE "char32" +#define LOG_ENABLE_DBG 0 +#include "log.h" +#include "debug.h" +#include "macros.h" +#include "xmalloc.h" + +/* + * For now, assume we can map directly to the corresponding wchar_t + * functions. This is true if: + * + * - both data types have the same size + * - both use the same encoding (though we require that encoding to be UTF-32) + */ + +_Static_assert( + sizeof(wchar_t) == sizeof(char32_t), "wchar_t vs. char32_t size mismatch"); + +#if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__ + #error "char32_t does not use UTF-32" +#endif +#if (!defined(__STDC_ISO_10646__) || !__STDC_ISO_10646__) && !defined(__FreeBSD__) + #error "wchar_t does not use UTF-32" +#endif + +size_t +c32len(const char32_t *s) +{ + return wcslen((const wchar_t *)s); +} + +UNITTEST +{ + xassert(c32len(U"") == 0); + xassert(c32len(U"foobar") == 6); +} + +int +c32cmp(const char32_t *s1, const char32_t *s2) +{ + return wcscmp((const wchar_t *)s1, (const wchar_t *)s2); +} + +UNITTEST +{ + xassert(c32cmp(U"foobar", U"foobar") == 0); + xassert(c32cmp(U"foo", U"foobar") < 0); + xassert(c32cmp(U"foobar", U"foo") > 0); + xassert(c32cmp(U"a", U"b") < 0); + xassert(c32cmp(U"b", U"a") > 0); +} + +char32_t * +c32ncpy(char32_t *dst, const char32_t *src, size_t n) +{ + return (char32_t *)wcsncpy((wchar_t *)dst, (const wchar_t *)src, n); +} + +UNITTEST +{ + char32_t copy[16]; + char32_t *ret = c32ncpy(copy, U"foobar", 16); + + xassert(ret == copy); + xassert(copy[0] == U'f'); + xassert(copy[1] == U'o'); + xassert(copy[2] == U'o'); + xassert(copy[3] == U'b'); + xassert(copy[4] == U'a'); + xassert(copy[5] == U'r'); + + unsigned char zeroes[(16 - 6) * sizeof(copy[0])] = {0}; + xassert(memcmp(©[6], zeroes, sizeof(zeroes)) == 0); +} + +char32_t * +c32cpy(char32_t *dst, const char32_t *src) +{ + return (char32_t *)wcscpy((wchar_t *)dst, (const wchar_t *)src); +} + +UNITTEST +{ + char32_t copy[16]; + memset(copy, 0x55, sizeof(copy)); + + char32_t *ret = c32cpy(copy, U"foobar"); + + xassert(ret == copy); + xassert(copy[0] == U'f'); + xassert(copy[1] == U'o'); + xassert(copy[2] == U'o'); + xassert(copy[3] == U'b'); + xassert(copy[4] == U'a'); + xassert(copy[5] == U'r'); + xassert(copy[6] == U'\0'); + + unsigned char fives[(16 - 6 - 1) * sizeof(copy[0])]; + memset(fives, 0x55, sizeof(fives)); + xassert(memcmp(©[7], fives, sizeof(fives)) == 0); +} + +char32_t * +c32chr(const char32_t *s, char32_t c) +{ + return (char32_t *)wcschr((const wchar_t *)s, c); +} + +int +c32casecmp(const char32_t *s1, const char32_t *s2) +{ + return wcscasecmp((const wchar_t *)s1, (const wchar_t *)s2); +} + +UNITTEST +{ + xassert(c32casecmp(U"foobar", U"FOOBAR") == 0); + xassert(c32casecmp(U"foo", U"FOOO") < 0); + xassert(c32casecmp(U"FOOO", U"foo") > 0); + xassert(c32casecmp(U"a", U"B") < 0); + xassert(c32casecmp(U"B", U"a") > 0); +} + +int +c32ncasecmp(const char32_t *s1, const char32_t *s2, size_t n) +{ + return wcsncasecmp((const wchar_t *)s1, (const wchar_t *)s2, n); +} + +UNITTEST +{ + xassert(c32ncasecmp(U"foo", U"FOObar", 3) == 0); + xassert(c32ncasecmp(U"foo", U"FOOO", 4) < 0); + xassert(c32ncasecmp(U"FOOO", U"foo", 4) > 0); + xassert(c32ncasecmp(U"a", U"BB", 1) < 0); + xassert(c32ncasecmp(U"BB", U"a", 1) > 0); +} + +char32_t * +c32ncat(char32_t *dst, const char32_t *src, size_t n) +{ + return (char32_t *)wcsncat((wchar_t *)dst, (const wchar_t *)src, n); +} + +UNITTEST +{ + char32_t dst[32] = U"foobar"; + char32_t *ret = c32ncat(dst, U"12345678XXXXXXXXX", 8); + + xassert(ret == dst); + xassert(c32cmp(dst, U"foobar12345678") == 0); +} + +char32_t * +c32cat(char32_t *dst, const char32_t *src) +{ + return (char32_t *)wcscat((wchar_t *)dst, (const wchar_t *)src); +} + +UNITTEST +{ + char32_t dst[32] = U"foobar"; + char32_t *ret = c32cat(dst, U"12345678"); + + xassert(ret == dst); + xassert(c32cmp(dst, U"foobar12345678") == 0); +} + +char32_t * +c32dup(const char32_t *s) +{ + return (char32_t *)wcsdup((const wchar_t *)s); +} + +UNITTEST +{ + char32_t *c = c32dup(U"foobar"); + xassert(c32cmp(c, U"foobar") == 0); + free(c); + + c = c32dup(U""); + xassert(c32cmp(c, U"") == 0); + free(c); +} + +size_t +mbsntoc32(char32_t *dst, const char *src, size_t nms, size_t len) +{ + mbstate_t ps = {0}; + + char32_t *out = dst; + const char *in = src; + + size_t consumed = 0; + size_t chars = 0; + size_t rc; + + while ((out == NULL || chars < len) && + consumed < nms && + (rc = mbrtoc32(out, in, nms - consumed, &ps)) != 0) + { + switch (rc) { + case 0: + goto done; + + case (size_t)-1: + case (size_t)-2: + case (size_t)-3: + goto err; + } + + in += rc; + consumed += rc; + chars++; + + if (out != NULL) + out++; + } + +done: + return chars; + +err: + return (char32_t)-1; +} + +UNITTEST +{ + const char input[] = "foobarzoo"; + char32_t c32[32]; + + size_t ret = mbsntoc32(NULL, input, sizeof(input), 0); + xassert(ret == 9); + + memset(c32, 0x55, sizeof(c32)); + ret = mbsntoc32(c32, input, sizeof(input), 32); + + xassert(ret == 9); + xassert(c32[0] == U'f'); + xassert(c32[1] == U'o'); + xassert(c32[2] == U'o'); + xassert(c32[3] == U'b'); + xassert(c32[4] == U'a'); + xassert(c32[5] == U'r'); + xassert(c32[6] == U'z'); + xassert(c32[7] == U'o'); + xassert(c32[8] == U'o'); + xassert(c32[9] == U'\0'); + xassert(c32[10] == 0x55555555); + + memset(c32, 0x55, sizeof(c32)); + ret = mbsntoc32(c32, input, 1, 32); + + xassert(ret == 1); + xassert(c32[0] == U'f'); + xassert(c32[1] == 0x55555555); + + memset(c32, 0x55, sizeof(c32)); + ret = mbsntoc32(c32, input, sizeof(input), 1); + + xassert(ret == 1); + xassert(c32[0] == U'f'); + xassert(c32[1] == 0x55555555); +} + +size_t +mbstoc32(char32_t *dst, const char *src, size_t len) +{ + return mbsntoc32(dst, src, strlen(src) + 1, len); +} + +UNITTEST +{ + const char input[] = "foobarzoo"; + char32_t c32[32]; + + size_t ret = mbstoc32(NULL, input, 0); + xassert(ret == 9); + + memset(c32, 0x55, sizeof(c32)); + ret = mbstoc32(c32, input, 32); + + xassert(ret == 9); + xassert(c32[0] == U'f'); + xassert(c32[1] == U'o'); + xassert(c32[2] == U'o'); + xassert(c32[3] == U'b'); + xassert(c32[4] == U'a'); + xassert(c32[5] == U'r'); + xassert(c32[6] == U'z'); + xassert(c32[7] == U'o'); + xassert(c32[8] == U'o'); + xassert(c32[9] == U'\0'); + xassert(c32[10] == 0x55555555); + + memset(c32, 0x55, sizeof(c32)); + ret = mbstoc32(c32, input, 1); + + xassert(ret == 1); + xassert(c32[0] == U'f'); + xassert(c32[1] == 0x55555555); +} + + +char32_t * +ambstoc32(const char *src) +{ + if (src == NULL) + return NULL; + + const size_t src_len = strlen(src); + + char32_t *ret = xmalloc((src_len + 1) * sizeof(ret[0])); + mbstate_t ps = {0}; + + char32_t *out = ret; + const char *in = src; + const char *const end = src + src_len + 1; + + size_t chars = 0; + size_t rc; + + while ((rc = mbrtoc32(out, in, end - in, &ps)) != 0) { + switch (rc) { + case (size_t)-1: + case (size_t)-2: + case (size_t)-3: + goto err; + } + + in += rc; + out++; + chars++; + } + + *out = U'\0'; + + ret = xrealloc(ret, (chars + 1) * sizeof(ret[0])); + return ret; + +err: + free(ret); + return NULL; +} + +UNITTEST +{ + setlocale(LC_CTYPE, "en_US.UTF-8"); + + char32_t *hello = ambstoc32(u8"hello"); + xassert(hello != NULL); + xassert(hello[0] == U'h'); + xassert(hello[1] == U'e'); + xassert(hello[2] == U'l'); + xassert(hello[3] == U'l'); + xassert(hello[4] == U'o'); + xassert(hello[5] == U'\0'); + free(hello); + + char32_t *swedish = ambstoc32(u8"åäö"); + xassert(swedish != NULL); + xassert(swedish[0] == U'å'); + xassert(swedish[1] == U'ä'); + xassert(swedish[2] == U'ö'); + xassert(swedish[3] == U'\0'); + free(swedish); + + char32_t *emoji = ambstoc32(u8"👨‍👩‍👧‍👦"); + xassert(emoji != NULL); + xassert(emoji[0] == U'👨'); + xassert(emoji[1] == U'‍'); + xassert(emoji[2] == U'👩'); + xassert(emoji[3] == U'‍'); + xassert(emoji[4] == U'👧'); + xassert(emoji[5] == U'‍'); + xassert(emoji[6] == U'👦'); + xassert(emoji[7] == U'\0'); + free(emoji); + + xassert(ambstoc32(NULL) == NULL); + setlocale(LC_CTYPE, "C"); +} + +char * +ac32tombs(const char32_t *src) +{ + if (src == NULL) + return NULL; + + const size_t src_len = c32len(src); + + size_t allocated = src_len + 1; + char *ret = xmalloc(allocated); + mbstate_t ps = {0}; + + char *out = ret; + const char32_t *const end = src + src_len + 1; + + size_t bytes = 0; + + char mb[MB_CUR_MAX]; + + for (const char32_t *in = src; in < end; in++) { + size_t rc = c32rtomb(mb, *in, &ps); + + switch (rc) { + case (size_t)-1: + goto err; + } + + if (bytes + rc > allocated) { + allocated *= 2; + ret = xrealloc(ret, allocated); + out = &ret[bytes]; + } + + for (size_t i = 0; i < rc; i++, out++) + *out = mb[i]; + + bytes += rc; + } + + xassert(ret[bytes - 1] == '\0'); + ret = xrealloc(ret, bytes); + return ret; + +err: + free(ret); + return NULL; +} + +UNITTEST +{ + setlocale(LC_CTYPE, "en_US.UTF-8"); + + char *s = ac32tombs(U"foobar"); + xassert(s != NULL); + xassert(strcmp(s, "foobar") == 0); + free(s); + + s = ac32tombs(U"åäö"); + xassert(s != NULL); + xassert(strcmp(s, u8"åäö") == 0); + free(s); + + s = ac32tombs(U"👨‍👩‍👧‍👦"); + xassert(s != NULL); + xassert(strcmp(s, u8"👨‍👩‍👧‍👦") == 0); + free(s); + + xassert(ac32tombs(NULL) == NULL); + setlocale(LC_CTYPE, "C"); +} + +char32_t +toc32lower(char32_t c) +{ + return (char32_t)towlower((wint_t)c); +} + +char32_t +toc32upper(char32_t c) +{ + return (char32_t)towupper((wint_t)c); +} + +bool +isc32space(char32_t c32) +{ + return iswspace((wint_t)c32); +} + +bool +isc32print(char32_t c32) +{ + return iswprint((wint_t)c32); +} + +bool +isc32graph(char32_t c32) +{ + return iswgraph((wint_t)c32); +} + +int +c32width(char32_t c) +{ + return wcwidth((wchar_t)c); +} + +int +c32swidth(const char32_t *s, size_t n) +{ + return wcswidth((const wchar_t *)s, n); +} diff --git a/char32.h b/char32.h new file mode 100644 index 00000000..0ce783e7 --- /dev/null +++ b/char32.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +size_t c32len(const char32_t *s); +int c32cmp(const char32_t *s1, const char32_t *s2); + +char32_t *c32ncpy(char32_t *dst, const char32_t *src, size_t n); +char32_t *c32cpy(char32_t *dst, const char32_t *src); +char32_t *c32ncat(char32_t *dst, const char32_t *src, size_t n); +char32_t *c32cat(char32_t *dst, const char32_t *src); +char32_t *c32dup(const char32_t *s); + +char32_t *c32chr(const char32_t *s, char32_t c); + +int c32casecmp(const char32_t *s1, const char32_t *s2); +int c32ncasecmp(const char32_t *s1, const char32_t *s2, size_t n); + +size_t mbsntoc32(char32_t *dst, const char *src, size_t nms, size_t len); +size_t mbstoc32(char32_t *dst, const char *src, size_t len); +char32_t *ambstoc32(const char *src); +char *ac32tombs(const char32_t *src); + +char32_t toc32lower(char32_t c); +char32_t toc32upper(char32_t c); + +bool isc32space(char32_t c32); +bool isc32print(char32_t c32); +bool isc32graph(char32_t c32); + +int c32width(char32_t c); +int c32swidth(const char32_t *s, size_t n); diff --git a/composed.h b/composed.h index faed22f7..17158407 100644 --- a/composed.h +++ b/composed.h @@ -1,10 +1,10 @@ #pragma once #include -#include +#include struct composed { - wchar_t *chars; + char32_t *chars; struct composed *left; struct composed *right; uint32_t key; diff --git a/config.c b/config.c index 18f3a5df..af2a6a4f 100644 --- a/config.c +++ b/config.c @@ -20,6 +20,7 @@ #define LOG_MODULE "config" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "char32.h" #include "debug.h" #include "input.h" #include "macros.h" @@ -484,10 +485,10 @@ done: } static int -wccmp(const void *_a, const void *_b) +c32cmp_single(const void *_a, const void *_b) { - const wchar_t *a = _a; - const wchar_t *b = _b; + const char32_t *a = _a; + const char32_t *b = _b; return *a - *b; } @@ -619,17 +620,16 @@ value_to_str(struct context *ctx, char **res) } static bool NOINLINE -value_to_wchars(struct context *ctx, wchar_t **res) +value_to_wchars(struct context *ctx, char32_t **res) { - size_t chars = mbstowcs(NULL, ctx->value, 0); - if (chars == (size_t)-1) { - LOG_CONTEXTUAL_ERR("not a valid string value"); + char32_t *s = ambstoc32(ctx->value); + if (s == NULL) { + LOG_CONTEXTUAL_ERR("not a valie string value"); return false; } free(*res); - *res = xmalloc((chars + 1) * sizeof(wchar_t)); - mbstowcs(*res, ctx->value, chars + 1); + *res = s; return true; } @@ -1171,7 +1171,7 @@ parse_section_url(struct context *ctx) len--; prot[len] = '\0'; - size_t chars = mbstowcs(NULL, prot, 0); + size_t chars = mbsntoc32(NULL, prot, len, 0); if (chars == (size_t)-1) { ctx->value = prot; LOG_CONTEXTUAL_ERRNO("invalid protocol"); @@ -1184,9 +1184,9 @@ parse_section_url(struct context *ctx) conf->url.prot_count * sizeof(conf->url.protocols[0])); size_t idx = conf->url.prot_count - 1; - conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(wchar_t)); - mbstowcs(conf->url.protocols[idx], prot, chars + 1); - wcscpy(&conf->url.protocols[idx][chars], L"://"); + conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(char32_t)); + mbsntoc32(conf->url.protocols[idx], prot, len, chars + 1); + c32cpy(&conf->url.protocols[idx][chars], U"://"); chars += 3; /* Include the "://" */ if (chars > conf->url.max_prot_len) @@ -1203,9 +1203,9 @@ parse_section_url(struct context *ctx) qsort( conf->url.uri_characters, - wcslen(conf->url.uri_characters), + c32len(conf->url.uri_characters), sizeof(conf->url.uri_characters[0]), - &wccmp); + &c32cmp_single); return true; } @@ -2748,7 +2748,7 @@ config_load(struct config *conf, const char *conf_path, .shell = get_shell(), .title = xstrdup("foot"), .app_id = xstrdup("foot"), - .word_delimiters = xwcsdup(L",│`|:\"'()[]{}<>"), + .word_delimiters = xc32dup(U",│`|:\"'()[]{}<>"), .size = { .type = CONF_SIZE_PX, .width = 700, @@ -2779,8 +2779,8 @@ config_load(struct config *conf, const char *conf_path, .command_focused = false, }, .url = { - .label_letters = xwcsdup(L"sadfjklewcmpgh"), - .uri_characters = xwcsdup(L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+=\"'()[]"), + .label_letters = xc32dup(U"sadfjklewcmpgh"), + .uri_characters = xc32dup(U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+=\"'()[]"), .osc8_underline = OSC8_UNDERLINE_URL_MODE, }, .can_shape_grapheme = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING, @@ -2789,7 +2789,7 @@ config_load(struct config *conf, const char *conf_path, .indicator = { .position = SCROLLBACK_INDICATOR_POSITION_RELATIVE, .format = SCROLLBACK_INDICATOR_FORMAT_TEXT, - .text = wcsdup(L""), + .text = xc32dup(U""), }, .multiplier = 3., }, @@ -2872,16 +2872,16 @@ config_load(struct config *conf, const char *conf_path, &conf->notify.argv.args); tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args); - static const wchar_t *url_protocols[] = { - L"http://", - L"https://", - L"ftp://", - L"ftps://", - L"file://", - L"gemini://", - L"gopher://", - L"irc://", - L"ircs://", + static const char32_t *url_protocols[] = { + U"http://", + U"https://", + U"ftp://", + U"ftps://", + U"file://", + U"gemini://", + U"gopher://", + U"irc://", + U"ircs://", }; conf->url.protocols = xmalloc( ALEN(url_protocols) * sizeof(conf->url.protocols[0])); @@ -2889,17 +2889,17 @@ config_load(struct config *conf, const char *conf_path, conf->url.max_prot_len = 0; for (size_t i = 0; i < ALEN(url_protocols); i++) { - size_t len = wcslen(url_protocols[i]); + size_t len = c32len(url_protocols[i]); if (len > conf->url.max_prot_len) conf->url.max_prot_len = len; - conf->url.protocols[i] = xwcsdup(url_protocols[i]); + conf->url.protocols[i] = xc32dup(url_protocols[i]); } qsort( conf->url.uri_characters, - wcslen(conf->url.uri_characters), + c32len(conf->url.uri_characters), sizeof(conf->url.uri_characters[0]), - &wccmp); + &c32cmp_single); tll_foreach(*initial_user_notifications, it) { tll_push_back(conf->notifications, it->item); @@ -3101,8 +3101,8 @@ config_clone(const struct config *old) conf->shell = xstrdup(old->shell); conf->title = xstrdup(old->title); conf->app_id = xstrdup(old->app_id); - conf->word_delimiters = xwcsdup(old->word_delimiters); - conf->scrollback.indicator.text = xwcsdup(old->scrollback.indicator.text); + conf->word_delimiters = xc32dup(old->word_delimiters); + conf->scrollback.indicator.text = xc32dup(old->scrollback.indicator.text); conf->server_socket_path = xstrdup(old->server_socket_path); spawn_template_clone(&conf->bell.command, &old->bell.command); spawn_template_clone(&conf->notify, &old->notify); @@ -3111,13 +3111,13 @@ config_clone(const struct config *old) config_font_list_clone(&conf->fonts[i], &old->fonts[i]); config_font_list_clone(&conf->csd.font, &old->csd.font); - conf->url.label_letters = xwcsdup(old->url.label_letters); - conf->url.uri_characters = xwcsdup(old->url.uri_characters); + conf->url.label_letters = xc32dup(old->url.label_letters); + conf->url.uri_characters = xc32dup(old->url.uri_characters); spawn_template_clone(&conf->url.launch, &old->url.launch); conf->url.protocols = xmalloc( old->url.prot_count * sizeof(conf->url.protocols[0])); for (size_t i = 0; i < old->url.prot_count; i++) - conf->url.protocols[i] = xwcsdup(old->url.protocols[i]); + conf->url.protocols[i] = xc32dup(old->url.protocols[i]); key_binding_list_clone(&conf->bindings.key, &old->bindings.key); key_binding_list_clone(&conf->bindings.search, &old->bindings.search); @@ -3237,13 +3237,13 @@ check_if_font_is_monospaced(const char *pattern, if (f == NULL) return true; - static const wchar_t chars[] = {L'a', L'i', L'l', L'M', L'W'}; + static const char32_t chars[] = {U'a', U'i', U'l', U'M', U'W'}; bool is_monospaced = true; int last_width = -1; for (size_t i = 0; i < sizeof(chars) / sizeof(chars[0]); i++) { - const struct fcft_glyph *g = fcft_glyph_rasterize( + const struct fcft_glyph *g = fcft_rasterize_char_utf32( f, chars[i], FCFT_SUBPIXEL_NONE); if (g == NULL) @@ -3253,15 +3253,15 @@ check_if_font_is_monospaced(const char *pattern, LOG_WARN("%s: font does not appear to be monospace; " "check your config, or disable this warning by " "setting [tweak].font-monospace-warn=no", - pattern); + f->name); static const char fmt[] = "%s: font does not appear to be monospace; " "check your config, or disable this warning by " "setting \033[1m[tweak].font-monospace-warn=no\033[22m"; - user_notification_add_fmt(notifications, USER_NOTIFICATION_WARNING, - fmt, pattern); + user_notification_add_fmt( + notifications, USER_NOTIFICATION_WARNING, fmt, f->name); is_monospaced = false; break; diff --git a/config.h b/config.h index 12e95e58..2f318d1d 100644 --- a/config.h +++ b/config.h @@ -2,6 +2,7 @@ #include #include +#include #include @@ -80,7 +81,7 @@ struct config { char *shell; char *title; char *app_id; - wchar_t *word_delimiters; + char32_t *word_delimiters; bool login_shell; bool locked_title; @@ -142,21 +143,21 @@ struct config { SCROLLBACK_INDICATOR_FORMAT_TEXT, } format; - wchar_t *text; + char32_t *text; } indicator; float multiplier; } scrollback; struct { - wchar_t *label_letters; + char32_t *label_letters; struct config_spawn_template launch; enum { OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS, } osc8_underline; - wchar_t **protocols; - wchar_t *uri_characters; + char32_t **protocols; + char32_t *uri_characters; size_t prot_count; size_t max_prot_len; } url; diff --git a/csi.c b/csi.c index e76e0786..deb77df7 100644 --- a/csi.c +++ b/csi.c @@ -14,6 +14,7 @@ #define LOG_MODULE "csi" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "char32.h" #include "config.h" #include "debug.h" #include "grid.h" @@ -742,7 +743,7 @@ csi_dispatch(struct terminal *term, uint8_t final) int count = vt_param_get(term, 0, 1); LOG_DBG("REP: '%lc' %d times", (wint_t)term->vt.last_printed, count); - const int width = wcwidth(term->vt.last_printed); + const int width = c32width(term->vt.last_printed); if (width > 0) { for (int i = 0; i < count; i++) term_print(term, term->vt.last_printed, width); diff --git a/extract.c b/extract.c index 7c1f0afb..310d94a3 100644 --- a/extract.c +++ b/extract.c @@ -1,12 +1,13 @@ #include "extract.h" -#include +#include #define LOG_MODULE "extract" #define LOG_ENABLE_DBG 1 #include "log.h" +#include "char32.h" struct extraction_context { - wchar_t *buf; + char32_t *buf; size_t size; size_t idx; size_t tab_spaces_left; @@ -40,7 +41,7 @@ ensure_size(struct extraction_context *ctx, size_t additional_chars) { while (ctx->size < ctx->idx + additional_chars) { size_t new_size = ctx->size == 0 ? 512 : ctx->size * 2; - wchar_t *new_buf = realloc(ctx->buf, new_size * sizeof(wchar_t)); + char32_t *new_buf = realloc(ctx->buf, new_size * sizeof(new_buf[0])); if (new_buf == NULL) return false; @@ -54,7 +55,7 @@ ensure_size(struct extraction_context *ctx, size_t additional_chars) } bool -extract_finish_wide(struct extraction_context *ctx, wchar_t **text, size_t *len) +extract_finish_wide(struct extraction_context *ctx, char32_t **text, size_t *len) { if (text == NULL) return false; @@ -72,41 +73,41 @@ extract_finish_wide(struct extraction_context *ctx, wchar_t **text, size_t *len) goto err; for (size_t i = 0; i < ctx->newline_count; i++) - ctx->buf[ctx->idx++] = L'\n'; + ctx->buf[ctx->idx++] = U'\n'; for (size_t i = 0; i < ctx->empty_count; i++) - ctx->buf[ctx->idx++] = L' '; + ctx->buf[ctx->idx++] = U' '; } if (ctx->idx == 0) { /* Selection of empty cells only */ if (!ensure_size(ctx, 1)) goto err; - ctx->buf[ctx->idx++] = L'\0'; + ctx->buf[ctx->idx++] = U'\0'; } else { xassert(ctx->idx > 0); xassert(ctx->idx <= ctx->size); switch (ctx->selection_kind) { default: - if (ctx->buf[ctx->idx - 1] == L'\n') - ctx->buf[ctx->idx - 1] = L'\0'; + if (ctx->buf[ctx->idx - 1] == U'\n') + ctx->buf[ctx->idx - 1] = U'\0'; break; case SELECTION_LINE_WISE: - if (ctx->buf[ctx->idx - 1] != L'\n') { + if (ctx->buf[ctx->idx - 1] != U'\n') { if (!ensure_size(ctx, 1)) goto err; - ctx->buf[ctx->idx++] = L'\n'; + ctx->buf[ctx->idx++] = U'\n'; } break; } - if (ctx->buf[ctx->idx - 1] != L'\0') { + if (ctx->buf[ctx->idx - 1] != U'\0') { if (!ensure_size(ctx, 1)) goto err; - ctx->buf[ctx->idx++] = L'\0'; + ctx->buf[ctx->idx++] = U'\0'; } } @@ -130,29 +131,20 @@ extract_finish(struct extraction_context *ctx, char **text, size_t *len) if (len != NULL) *len = 0; - wchar_t *wtext; + char32_t *wtext; if (!extract_finish_wide(ctx, &wtext, NULL)) return false; bool ret = false; - size_t _len = wcstombs(NULL, wtext, 0); - if (_len == (size_t)-1) { - LOG_ERRNO("failed to convert selection to UTF-8"); + *text = ac32tombs(wtext); + if (*text == NULL) { + LOG_ERR("failed to convert selection to UTF-8"); goto out; } - *text = malloc(_len + 1); - if (unlikely(text == NULL)) { - LOG_ERRNO("malloc() failed"); - goto out; - } - - wcstombs(*text, wtext, _len + 1); - if (len != NULL) - *len = _len; - + *len = strlen(*text); ret = true; out: @@ -188,7 +180,7 @@ extract_one(const struct terminal *term, const struct row *row, if (!ensure_size(ctx, ctx->empty_count)) goto err; for (size_t i = 0; i < ctx->empty_count; i++) - ctx->buf[ctx->idx++] = L' '; + ctx->buf[ctx->idx++] = U' '; } ctx->empty_count = 0; } @@ -197,13 +189,13 @@ extract_one(const struct terminal *term, const struct row *row, if (!ensure_size(ctx, 1)) goto err; - ctx->buf[ctx->idx++] = L'\n'; + ctx->buf[ctx->idx++] = U'\n'; if (!ctx->strip_trailing_empty) { if (!ensure_size(ctx, ctx->empty_count)) goto err; for (size_t i = 0; i < ctx->empty_count; i++) - ctx->buf[ctx->idx++] = L' '; + ctx->buf[ctx->idx++] = U' '; } ctx->empty_count = 0; } @@ -211,7 +203,7 @@ extract_one(const struct terminal *term, const struct row *row, ctx->tab_spaces_left = 0; } - if (cell->wc == L' ' && ctx->tab_spaces_left > 0) { + if (cell->wc == U' ' && ctx->tab_spaces_left > 0) { ctx->tab_spaces_left--; return true; } @@ -230,10 +222,10 @@ extract_one(const struct terminal *term, const struct row *row, goto err; for (size_t i = 0; i < ctx->newline_count; i++) - ctx->buf[ctx->idx++] = L'\n'; + ctx->buf[ctx->idx++] = U'\n'; for (size_t i = 0; i < ctx->empty_count; i++) - ctx->buf[ctx->idx++] = L' '; + ctx->buf[ctx->idx++] = U' '; ctx->newline_count = 0; ctx->empty_count = 0; @@ -255,7 +247,7 @@ extract_one(const struct terminal *term, const struct row *row, goto err; ctx->buf[ctx->idx++] = cell->wc; - if (cell->wc == L'\t') { + if (cell->wc == U'\t') { int next_tab_stop = term->cols - 1; tll_foreach(term->tab_stops, it) { if (it->item > col) { diff --git a/extract.h b/extract.h index dc802bc5..30bec494 100644 --- a/extract.h +++ b/extract.h @@ -2,7 +2,7 @@ #include #include -#include +#include #include "terminal.h" @@ -18,4 +18,4 @@ bool extract_one( bool extract_finish( struct extraction_context *context, char **text, size_t *len); bool extract_finish_wide( - struct extraction_context *context, wchar_t **text, size_t *len); + struct extraction_context *context, char32_t **text, size_t *len); diff --git a/ime.c b/ime.c index 81e63884..e7e5937a 100644 --- a/ime.c +++ b/ime.c @@ -9,6 +9,7 @@ #define LOG_MODULE "ime" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "char32.h" #include "render.h" #include "search.h" #include "terminal.h" @@ -159,26 +160,29 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, /* 4. Calculate surrounding text to send - not supported */ /* 5. Insert new pre-edit text */ - size_t wchars = seat->ime.preedit.pending.text != NULL - ? mbstowcs(NULL, seat->ime.preedit.pending.text, 0) - : 0; + char32_t *allocated_preedit_text = NULL; - if (wchars == 0 || wchars == (size_t)-1) { + if (seat->ime.preedit.pending.text == NULL || + seat->ime.preedit.pending.text[0] == '\0' || + (allocated_preedit_text = ambstoc32(seat->ime.preedit.pending.text)) == NULL) + { ime_reset_pending_preedit(seat); return; } - /* First, convert to unicode */ - seat->ime.preedit.text = xmalloc((wchars + 1) * sizeof(wchar_t)); - mbstowcs(seat->ime.preedit.text, seat->ime.preedit.pending.text, wchars); - seat->ime.preedit.text[wchars] = L'\0'; + xassert(seat->ime.preedit.pending.text != NULL); + xassert(allocated_preedit_text != NULL); + + seat->ime.preedit.text = allocated_preedit_text; + + size_t wchars = c32len(seat->ime.preedit.text); /* Next, count number of cells needed */ size_t cell_count = 0; size_t widths[wchars + 1]; for (size_t i = 0; i < wchars; i++) { - int width = max(wcwidth(seat->ime.preedit.text[i]), 1); + int width = max(c32width(seat->ime.preedit.text[i]), 1); widths[i] = width; cell_count += width; } @@ -238,7 +242,7 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, * * To do this, we use mblen() to step though the utf-8 * pre-edit string, advancing a unicode character index as - * we go, *and* advancing a *cell* index using wcwidth() + * we go, *and* advancing a *cell* index using c32width() * of the unicode character. * * When we find the matching *byte* index, we at the same diff --git a/input.c b/input.c index 10d36b04..68db9929 100644 --- a/input.c +++ b/input.c @@ -1123,16 +1123,15 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term, } else if (term->meta.eight_bit && count == 1) { - const wchar_t wc = 0x80 | utf8[0]; + const char32_t wc = 0x80 | utf8[0]; - char wc_as_utf8[8]; - mbstate_t ps = {0}; - size_t chars = wcrtomb(wc_as_utf8, wc, &ps); + char utf8_meta[MB_CUR_MAX]; + size_t chars = c32rtomb(utf8_meta, wc, &(mbstate_t){0}); if (chars != (size_t)-1) - term_to_slave(term, wc_as_utf8, chars); + term_to_slave(term, utf8_meta, chars); else - term_to_slave(term, wc_as_utf8, count); + term_to_slave(term, utf8, count); } else { @@ -1585,8 +1584,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, xkb_compose_state_get_utf8( seat->kbd.xkb_compose_state, (char *)utf8, count + 1); - wchar_t wc; - if (mbtowc(&wc, (const char *)utf8, count) == count) + char32_t wc; + if (mbrtoc32(&wc, (const char *)utf8, count, &(mbstate_t){0}) == count) utf32 = wc; } else { xkb_state_key_get_utf8( diff --git a/main.c b/main.c index 9a87678e..756a42b8 100644 --- a/main.c +++ b/main.c @@ -34,6 +34,12 @@ #include "xmalloc.h" #include "xsnprintf.h" +#include "char32.h" + +#if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__ + #error "char32_t does not use UTF-32" +#endif + static bool fdm_sigint(struct fdm *fdm, int signo, void *data) { @@ -90,16 +96,14 @@ print_usage(const char *prog_name) bool locale_is_utf8(void) { - xassert(strlen(u8"ö") == 2); + static const char u8[] = u8"ö"; + xassert(strlen(u8) == 2); - wchar_t w; - if (mbtowc(&w, u8"ö", 2) != 2) + char32_t w; + if (mbrtoc32(&w, u8, 2, &(mbstate_t){0}) != 2) return false; - if (w != U'ö') - return false; - - return true; + return w == U'ö'; } struct shutdown_context { @@ -408,15 +412,6 @@ main(int argc, char *const *argv) log_init(log_colorize, as_server && log_syslog, as_server ? LOG_FACILITY_DAEMON : LOG_FACILITY_USER, log_level); - _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, - "fcft log level enum offset"); - _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, - "fcft colorize enum mismatch"); - fcft_log_init( - (enum fcft_log_colorize)log_colorize, - as_server && log_syslog, - (enum fcft_log_class)log_level); - if (argc > 0) { argc -= optind; argv += optind; @@ -499,6 +494,14 @@ main(int argc, char *const *argv) return EXIT_SUCCESS; } + _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, + "fcft log level enum offset"); + _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, + "fcft colorize enum mismatch"); + fcft_init( + (enum fcft_log_colorize)log_colorize, + as_server && log_syslog, + (enum fcft_log_class)log_level); fcft_set_scaling_filter(conf.tweak.fcft_filter); if (conf_term != NULL) { @@ -646,6 +649,8 @@ main(int argc, char *const *argv) if (aborted || tll_length(wayl->terms) == 0) ret = EXIT_SUCCESS; + LOG_INFO("%zu", c32len(U"foobar")); + out: free(_cwd); server_destroy(server); @@ -665,6 +670,7 @@ out: unlink(pid_file); LOG_INFO("goodbye"); + fcft_fini(); log_deinit(); return ret == EXIT_SUCCESS && !as_server ? shutdown_ctx.exit_code : ret; } diff --git a/meson.build b/meson.build index bc956216..fd68f6f7 100644 --- a/meson.build +++ b/meson.build @@ -90,7 +90,7 @@ if utf8proc.found() endif tllist = dependency('tllist', version: '>=1.0.4', fallback: 'tllist') -fcft = dependency('fcft', version: ['>=2.4.0', '<3.0.0'], fallback: 'fcft') +fcft = dependency('fcft', version: ['>=3.0.0', '<4.0.0'], fallback: 'fcft') wayland_protocols_datadir = wayland_protocols.get_pkgconfig_variable('pkgdatadir') @@ -149,6 +149,7 @@ builtin_terminfo = custom_target( common = static_library( 'common', 'log.c', 'log.h', + 'char32.c', 'char32.h', 'debug.c', 'debug.h', 'xmalloc.c', 'xmalloc.h', 'xsnprintf.c', 'xsnprintf.h' diff --git a/misc.c b/misc.c index ea89e94e..7e33e65a 100644 --- a/misc.c +++ b/misc.c @@ -1,17 +1,16 @@ #include "misc.h" - -#include +#include "char32.h" bool -isword(wchar_t wc, bool spaces_only, const wchar_t *delimiters) +isword(char32_t wc, bool spaces_only, const char32_t *delimiters) { if (spaces_only) - return iswgraph(wc); + return isc32graph(wc); - if (wcschr(delimiters, wc) != NULL) + if (c32chr(delimiters, wc) != NULL) return false; - return iswgraph(wc); + return isc32graph(wc); } void diff --git a/misc.h b/misc.h index 9bbc4744..ba8b2ce7 100644 --- a/misc.h +++ b/misc.h @@ -1,9 +1,9 @@ #pragma once #include -#include +#include #include -bool isword(wchar_t wc, bool spaces_only, const wchar_t *delimiters); +bool isword(char32_t wc, bool spaces_only, const char32_t *delimiters); void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res); diff --git a/render.c b/render.c index 8cdcd3ce..024d0954 100644 --- a/render.c +++ b/render.c @@ -29,6 +29,7 @@ #define LOG_ENABLE_DBG 0 #include "log.h" #include "box-drawing.h" +#include "char32.h" #include "config.h" #include "grid.h" #include "hsl.h" @@ -566,7 +567,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, const struct fcft_glyph **glyphs = NULL; unsigned glyph_count = 0; - wchar_t base = cell->wc; + char32_t base = cell->wc; int cell_cols = 1; if (base != 0) { @@ -637,9 +638,8 @@ render_cell(struct terminal *term, pixman_image_t *pix, base = composed->chars[0]; if (term->conf->can_shape_grapheme && term->conf->tweak.grapheme_shaping) { - grapheme = fcft_grapheme_rasterize( - font, composed->count, composed->chars, - 0, NULL, term->font_subpixel); + grapheme = fcft_rasterize_grapheme_utf32( + font, composed->count, composed->chars, term->font_subpixel); } if (grapheme != NULL) { @@ -654,7 +654,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (single == NULL && grapheme == NULL) { xassert(base != 0); - single = fcft_glyph_rasterize(font, base, term->font_subpixel); + single = fcft_rasterize_char_utf32(font, base, term->font_subpixel); if (single == NULL) { glyph_count = 0; cell_cols = 1; @@ -717,7 +717,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus) draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols); - if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == L'\t' || + if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == U'\t' || (unlikely(cell->attrs.conceal) && !is_selected)) { goto draw_cursor; @@ -758,7 +758,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, assert(glyph_count == 1); for (size_t i = 1; i < composed->count; i++) { - const struct fcft_glyph *g = fcft_glyph_rasterize( + const struct fcft_glyph *g = fcft_rasterize_char_utf32( font, composed->chars[i], term->font_subpixel); if (g == NULL) @@ -1414,7 +1414,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat, if (cell->wc >= CELL_SPACER) continue; - int width = max(1, wcwidth(cell->wc)); + int width = max(1, c32width(cell->wc)); if (col_idx + i + width > term->cols) break; @@ -1626,7 +1626,7 @@ static void render_osd(struct terminal *term, struct wl_surface *surf, struct wl_subsurface *sub_surf, struct fcft_font *font, struct buffer *buf, - const wchar_t *text, uint32_t _fg, uint32_t _bg, + const char32_t *text, uint32_t _fg, uint32_t _bg, unsigned x, unsigned y) { pixman_region32_t clip; @@ -1643,14 +1643,15 @@ render_osd(struct terminal *term, pixman_color_t fg = color_hex_to_pixman(_fg); const int x_ofs = term->font_x_ofs; - const size_t len = wcslen(text); + const size_t len = c32len(text); struct fcft_text_run *text_run = NULL; const struct fcft_glyph **glyphs = NULL; const struct fcft_glyph *_glyphs[len]; size_t glyph_count = 0; if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { - text_run = fcft_text_run_rasterize(font, len, text, term->font_subpixel); + text_run = fcft_rasterize_text_run_utf32( + font, len, (const char32_t *)text, term->font_subpixel); if (text_run != NULL) { glyphs = text_run->glyphs; @@ -1660,7 +1661,7 @@ render_osd(struct terminal *term, if (glyphs == NULL) { for (size_t i = 0; i < len; i++) { - const struct fcft_glyph *glyph = fcft_glyph_rasterize( + const struct fcft_glyph *glyph = fcft_rasterize_char_utf32( font, text[i], term->font_subpixel); if (glyph == NULL) @@ -1740,20 +1741,15 @@ render_csd_title(struct terminal *term, const struct csd_data *info, fg = color_dim(term, fg); } - const wchar_t *title_text = L""; - wchar_t *_title_text = NULL; - - int chars = mbstowcs(NULL, term->window_title, 0); - if (chars >= 0) { - _title_text = xmalloc((chars + 1) * sizeof(wchar_t)); - mbstowcs(_title_text, term->window_title, chars + 1); - title_text = _title_text; - } + char32_t *_title_text = ambstoc32(term->window_title); + const char32_t *title_text = _title_text != NULL ? _title_text : U""; struct wl_window *win = term->window; - const int margin = win->csd.font->space_advance.x > 0 - ? win->csd.font->space_advance.x - : win->csd.font->max_advance.x; + + const struct fcft_glyph *M = fcft_rasterize_char_utf32( + win->csd.font, U'M', term->font_subpixel); + + const int margin = M != NULL ? M->advance.x : win->csd.font->max_advance.x; render_osd(term, surf->surf, surf->sub, win->csd.font, buf, title_text, fg, bg, margin, @@ -2197,25 +2193,31 @@ render_scrollback_position(struct terminal *term) ? 1.0 : (double)rebased_view / (populated_rows - term->rows); - wchar_t _text[64]; - const wchar_t *text = _text; + char32_t _text[64]; + const char32_t *text = _text; int cell_count = 0; /* *What* to render */ switch (term->conf->scrollback.indicator.format) { - case SCROLLBACK_INDICATOR_FORMAT_PERCENTAGE: - swprintf(_text, sizeof(_text) / sizeof(_text[0]), L"%u%%", (int)(100 * percent)); + case SCROLLBACK_INDICATOR_FORMAT_PERCENTAGE: { + char percent_str[8]; + snprintf(percent_str, sizeof(percent_str), "%u%%", (int)(100 * percent)); + mbstoc32(_text, percent_str, ALEN(_text)); cell_count = 3; break; + } - case SCROLLBACK_INDICATOR_FORMAT_LINENO: - swprintf(_text, sizeof(_text) / sizeof(_text[0]), L"%d", rebased_view + 1); - cell_count = 1 + (int)log10(term->grid->num_rows); + case SCROLLBACK_INDICATOR_FORMAT_LINENO: { + char lineno_str[64]; + snprintf(lineno_str, sizeof(lineno_str), "%d", rebased_view + 1); + mbstoc32(_text, lineno_str, ALEN(_text)); + cell_count = ceil(log10(term->grid->num_rows)); break; + } case SCROLLBACK_INDICATOR_FORMAT_TEXT: text = term->conf->scrollback.indicator.text; - cell_count = wcslen(text); + cell_count = c32len(text); break; } @@ -2281,7 +2283,7 @@ render_scrollback_position(struct terminal *term) win->scrollback_indicator.sub, term->fonts[0], buf, text, fg, 0xffu << 24 | bg, - width - margin - wcslen(text) * term->cell_width, margin); + width - margin - c32len(text) * term->cell_width, margin); } static void @@ -2289,12 +2291,15 @@ render_render_timer(struct terminal *term, struct timespec render_time) { struct wl_window *win = term->window; - wchar_t text[256]; + char usecs_str[256]; double usecs = render_time.tv_sec * 1000000 + render_time.tv_nsec / 1000.0; - swprintf(text, sizeof(text) / sizeof(text[0]), L"%.2f µs", usecs); + snprintf(usecs_str, sizeof(usecs_str), "%.2f µs", usecs); + + char32_t text[256]; + mbstoc32(text, usecs_str, ALEN(text)); const int scale = term->scale; - const int cell_count = wcslen(text); + const int cell_count = c32len(text); const int margin = 3 * scale; const int width = (2 * margin + cell_count * term->cell_width + scale - 1) / scale * scale; @@ -2846,34 +2851,34 @@ render_search_box(struct terminal *term) size_t text_len = term->search.len; if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL) - text_len += wcslen(ime_seat->ime.preedit.text); + text_len += c32len(ime_seat->ime.preedit.text); - wchar_t *text = xmalloc((text_len + 1) * sizeof(wchar_t)); - text[0] = L'\0'; + char32_t *text = xmalloc((text_len + 1) * sizeof(char32_t)); + text[0] = U'\0'; /* Copy everything up to the cursor */ - wcsncpy(text, term->search.buf, term->search.cursor); - text[term->search.cursor] = L'\0'; + c32ncpy(text, term->search.buf, term->search.cursor); + text[term->search.cursor] = U'\0'; /* Insert pre-edit text at cursor */ if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL) - wcscat(text, ime_seat->ime.preedit.text); + c32cat(text, ime_seat->ime.preedit.text); /* And finally everything after the cursor */ - wcsncat(text, &term->search.buf[term->search.cursor], + c32ncat(text, &term->search.buf[term->search.cursor], term->search.len - term->search.cursor); #else - const wchar_t *text = term->search.buf; + const char32_t *text = term->search.buf; const size_t text_len = term->search.len; #endif /* Calculate the width of each character */ int widths[text_len + 1]; for (size_t i = 0; i < text_len; i++) - widths[i] = max(0, wcwidth(text[i])); + widths[i] = max(0, c32width(text[i])); widths[text_len] = 0; - const size_t total_cells = wcswidth(text, text_len); + const size_t total_cells = c32swidth(text, text_len); const size_t wanted_visible_cells = max(20, total_cells); xassert(term->scale >= 1); @@ -3070,7 +3075,7 @@ render_search_box(struct terminal *term) continue; } - const struct fcft_glyph *glyph = fcft_glyph_rasterize( + const struct fcft_glyph *glyph = fcft_rasterize_char_utf32( font, text[i], term->font_subpixel); if (glyph == NULL) { @@ -3195,7 +3200,7 @@ render_urls(struct terminal *term) /* Positioning data + label contents */ struct { const struct wl_url *url; - wchar_t *text; + char32_t *text; int x; int y; } info[tll_length(win->urls)]; @@ -3208,8 +3213,8 @@ render_urls(struct terminal *term) tll_foreach(win->urls, it) { const struct url *url = it->item.url; - const wchar_t *key = url->key; - const size_t entered_key_len = wcslen(term->url_keys); + const char32_t *key = url->key; + const size_t entered_key_len = c32len(term->url_keys); if (key == NULL) { /* TODO: if we decide to use the .text field, we cannot @@ -3232,9 +3237,9 @@ render_urls(struct terminal *term) if (_row < view_start || _row > view_end) hide = true; - if (wcslen(key) <= entered_key_len) + if (c32len(key) <= entered_key_len) hide = true; - if (wcsncasecmp(term->url_keys, key, entered_key_len) != 0) + if (c32ncasecmp(term->url_keys, key, entered_key_len) != 0) hide = true; if (hide) { @@ -3264,32 +3269,34 @@ render_urls(struct terminal *term) term->width - term->margins.left - term->margins.right - x; const int max_cols = max_width / term->cell_width; - const size_t key_len = wcslen(key); + const size_t key_len = c32len(key); - size_t url_len = mbstowcs(NULL, url->url, 0); + size_t url_len = mbstoc32(NULL, url->url, 0); if (url_len == (size_t)-1) url_len = 0; - wchar_t url_wchars[url_len + 1]; - mbstowcs(url_wchars, url->url, url_len + 1); + char32_t url_wchars[url_len + 1]; + mbstoc32(url_wchars, url->url, url_len + 1); /* Format label, not yet subject to any size limitations */ size_t chars = key_len + (show_url ? (2 + url_len) : 0); - wchar_t label[chars + 1]; - label[chars] = L'\0'; + char32_t label[chars + 1]; + label[chars] = U'\0'; - if (show_url) - swprintf(label, chars + 1, L"%ls: %ls", key, url_wchars); - else - wcsncpy(label, key, chars); + if (show_url) { + c32cpy(label, key); + c32cat(label, U": "); + c32cat(label, url_wchars); + } else + c32ncpy(label, key, chars); /* Upper case the key characters */ - for (size_t i = 0; i < wcslen(key); i++) - label[i] = towupper(label[i]); + for (size_t i = 0; i < c32len(key); i++) + label[i] = toc32upper(label[i]); /* Blank already entered key characters */ for (size_t i = 0; i < entered_key_len; i++) - label[i] = L' '; + label[i] = U' '; /* * Don’t extend outside our window @@ -3303,16 +3310,16 @@ render_urls(struct terminal *term) int cols = 0; - for (size_t i = 0; i <= wcslen(label); i++) { - int _cols = wcswidth(label, i); + for (size_t i = 0; i <= c32len(label); i++) { + int _cols = c32swidth(label, i); if (_cols == (size_t)-1) continue; if (_cols >= max_cols) { if (i > 0) - label[i - 1] = L'…'; - label[i] = L'\0'; + label[i - 1] = U'…'; + label[i] = U'\0'; cols = max_cols; break; } @@ -3328,7 +3335,7 @@ render_urls(struct terminal *term) (2 * y_margin + term->cell_height + scale - 1) / scale * scale; info[render_count].url = &it->item; - info[render_count].text = xwcsdup(label); + info[render_count].text = xc32dup(label); info[render_count].x = x; info[render_count].y = y; @@ -3353,7 +3360,7 @@ render_urls(struct terminal *term) struct wl_surface *surf = info[i].url->surf.surf; struct wl_subsurface *sub_surf = info[i].url->surf.sub; - const wchar_t *label = info[i].text; + const char32_t *label = info[i].text; const int x = info[i].x; const int y = info[i].y; diff --git a/search.c b/search.c index 8c5051af..8015f265 100644 --- a/search.c +++ b/search.c @@ -1,8 +1,6 @@ #include "search.h" #include -#include -#include #include #include @@ -10,6 +8,7 @@ #define LOG_MODULE "search" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "char32.h" #include "config.h" #include "extract.h" #include "grid.h" @@ -64,7 +63,7 @@ search_ensure_size(struct terminal *term, size_t wanted_size) { while (wanted_size >= term->search.sz) { size_t new_sz = term->search.sz == 0 ? 64 : term->search.sz * 2; - wchar_t *new_buf = realloc(term->search.buf, new_sz * sizeof(term->search.buf[0])); + char32_t *new_buf = realloc(term->search.buf, new_sz * sizeof(term->search.buf[0])); if (new_buf == NULL) { LOG_ERRNO("failed to resize search buffer"); @@ -145,7 +144,7 @@ search_begin(struct terminal *term) term->search.len = 0; term->search.sz = 64; term->search.buf = xmalloc(term->search.sz * sizeof(term->search.buf[0])); - term->search.buf[0] = L'\0'; + term->search.buf[0] = U'\0'; term_xcursor_update(term); render_refresh_search(term); @@ -256,7 +255,7 @@ matches_cell(const struct terminal *term, const struct cell *cell, size_t search { assert(search_ofs < term->search.len); - wchar_t base = cell->wc; + char32_t base = cell->wc; const struct composed *composed = NULL; if (base >= CELL_COMB_CHARS_LO && base <= CELL_COMB_CHARS_HI) @@ -265,10 +264,10 @@ matches_cell(const struct terminal *term, const struct cell *cell, size_t search base = composed->chars[0]; } - if (composed == NULL && base == 0 && term->search.buf[search_ofs] == L' ') + if (composed == NULL && base == 0 && term->search.buf[search_ofs] == U' ') return 1; - if (wcsncasecmp(&base, &term->search.buf[search_ofs], 1) != 0) + if (c32ncasecmp(&base, &term->search.buf[search_ofs], 1) != 0) return -1; if (composed != NULL) { @@ -405,11 +404,11 @@ search_find_next(struct terminal *term) } static void -add_wchars(struct terminal *term, wchar_t *src, size_t count) +add_wchars(struct terminal *term, char32_t *src, size_t count) { /* Strip non-printable characters */ for (size_t i = 0, j = 0, orig_count = count; i < orig_count; i++) { - if (iswprint(src[i])) + if (isc32print(src[i])) src[j++] = src[i]; else count--; @@ -422,32 +421,27 @@ add_wchars(struct terminal *term, wchar_t *src, size_t count) memmove(&term->search.buf[term->search.cursor + count], &term->search.buf[term->search.cursor], - (term->search.len - term->search.cursor) * sizeof(wchar_t)); + (term->search.len - term->search.cursor) * sizeof(char32_t)); - memcpy(&term->search.buf[term->search.cursor], src, count * sizeof(wchar_t)); + memcpy(&term->search.buf[term->search.cursor], src, count * sizeof(char32_t)); term->search.len += count; term->search.cursor += count; - term->search.buf[term->search.len] = L'\0'; + term->search.buf[term->search.len] = U'\0'; } void search_add_chars(struct terminal *term, const char *src, size_t count) { - const char *_src = src; - mbstate_t ps = {0}; - size_t wchars = mbsnrtowcs(NULL, &_src, count, 0, &ps); - - if (wchars == -1) { - LOG_ERRNO("failed to convert %.*s to wchars", (int)count, src); + size_t chars = mbsntoc32(NULL, src, count, 0); + if (chars == (size_t)-1) { + LOG_ERRNO("failed to convert %.*s to Unicode", (int)count, src); return; } - _src = src; - ps = (mbstate_t){0}; - wchar_t wcs[wchars + 1]; - mbsnrtowcs(wcs, &_src, count, wchars, &ps); - add_wchars(term, wcs, wchars); + char32_t c32s[chars + 1]; + mbsntoc32(c32s, src, count, chars); + add_wchars(term, c32s, chars); } static void @@ -502,7 +496,7 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only) break; } while (pos.col != new_end.col || pos.row != new_end.row); - wchar_t *new_text; + char32_t *new_text; size_t new_len; if (!extract_finish_wide(ctx, &new_text, &new_len)) @@ -512,7 +506,7 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only) return; for (size_t i = 0; i < new_len; i++) { - if (new_text[i] == L'\n') { + if (new_text[i] == U'\n') { /* extract() adds newlines, which we never match against */ continue; } @@ -520,7 +514,7 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only) term->search.buf[term->search.len++] = new_text[i]; } - term->search.buf[term->search.len] = L'\0'; + term->search.buf[term->search.len] = U'\0'; free(new_text); if (move_cursor) @@ -544,22 +538,22 @@ distance_next_word(const struct terminal *term) /* First eat non-whitespace. This is the word we're skipping past */ while (cursor < term->search.len) { - if (iswspace(term->search.buf[cursor++])) + if (isc32space(term->search.buf[cursor++])) break; } - xassert(cursor == term->search.len || iswspace(term->search.buf[cursor - 1])); + xassert(cursor == term->search.len || isc32space(term->search.buf[cursor - 1])); /* Now skip past whitespace, so that we end up at the beginning of * the next word */ while (cursor < term->search.len) { - if (!iswspace(term->search.buf[cursor++])) + if (!isc32space(term->search.buf[cursor++])) break; } - xassert(cursor == term->search.len || !iswspace(term->search.buf[cursor - 1])); + xassert(cursor == term->search.len || !isc32space(term->search.buf[cursor - 1])); - if (cursor < term->search.len && !iswspace(term->search.buf[cursor])) + if (cursor < term->search.len && !isc32space(term->search.buf[cursor])) cursor--; return cursor - term->search.cursor; @@ -572,20 +566,20 @@ distance_prev_word(const struct terminal *term) /* First, eat whitespace prefix */ while (cursor > 0) { - if (!iswspace(term->search.buf[--cursor])) + if (!isc32space(term->search.buf[--cursor])) break; } - xassert(cursor == 0 || !iswspace(term->search.buf[cursor])); + xassert(cursor == 0 || !isc32space(term->search.buf[cursor])); /* Now eat non-whitespace. This is the word we're skipping past */ while (cursor > 0) { - if (iswspace(term->search.buf[--cursor])) + if (isc32space(term->search.buf[--cursor])) break; } - xassert(cursor == 0 || iswspace(term->search.buf[cursor])); - if (cursor > 0 && iswspace(term->search.buf[cursor])) + xassert(cursor == 0 || isc32space(term->search.buf[cursor])); + if (cursor > 0 && isc32space(term->search.buf[cursor])) cursor++; return term->search.cursor - cursor; @@ -603,7 +597,7 @@ from_clipboard_done(void *user) { struct terminal *term = user; - LOG_DBG("search: buffer: %ls", term->search.buf); + LOG_DBG("search: buffer: %ls", (const wchar_t *)term->search.buf); search_find_next(term); render_refresh_search(term); } @@ -741,9 +735,9 @@ execute_binding(struct seat *seat, struct terminal *term, memmove( &term->search.buf[term->search.cursor - 1], &term->search.buf[term->search.cursor], - (term->search.len - term->search.cursor) * sizeof(wchar_t)); + (term->search.len - term->search.cursor) * sizeof(char32_t)); term->search.cursor--; - term->search.buf[--term->search.len] = L'\0'; + term->search.buf[--term->search.len] = U'\0'; *update_search_result = *redraw = true; } return true; @@ -756,7 +750,7 @@ execute_binding(struct seat *seat, struct terminal *term, if (diff > 0) { memmove(&term->search.buf[new_cursor], &term->search.buf[old_cursor], - (term->search.len - old_cursor) * sizeof(wchar_t)); + (term->search.len - old_cursor) * sizeof(char32_t)); term->search.len -= diff; term->search.cursor = new_cursor; @@ -770,8 +764,8 @@ execute_binding(struct seat *seat, struct terminal *term, memmove( &term->search.buf[term->search.cursor], &term->search.buf[term->search.cursor + 1], - (term->search.len - term->search.cursor - 1) * sizeof(wchar_t)); - term->search.buf[--term->search.len] = L'\0'; + (term->search.len - term->search.cursor - 1) * sizeof(char32_t)); + term->search.buf[--term->search.len] = U'\0'; *update_search_result = *redraw = true; } return true; @@ -783,7 +777,7 @@ execute_binding(struct seat *seat, struct terminal *term, if (diff > 0) { memmove(&term->search.buf[cursor], &term->search.buf[cursor + diff], - (term->search.len - (cursor + diff)) * sizeof(wchar_t)); + (term->search.len - (cursor + diff)) * sizeof(char32_t)); term->search.len -= diff; *update_search_result = *redraw = true; @@ -905,7 +899,7 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, search_add_chars(term, (const char *)buf, count); update_search: - LOG_DBG("search: buffer: %ls", term->search.buf); + LOG_DBG("search: buffer: %ls", (const wchar_t *)term->search.buf); if (update_search_result) search_find_next(term); if (redraw) diff --git a/selection.c b/selection.c index 595fd1c8..0ed25cd7 100644 --- a/selection.c +++ b/selection.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -15,6 +14,7 @@ #include "log.h" #include "async.h" +#include "char32.h" #include "commands.h" #include "config.h" #include "extract.h" @@ -235,7 +235,7 @@ selection_find_word_boundary_left(struct terminal *term, struct coord *pos, bool spaces_only) { const struct row *r = grid_row_in_view(term->grid, pos->row); - wchar_t c = r->cells[pos->col].wc; + char32_t c = r->cells[pos->col].wc; while (c >= CELL_SPACER) { xassert(pos->col > 0); @@ -248,7 +248,7 @@ selection_find_word_boundary_left(struct terminal *term, struct coord *pos, if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI) c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; - bool initial_is_space = c == 0 || iswspace(c); + bool initial_is_space = c == 0 || isc32space(c); bool initial_is_delim = !initial_is_space && !isword(c, spaces_only, term->conf->word_delimiters); bool initial_is_word = @@ -285,7 +285,7 @@ selection_find_word_boundary_left(struct terminal *term, struct coord *pos, if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI) c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; - bool is_space = c == 0 || iswspace(c); + bool is_space = c == 0 || isc32space(c); bool is_delim = !is_space && !isword(c, spaces_only, term->conf->word_delimiters); bool is_word = @@ -308,7 +308,7 @@ selection_find_word_boundary_right(struct terminal *term, struct coord *pos, bool spaces_only) { const struct row *r = grid_row_in_view(term->grid, pos->row); - wchar_t c = r->cells[pos->col].wc; + char32_t c = r->cells[pos->col].wc; while (c >= CELL_SPACER) { xassert(pos->col > 0); @@ -321,7 +321,7 @@ selection_find_word_boundary_right(struct terminal *term, struct coord *pos, if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI) c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; - bool initial_is_space = c == 0 || iswspace(c); + bool initial_is_space = c == 0 || isc32space(c); bool initial_is_delim = !initial_is_space && !isword(c, spaces_only, term->conf->word_delimiters); bool initial_is_word = @@ -360,7 +360,7 @@ selection_find_word_boundary_right(struct terminal *term, struct coord *pos, if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI) c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; - bool is_space = c == 0 || iswspace(c); + bool is_space = c == 0 || isc32space(c); bool is_delim = !is_space && !isword(c, spaces_only, term->conf->word_delimiters); bool is_word = @@ -668,7 +668,7 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term, bool keep_going = true; while (keep_going) { const struct row *row = term->grid->rows[pivot_end->row & (term->grid->num_rows - 1)]; - const wchar_t wc = row->cells[pivot_end->col].wc; + const char32_t wc = row->cells[pivot_end->col].wc; keep_going = wc >= CELL_SPACER; @@ -684,7 +684,7 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term, bool keep_going = true; while (keep_going) { const struct row *row = term->grid->rows[pivot_start->row & (term->grid->num_rows - 1)]; - const wchar_t wc = pivot_start->col < term->cols - 1 + const char32_t wc = pivot_start->col < term->cols - 1 ? row->cells[pivot_start->col + 1].wc : 0; keep_going = wc >= CELL_SPACER; diff --git a/terminal.c b/terminal.c index 84a43290..3b935909 100644 --- a/terminal.c +++ b/terminal.c @@ -671,16 +671,12 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) const struct config *conf = term->conf; - const struct fcft_glyph *M = fcft_glyph_rasterize( - term->fonts[0], L'M', term->font_subpixel); + const struct fcft_glyph *M = fcft_rasterize_char_utf32( + fonts[0], U'M', term->font_subpixel); + int advance = M != NULL ? M->advance.x : term->fonts[0]->max_advance.x; - term->cell_width = - (M != NULL - ? M->advance.x - : (term->fonts[0]->space_advance.x > 0 - ? term->fonts[0]->space_advance.x - : term->fonts[0]->max_advance.x)) - + term_pt_or_px_as_pixels(term, &conf->letter_spacing); + term->cell_width = advance + + term_pt_or_px_as_pixels(term, &conf->letter_spacing); term->cell_height = term->font_line_height.px >= 0 ? term_pt_or_px_as_pixels(term, &term->font_line_height) @@ -3208,7 +3204,7 @@ print_spacer(struct terminal *term, int col, int remaining) } void -term_print(struct terminal *term, wchar_t wc, int width) +term_print(struct terminal *term, char32_t wc, int width) { xassert(width > 0); @@ -3218,11 +3214,11 @@ term_print(struct terminal *term, wchar_t wc, int width) wc >= 0x60 && wc <= 0x7e) { /* 0x60 - 0x7e */ - static const wchar_t vt100_0[] = { - L'◆', L'▒', L'␉', L'␌', L'␍', L'␊', L'°', L'±', /* ` - g */ - L'␤', L'␋', L'┘', L'┐', L'┌', L'└', L'┼', L'⎺', /* h - o */ - L'⎻', L'─', L'⎼', L'⎽', L'├', L'┤', L'┴', L'┬', /* p - w */ - L'│', L'≤', L'≥', L'π', L'≠', L'£', L'·', /* x - ~ */ + static const char32_t vt100_0[] = { + U'◆', U'▒', U'␉', U'␌', U'␍', U'␊', U'°', U'±', /* ` - g */ + U'␤', U'␋', U'┘', U'┐', U'┌', U'└', U'┼', U'⎺', /* h - o */ + U'⎻', U'─', U'⎼', U'⎽', U'├', U'┤', U'┴', U'┬', /* p - w */ + U'│', U'≤', U'≥', U'π', U'≠', U'£', U'·', /* x - ~ */ }; xassert(width == 1); @@ -3291,13 +3287,13 @@ term_print(struct terminal *term, wchar_t wc, int width) } static void -ascii_printer_generic(struct terminal *term, wchar_t wc) +ascii_printer_generic(struct terminal *term, char32_t wc) { term_print(term, wc, 1); } static void -ascii_printer_fast(struct terminal *term, wchar_t wc) +ascii_printer_fast(struct terminal *term, char32_t wc) { struct grid *grid = term->grid; @@ -3333,7 +3329,7 @@ ascii_printer_fast(struct terminal *term, wchar_t wc) } static void -ascii_printer_single_shift(struct terminal *term, wchar_t wc) +ascii_printer_single_shift(struct terminal *term, char32_t wc) { ascii_printer_generic(term, wc); term->charsets.selected = term->charsets.saved; @@ -3343,7 +3339,7 @@ ascii_printer_single_shift(struct terminal *term, wchar_t wc) void term_update_ascii_printer(struct terminal *term) { - void (*new_printer)(struct terminal *term, wchar_t wc) = + void (*new_printer)(struct terminal *term, char32_t wc) = unlikely(tll_length(term->grid->sixel_images) > 0 || term->vt.osc8.uri != NULL || term->charsets.set[term->charsets.selected] == CHARSET_GRAPHIC || diff --git a/terminal.h b/terminal.h index 1ff4b3b6..e42c94fc 100644 --- a/terminal.h +++ b/terminal.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -65,7 +64,7 @@ static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large"); #define CELL_SPACER (CELL_COMB_CHARS_HI + 1) struct cell { - wchar_t wc; + char32_t wc; struct attributes attrs; }; static_assert(sizeof(struct cell) == 12, "bad size"); @@ -182,11 +181,11 @@ struct vt_param { struct vt { int state; /* enum state */ - wchar_t last_printed; + char32_t last_printed; #if defined(FOOT_GRAPHEME_CLUSTERING) utf8proc_int32_t grapheme_state; #endif - wchar_t utf8; + char32_t utf8; struct { struct vt_param v[16]; uint8_t idx; @@ -290,7 +289,7 @@ enum url_action { URL_ACTION_COPY, URL_ACTION_LAUNCH }; struct url { uint64_t id; char *url; - wchar_t *key; + char32_t *key; struct coord start; struct coord end; enum url_action action; @@ -311,7 +310,7 @@ struct terminal { struct reaper *reaper; const struct config *conf; - void (*ascii_printer)(struct terminal *term, wchar_t c); + void (*ascii_printer)(struct terminal *term, char32_t c); pid_t slave; int ptmx; @@ -504,7 +503,7 @@ struct terminal { bool is_searching; struct { - wchar_t *buf; + char32_t *buf; size_t len; size_t sz; size_t cursor; @@ -516,7 +515,7 @@ struct terminal { size_t match_len; struct { - wchar_t *buf; + char32_t *buf; size_t len; } last; } search; @@ -639,7 +638,7 @@ struct terminal { /* TODO: wrap in a struct */ url_list_t urls; - wchar_t url_keys[5]; + char32_t url_keys[5]; bool urls_show_uri_on_jump_label; struct grid *url_grid_snapshot; @@ -734,7 +733,7 @@ void term_cursor_up(struct terminal *term, int count); void term_cursor_down(struct terminal *term, int count); void term_cursor_blink_update(struct terminal *term); -void term_print(struct terminal *term, wchar_t wc, int width); +void term_print(struct terminal *term, char32_t wc, int width); void term_scroll(struct terminal *term, int rows); void term_scroll_reverse(struct terminal *term, int rows); diff --git a/tests/test-config.c b/tests/test-config.c index d22e96a8..657ea00a 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -70,17 +70,17 @@ test_string(struct context *ctx, bool (*parse_fun)(struct context *ctx), } static void -test_wstring(struct context *ctx, bool (*parse_fun)(struct context *ctx), - const char *key, wchar_t *const *ptr) +test_c32string(struct context *ctx, bool (*parse_fun)(struct context *ctx), + const char *key, char32_t *const *ptr) { ctx->key = key; static const struct { const char *option_string; - const wchar_t *value; + const char32_t *value; bool invalid; } input[] = { - {"a string", L"a string"}, + {"a string", U"a string"}, }; for (size_t i = 0; i < ALEN(input); i++) { @@ -96,10 +96,11 @@ test_wstring(struct context *ctx, bool (*parse_fun)(struct context *ctx), BUG("[%s].%s=%s: failed to parse", ctx->section, ctx->key, ctx->value); } - if (wcscmp(*ptr, input[i].value) != 0) { + if (c32cmp(*ptr, input[i].value) != 0) { BUG("[%s].%s=%s: set value (%ls) not the expected one (%ls)", ctx->section, ctx->key, ctx->value, - *ptr, input[i].value); + (const wchar_t *)*ptr, + (const wchar_t *)input[i].value); } } } @@ -412,7 +413,7 @@ test_section_main(void) test_string(&ctx, &parse_section_main, "term", &conf.term); test_string(&ctx, &parse_section_main, "app-id", &conf.app_id); - test_wstring(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters); + test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters); test_boolean(&ctx, &parse_section_main, "login-shell", &conf.login_shell); test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs); @@ -526,7 +527,7 @@ test_section_url(void) (const char *[]){"url-mode", "always"}, (int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS}, (int *)&conf.url.osc8_underline); - test_wstring(&ctx, &parse_section_url, "label-letters", &conf.url.label_letters); + test_c32string(&ctx, &parse_section_url, "label-letters", &conf.url.label_letters); /* TODO: protocols (list of wchars) */ /* TODO: uri-characters (wchar string, but sorted) */ diff --git a/url-mode.c b/url-mode.c index b565a87d..82002fa0 100644 --- a/url-mode.c +++ b/url-mode.c @@ -11,6 +11,7 @@ #define LOG_MODULE "url-mode" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "char32.h" #include "grid.h" #include "render.h" #include "selection.h" @@ -150,11 +151,11 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key, } } - size_t seq_len = wcslen(term->url_keys); + size_t seq_len = c32len(term->url_keys); if (sym == XKB_KEY_BackSpace) { if (seq_len > 0) { - term->url_keys[seq_len - 1] = L'\0'; + term->url_keys[seq_len - 1] = U'\0'; render_refresh_urls(term); } @@ -164,7 +165,7 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key, if (mods & ~consumed) return; - wchar_t wc = xkb_state_key_get_utf32(seat->kbd.xkb_state, key); + char32_t wc = xkb_state_key_get_utf32(seat->kbd.xkb_state, key); /* * Determine if this is a “valid” key. I.e. if there is an URL @@ -180,11 +181,11 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key, continue; const struct url *url = &it->item; - const size_t key_len = wcslen(it->item.key); + const size_t key_len = c32len(it->item.key); if (key_len >= seq_len + 1 && - wcsncasecmp(url->key, term->url_keys, seq_len) == 0 && - towlower(url->key[seq_len]) == towlower(wc)) + c32ncasecmp(url->key, term->url_keys, seq_len) == 0 && + toc32lower(url->key[seq_len]) == toc32lower(wc)) { is_valid = true; if (key_len == seq_len + 1) { @@ -207,10 +208,10 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key, } static int -wccmp(const void *_a, const void *_b) +c32cmp_single(const void *_a, const void *_b) { - const wchar_t *a = _a; - const wchar_t *b = _b; + const char32_t *a = _a; + const char32_t *b = _b; return *a - *b; } @@ -220,16 +221,16 @@ auto_detected(const struct terminal *term, enum url_action action, { const struct config *conf = term->conf; - const wchar_t *uri_characters = conf->url.uri_characters; + const char32_t *uri_characters = conf->url.uri_characters; if (uri_characters == NULL) return; - const size_t uri_characters_count = wcslen(uri_characters); + const size_t uri_characters_count = c32len(uri_characters); if (uri_characters_count == 0) return; size_t max_prot_len = conf->url.max_prot_len; - wchar_t proto_chars[max_prot_len]; + char32_t proto_chars[max_prot_len]; struct coord proto_start[max_prot_len]; size_t proto_char_count = 0; @@ -239,7 +240,7 @@ auto_detected(const struct terminal *term, enum url_action action, } state = STATE_PROTOCOL; struct coord start = {-1, -1}; - wchar_t url[term->cols * term->rows + 1]; + char32_t url[term->cols * term->rows + 1]; size_t len = 0; ssize_t parenthesis = 0; @@ -251,7 +252,7 @@ auto_detected(const struct terminal *term, enum url_action action, for (int c = 0; c < term->cols; c++) { const struct cell *cell = &row->cells[c]; - wchar_t wc = cell->wc; + char32_t wc = cell->wc; switch (state) { case STATE_PROTOCOL: @@ -268,18 +269,18 @@ auto_detected(const struct terminal *term, enum url_action action, proto_char_count++; for (size_t i = 0; i < conf->url.prot_count; i++) { - size_t prot_len = wcslen(conf->url.protocols[i]); + size_t prot_len = c32len(conf->url.protocols[i]); if (proto_char_count < prot_len) continue; - const wchar_t *proto = &proto_chars[max_prot_len - prot_len]; + const char32_t *proto = &proto_chars[max_prot_len - prot_len]; - if (wcsncasecmp(conf->url.protocols[i], proto, prot_len) == 0) { + if (c32ncasecmp(conf->url.protocols[i], proto, prot_len) == 0) { state = STATE_URL; start = proto_start[max_prot_len - prot_len]; - wcsncpy(url, proto, prot_len); + c32ncpy(url, proto, prot_len); len = prot_len; parenthesis = brackets = ltgts = 0; @@ -289,12 +290,12 @@ auto_detected(const struct terminal *term, enum url_action action, break; case STATE_URL: { - const wchar_t *match = bsearch( + const char32_t *match = bsearch( &wc, uri_characters, uri_characters_count, sizeof(uri_characters[0]), - &wccmp); + &c32cmp_single); bool emit_url = false; @@ -313,36 +314,36 @@ auto_detected(const struct terminal *term, enum url_action action, url[len++] = wc; break; - case L'(': + case U'(': parenthesis++; url[len++] = wc; break; - case L'[': + case U'[': brackets++; url[len++] = wc; break; - case L'<': + case U'<': ltgts++; url[len++] = wc; break; - case L')': + case U')': if (--parenthesis < 0) emit_url = true; else url[len++] = wc; break; - case L']': + case U']': if (--brackets < 0) emit_url = true; else url[len++] = wc; break; - case L'>': + case U'>': if (--ltgts < 0) emit_url = true; else @@ -374,8 +375,8 @@ auto_detected(const struct terminal *term, enum url_action action, bool done = false; do { switch (url[len - 1]) { - case L'.': case L',': case L':': case L';': case L'?': - case L'!': case L'"': case L'\'': case L'%': + case U'.': case U',': case U':': case U';': case U'?': + case U'!': case U'"': case U'\'': case U'%': len--; end.col--; if (end.col < 0) { @@ -390,16 +391,13 @@ auto_detected(const struct terminal *term, enum url_action action, } } while (!done); - url[len] = L'\0'; + url[len] = U'\0'; start.row += term->grid->view; end.row += term->grid->view; - size_t chars = wcstombs(NULL, url, 0); - if (chars != (size_t)-1) { - char *url_utf8 = xmalloc((chars + 1) * sizeof(wchar_t)); - wcstombs(url_utf8, url, chars + 1); - + char *url_utf8 = ac32tombs(url); + if (url_utf8 != NULL) { tll_push_back( *urls, ((struct url){ @@ -527,40 +525,40 @@ urls_collect(const struct terminal *term, enum url_action action, url_list_t *ur } static int -wcscmp_qsort_wrapper(const void *_a, const void *_b) +c32cmp_qsort_wrapper(const void *_a, const void *_b) { - const wchar_t *a = *(const wchar_t **)_a; - const wchar_t *b = *(const wchar_t **)_b; - return wcscmp(a, b); + const char32_t *a = *(const char32_t **)_a; + const char32_t *b = *(const char32_t **)_b; + return c32cmp(a, b); } static void generate_key_combos(const struct config *conf, - size_t count, wchar_t *combos[static count]) + size_t count, char32_t *combos[static count]) { - const wchar_t *alphabet = conf->url.label_letters; - const size_t alphabet_len = wcslen(alphabet); + const char32_t *alphabet = conf->url.label_letters; + const size_t alphabet_len = c32len(alphabet); size_t hints_count = 1; - wchar_t **hints = xmalloc(hints_count * sizeof(hints[0])); + char32_t **hints = xmalloc(hints_count * sizeof(hints[0])); - hints[0] = xwcsdup(L""); + hints[0] = xc32dup(U""); size_t offset = 0; do { - const wchar_t *prefix = hints[offset++]; - const size_t prefix_len = wcslen(prefix); + const char32_t *prefix = hints[offset++]; + const size_t prefix_len = c32len(prefix); hints = xrealloc(hints, (hints_count + alphabet_len) * sizeof(hints[0])); - const wchar_t *wc = &alphabet[0]; + const char32_t *wc = &alphabet[0]; for (size_t i = 0; i < alphabet_len; i++, wc++) { - wchar_t *hint = xmalloc((prefix_len + 1 + 1) * sizeof(wchar_t)); + char32_t *hint = xmalloc((prefix_len + 1 + 1) * sizeof(char32_t)); hints[hints_count + i] = hint; /* Will be reversed later */ hint[0] = *wc; - wcscpy(&hint[1], prefix); + c32cpy(&hint[1], prefix); } hints_count += alphabet_len; } while (hints_count - offset < count); @@ -578,13 +576,13 @@ generate_key_combos(const struct config *conf, /* Sorting is a kind of shuffle, since we’re sorting on the * *reversed* strings */ - qsort(combos, count, sizeof(wchar_t *), &wcscmp_qsort_wrapper); + qsort(combos, count, sizeof(char32_t *), &c32cmp_qsort_wrapper); /* Reverse all strings */ for (size_t i = 0; i < count; i++) { - const size_t len = wcslen(combos[i]); + const size_t len = c32len(combos[i]); for (size_t j = 0; j < len / 2; j++) { - wchar_t tmp = combos[i][j]; + char32_t tmp = combos[i][j]; combos[i][j] = combos[i][len - j - 1]; combos[i][len - j - 1] = tmp; } @@ -599,7 +597,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls) return; uint64_t seen_ids[count]; - wchar_t *combos[count]; + char32_t *combos[count]; generate_key_combos(conf, count, combos); size_t combo_idx = 0; @@ -629,7 +627,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls) break; if (strcmp(it->item.url, it2->item.url) == 0) { - it->item.key = xwcsdup(it2->item.key); + it->item.key = xc32dup(it2->item.key); url_already_seen = true; break; } @@ -648,9 +646,11 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls) if (it->item.key == NULL) continue; - char key[32]; - wcstombs(key, it->item.key, sizeof(key) - 1); + char *key = ac32tombs(it->item.key); + xassert(key != NULL); + LOG_DBG("URL: %s (%s)", it->item.url, key); + free(key); } #endif } diff --git a/vt.c b/vt.c index 736a2cfd..d496fcfe 100644 --- a/vt.c +++ b/vt.c @@ -11,6 +11,7 @@ #define LOG_MODULE "vt" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "char32.h" #include "config.h" #include "csi.h" #include "dcs.h" @@ -182,14 +183,14 @@ action_execute(struct terminal *term, uint8_t c) struct row *row = term->grid->cur_row; bool emit_tab_char = (row->cells[start_col].wc == 0 || - row->cells[start_col].wc == L' '); + row->cells[start_col].wc == U' '); /* Check if all cells from here until the next tab stop are empty */ for (const struct cell *cell = &row->cells[start_col + 1]; cell < &row->cells[new_col]; cell++) { - if (!(cell->wc == 0 || cell->wc == L' ')) { + if (!(cell->wc == 0 || cell->wc == U' ')) { emit_tab_char = false; break; } @@ -202,14 +203,14 @@ action_execute(struct terminal *term, uint8_t c) if (emit_tab_char) { row->dirty = true; - row->cells[start_col].wc = '\t'; + row->cells[start_col].wc = U'\t'; row->cells[start_col].attrs.clean = 0; for (struct cell *cell = &row->cells[start_col + 1]; cell < &row->cells[new_col]; cell++) { - cell->wc = L' '; + cell->wc = U' '; cell->attrs.clean = 0; } } @@ -549,7 +550,7 @@ action_esc_dispatch(struct terminal *term, uint8_t final) for (int r = 0; r < term->rows; r++) { struct row *row = grid_row(term->grid, r); for (int c = 0; c < term->cols; c++) { - row->cells[c].wc = L'E'; + row->cells[c].wc = U'E'; row->cells[c].attrs = (struct attributes){0}; } row->dirty = true; @@ -617,9 +618,9 @@ chain_key(uint32_t old_key, uint32_t new_wc) } static void -action_utf8_print(struct terminal *term, wchar_t wc) +action_utf8_print(struct terminal *term, char32_t wc) { - int width = wcwidth(wc); + int width = c32width(wc); const bool grapheme_clustering = term->conf->tweak.grapheme_shaping; #if !defined(FOOT_GRAPHEME_CLUSTERING) @@ -640,8 +641,8 @@ action_utf8_print(struct terminal *term, wchar_t wc) col--; xassert(col >= 0 && col < term->cols); - wchar_t base = row->cells[col].wc; - wchar_t UNUSED last = base; + char32_t base = row->cells[col].wc; + char32_t UNUSED last = base; /* Is base cell already a cluster? */ const struct composed *composed = @@ -672,7 +673,7 @@ action_utf8_print(struct terminal *term, wchar_t wc) } #endif - int base_width = wcwidth(base); + int base_width = c32width(base); if (base_width > 0) { term->grid->cursor.point.col = col; term->grid->cursor.lcf = false; @@ -682,11 +683,11 @@ action_utf8_print(struct terminal *term, wchar_t wc) bool comb_from_primary; bool pre_from_primary; - wchar_t precomposed = fcft_precompose( + char32_t precomposed = fcft_precompose( term->fonts[0], base, wc, &base_from_primary, &comb_from_primary, &pre_from_primary); - int precomposed_width = wcwidth(precomposed); + int precomposed_width = c32width(precomposed); /* * Only use the pre-composed character if: @@ -698,7 +699,7 @@ action_utf8_print(struct terminal *term, wchar_t wc) * font */ - if (precomposed != (wchar_t)-1 && + if (precomposed != (char32_t)-1 && precomposed_width == base_width && (pre_from_primary || !base_from_primary || diff --git a/wayland.h b/wayland.h index 718ad9a2..c3e6bb6d 100644 --- a/wayland.h +++ b/wayland.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -278,7 +279,7 @@ struct seat { int32_t cursor_end; } pending; - wchar_t *text; + char32_t *text; struct cell *cells; int count; struct { diff --git a/xmalloc.c b/xmalloc.c index 9e305061..5d1fc997 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "xmalloc.h" #include "debug.h" @@ -45,18 +44,18 @@ xstrdup(const char *str) return check_alloc(strdup(str)); } -wchar_t * -xwcsdup(const wchar_t *str) -{ - return check_alloc(wcsdup(str)); -} - char * xstrndup(const char *str, size_t n) { return check_alloc(strndup(str, n)); } +char32_t * +xc32dup(const char32_t *str) +{ + return check_alloc(c32dup(str)); +} + static VPRINTF(2) int xvasprintf_(char **strp, const char *format, va_list ap) { diff --git a/xmalloc.h b/xmalloc.h index bc0dd2d8..8a3098b8 100644 --- a/xmalloc.h +++ b/xmalloc.h @@ -3,6 +3,9 @@ #include #include #include +#include + +#include "char32.h" #include "macros.h" void *xmalloc(size_t size) XMALLOC; @@ -12,4 +15,4 @@ char *xstrdup(const char *str) XSTRDUP; char *xstrndup(const char *str, size_t n) XSTRDUP; char *xasprintf(const char *format, ...) PRINTF(1) XMALLOC; char *xvasprintf(const char *format, va_list va) VPRINTF(1) XMALLOC; -wchar_t *xwcsdup(const wchar_t *str) XSTRDUP; +char32_t *xc32dup(const char32_t *str) XSTRDUP;