fcft: adapt to API changes in fcft-3.x

Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.

Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.

For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).

Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.

These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.

For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the  __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.

FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.

Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.

Other fcft API changes:

* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
This commit is contained in:
Daniel Eklöf 2021-08-21 14:50:42 +02:00
parent 2be8c39044
commit e0227266ca
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
32 changed files with 962 additions and 385 deletions

View file

@ -32,6 +32,11 @@ sources:
# to: <committer> # to: <committer>
tasks: tasks:
- fcft: |
cd foot/subprojects
git clone https://codeberg.org/dnkl/fcft.git
cd fcft && git checkout 3.0-dev && cd ..
cd ../..
- debug: | - debug: |
mkdir -p bld/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 meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug

View file

@ -27,6 +27,11 @@ sources:
# to: <committer> # to: <committer>
tasks: tasks:
- fcft: |
cd foot/subprojects
git clone https://codeberg.org/dnkl/fcft.git
cd fcft && git checkout 3.0-dev && cd ..
cd ../..
- debug: | - debug: |
mkdir -p bld/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 meson --buildtype=debug -Dterminfo=disabled -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug

View file

@ -17,6 +17,10 @@ debug-x64:
image: alpine:edge image: alpine:edge
stage: build stage: build
script: script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/debug - mkdir -p bld/debug
- cd bld/debug - cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - 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 stage: build
script: script:
- apk del harfbuzz harfbuzz-dev utf8proc utf8proc-dev - 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 - mkdir -p bld/debug
- cd bld/debug - cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=disabled -Dfcft:grapheme-shaping=disabled -Dfcft:run-shaping=disabled -Dfcft:test-text-shaping=false ../../ - 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 image: alpine:edge
stage: build stage: build
script: script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/release - mkdir -p bld/release
- cd bld/release - cd bld/release
- meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - 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 image: i386/alpine:edge
stage: build stage: build
script: script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/debug - mkdir -p bld/debug
- cd bld/debug - cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - 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 image: i386/alpine:edge
stage: build stage: build
script: script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/release - mkdir -p bld/release
- cd bld/release - cd bld/release
- meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../

View file

@ -22,6 +22,7 @@ pipeline:
- mkdir -p subprojects && cd subprojects - mkdir -p subprojects && cd subprojects
- git clone https://codeberg.org/dnkl/tllist.git - git clone https://codeberg.org/dnkl/tllist.git
- git clone https://codeberg.org/dnkl/fcft.git - git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd .. - cd ..
x64: x64:

View file

