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

View file

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

View file

@ -17,6 +17,10 @@ debug-x64:
image: alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/debug
- cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../
@ -31,6 +35,10 @@ debug-x64-no-grapheme-clustering:
stage: build
script:
- apk del harfbuzz harfbuzz-dev utf8proc utf8proc-dev
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/debug
- cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=disabled -Dfcft:grapheme-shaping=disabled -Dfcft:run-shaping=disabled -Dfcft:test-text-shaping=false ../../
@ -46,6 +54,10 @@ release-x64:
image: alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/release
- cd bld/release
- meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../
@ -61,6 +73,10 @@ debug-x86:
image: i386/alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/debug
- cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../
@ -76,6 +92,10 @@ release-x86:
image: i386/alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd fcft && git checkout 3.0-dev && cd ..
- cd ..
- mkdir -p bld/release
- cd bld/release
- meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#pragma once
#include <uchar.h>
#include <fcft/fcft.h>
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
#include <stdint.h>
#include <wchar.h>
#include <uchar.h>
struct composed {
wchar_t *chars;
char32_t *chars;
struct composed *left;
struct composed *right;
uint32_t key;

View file

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

View file

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

3
csi.c
View file

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

View file

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

View file

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

24
ime.c
View file

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

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

38
main.c
View file

@ -34,6 +34,12 @@
#include "xmalloc.h"
#include "xsnprintf.h"
#include "char32.h"
#if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__
#error "char32_t does not use UTF-32"
#endif
static bool
fdm_sigint(struct fdm *fdm, int signo, void *data)
{
@ -90,16 +96,14 @@ print_usage(const char *prog_name)
bool
locale_is_utf8(void)
{
xassert(strlen(u8"ö") == 2);
static const char u8[] = u8"ö";
xassert(strlen(u8) == 2);
wchar_t w;
if (mbtowc(&w, u8"ö", 2) != 2)
char32_t w;
if (mbrtoc32(&w, u8, 2, &(mbstate_t){0}) != 2)
return false;
if (w != U'ö')
return false;
return true;
return w == U'ö';
}
struct shutdown_context {
@ -408,15 +412,6 @@ main(int argc, char *const *argv)
log_init(log_colorize, as_server && log_syslog,
as_server ? LOG_FACILITY_DAEMON : LOG_FACILITY_USER, log_level);
_Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR,
"fcft log level enum offset");
_Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS,
"fcft colorize enum mismatch");
fcft_log_init(
(enum fcft_log_colorize)log_colorize,
as_server && log_syslog,
(enum fcft_log_class)log_level);
if (argc > 0) {
argc -= optind;
argv += optind;
@ -499,6 +494,14 @@ main(int argc, char *const *argv)
return EXIT_SUCCESS;
}
_Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR,
"fcft log level enum offset");
_Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS,
"fcft colorize enum mismatch");
fcft_init(
(enum fcft_log_colorize)log_colorize,
as_server && log_syslog,
(enum fcft_log_class)log_level);
fcft_set_scaling_filter(conf.tweak.fcft_filter);
if (conf_term != NULL) {
@ -646,6 +649,8 @@ main(int argc, char *const *argv)
if (aborted || tll_length(wayl->terms) == 0)
ret = EXIT_SUCCESS;
LOG_INFO("%zu", c32len(U"foobar"));
out:
free(_cwd);
server_destroy(server);
@ -665,6 +670,7 @@ out:
unlink(pid_file);
LOG_INFO("goodbye");
fcft_fini();
log_deinit();
return ret == EXIT_SUCCESS && !as_server ? shutdown_ctx.exit_code : ret;
}

View file

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

11
misc.c
View file

@ -1,17 +1,16 @@
#include "misc.h"
#include <wctype.h>
#include "char32.h"
bool
isword(wchar_t wc, bool spaces_only, const wchar_t *delimiters)
isword(char32_t wc, bool spaces_only, const char32_t *delimiters)
{
if (spaces_only)
return iswgraph(wc);
return isc32graph(wc);
if (wcschr(delimiters, wc) != NULL)
if (c32chr(delimiters, wc) != NULL)
return false;
return iswgraph(wc);
return isc32graph(wc);
}
void

4
misc.h
View file

@ -1,9 +1,9 @@
#pragma once
#include <stdbool.h>
#include <wchar.h>
#include <uchar.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);

149
render.c
View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

27
vt.c
View file

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

View file

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

View file

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

View file

@ -3,6 +3,9 @@
#include <stdarg.h>
#include <stddef.h>
#include <wchar.h>
#include <uchar.h>
#include "char32.h"
#include "macros.h"
void *xmalloc(size_t size) XMALLOC;
@ -12,4 +15,4 @@ char *xstrdup(const char *str) XSTRDUP;
char *xstrndup(const char *str, size_t n) XSTRDUP;
char *xasprintf(const char *format, ...) PRINTF(1) XMALLOC;
char *xvasprintf(const char *format, va_list va) VPRINTF(1) XMALLOC;
wchar_t *xwcsdup(const wchar_t *str) XSTRDUP;
char32_t *xc32dup(const char32_t *str) XSTRDUP;