@ -334,6 +334,7 @@
* `dpi-aware=auto`: fonts are now scaled using the monitors DPI only * `dpi-aware=auto`: fonts are now scaled using the monitors DPI only
when **all** monitors have a scaling factor of one when **all** monitors have a scaling factor of one
(https://codeberg.org/dnkl/foot/issues/714). (https://codeberg.org/dnkl/foot/issues/714).
* fcft >= 3.0.0 in now required.
### Fixed ### Fixed

View file

@ -1259,7 +1259,7 @@ set_a1_bit(uint8_t *data, size_t ofs, size_t bit_no)
} }
static void NOINLINE 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 pixman_format_code_t fmt = buf->format;
const int supersample = fmt == PIXMAN_a8 ? 4 : 1; 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. * Hence the thick % 2 ^ width % 2 in the expressions below.
*/ */
switch (wc) { switch (wc) {
case L'': case U'':
row_end = height - row - (thick_is_odd ^ height_is_odd); row_end = height - row - (thick_is_odd ^ height_is_odd);
row_start = row_end - thick; row_start = row_end - thick;
col_end = width - col - (thick_is_odd ^ width_is_odd); col_end = width - col - (thick_is_odd ^ width_is_odd);
col_start = col_end - thick; col_start = col_end - thick;
break; break;
case L'': case U'':
row_end = height - row - (thick_is_odd ^ height_is_odd); row_end = height - row - (thick_is_odd ^ height_is_odd);
row_start = row_end - thick; row_start = row_end - thick;
col_start = col - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); col_start = col - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0);
col_end = col_start + thick; col_end = col_start + thick;
break; break;
case L'': case U'':
row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0);
row_end = row_start + thick; row_end = row_start + thick;
col_end = width - col - (thick_is_odd ^ width_is_odd); col_end = width - col - (thick_is_odd ^ width_is_odd);
col_start = col_end - thick; col_start = col_end - thick;
break; break;
case L'': case U'':
row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); row_start = row - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0);
row_end = row_start + thick; row_end = row_start + thick;
col_start = col - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); 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. * are.
*/ */
if (wc == L'' || wc == L'') { if (wc == U'' || wc == U'') {
for (int y = 0; y < thick; y++) { for (int y = 0; y < thick; y++) {
int row = (height - thick) / 2 + y - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0); int row = (height - thick) / 2 + y - ((thick_is_odd ^ height_is_odd) ? supersample / 2 : 0);
for (int col = width - supersample; col < width; col++) { 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++) { for (int x = 0; x < thick; x++) {
int col = (width - thick) / 2 + x - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0); int col = (width - thick) / 2 + x - ((thick_is_odd ^ width_is_odd) ? supersample / 2 : 0);
for (int row = height - supersample; row < height; row++) { for (int row = height - supersample; row < height; row++) {
@ -1924,7 +1924,7 @@ quad_lower_right(struct buf *buf)
} }
static void NOINLINE static void NOINLINE
draw_quadrant(struct buf *buf, wchar_t wc) draw_quadrant(struct buf *buf, char32_t wc)
{ {
enum { enum {
UPPER_LEFT = 1 << 0, UPPER_LEFT = 1 << 0,
@ -1966,7 +1966,7 @@ draw_quadrant(struct buf *buf, wchar_t wc)
} }
static void NOINLINE 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 w = min(buf->width / 4, buf->height / 8);
int x_spacing = buf->width / 4; int x_spacing = buf->width / 4;
@ -2091,7 +2091,7 @@ sextant_lower_right(struct buf *buf)
} }
static void NOINLINE static void NOINLINE
draw_sextant(struct buf *buf, wchar_t wc) draw_sextant(struct buf *buf, char32_t wc)
{ {
/* /*
* Each byte encodes one sextant: * Each byte encodes one sextant:
@ -2209,7 +2209,7 @@ draw_sextant(struct buf *buf, wchar_t wc)
} }
static void NOINLINE 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 width = buf->width;
const int height = buf->height; const int height = buf->height;
@ -2476,7 +2476,7 @@ draw_wedge_triangle(struct buf *buf, wchar_t wc)
} }
static void NOINLINE 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); draw_wedge_triangle(buf, wc);
@ -2486,7 +2486,7 @@ draw_wedge_triangle_inverted(struct buf *buf, wchar_t wc)
} }
static void NOINLINE 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); draw_wedge_triangle(buf, wc);
@ -2591,7 +2591,7 @@ draw_right_seven_eighths_block(struct buf *buf)
} }
static void static void
draw_glyph(struct buf *buf, wchar_t wc) draw_glyph(struct buf *buf, char32_t wc)
{ {
IGNORE_WARNING("-Wpedantic") IGNORE_WARNING("-Wpedantic")
@ -2829,7 +2829,7 @@ draw_glyph(struct buf *buf, wchar_t wc)
} }
struct fcft_glyph * COLD 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 width = term->cell_width;
int height = term->cell_height; 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)); struct fcft_glyph *glyph = xmalloc(sizeof(*glyph));
*glyph = (struct fcft_glyph){ *glyph = (struct fcft_glyph){
.wc = wc, .cp = wc,
.cols = 1, .cols = 1,
.pix = buf.pix, .pix = buf.pix,
.x = -term->font_x_ofs, .x = -term->font_x_ofs,

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <uchar.h>
#include <fcft/fcft.h> #include <fcft/fcft.h>
struct terminal; 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);

505
char32.c Normal file
View file

@ -0,0 +1,505 @@
#include "char32.h"
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <wctype.h>
#include <wchar.h>
#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(&copy[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(&copy[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);
}

35
char32.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <stdbool.h>
#include <uchar.h>
#include <stddef.h>
#include <stdarg.h>
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);

View file

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <wchar.h> #include <uchar.h>
struct composed { struct composed {
wchar_t *chars; char32_t *chars;
struct composed *left; struct composed *left;
struct composed *right; struct composed *right;
uint32_t key; uint32_t key;

View file

@ -20,6 +20,7 @@
#define LOG_MODULE "config" #define LOG_MODULE "config"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "char32.h"
#include "debug.h" #include "debug.h"
#include "input.h" #include "input.h"
#include "macros.h" #include "macros.h"
@ -484,10 +485,10 @@ done:
} }
static int static int
wccmp(const void *_a, const void *_b) c32cmp_single(const void *_a, const void *_b)
{ {
const wchar_t *a = _a; const char32_t *a = _a;
const wchar_t *b = _b; const char32_t *b = _b;
return *a - *b; return *a - *b;
} }
@ -619,17 +620,16 @@ value_to_str(struct context *ctx, char **res)
} }
static bool NOINLINE 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); char32_t *s = ambstoc32(ctx->value);
if (chars == (size_t)-1) { if (s == NULL) {
LOG_CONTEXTUAL_ERR("not a valid string value"); LOG_CONTEXTUAL_ERR("not a valie string value");
return false; return false;
} }
free(*res); free(*res);
*res = xmalloc((chars + 1) * sizeof(wchar_t)); *res = s;
mbstowcs(*res, ctx->value, chars + 1);
return true; return true;
} }
@ -1171,7 +1171,7 @@ parse_section_url(struct context *ctx)
len--; len--;
prot[len] = '\0'; prot[len] = '\0';
size_t chars = mbstowcs(NULL, prot, 0); size_t chars = mbsntoc32(NULL, prot, len, 0);
if (chars == (size_t)-1) { if (chars == (size_t)-1) {
ctx->value = prot; ctx->value = prot;
LOG_CONTEXTUAL_ERRNO("invalid protocol"); LOG_CONTEXTUAL_ERRNO("invalid protocol");
@ -1184,9 +1184,9 @@ parse_section_url(struct context *ctx)
conf->url.prot_count * sizeof(conf->url.protocols[0])); conf->url.prot_count * sizeof(conf->url.protocols[0]));
size_t idx = conf->url.prot_count - 1; size_t idx = conf->url.prot_count - 1;
conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(wchar_t)); conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(char32_t));
mbstowcs(conf->url.protocols[idx], prot, chars + 1); mbsntoc32(conf->url.protocols[idx], prot, len, chars + 1);
wcscpy(&conf->url.protocols[idx][chars], L"://"); c32cpy(&conf->url.protocols[idx][chars], U"://");
chars += 3; /* Include the "://" */ chars += 3; /* Include the "://" */
if (chars > conf->url.max_prot_len) if (chars > conf->url.max_prot_len)
@ -1203,9 +1203,9 @@ parse_section_url(struct context *ctx)
qsort( qsort(
conf->url.uri_characters, conf->url.uri_characters,
wcslen(conf->url.uri_characters), c32len(conf->url.uri_characters),
sizeof(conf->url.uri_characters[0]), sizeof(conf->url.uri_characters[0]),
&wccmp); &c32cmp_single);
return true; return true;
} }
@ -2748,7 +2748,7 @@ config_load(struct config *conf, const char *conf_path,
.shell = get_shell(), .shell = get_shell(),
.title = xstrdup("foot"), .title = xstrdup("foot"),
.app_id = xstrdup("foot"), .app_id = xstrdup("foot"),
.word_delimiters = xwcsdup(L",│`|:\"'()[]{}<>"), .word_delimiters = xc32dup(U",│`|:\"'()[]{}<>"),
.size = { .size = {
.type = CONF_SIZE_PX, .type = CONF_SIZE_PX,
.width = 700, .width = 700,
@ -2779,8 +2779,8 @@ config_load(struct config *conf, const char *conf_path,
.command_focused = false, .command_focused = false,
}, },
.url = { .url = {
.label_letters = xwcsdup(L"sadfjklewcmpgh"), .label_letters = xc32dup(U"sadfjklewcmpgh"),
.uri_characters = xwcsdup(L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+=\"'()[]"), .uri_characters = xc32dup(U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+=\"'()[]"),
.osc8_underline = OSC8_UNDERLINE_URL_MODE, .osc8_underline = OSC8_UNDERLINE_URL_MODE,
}, },
.can_shape_grapheme = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING, .can_shape_grapheme = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING,
@ -2789,7 +2789,7 @@ config_load(struct config *conf, const char *conf_path,
.indicator = { .indicator = {
.position = SCROLLBACK_INDICATOR_POSITION_RELATIVE, .position = SCROLLBACK_INDICATOR_POSITION_RELATIVE,
.format = SCROLLBACK_INDICATOR_FORMAT_TEXT, .format = SCROLLBACK_INDICATOR_FORMAT_TEXT,
.text = wcsdup(L""), .text = xc32dup(U""),
}, },
.multiplier = 3., .multiplier = 3.,
}, },
@ -2872,16 +2872,16 @@ config_load(struct config *conf, const char *conf_path,
&conf->notify.argv.args); &conf->notify.argv.args);
tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args); tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args);
static const wchar_t *url_protocols[] = { static const char32_t *url_protocols[] = {
L"http://", U"http://",
L"https://", U"https://",
L"ftp://", U"ftp://",
L"ftps://", U"ftps://",
L"file://", U"file://",
L"gemini://", U"gemini://",
L"gopher://", U"gopher://",
L"irc://", U"irc://",
L"ircs://", U"ircs://",
}; };
conf->url.protocols = xmalloc( conf->url.protocols = xmalloc(
ALEN(url_protocols) * sizeof(conf->url.protocols[0])); 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; conf->url.max_prot_len = 0;
for (size_t i = 0; i < ALEN(url_protocols); i++) { 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) if (len > conf->url.max_prot_len)
conf->url.max_prot_len = len; conf->url.max_prot_len = len;
conf->url.protocols[i] = xwcsdup(url_protocols[i]); conf->url.protocols[i] = xc32dup(url_protocols[i]);
} }
qsort( qsort(
conf->url.uri_characters, conf->url.uri_characters,
wcslen(conf->url.uri_characters), c32len(conf->url.uri_characters),
sizeof(conf->url.uri_characters[0]), sizeof(conf->url.uri_characters[0]),
&wccmp); &c32cmp_single);
tll_foreach(*initial_user_notifications, it) { tll_foreach(*initial_user_notifications, it) {
tll_push_back(conf->notifications, it->item); tll_push_back(conf->notifications, it->item);
@ -3101,8 +3101,8 @@ config_clone(const struct config *old)
conf->shell = xstrdup(old->shell); conf->shell = xstrdup(old->shell);
conf->title = xstrdup(old->title); conf->title = xstrdup(old->title);
conf->app_id = xstrdup(old->app_id); conf->app_id = xstrdup(old->app_id);
conf->word_delimiters = xwcsdup(old->word_delimiters); conf->word_delimiters = xc32dup(old->word_delimiters);
conf->scrollback.indicator.text = xwcsdup(old->scrollback.indicator.text); conf->scrollback.indicator.text = xc32dup(old->scrollback.indicator.text);
conf->server_socket_path = xstrdup(old->server_socket_path); conf->server_socket_path = xstrdup(old->server_socket_path);
spawn_template_clone(&conf->bell.command, &old->bell.command); spawn_template_clone(&conf->bell.command, &old->bell.command);
spawn_template_clone(&conf->notify, &old->notify); 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->fonts[i], &old->fonts[i]);
config_font_list_clone(&conf->csd.font, &old->csd.font); config_font_list_clone(&conf->csd.font, &old->csd.font);
conf->url.label_letters = xwcsdup(old->url.label_letters); conf->url.label_letters = xc32dup(old->url.label_letters);
conf->url.uri_characters = xwcsdup(old->url.uri_characters); conf->url.uri_characters = xc32dup(old->url.uri_characters);
spawn_template_clone(&conf->url.launch, &old->url.launch); spawn_template_clone(&conf->url.launch, &old->url.launch);
conf->url.protocols = xmalloc( conf->url.protocols = xmalloc(
old->url.prot_count * sizeof(conf->url.protocols[0])); old->url.prot_count * sizeof(conf->url.protocols[0]));
for (size_t i = 0; i < old->url.prot_count; i++) 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.key, &old->bindings.key);
key_binding_list_clone(&conf->bindings.search, &old->bindings.search); 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) if (f == NULL)
return true; 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; bool is_monospaced = true;
int last_width = -1; int last_width = -1;
for (size_t i = 0; i < sizeof(chars) / sizeof(chars[0]); i++) { 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); f, chars[i], FCFT_SUBPIXEL_NONE);
if (g == NULL) 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; " LOG_WARN("%s: font does not appear to be monospace; "
"check your config, or disable this warning by " "check your config, or disable this warning by "
"setting [tweak].font-monospace-warn=no", "setting [tweak].font-monospace-warn=no",
pattern); f->name);
static const char fmt[] = static const char fmt[] =
"%s: font does not appear to be monospace; " "%s: font does not appear to be monospace; "
"check your config, or disable this warning by " "check your config, or disable this warning by "
"setting \033[1m[tweak].font-monospace-warn=no\033[22m"; "setting \033[1m[tweak].font-monospace-warn=no\033[22m";
user_notification_add_fmt(notifications, USER_NOTIFICATION_WARNING, user_notification_add_fmt(
fmt, pattern); notifications, USER_NOTIFICATION_WARNING, fmt, f->name);
is_monospaced = false; is_monospaced = false;
break; break;

View file

@ -2,6 +2,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <uchar.h>
#include <tllist.h> #include <tllist.h>
@ -80,7 +81,7 @@ struct config {
char *shell; char *shell;
char *title; char *title;
char *app_id; char *app_id;
wchar_t *word_delimiters; char32_t *word_delimiters;
bool login_shell; bool login_shell;
bool locked_title; bool locked_title;
@ -142,21 +143,21 @@ struct config {
SCROLLBACK_INDICATOR_FORMAT_TEXT, SCROLLBACK_INDICATOR_FORMAT_TEXT,
} format; } format;
wchar_t *text; char32_t *text;
} indicator; } indicator;
float multiplier; float multiplier;
} scrollback; } scrollback;
struct { struct {
wchar_t *label_letters; char32_t *label_letters;
struct config_spawn_template launch; struct config_spawn_template launch;
enum { enum {
OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_URL_MODE,
OSC8_UNDERLINE_ALWAYS, OSC8_UNDERLINE_ALWAYS,
} osc8_underline; } osc8_underline;
wchar_t **protocols; char32_t **protocols;
wchar_t *uri_characters; char32_t *uri_characters;
size_t prot_count; size_t prot_count;
size_t max_prot_len; size_t max_prot_len;
} url; } url;

3
csi.c
View file

@ -14,6 +14,7 @@
#define LOG_MODULE "csi" #define LOG_MODULE "csi"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "char32.h"
#include "config.h" #include "config.h"
#include "debug.h" #include "debug.h"
#include "grid.h" #include "grid.h"
@ -742,7 +743,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
int count = vt_param_get(term, 0, 1); int count = vt_param_get(term, 0, 1);
LOG_DBG("REP: '%lc' %d times", (wint_t)term->vt.last_printed, count); 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) { if (width > 0) {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
term_print(term, term->vt.last_printed, width); term_print(term, term->vt.last_printed, width);

View file

@ -1,12 +1,13 @@
#include "extract.h" #include "extract.h"
#include <stdlib.h> #include <string.h>
#define LOG_MODULE "extract" #define LOG_MODULE "extract"
#define LOG_ENABLE_DBG 1 #define LOG_ENABLE_DBG 1
#include "log.h" #include "log.h"
#include "char32.h"
struct extraction_context { struct extraction_context {
wchar_t *buf; char32_t *buf;
size_t size; size_t size;
size_t idx; size_t idx;
size_t tab_spaces_left; 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) { while (ctx->size < ctx->idx + additional_chars) {
size_t new_size = ctx->size == 0 ? 512 : ctx->size * 2; 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) if (new_buf == NULL)
return false; return false;
@ -54,7 +55,7 @@ ensure_size(struct extraction_context *ctx, size_t additional_chars)
} }
bool 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) if (text == NULL)
return false; return false;
@ -72,41 +73,41 @@ extract_finish_wide(struct extraction_context *ctx, wchar_t **text, size_t *len)
goto err; goto err;
for (size_t i = 0; i < ctx->newline_count; i++) 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++) for (size_t i = 0; i < ctx->empty_count; i++)
ctx->buf[ctx->idx++] = L' '; ctx->buf[ctx->idx++] = U' ';
} }
if (ctx->idx == 0) { if (ctx->idx == 0) {
/* Selection of empty cells only */ /* Selection of empty cells only */
if (!ensure_size(ctx, 1)) if (!ensure_size(ctx, 1))
goto err; goto err;
ctx->buf[ctx->idx++] = L'\0'; ctx->buf[ctx->idx++] = U'\0';
} else { } else {
xassert(ctx->idx > 0); xassert(ctx->idx > 0);
xassert(ctx->idx <= ctx->size); xassert(ctx->idx <= ctx->size);
switch (ctx->selection_kind) { switch (ctx->selection_kind) {
default: default:
if (ctx->buf[ctx->idx - 1] == L'\n') if (ctx->buf[ctx->idx - 1] == U'\n')
ctx->buf[ctx->idx - 1] = L'\0'; ctx->buf[ctx->idx - 1] = U'\0';
break; break;
case SELECTION_LINE_WISE: 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)) if (!ensure_size(ctx, 1))
goto err; goto err;
ctx->buf[ctx->idx++] = L'\n'; ctx->buf[ctx->idx++] = U'\n';
} }
break; break;
} }
if (ctx->buf[ctx->idx - 1] != L'\0') { if (ctx->buf[ctx->idx - 1] != U'\0') {
if (!ensure_size(ctx, 1)) if (!ensure_size(ctx, 1))
goto err; 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) if (len != NULL)
*len = 0; *len = 0;
wchar_t *wtext; char32_t *wtext;
if (!extract_finish_wide(ctx, &wtext, NULL)) if (!extract_finish_wide(ctx, &wtext, NULL))
return false; return false;
bool ret = false; bool ret = false;
size_t _len = wcstombs(NULL, wtext, 0); *text = ac32tombs(wtext);
if (_len == (size_t)-1) { if (*text == NULL) {
LOG_ERRNO("failed to convert selection to UTF-8"); LOG_ERR("failed to convert selection to UTF-8");
goto out; goto out;
} }
*text = malloc(_len + 1);
if (unlikely(text == NULL)) {
LOG_ERRNO("malloc() failed");
goto out;
}
wcstombs(*text, wtext, _len + 1);
if (len != NULL) if (len != NULL)
*len = _len; *len = strlen(*text);
ret = true; ret = true;
out: out:
@ -188,7 +180,7 @@ extract_one(const struct terminal *term, const struct row *row,
if (!ensure_size(ctx, ctx->empty_count)) if (!ensure_size(ctx, ctx->empty_count))
goto err; goto err;
for (size_t i = 0; i < ctx->empty_count; i++) for (size_t i = 0; i < ctx->empty_count; i++)
ctx->buf[ctx->idx++] = L' '; ctx->buf[ctx->idx++] = U' ';
} }
ctx->empty_count = 0; ctx->empty_count = 0;
} }
@ -197,13 +189,13 @@ extract_one(const struct terminal *term, const struct row *row,
if (!ensure_size(ctx, 1)) if (!ensure_size(ctx, 1))
goto err; goto err;
ctx->buf[ctx->idx++] = L'\n'; ctx->buf[ctx->idx++] = U'\n';
if (!ctx->strip_trailing_empty) { if (!ctx->strip_trailing_empty) {
if (!ensure_size(ctx, ctx->empty_count)) if (!ensure_size(ctx, ctx->empty_count))
goto err; goto err;
for (size_t i = 0; i < ctx->empty_count; i++) for (size_t i = 0; i < ctx->empty_count; i++)
ctx->buf[ctx->idx++] = L' '; ctx->buf[ctx->idx++] = U' ';
} }
ctx->empty_count = 0; ctx->empty_count = 0;
} }
@ -211,7 +203,7 @@ extract_one(const struct terminal *term, const struct row *row,
ctx->tab_spaces_left = 0; 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--; ctx->tab_spaces_left--;
return true; return true;
} }
@ -230,10 +222,10 @@ extract_one(const struct terminal *term, const struct row *row,
goto err; goto err;
for (size_t i = 0; i < ctx->newline_count; i++) 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++) 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->newline_count = 0;
ctx->empty_count = 0; ctx->empty_count = 0;
@ -255,7 +247,7 @@ extract_one(const struct terminal *term, const struct row *row,
goto err; goto err;
ctx->buf[ctx->idx++] = cell->wc; ctx->buf[ctx->idx++] = cell->wc;
if (cell->wc == L'\t') { if (cell->wc == U'\t') {
int next_tab_stop = term->cols - 1; int next_tab_stop = term->cols - 1;
tll_foreach(term->tab_stops, it) { tll_foreach(term->tab_stops, it) {
if (it->item > col) { if (it->item > col) {

View file

@ -2,7 +2,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <wchar.h> #include <uchar.h>
#include "terminal.h" #include "terminal.h"
@ -18,4 +18,4 @@ bool extract_one(
bool extract_finish( bool extract_finish(
struct extraction_context *context, char **text, size_t *len); struct extraction_context *context, char **text, size_t *len);
bool extract_finish_wide( bool extract_finish_wide(
struct extraction_context *context, wchar_t **text, size_t *len); struct extraction_context *context, char32_t **text, size_t *len);

24
ime.c
View file

@ -9,6 +9,7 @@
#define LOG_MODULE "ime" #define LOG_MODULE "ime"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "char32.h"
#include "render.h" #include "render.h"
#include "search.h" #include "search.h"
#include "terminal.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 */ /* 4. Calculate surrounding text to send - not supported */
/* 5. Insert new pre-edit text */ /* 5. Insert new pre-edit text */
size_t wchars = seat->ime.preedit.pending.text != NULL char32_t *allocated_preedit_text = NULL;
? mbstowcs(NULL, seat->ime.preedit.pending.text, 0)
: 0;
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); ime_reset_pending_preedit(seat);
return; return;
} }
/* First, convert to unicode */ xassert(seat->ime.preedit.pending.text != NULL);
seat->ime.preedit.text = xmalloc((wchars + 1) * sizeof(wchar_t)); xassert(allocated_preedit_text != NULL);
mbstowcs(seat->ime.preedit.text, seat->ime.preedit.pending.text, wchars);
seat->ime.preedit.text[wchars] = L'\0'; seat->ime.preedit.text = allocated_preedit_text;
size_t wchars = c32len(seat->ime.preedit.text);
/* Next, count number of cells needed */ /* Next, count number of cells needed */
size_t cell_count = 0; size_t cell_count = 0;
size_t widths[wchars + 1]; size_t widths[wchars + 1];
for (size_t i = 0; i < wchars; i++) { 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; widths[i] = width;
cell_count += 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 * To do this, we use mblen() to step though the utf-8
* pre-edit string, advancing a unicode character index as * 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. * of the unicode character.
* *
* When we find the matching *byte* index, we at the same * When we find the matching *byte* index, we at the same

15
input.c
View file

@ -1123,16 +1123,15 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term,
} }
else if (term->meta.eight_bit && count == 1) { 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]; char utf8_meta[MB_CUR_MAX];
mbstate_t ps = {0}; size_t chars = c32rtomb(utf8_meta, wc, &(mbstate_t){0});
size_t chars = wcrtomb(wc_as_utf8, wc, &ps);
if (chars != (size_t)-1) if (chars != (size_t)-1)
term_to_slave(term, wc_as_utf8, chars); term_to_slave(term, utf8_meta, chars);
else else
term_to_slave(term, wc_as_utf8, count); term_to_slave(term, utf8, count);
} }
else { else {
@ -1585,8 +1584,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
xkb_compose_state_get_utf8( xkb_compose_state_get_utf8(
seat->kbd.xkb_compose_state, (char *)utf8, count + 1); seat->kbd.xkb_compose_state, (char *)utf8, count + 1);
wchar_t wc; char32_t wc;
if (mbtowc(&wc, (const char *)utf8, count) == count) if (mbrtoc32(&wc, (const char *)utf8, count, &(mbstate_t){0}) == count)
utf32 = wc; utf32 = wc;
} else { } else {
xkb_state_key_get_utf8( xkb_state_key_get_utf8(

38
main.c
View file

@ -34,6 +34,12 @@
#include "xmalloc.h" #include "xmalloc.h"
#include "xsnprintf.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 static bool
fdm_sigint(struct fdm *fdm, int signo, void *data) fdm_sigint(struct fdm *fdm, int signo, void *data)
{ {
@ -90,16 +96,14 @@ print_usage(const char *prog_name)
bool bool
locale_is_utf8(void) locale_is_utf8(void)
{ {
xassert(strlen(u8"ö") == 2); static const char u8[] = u8"ö";
xassert(strlen(u8) == 2);
wchar_t w; char32_t w;
if (mbtowc(&w, u8"ö", 2) != 2) if (mbrtoc32(&w, u8, 2, &(mbstate_t){0}) != 2)
return false; return false;
if (w != U'ö') return w == U'ö';
return false;
return true;
} }
struct shutdown_context { struct shutdown_context {
@ -408,15 +412,6 @@ main(int argc, char *const *argv)
log_init(log_colorize, as_server && log_syslog, log_init(log_colorize, as_server && log_syslog,
as_server ? LOG_FACILITY_DAEMON : LOG_FACILITY_USER, log_level); 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) { if (argc > 0) {
argc -= optind; argc -= optind;
argv += optind; argv += optind;
@ -499,6 +494,14 @@ main(int argc, char *const *argv)
return EXIT_SUCCESS; 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); fcft_set_scaling_filter(conf.tweak.fcft_filter);
if (conf_term != NULL) { if (conf_term != NULL) {
@ -646,6 +649,8 @@ main(int argc, char *const *argv)
if (aborted || tll_length(wayl->terms) == 0) if (aborted || tll_length(wayl->terms) == 0)
ret = EXIT_SUCCESS; ret = EXIT_SUCCESS;
LOG_INFO("%zu", c32len(U"foobar"));
out: out:
free(_cwd); free(_cwd);
server_destroy(server); server_destroy(server);
@ -665,6 +670,7 @@ out:
unlink(pid_file); unlink(pid_file);
LOG_INFO("goodbye"); LOG_INFO("goodbye");
fcft_fini();
log_deinit(); log_deinit();
return ret == EXIT_SUCCESS && !as_server ? shutdown_ctx.exit_code : ret; return ret == EXIT_SUCCESS && !as_server ? shutdown_ctx.exit_code : ret;
} }

View file

@ -90,7 +90,7 @@ if utf8proc.found()
endif endif
tllist = dependency('tllist', version: '>=1.0.4', fallback: 'tllist') 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') wayland_protocols_datadir = wayland_protocols.get_pkgconfig_variable('pkgdatadir')
@ -149,6 +149,7 @@ builtin_terminfo = custom_target(
common = static_library( common = static_library(
'common', 'common',
'log.c', 'log.h', 'log.c', 'log.h',
'char32.c', 'char32.h',
'debug.c', 'debug.h', 'debug.c', 'debug.h',
'xmalloc.c', 'xmalloc.h', 'xmalloc.c', 'xmalloc.h',
'xsnprintf.c', 'xsnprintf.h' 'xsnprintf.c', 'xsnprintf.h'

11
misc.c
View file

@ -1,17 +1,16 @@
#include "misc.h" #include "misc.h"
#include "char32.h"
#include <wctype.h>
bool 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) if (spaces_only)
return iswgraph(wc); return isc32graph(wc);
if (wcschr(delimiters, wc) != NULL) if (c32chr(delimiters, wc) != NULL)
return false; return false;
return iswgraph(wc); return isc32graph(wc);
} }
void void

4
misc.h
View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <wchar.h> #include <uchar.h>
#include <time.h> #include <time.h>
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); void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res);

149
render.c
View file

@ -29,6 +29,7 @@
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "box-drawing.h" #include "box-drawing.h"
#include "char32.h"
#include "config.h" #include "config.h"
#include "grid.h" #include "grid.h"
#include "hsl.h" #include "hsl.h"
@ -566,7 +567,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
const struct fcft_glyph **glyphs = NULL; const struct fcft_glyph **glyphs = NULL;
unsigned glyph_count = 0; unsigned glyph_count = 0;
wchar_t base = cell->wc; char32_t base = cell->wc;
int cell_cols = 1; int cell_cols = 1;
if (base != 0) { if (base != 0) {
@ -637,9 +638,8 @@ render_cell(struct terminal *term, pixman_image_t *pix,
base = composed->chars[0]; base = composed->chars[0];
if (term->conf->can_shape_grapheme && term->conf->tweak.grapheme_shaping) { if (term->conf->can_shape_grapheme && term->conf->tweak.grapheme_shaping) {
grapheme = fcft_grapheme_rasterize( grapheme = fcft_rasterize_grapheme_utf32(
font, composed->count, composed->chars, font, composed->count, composed->chars, term->font_subpixel);
0, NULL, term->font_subpixel);
} }
if (grapheme != NULL) { if (grapheme != NULL) {
@ -654,7 +654,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
if (single == NULL && grapheme == NULL) { if (single == NULL && grapheme == NULL) {
xassert(base != 0); 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) { if (single == NULL) {
glyph_count = 0; glyph_count = 0;
cell_cols = 1; 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) if (has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus)
draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols); 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)) (unlikely(cell->attrs.conceal) && !is_selected))
{ {
goto draw_cursor; goto draw_cursor;
@ -758,7 +758,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
assert(glyph_count == 1); assert(glyph_count == 1);
for (size_t i = 1; i < composed->count; i++) { 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); font, composed->chars[i], term->font_subpixel);
if (g == NULL) if (g == NULL)
@ -1414,7 +1414,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
if (cell->wc >= CELL_SPACER) if (cell->wc >= CELL_SPACER)
continue; continue;
int width = max(1, wcwidth(cell->wc)); int width = max(1, c32width(cell->wc));
if (col_idx + i + width > term->cols) if (col_idx + i + width > term->cols)
break; break;
@ -1626,7 +1626,7 @@ static void
render_osd(struct terminal *term, render_osd(struct terminal *term,
struct wl_surface *surf, struct wl_subsurface *sub_surf, struct wl_surface *surf, struct wl_subsurface *sub_surf,
struct fcft_font *font, struct buffer *buf, 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) unsigned x, unsigned y)
{ {
pixman_region32_t clip; pixman_region32_t clip;
@ -1643,14 +1643,15 @@ render_osd(struct terminal *term,
pixman_color_t fg = color_hex_to_pixman(_fg); pixman_color_t fg = color_hex_to_pixman(_fg);
const int x_ofs = term->font_x_ofs; 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; struct fcft_text_run *text_run = NULL;
const struct fcft_glyph **glyphs = NULL; const struct fcft_glyph **glyphs = NULL;
const struct fcft_glyph *_glyphs[len]; const struct fcft_glyph *_glyphs[len];
size_t glyph_count = 0; size_t glyph_count = 0;
if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { 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) { if (text_run != NULL) {
glyphs = text_run->glyphs; glyphs = text_run->glyphs;
@ -1660,7 +1661,7 @@ render_osd(struct terminal *term,
if (glyphs == NULL) { if (glyphs == NULL) {
for (size_t i = 0; i < len; i++) { 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); font, text[i], term->font_subpixel);
if (glyph == NULL) if (glyph == NULL)
@ -1740,20 +1741,15 @@ render_csd_title(struct terminal *term, const struct csd_data *info,
fg = color_dim(term, fg); fg = color_dim(term, fg);
} }
const wchar_t *title_text = L""; char32_t *_title_text = ambstoc32(term->window_title);
wchar_t *_title_text = NULL; const char32_t *title_text = _title_text != NULL ? _title_text : U"";
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;
}
struct wl_window *win = term->window; struct wl_window *win = term->window;
const int margin = win->csd.font->space_advance.x > 0
? win->csd.font->space_advance.x const struct fcft_glyph *M = fcft_rasterize_char_utf32(
: win->csd.font->max_advance.x; 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, render_osd(term, surf->surf, surf->sub, win->csd.font,
buf, title_text, fg, bg, margin, buf, title_text, fg, bg, margin,
@ -2197,25 +2193,31 @@ render_scrollback_position(struct terminal *term)
? 1.0 ? 1.0
: (double)rebased_view / (populated_rows - term->rows); : (double)rebased_view / (populated_rows - term->rows);
wchar_t _text[64]; char32_t _text[64];
const wchar_t *text = _text; const char32_t *text = _text;
int cell_count = 0; int cell_count = 0;
/* *What* to render */ /* *What* to render */
switch (term->conf->scrollback.indicator.format) { switch (term->conf->scrollback.indicator.format) {
case SCROLLBACK_INDICATOR_FORMAT_PERCENTAGE: case SCROLLBACK_INDICATOR_FORMAT_PERCENTAGE: {
swprintf(_text, sizeof(_text) / sizeof(_text[0]), L"%u%%", (int)(100 * percent)); char percent_str[8];
snprintf(percent_str, sizeof(percent_str), "%u%%", (int)(100 * percent));
mbstoc32(_text, percent_str, ALEN(_text));
cell_count = 3; cell_count = 3;
break; break;
}
case SCROLLBACK_INDICATOR_FORMAT_LINENO: case SCROLLBACK_INDICATOR_FORMAT_LINENO: {
swprintf(_text, sizeof(_text) / sizeof(_text[0]), L"%d", rebased_view + 1); char lineno_str[64];
cell_count = 1 + (int)log10(term->grid->num_rows); 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; break;
}
case SCROLLBACK_INDICATOR_FORMAT_TEXT: case SCROLLBACK_INDICATOR_FORMAT_TEXT:
text = term->conf->scrollback.indicator.text; text = term->conf->scrollback.indicator.text;
cell_count = wcslen(text); cell_count = c32len(text);
break; break;
} }
@ -2281,7 +2283,7 @@ render_scrollback_position(struct terminal *term)
win->scrollback_indicator.sub, win->scrollback_indicator.sub,
term->fonts[0], buf, text, term->fonts[0], buf, text,
fg, 0xffu << 24 | bg, fg, 0xffu << 24 | bg,
width - margin - wcslen(text) * term->cell_width, margin); width - margin - c32len(text) * term->cell_width, margin);
} }
static void static void
@ -2289,12 +2291,15 @@ render_render_timer(struct terminal *term, struct timespec render_time)
{ {
struct wl_window *win = term->window; 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; 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 scale = term->scale;
const int cell_count = wcslen(text); const int cell_count = c32len(text);
const int margin = 3 * scale; const int margin = 3 * scale;
const int width = const int width =
(2 * margin + cell_count * term->cell_width + scale - 1) / scale * scale; (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; size_t text_len = term->search.len;
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL) 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)); char32_t *text = xmalloc((text_len + 1) * sizeof(char32_t));
text[0] = L'\0'; text[0] = U'\0';
/* Copy everything up to the cursor */ /* Copy everything up to the cursor */
wcsncpy(text, term->search.buf, term->search.cursor); c32ncpy(text, term->search.buf, term->search.cursor);
text[term->search.cursor] = L'\0'; text[term->search.cursor] = U'\0';
/* Insert pre-edit text at cursor */ /* Insert pre-edit text at cursor */
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL) 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 */ /* 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); term->search.len - term->search.cursor);
#else #else
const wchar_t *text = term->search.buf; const char32_t *text = term->search.buf;
const size_t text_len = term->search.len; const size_t text_len = term->search.len;
#endif #endif
/* Calculate the width of each character */ /* Calculate the width of each character */
int widths[text_len + 1]; int widths[text_len + 1];
for (size_t i = 0; i < text_len; i++) 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; 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); const size_t wanted_visible_cells = max(20, total_cells);
xassert(term->scale >= 1); xassert(term->scale >= 1);
@ -3070,7 +3075,7 @@ render_search_box(struct terminal *term)
continue; continue;
} }
const struct fcft_glyph *glyph = fcft_glyph_rasterize( const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(
font, text[i], term->font_subpixel); font, text[i], term->font_subpixel);
if (glyph == NULL) { if (glyph == NULL) {
@ -3195,7 +3200,7 @@ render_urls(struct terminal *term)
/* Positioning data + label contents */ /* Positioning data + label contents */
struct { struct {
const struct wl_url *url; const struct wl_url *url;
wchar_t *text; char32_t *text;
int x; int x;
int y; int y;
} info[tll_length(win->urls)]; } info[tll_length(win->urls)];
@ -3208,8 +3213,8 @@ render_urls(struct terminal *term)
tll_foreach(win->urls, it) { tll_foreach(win->urls, it) {
const struct url *url = it->item.url; const struct url *url = it->item.url;
const wchar_t *key = url->key; const char32_t *key = url->key;
const size_t entered_key_len = wcslen(term->url_keys); const size_t entered_key_len = c32len(term->url_keys);
if (key == NULL) { if (key == NULL) {
/* TODO: if we decide to use the .text field, we cannot /* 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) if (_row < view_start || _row > view_end)
hide = true; hide = true;
if (wcslen(key) <= entered_key_len) if (c32len(key) <= entered_key_len)
hide = true; hide = true;
if (wcsncasecmp(term->url_keys, key, entered_key_len) != 0) if (c32ncasecmp(term->url_keys, key, entered_key_len) != 0)
hide = true; hide = true;
if (hide) { if (hide) {
@ -3264,32 +3269,34 @@ render_urls(struct terminal *term)
term->width - term->margins.left - term->margins.right - x; term->width - term->margins.left - term->margins.right - x;
const int max_cols = max_width / term->cell_width; 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) if (url_len == (size_t)-1)
url_len = 0; url_len = 0;
wchar_t url_wchars[url_len + 1]; char32_t url_wchars[url_len + 1];
mbstowcs(url_wchars, url->url, url_len + 1); mbstoc32(url_wchars, url->url, url_len + 1);
/* Format label, not yet subject to any size limitations */ /* Format label, not yet subject to any size limitations */
size_t chars = key_len + (show_url ? (2 + url_len) : 0); size_t chars = key_len + (show_url ? (2 + url_len) : 0);
wchar_t label[chars + 1]; char32_t label[chars + 1];
label[chars] = L'\0'; label[chars] = U'\0';
if (show_url) if (show_url) {
swprintf(label, chars + 1, L"%ls: %ls", key, url_wchars); c32cpy(label, key);
else c32cat(label, U": ");
wcsncpy(label, key, chars); c32cat(label, url_wchars);
} else
c32ncpy(label, key, chars);
/* Upper case the key characters */ /* Upper case the key characters */
for (size_t i = 0; i < wcslen(key); i++) for (size_t i = 0; i < c32len(key); i++)
label[i] = towupper(label[i]); label[i] = toc32upper(label[i]);
/* Blank already entered key characters */ /* Blank already entered key characters */
for (size_t i = 0; i < entered_key_len; i++) for (size_t i = 0; i < entered_key_len; i++)
label[i] = L' '; label[i] = U' ';
/* /*
* Dont extend outside our window * Dont extend outside our window
@ -3303,16 +3310,16 @@ render_urls(struct terminal *term)
int cols = 0; int cols = 0;
for (size_t i = 0; i <= wcslen(label); i++) { for (size_t i = 0; i <= c32len(label); i++) {
int _cols = wcswidth(label, i); int _cols = c32swidth(label, i);
if (_cols == (size_t)-1) if (_cols == (size_t)-1)
continue; continue;
if (_cols >= max_cols) { if (_cols >= max_cols) {
if (i > 0) if (i > 0)
label[i - 1] = L''; label[i - 1] = U'';
label[i] = L'\0'; label[i] = U'\0';
cols = max_cols; cols = max_cols;
break; break;
} }
@ -3328,7 +3335,7 @@ render_urls(struct terminal *term)
(2 * y_margin + term->cell_height + scale - 1) / scale * scale; (2 * y_margin + term->cell_height + scale - 1) / scale * scale;
info[render_count].url = &it->item; 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].x = x;
info[render_count].y = y; 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_surface *surf = info[i].url->surf.surf;
struct wl_subsurface *sub_surf = info[i].url->surf.sub; 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 x = info[i].x;
const int y = info[i].y; const int y = info[i].y;

View file

@ -1,8 +1,6 @@
#include "search.h" #include "search.h"
#include <string.h> #include <string.h>
#include <wchar.h>
#include <wctype.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <xkbcommon/xkbcommon-compose.h> #include <xkbcommon/xkbcommon-compose.h>
@ -10,6 +8,7 @@
#define LOG_MODULE "search" #define LOG_MODULE "search"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "char32.h"
#include "config.h" #include "config.h"
#include "extract.h" #include "extract.h"
#include "grid.h" #include "grid.h"
@ -64,7 +63,7 @@ search_ensure_size(struct terminal *term, size_t wanted_size)
{ {
while (wanted_size >= term->search.sz) { while (wanted_size >= term->search.sz) {
size_t new_sz = term->search.sz == 0 ? 64 : term->search.sz * 2; 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) { if (new_buf == NULL) {
LOG_ERRNO("failed to resize search buffer"); LOG_ERRNO("failed to resize search buffer");
@ -145,7 +144,7 @@ search_begin(struct terminal *term)
term->search.len = 0; term->search.len = 0;
term->search.sz = 64; term->search.sz = 64;
term->search.buf = xmalloc(term->search.sz * sizeof(term->search.buf[0])); 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); term_xcursor_update(term);
render_refresh_search(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); assert(search_ofs < term->search.len);
wchar_t base = cell->wc; char32_t base = cell->wc;
const struct composed *composed = NULL; const struct composed *composed = NULL;
if (base >= CELL_COMB_CHARS_LO && base <= CELL_COMB_CHARS_HI) 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]; 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; return 1;
if (wcsncasecmp(&base, &term->search.buf[search_ofs], 1) != 0) if (c32ncasecmp(&base, &term->search.buf[search_ofs], 1) != 0)
return -1; return -1;
if (composed != NULL) { if (composed != NULL) {
@ -405,11 +404,11 @@ search_find_next(struct terminal *term)
} }
static void 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 */ /* Strip non-printable characters */
for (size_t i = 0, j = 0, orig_count = count; i < orig_count; i++) { 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]; src[j++] = src[i];
else else
count--; count--;
@ -422,32 +421,27 @@ add_wchars(struct terminal *term, wchar_t *src, size_t count)
memmove(&term->search.buf[term->search.cursor + count], memmove(&term->search.buf[term->search.cursor + count],
&term->search.buf[term->search.cursor], &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.len += count;
term->search.cursor += count; term->search.cursor += count;
term->search.buf[term->search.len] = L'\0'; term->search.buf[term->search.len] = U'\0';
} }
void void
search_add_chars(struct terminal *term, const char *src, size_t count) search_add_chars(struct terminal *term, const char *src, size_t count)
{ {
const char *_src = src; size_t chars = mbsntoc32(NULL, src, count, 0);
mbstate_t ps = {0}; if (chars == (size_t)-1) {
size_t wchars = mbsnrtowcs(NULL, &_src, count, 0, &ps); LOG_ERRNO("failed to convert %.*s to Unicode", (int)count, src);
if (wchars == -1) {
LOG_ERRNO("failed to convert %.*s to wchars", (int)count, src);
return; return;
} }
_src = src; char32_t c32s[chars + 1];
ps = (mbstate_t){0}; mbsntoc32(c32s, src, count, chars);
wchar_t wcs[wchars + 1]; add_wchars(term, c32s, chars);
mbsnrtowcs(wcs, &_src, count, wchars, &ps);
add_wchars(term, wcs, wchars);
} }
static void static void
@ -502,7 +496,7 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only)
break; break;
} while (pos.col != new_end.col || pos.row != new_end.row); } while (pos.col != new_end.col || pos.row != new_end.row);
wchar_t *new_text; char32_t *new_text;
size_t new_len; size_t new_len;
if (!extract_finish_wide(ctx, &new_text, &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; return;
for (size_t i = 0; i < new_len; i++) { 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 */ /* extract() adds newlines, which we never match against */
continue; 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++] = new_text[i];
} }
term->search.buf[term->search.len] = L'\0'; term->search.buf[term->search.len] = U'\0';
free(new_text); free(new_text);
if (move_cursor) 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 */ /* First eat non-whitespace. This is the word we're skipping past */
while (cursor < term->search.len) { while (cursor < term->search.len) {
if (iswspace(term->search.buf[cursor++])) if (isc32space(term->search.buf[cursor++]))
break; 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 /* Now skip past whitespace, so that we end up at the beginning of
* the next word */ * the next word */
while (cursor < term->search.len) { while (cursor < term->search.len) {
if (!iswspace(term->search.buf[cursor++])) if (!isc32space(term->search.buf[cursor++]))
break; 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--; cursor--;
return cursor - term->search.cursor; return cursor - term->search.cursor;
@ -572,20 +566,20 @@ distance_prev_word(const struct terminal *term)
/* First, eat whitespace prefix */ /* First, eat whitespace prefix */
while (cursor > 0) { while (cursor > 0) {
if (!iswspace(term->search.buf[--cursor])) if (!isc32space(term->search.buf[--cursor]))
break; 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 */ /* Now eat non-whitespace. This is the word we're skipping past */
while (cursor > 0) { while (cursor > 0) {
if (iswspace(term->search.buf[--cursor])) if (isc32space(term->search.buf[--cursor]))
break; break;
} }
xassert(cursor == 0 || iswspace(term->search.buf[cursor])); xassert(cursor == 0 || isc32space(term->search.buf[cursor]));
if (cursor > 0 && iswspace(term->search.buf[cursor])) if (cursor > 0 && isc32space(term->search.buf[cursor]))
cursor++; cursor++;
return term->search.cursor - cursor; return term->search.cursor - cursor;
@ -603,7 +597,7 @@ from_clipboard_done(void *user)
{ {
struct terminal *term = 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); search_find_next(term);
render_refresh_search(term); render_refresh_search(term);
} }
@ -741,9 +735,9 @@ execute_binding(struct seat *seat, struct terminal *term,
memmove( memmove(
&term->search.buf[term->search.cursor - 1], &term->search.buf[term->search.cursor - 1],
&term->search.buf[term->search.cursor], &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.cursor--;
term->search.buf[--term->search.len] = L'\0'; term->search.buf[--term->search.len] = U'\0';
*update_search_result = *redraw = true; *update_search_result = *redraw = true;
} }
return true; return true;
@ -756,7 +750,7 @@ execute_binding(struct seat *seat, struct terminal *term,
if (diff > 0) { if (diff > 0) {
memmove(&term->search.buf[new_cursor], memmove(&term->search.buf[new_cursor],
&term->search.buf[old_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.len -= diff;
term->search.cursor = new_cursor; term->search.cursor = new_cursor;
@ -770,8 +764,8 @@ execute_binding(struct seat *seat, struct terminal *term,
memmove( memmove(
&term->search.buf[term->search.cursor], &term->search.buf[term->search.cursor],
&term->search.buf[term->search.cursor + 1], &term->search.buf[term->search.cursor + 1],
(term->search.len - term->search.cursor - 1) * sizeof(wchar_t)); (term->search.len - term->search.cursor - 1) * sizeof(char32_t));
term->search.buf[--term->search.len] = L'\0'; term->search.buf[--term->search.len] = U'\0';
*update_search_result = *redraw = true; *update_search_result = *redraw = true;
} }
return true; return true;
@ -783,7 +777,7 @@ execute_binding(struct seat *seat, struct terminal *term,
if (diff > 0) { if (diff > 0) {
memmove(&term->search.buf[cursor], memmove(&term->search.buf[cursor],
&term->search.buf[cursor + diff], &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; term->search.len -= diff;
*update_search_result = *redraw = true; *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); search_add_chars(term, (const char *)buf, count);
update_search: 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) if (update_search_result)
search_find_next(term); search_find_next(term);
if (redraw) if (redraw)

View file

@ -5,7 +5,6 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <wctype.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/timerfd.h> #include <sys/timerfd.h>
@ -15,6 +14,7 @@
#include "log.h" #include "log.h"
#include "async.h" #include "async.h"
#include "char32.h"
#include "commands.h" #include "commands.h"
#include "config.h" #include "config.h"
#include "extract.h" #include "extract.h"
@ -235,7 +235,7 @@ selection_find_word_boundary_left(struct terminal *term, struct coord *pos,
bool spaces_only) bool spaces_only)
{ {
const struct row *r = grid_row_in_view(term->grid, pos->row); 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) { while (c >= CELL_SPACER) {
xassert(pos->col > 0); 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) if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI)
c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; 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 = bool initial_is_delim =
!initial_is_space && !isword(c, spaces_only, term->conf->word_delimiters); !initial_is_space && !isword(c, spaces_only, term->conf->word_delimiters);
bool initial_is_word = 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) if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI)
c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; 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 = bool is_delim =
!is_space && !isword(c, spaces_only, term->conf->word_delimiters); !is_space && !isword(c, spaces_only, term->conf->word_delimiters);
bool is_word = bool is_word =
@ -308,7 +308,7 @@ selection_find_word_boundary_right(struct terminal *term, struct coord *pos,
bool spaces_only) bool spaces_only)
{ {
const struct row *r = grid_row_in_view(term->grid, pos->row); 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) { while (c >= CELL_SPACER) {
xassert(pos->col > 0); 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) if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI)
c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; 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 = bool initial_is_delim =
!initial_is_space && !isword(c, spaces_only, term->conf->word_delimiters); !initial_is_space && !isword(c, spaces_only, term->conf->word_delimiters);
bool initial_is_word = 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) if (c >= CELL_COMB_CHARS_LO && c <= CELL_COMB_CHARS_HI)
c = composed_lookup(term->composed, c - CELL_COMB_CHARS_LO)->chars[0]; 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 = bool is_delim =
!is_space && !isword(c, spaces_only, term->conf->word_delimiters); !is_space && !isword(c, spaces_only, term->conf->word_delimiters);
bool is_word = bool is_word =
@ -668,7 +668,7 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term,
bool keep_going = true; bool keep_going = true;
while (keep_going) { while (keep_going) {
const struct row *row = term->grid->rows[pivot_end->row & (term->grid->num_rows - 1)]; 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; keep_going = wc >= CELL_SPACER;
@ -684,7 +684,7 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term,
bool keep_going = true; bool keep_going = true;
while (keep_going) { while (keep_going) {
const struct row *row = term->grid->rows[pivot_start->row & (term->grid->num_rows - 1)]; 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; ? row->cells[pivot_start->col + 1].wc : 0;
keep_going = wc >= CELL_SPACER; keep_going = wc >= CELL_SPACER;

View file

@ -671,16 +671,12 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
const struct config *conf = term->conf; const struct config *conf = term->conf;
const struct fcft_glyph *M = fcft_glyph_rasterize( const struct fcft_glyph *M = fcft_rasterize_char_utf32(
term->fonts[0], L'M', term->font_subpixel); fonts[0], U'M', term->font_subpixel);
int advance = M != NULL ? M->advance.x : term->fonts[0]->max_advance.x;
term->cell_width = term->cell_width = advance +
(M != NULL term_pt_or_px_as_pixels(term, &conf->letter_spacing);
? 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_height = term->font_line_height.px >= 0 term->cell_height = term->font_line_height.px >= 0
? term_pt_or_px_as_pixels(term, &term->font_line_height) ? 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 void
term_print(struct terminal *term, wchar_t wc, int width) term_print(struct terminal *term, char32_t wc, int width)
{ {
xassert(width > 0); xassert(width > 0);
@ -3218,11 +3214,11 @@ term_print(struct terminal *term, wchar_t wc, int width)
wc >= 0x60 && wc <= 0x7e) wc >= 0x60 && wc <= 0x7e)
{ {
/* 0x60 - 0x7e */ /* 0x60 - 0x7e */
static const wchar_t vt100_0[] = { static const char32_t vt100_0[] = {
L'', L'', L'', L'', L'', L'', L'°', L'±', /* ` - g */ U'', U'', U'', U'', U'', U'', U'°', U'±', /* ` - g */
L'', L'', L'', L'', L'', L'', L'', L'', /* h - o */ U'', U'', U'', U'', U'', U'', U'', U'', /* h - o */
L'', L'', L'', L'', L'', L'', L'', L'', /* p - w */ U'', U'', U'', U'', U'', U'', U'', U'', /* p - w */
L'', L'', L'', L'π', L'', L'£', L'·', /* x - ~ */ U'', U'', U'', U'π', U'', U'£', U'·', /* x - ~ */
}; };
xassert(width == 1); xassert(width == 1);
@ -3291,13 +3287,13 @@ term_print(struct terminal *term, wchar_t wc, int width)
} }
static void 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); term_print(term, wc, 1);
} }
static void 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; struct grid *grid = term->grid;
@ -3333,7 +3329,7 @@ ascii_printer_fast(struct terminal *term, wchar_t wc)
} }
static void 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); ascii_printer_generic(term, wc);
term->charsets.selected = term->charsets.saved; term->charsets.selected = term->charsets.saved;
@ -3343,7 +3339,7 @@ ascii_printer_single_shift(struct terminal *term, wchar_t wc)
void void
term_update_ascii_printer(struct terminal *term) 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 || unlikely(tll_length(term->grid->sixel_images) > 0 ||
term->vt.osc8.uri != NULL || term->vt.osc8.uri != NULL ||
term->charsets.set[term->charsets.selected] == CHARSET_GRAPHIC || term->charsets.set[term->charsets.selected] == CHARSET_GRAPHIC ||

View file

@ -3,7 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <wchar.h>
#include <threads.h> #include <threads.h>
#include <semaphore.h> #include <semaphore.h>
@ -65,7 +64,7 @@ static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
#define CELL_SPACER (CELL_COMB_CHARS_HI + 1) #define CELL_SPACER (CELL_COMB_CHARS_HI + 1)
struct cell { struct cell {
wchar_t wc; char32_t wc;
struct attributes attrs; struct attributes attrs;
}; };
static_assert(sizeof(struct cell) == 12, "bad size"); static_assert(sizeof(struct cell) == 12, "bad size");
@ -182,11 +181,11 @@ struct vt_param {
struct vt { struct vt {
int state; /* enum state */ int state; /* enum state */
wchar_t last_printed; char32_t last_printed;
#if defined(FOOT_GRAPHEME_CLUSTERING) #if defined(FOOT_GRAPHEME_CLUSTERING)
utf8proc_int32_t grapheme_state; utf8proc_int32_t grapheme_state;
#endif #endif
wchar_t utf8; char32_t utf8;
struct { struct {
struct vt_param v[16]; struct vt_param v[16];
uint8_t idx; uint8_t idx;
@ -290,7 +289,7 @@ enum url_action { URL_ACTION_COPY, URL_ACTION_LAUNCH };
struct url { struct url {
uint64_t id; uint64_t id;
char *url; char *url;
wchar_t *key; char32_t *key;
struct coord start; struct coord start;
struct coord end; struct coord end;
enum url_action action; enum url_action action;
@ -311,7 +310,7 @@ struct terminal {
struct reaper *reaper; struct reaper *reaper;
const struct config *conf; 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; pid_t slave;
int ptmx; int ptmx;
@ -504,7 +503,7 @@ struct terminal {
bool is_searching; bool is_searching;
struct { struct {
wchar_t *buf; char32_t *buf;
size_t len; size_t len;
size_t sz; size_t sz;
size_t cursor; size_t cursor;
@ -516,7 +515,7 @@ struct terminal {
size_t match_len; size_t match_len;
struct { struct {
wchar_t *buf; char32_t *buf;
size_t len; size_t len;
} last; } last;
} search; } search;
@ -639,7 +638,7 @@ struct terminal {
/* TODO: wrap in a struct */ /* TODO: wrap in a struct */
url_list_t urls; url_list_t urls;
wchar_t url_keys[5]; char32_t url_keys[5];
bool urls_show_uri_on_jump_label; bool urls_show_uri_on_jump_label;
struct grid *url_grid_snapshot; 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_down(struct terminal *term, int count);
void term_cursor_blink_update(struct terminal *term); 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(struct terminal *term, int rows);
void term_scroll_reverse(struct terminal *term, int rows); void term_scroll_reverse(struct terminal *term, int rows);

View file

@ -70,17 +70,17 @@ test_string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
} }
static void static void
test_wstring(struct context *ctx, bool (*parse_fun)(struct context *ctx), test_c32string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, wchar_t *const *ptr) const char *key, char32_t *const *ptr)
{ {
ctx->key = key; ctx->key = key;
static const struct { static const struct {
const char *option_string; const char *option_string;
const wchar_t *value; const char32_t *value;
bool invalid; bool invalid;
} input[] = { } input[] = {
{"a string", L"a string"}, {"a string", U"a string"},
}; };
for (size_t i = 0; i < ALEN(input); i++) { 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", BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value); 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)", BUG("[%s].%s=%s: set value (%ls) not the expected one (%ls)",
ctx->section, ctx->key, ctx->value, 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, "term", &conf.term);
test_string(&ctx, &parse_section_main, "app-id", &conf.app_id); 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, "login-shell", &conf.login_shell);
test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs); 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"}, (const char *[]){"url-mode", "always"},
(int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS}, (int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS},
(int *)&conf.url.osc8_underline); (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: protocols (list of wchars) */
/* TODO: uri-characters (wchar string, but sorted) */ /* TODO: uri-characters (wchar string, but sorted) */

View file

@ -11,6 +11,7 @@
#define LOG_MODULE "url-mode" #define LOG_MODULE "url-mode"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "char32.h"
#include "grid.h" #include "grid.h"
#include "render.h" #include "render.h"
#include "selection.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 (sym == XKB_KEY_BackSpace) {
if (seq_len > 0) { if (seq_len > 0) {
term->url_keys[seq_len - 1] = L'\0'; term->url_keys[seq_len - 1] = U'\0';
render_refresh_urls(term); render_refresh_urls(term);
} }
@ -164,7 +165,7 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key,
if (mods & ~consumed) if (mods & ~consumed)
return; 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 * 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; continue;
const struct url *url = &it->item; 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 && if (key_len >= seq_len + 1 &&
wcsncasecmp(url->key, term->url_keys, seq_len) == 0 && c32ncasecmp(url->key, term->url_keys, seq_len) == 0 &&
towlower(url->key[seq_len]) == towlower(wc)) toc32lower(url->key[seq_len]) == toc32lower(wc))
{ {
is_valid = true; is_valid = true;
if (key_len == seq_len + 1) { if (key_len == seq_len + 1) {
@ -207,10 +208,10 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key,
} }
static int static int
wccmp(const void *_a, const void *_b) c32cmp_single(const void *_a, const void *_b)
{ {
const wchar_t *a = _a; const char32_t *a = _a;
const wchar_t *b = _b; const char32_t *b = _b;
return *a - *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 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) if (uri_characters == NULL)
return; return;
const size_t uri_characters_count = wcslen(uri_characters); const size_t uri_characters_count = c32len(uri_characters);
if (uri_characters_count == 0) if (uri_characters_count == 0)
return; return;
size_t max_prot_len = conf->url.max_prot_len; 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]; struct coord proto_start[max_prot_len];
size_t proto_char_count = 0; size_t proto_char_count = 0;
@ -239,7 +240,7 @@ auto_detected(const struct terminal *term, enum url_action action,
} state = STATE_PROTOCOL; } state = STATE_PROTOCOL;
struct coord start = {-1, -1}; 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; size_t len = 0;
ssize_t parenthesis = 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++) { for (int c = 0; c < term->cols; c++) {
const struct cell *cell = &row->cells[c]; const struct cell *cell = &row->cells[c];
wchar_t wc = cell->wc; char32_t wc = cell->wc;
switch (state) { switch (state) {
case STATE_PROTOCOL: case STATE_PROTOCOL:
@ -268,18 +269,18 @@ auto_detected(const struct terminal *term, enum url_action action,
proto_char_count++; proto_char_count++;
for (size_t i = 0; i < conf->url.prot_count; i++) { 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) if (proto_char_count < prot_len)
continue; 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; state = STATE_URL;
start = proto_start[max_prot_len - prot_len]; start = proto_start[max_prot_len - prot_len];
wcsncpy(url, proto, prot_len); c32ncpy(url, proto, prot_len);
len = prot_len; len = prot_len;
parenthesis = brackets = ltgts = 0; parenthesis = brackets = ltgts = 0;
@ -289,12 +290,12 @@ auto_detected(const struct terminal *term, enum url_action action,
break; break;
case STATE_URL: { case STATE_URL: {
const wchar_t *match = bsearch( const char32_t *match = bsearch(
&wc, &wc,
uri_characters, uri_characters,
uri_characters_count, uri_characters_count,
sizeof(uri_characters[0]), sizeof(uri_characters[0]),
&wccmp); &c32cmp_single);
bool emit_url = false; bool emit_url = false;
@ -313,36 +314,36 @@ auto_detected(const struct terminal *term, enum url_action action,
url[len++] = wc; url[len++] = wc;
break; break;
case L'(': case U'(':
parenthesis++; parenthesis++;
url[len++] = wc; url[len++] = wc;
break; break;
case L'[': case U'[':
brackets++; brackets++;
url[len++] = wc; url[len++] = wc;
break; break;
case L'<': case U'<':
ltgts++; ltgts++;
url[len++] = wc; url[len++] = wc;
break; break;
case L')': case U')':
if (--parenthesis < 0) if (--parenthesis < 0)
emit_url = true; emit_url = true;
else else
url[len++] = wc; url[len++] = wc;
break; break;
case L']': case U']':
if (--brackets < 0) if (--brackets < 0)
emit_url = true; emit_url = true;
else else
url[len++] = wc; url[len++] = wc;
break; break;
case L'>': case U'>':
if (--ltgts < 0) if (--ltgts < 0)
emit_url = true; emit_url = true;
else else
@ -374,8 +375,8 @@ auto_detected(const struct terminal *term, enum url_action action,
bool done = false; bool done = false;
do { do {
switch (url[len - 1]) { switch (url[len - 1]) {
case L'.': case L',': case L':': case L';': case L'?': case U'.': case U',': case U':': case U';': case U'?':
case L'!': case L'"': case L'\'': case L'%': case U'!': case U'"': case U'\'': case U'%':
len--; len--;
end.col--; end.col--;
if (end.col < 0) { if (end.col < 0) {
@ -390,16 +391,13 @@ auto_detected(const struct terminal *term, enum url_action action,
} }
} while (!done); } while (!done);
url[len] = L'\0'; url[len] = U'\0';
start.row += term->grid->view; start.row += term->grid->view;
end.row += term->grid->view; end.row += term->grid->view;
size_t chars = wcstombs(NULL, url, 0); char *url_utf8 = ac32tombs(url);
if (chars != (size_t)-1) { if (url_utf8 != NULL) {
char *url_utf8 = xmalloc((chars + 1) * sizeof(wchar_t));
wcstombs(url_utf8, url, chars + 1);
tll_push_back( tll_push_back(
*urls, *urls,
((struct url){ ((struct url){
@ -527,40 +525,40 @@ urls_collect(const struct terminal *term, enum url_action action, url_list_t *ur
} }
static int 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 char32_t *a = *(const char32_t **)_a;
const wchar_t *b = *(const wchar_t **)_b; const char32_t *b = *(const char32_t **)_b;
return wcscmp(a, b); return c32cmp(a, b);
} }
static void static void
generate_key_combos(const struct config *conf, 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 char32_t *alphabet = conf->url.label_letters;
const size_t alphabet_len = wcslen(alphabet); const size_t alphabet_len = c32len(alphabet);
size_t hints_count = 1; 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; size_t offset = 0;
do { do {
const wchar_t *prefix = hints[offset++]; const char32_t *prefix = hints[offset++];
const size_t prefix_len = wcslen(prefix); const size_t prefix_len = c32len(prefix);
hints = xrealloc(hints, (hints_count + alphabet_len) * sizeof(hints[0])); 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++) { 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; hints[hints_count + i] = hint;
/* Will be reversed later */ /* Will be reversed later */
hint[0] = *wc; hint[0] = *wc;
wcscpy(&hint[1], prefix); c32cpy(&hint[1], prefix);
} }
hints_count += alphabet_len; hints_count += alphabet_len;
} while (hints_count - offset < count); } while (hints_count - offset < count);
@ -578,13 +576,13 @@ generate_key_combos(const struct config *conf,
/* Sorting is a kind of shuffle, since were sorting on the /* Sorting is a kind of shuffle, since were sorting on the
* *reversed* strings */ * *reversed* strings */
qsort(combos, count, sizeof(wchar_t *), &wcscmp_qsort_wrapper); qsort(combos, count, sizeof(char32_t *), &c32cmp_qsort_wrapper);
/* Reverse all strings */ /* Reverse all strings */
for (size_t i = 0; i < count; i++) { 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++) { 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][j] = combos[i][len - j - 1];
combos[i][len - j - 1] = tmp; combos[i][len - j - 1] = tmp;
} }
@ -599,7 +597,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
return; return;
uint64_t seen_ids[count]; uint64_t seen_ids[count];
wchar_t *combos[count]; char32_t *combos[count];
generate_key_combos(conf, count, combos); generate_key_combos(conf, count, combos);
size_t combo_idx = 0; size_t combo_idx = 0;
@ -629,7 +627,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
break; break;
if (strcmp(it->item.url, it2->item.url) == 0) { 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; url_already_seen = true;
break; break;
} }
@ -648,9 +646,11 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
if (it->item.key == NULL) if (it->item.key == NULL)
continue; continue;
char key[32]; char *key = ac32tombs(it->item.key);
wcstombs(key, it->item.key, sizeof(key) - 1); xassert(key != NULL);
LOG_DBG("URL: %s (%s)", it->item.url, key); LOG_DBG("URL: %s (%s)", it->item.url, key);
free(key);
} }
#endif #endif
} }

27
vt.c
View file

@ -11,6 +11,7 @@
#define LOG_MODULE "vt" #define LOG_MODULE "vt"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "char32.h"
#include "config.h" #include "config.h"
#include "csi.h" #include "csi.h"
#include "dcs.h" #include "dcs.h"
@ -182,14 +183,14 @@ action_execute(struct terminal *term, uint8_t c)
struct row *row = term->grid->cur_row; struct row *row = term->grid->cur_row;
bool emit_tab_char = (row->cells[start_col].wc == 0 || 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 */ /* Check if all cells from here until the next tab stop are empty */
for (const struct cell *cell = &row->cells[start_col + 1]; for (const struct cell *cell = &row->cells[start_col + 1];
cell < &row->cells[new_col]; cell < &row->cells[new_col];
cell++) cell++)
{ {
if (!(cell->wc == 0 || cell->wc == L' ')) { if (!(cell->wc == 0 || cell->wc == U' ')) {
emit_tab_char = false; emit_tab_char = false;
break; break;
} }
@ -202,14 +203,14 @@ action_execute(struct terminal *term, uint8_t c)
if (emit_tab_char) { if (emit_tab_char) {
row->dirty = true; row->dirty = true;
row->cells[start_col].wc = '\t'; row->cells[start_col].wc = U'\t';
row->cells[start_col].attrs.clean = 0; row->cells[start_col].attrs.clean = 0;
for (struct cell *cell = &row->cells[start_col + 1]; for (struct cell *cell = &row->cells[start_col + 1];
cell < &row->cells[new_col]; cell < &row->cells[new_col];
cell++) cell++)
{ {
cell->wc = L' '; cell->wc = U' ';
cell->attrs.clean = 0; 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++) { for (int r = 0; r < term->rows; r++) {
struct row *row = grid_row(term->grid, r); struct row *row = grid_row(term->grid, r);
for (int c = 0; c < term->cols; c++) { 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->cells[c].attrs = (struct attributes){0};
} }
row->dirty = true; row->dirty = true;
@ -617,9 +618,9 @@ chain_key(uint32_t old_key, uint32_t new_wc)
} }
static void 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; const bool grapheme_clustering = term->conf->tweak.grapheme_shaping;
#if !defined(FOOT_GRAPHEME_CLUSTERING) #if !defined(FOOT_GRAPHEME_CLUSTERING)
@ -640,8 +641,8 @@ action_utf8_print(struct terminal *term, wchar_t wc)
col--; col--;
xassert(col >= 0 && col < term->cols); xassert(col >= 0 && col < term->cols);
wchar_t base = row->cells[col].wc; char32_t base = row->cells[col].wc;
wchar_t UNUSED last = base; char32_t UNUSED last = base;
/* Is base cell already a cluster? */ /* Is base cell already a cluster? */
const struct composed *composed = const struct composed *composed =
@ -672,7 +673,7 @@ action_utf8_print(struct terminal *term, wchar_t wc)
} }
#endif #endif
int base_width = wcwidth(base); int base_width = c32width(base);
if (base_width > 0) { if (base_width > 0) {
term->grid->cursor.point.col = col; term->grid->cursor.point.col = col;
term->grid->cursor.lcf = false; term->grid->cursor.lcf = false;
@ -682,11 +683,11 @@ action_utf8_print(struct terminal *term, wchar_t wc)
bool comb_from_primary; bool comb_from_primary;
bool pre_from_primary; bool pre_from_primary;
wchar_t precomposed = fcft_precompose( char32_t precomposed = fcft_precompose(
term->fonts[0], base, wc, &base_from_primary, term->fonts[0], base, wc, &base_from_primary,
&comb_from_primary, &pre_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: * Only use the pre-composed character if:
@ -698,7 +699,7 @@ action_utf8_print(struct terminal *term, wchar_t wc)
* font * font
*/ */
if (precomposed != (wchar_t)-1 && if (precomposed != (char32_t)-1 &&
precomposed_width == base_width && precomposed_width == base_width &&
(pre_from_primary || (pre_from_primary ||
!base_from_primary || !base_from_primary ||

View file

@ -3,6 +3,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <time.h> #include <time.h>
#include <uchar.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
@ -278,7 +279,7 @@ struct seat {
int32_t cursor_end; int32_t cursor_end;
} pending; } pending;
wchar_t *text; char32_t *text;
struct cell *cells; struct cell *cells;
int count; int count;
struct { struct {

View file

@ -3,7 +3,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <wchar.h>
#include "xmalloc.h" #include "xmalloc.h"
#include "debug.h" #include "debug.h"
@ -45,18 +44,18 @@ xstrdup(const char *str)
return check_alloc(strdup(str)); return check_alloc(strdup(str));
} }
wchar_t *
xwcsdup(const wchar_t *str)
{
return check_alloc(wcsdup(str));
}
char * char *
xstrndup(const char *str, size_t n) xstrndup(const char *str, size_t n)
{ {
return check_alloc(strndup(str, n)); return check_alloc(strndup(str, n));
} }
char32_t *
xc32dup(const char32_t *str)
{
return check_alloc(c32dup(str));
}
static VPRINTF(2) int static VPRINTF(2) int
xvasprintf_(char **strp, const char *format, va_list ap) xvasprintf_(char **strp, const char *format, va_list ap)
{ {

View file

@ -3,6 +3,9 @@
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#include <wchar.h> #include <wchar.h>
#include <uchar.h>
#include "char32.h"
#include "macros.h" #include "macros.h"
void *xmalloc(size_t size) XMALLOC; 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 *xstrndup(const char *str, size_t n) XSTRDUP;
char *xasprintf(const char *format, ...) PRINTF(1) XMALLOC; char *xasprintf(const char *format, ...) PRINTF(1) XMALLOC;
char *xvasprintf(const char *format, va_list va) VPRINTF(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;