mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
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:
parent
2be8c39044
commit
e0227266ca
32 changed files with 962 additions and 385 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ../../
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -334,6 +334,7 @@
|
|||
* `dpi-aware=auto`: fonts are now scaled using the monitor’s DPI only
|
||||
when **all** monitors have a scaling factor of one
|
||||
(https://codeberg.org/dnkl/foot/issues/714).
|
||||
* fcft >= 3.0.0 in now required.
|
||||
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
505
char32.c
Normal 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(©[6], zeroes, sizeof(zeroes)) == 0);
|
||||
}
|
||||
|
||||
char32_t *
|
||||
c32cpy(char32_t *dst, const char32_t *src)
|
||||
{
|
||||
return (char32_t *)wcscpy((wchar_t *)dst, (const wchar_t *)src);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
char32_t copy[16];
|
||||
memset(copy, 0x55, sizeof(copy));
|
||||
|
||||
char32_t *ret = c32cpy(copy, U"foobar");
|
||||
|
||||
xassert(ret == copy);
|
||||
xassert(copy[0] == U'f');
|
||||
xassert(copy[1] == U'o');
|
||||
xassert(copy[2] == U'o');
|
||||
xassert(copy[3] == U'b');
|
||||
xassert(copy[4] == U'a');
|
||||
xassert(copy[5] == U'r');
|
||||
xassert(copy[6] == U'\0');
|
||||
|
||||
unsigned char fives[(16 - 6 - 1) * sizeof(copy[0])];
|
||||
memset(fives, 0x55, sizeof(fives));
|
||||
xassert(memcmp(©[7], fives, sizeof(fives)) == 0);
|
||||
}
|
||||
|
||||
char32_t *
|
||||
c32chr(const char32_t *s, char32_t c)
|
||||
{
|
||||
return (char32_t *)wcschr((const wchar_t *)s, c);
|
||||
}
|
||||
|
||||
int
|
||||
c32casecmp(const char32_t *s1, const char32_t *s2)
|
||||
{
|
||||
return wcscasecmp((const wchar_t *)s1, (const wchar_t *)s2);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
xassert(c32casecmp(U"foobar", U"FOOBAR") == 0);
|
||||
xassert(c32casecmp(U"foo", U"FOOO") < 0);
|
||||
xassert(c32casecmp(U"FOOO", U"foo") > 0);
|
||||
xassert(c32casecmp(U"a", U"B") < 0);
|
||||
xassert(c32casecmp(U"B", U"a") > 0);
|
||||
}
|
||||
|
||||
int
|
||||
c32ncasecmp(const char32_t *s1, const char32_t *s2, size_t n)
|
||||
{
|
||||
return wcsncasecmp((const wchar_t *)s1, (const wchar_t *)s2, n);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
xassert(c32ncasecmp(U"foo", U"FOObar", 3) == 0);
|
||||
xassert(c32ncasecmp(U"foo", U"FOOO", 4) < 0);
|
||||
xassert(c32ncasecmp(U"FOOO", U"foo", 4) > 0);
|
||||
xassert(c32ncasecmp(U"a", U"BB", 1) < 0);
|
||||
xassert(c32ncasecmp(U"BB", U"a", 1) > 0);
|
||||
}
|
||||
|
||||
char32_t *
|
||||
c32ncat(char32_t *dst, const char32_t *src, size_t n)
|
||||
{
|
||||
return (char32_t *)wcsncat((wchar_t *)dst, (const wchar_t *)src, n);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
char32_t dst[32] = U"foobar";
|
||||
char32_t *ret = c32ncat(dst, U"12345678XXXXXXXXX", 8);
|
||||
|
||||
xassert(ret == dst);
|
||||
xassert(c32cmp(dst, U"foobar12345678") == 0);
|
||||
}
|
||||
|
||||
char32_t *
|
||||
c32cat(char32_t *dst, const char32_t *src)
|
||||
{
|
||||
return (char32_t *)wcscat((wchar_t *)dst, (const wchar_t *)src);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
char32_t dst[32] = U"foobar";
|
||||
char32_t *ret = c32cat(dst, U"12345678");
|
||||
|
||||
xassert(ret == dst);
|
||||
xassert(c32cmp(dst, U"foobar12345678") == 0);
|
||||
}
|
||||
|
||||
char32_t *
|
||||
c32dup(const char32_t *s)
|
||||
{
|
||||
return (char32_t *)wcsdup((const wchar_t *)s);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
char32_t *c = c32dup(U"foobar");
|
||||
xassert(c32cmp(c, U"foobar") == 0);
|
||||
free(c);
|
||||
|
||||
c = c32dup(U"");
|
||||
xassert(c32cmp(c, U"") == 0);
|
||||
free(c);
|
||||
}
|
||||
|
||||
size_t
|
||||
mbsntoc32(char32_t *dst, const char *src, size_t nms, size_t len)
|
||||
{
|
||||
mbstate_t ps = {0};
|
||||
|
||||
char32_t *out = dst;
|
||||
const char *in = src;
|
||||
|
||||
size_t consumed = 0;
|
||||
size_t chars = 0;
|
||||
size_t rc;
|
||||
|
||||
while ((out == NULL || chars < len) &&
|
||||
consumed < nms &&
|
||||
(rc = mbrtoc32(out, in, nms - consumed, &ps)) != 0)
|
||||
{
|
||||
switch (rc) {
|
||||
case 0:
|
||||
goto done;
|
||||
|
||||
case (size_t)-1:
|
||||
case (size_t)-2:
|
||||
case (size_t)-3:
|
||||
goto err;
|
||||
}
|
||||
|
||||
in += rc;
|
||||
consumed += rc;
|
||||
chars++;
|
||||
|
||||
if (out != NULL)
|
||||
out++;
|
||||
}
|
||||
|
||||
done:
|
||||
return chars;
|
||||
|
||||
err:
|
||||
return (char32_t)-1;
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
const char input[] = "foobarzoo";
|
||||
char32_t c32[32];
|
||||
|
||||
size_t ret = mbsntoc32(NULL, input, sizeof(input), 0);
|
||||
xassert(ret == 9);
|
||||
|
||||
memset(c32, 0x55, sizeof(c32));
|
||||
ret = mbsntoc32(c32, input, sizeof(input), 32);
|
||||
|
||||
xassert(ret == 9);
|
||||
xassert(c32[0] == U'f');
|
||||
xassert(c32[1] == U'o');
|
||||
xassert(c32[2] == U'o');
|
||||
xassert(c32[3] == U'b');
|
||||
xassert(c32[4] == U'a');
|
||||
xassert(c32[5] == U'r');
|
||||
xassert(c32[6] == U'z');
|
||||
xassert(c32[7] == U'o');
|
||||
xassert(c32[8] == U'o');
|
||||
xassert(c32[9] == U'\0');
|
||||
xassert(c32[10] == 0x55555555);
|
||||
|
||||
memset(c32, 0x55, sizeof(c32));
|
||||
ret = mbsntoc32(c32, input, 1, 32);
|
||||
|
||||
xassert(ret == 1);
|
||||
xassert(c32[0] == U'f');
|
||||
xassert(c32[1] == 0x55555555);
|
||||
|
||||
memset(c32, 0x55, sizeof(c32));
|
||||
ret = mbsntoc32(c32, input, sizeof(input), 1);
|
||||
|
||||
xassert(ret == 1);
|
||||
xassert(c32[0] == U'f');
|
||||
xassert(c32[1] == 0x55555555);
|
||||
}
|
||||
|
||||
size_t
|
||||
mbstoc32(char32_t *dst, const char *src, size_t len)
|
||||
{
|
||||
return mbsntoc32(dst, src, strlen(src) + 1, len);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
const char input[] = "foobarzoo";
|
||||
char32_t c32[32];
|
||||
|
||||
size_t ret = mbstoc32(NULL, input, 0);
|
||||
xassert(ret == 9);
|
||||
|
||||
memset(c32, 0x55, sizeof(c32));
|
||||
ret = mbstoc32(c32, input, 32);
|
||||
|
||||
xassert(ret == 9);
|
||||
xassert(c32[0] == U'f');
|
||||
xassert(c32[1] == U'o');
|
||||
xassert(c32[2] == U'o');
|
||||
xassert(c32[3] == U'b');
|
||||
xassert(c32[4] == U'a');
|
||||
xassert(c32[5] == U'r');
|
||||
xassert(c32[6] == U'z');
|
||||
xassert(c32[7] == U'o');
|
||||
xassert(c32[8] == U'o');
|
||||
xassert(c32[9] == U'\0');
|
||||
xassert(c32[10] == 0x55555555);
|
||||
|
||||
memset(c32, 0x55, sizeof(c32));
|
||||
ret = mbstoc32(c32, input, 1);
|
||||
|
||||
xassert(ret == 1);
|
||||
xassert(c32[0] == U'f');
|
||||
xassert(c32[1] == 0x55555555);
|
||||
}
|
||||
|
||||
|
||||
char32_t *
|
||||
ambstoc32(const char *src)
|
||||
{
|
||||
if (src == NULL)
|
||||
return NULL;
|
||||
|
||||
const size_t src_len = strlen(src);
|
||||
|
||||
char32_t *ret = xmalloc((src_len + 1) * sizeof(ret[0]));
|
||||
mbstate_t ps = {0};
|
||||
|
||||
char32_t *out = ret;
|
||||
const char *in = src;
|
||||
const char *const end = src + src_len + 1;
|
||||
|
||||
size_t chars = 0;
|
||||
size_t rc;
|
||||
|
||||
while ((rc = mbrtoc32(out, in, end - in, &ps)) != 0) {
|
||||
switch (rc) {
|
||||
case (size_t)-1:
|
||||
case (size_t)-2:
|
||||
case (size_t)-3:
|
||||
goto err;
|
||||
}
|
||||
|
||||
in += rc;
|
||||
out++;
|
||||
chars++;
|
||||
}
|
||||
|
||||
*out = U'\0';
|
||||
|
||||
ret = xrealloc(ret, (chars + 1) * sizeof(ret[0]));
|
||||
return ret;
|
||||
|
||||
err:
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
setlocale(LC_CTYPE, "en_US.UTF-8");
|
||||
|
||||
char32_t *hello = ambstoc32(u8"hello");
|
||||
xassert(hello != NULL);
|
||||
xassert(hello[0] == U'h');
|
||||
xassert(hello[1] == U'e');
|
||||
xassert(hello[2] == U'l');
|
||||
xassert(hello[3] == U'l');
|
||||
xassert(hello[4] == U'o');
|
||||
xassert(hello[5] == U'\0');
|
||||
free(hello);
|
||||
|
||||
char32_t *swedish = ambstoc32(u8"åäö");
|
||||
xassert(swedish != NULL);
|
||||
xassert(swedish[0] == U'å');
|
||||
xassert(swedish[1] == U'ä');
|
||||
xassert(swedish[2] == U'ö');
|
||||
xassert(swedish[3] == U'\0');
|
||||
free(swedish);
|
||||
|
||||
char32_t *emoji = ambstoc32(u8"👨👩👧👦");
|
||||
xassert(emoji != NULL);
|
||||
xassert(emoji[0] == U'👨');
|
||||
xassert(emoji[1] == U'');
|
||||
xassert(emoji[2] == U'👩');
|
||||
xassert(emoji[3] == U'');
|
||||
xassert(emoji[4] == U'👧');
|
||||
xassert(emoji[5] == U'');
|
||||
xassert(emoji[6] == U'👦');
|
||||
xassert(emoji[7] == U'\0');
|
||||
free(emoji);
|
||||
|
||||
xassert(ambstoc32(NULL) == NULL);
|
||||
setlocale(LC_CTYPE, "C");
|
||||
}
|
||||
|
||||
char *
|
||||
ac32tombs(const char32_t *src)
|
||||
{
|
||||
if (src == NULL)
|
||||
return NULL;
|
||||
|
||||
const size_t src_len = c32len(src);
|
||||
|
||||
size_t allocated = src_len + 1;
|
||||
char *ret = xmalloc(allocated);
|
||||
mbstate_t ps = {0};
|
||||
|
||||
char *out = ret;
|
||||
const char32_t *const end = src + src_len + 1;
|
||||
|
||||
size_t bytes = 0;
|
||||
|
||||
char mb[MB_CUR_MAX];
|
||||
|
||||
for (const char32_t *in = src; in < end; in++) {
|
||||
size_t rc = c32rtomb(mb, *in, &ps);
|
||||
|
||||
switch (rc) {
|
||||
case (size_t)-1:
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bytes + rc > allocated) {
|
||||
allocated *= 2;
|
||||
ret = xrealloc(ret, allocated);
|
||||
out = &ret[bytes];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < rc; i++, out++)
|
||||
*out = mb[i];
|
||||
|
||||
bytes += rc;
|
||||
}
|
||||
|
||||
xassert(ret[bytes - 1] == '\0');
|
||||
ret = xrealloc(ret, bytes);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
setlocale(LC_CTYPE, "en_US.UTF-8");
|
||||
|
||||
char *s = ac32tombs(U"foobar");
|
||||
xassert(s != NULL);
|
||||
xassert(strcmp(s, "foobar") == 0);
|
||||
free(s);
|
||||
|
||||
s = ac32tombs(U"åäö");
|
||||
xassert(s != NULL);
|
||||
xassert(strcmp(s, u8"åäö") == 0);
|
||||
free(s);
|
||||
|
||||
s = ac32tombs(U"👨👩👧👦");
|
||||
xassert(s != NULL);
|
||||
xassert(strcmp(s, u8"👨👩👧👦") == 0);
|
||||
free(s);
|
||||
|
||||
xassert(ac32tombs(NULL) == NULL);
|
||||
setlocale(LC_CTYPE, "C");
|
||||
}
|
||||
|
||||
char32_t
|
||||
toc32lower(char32_t c)
|
||||
{
|
||||
return (char32_t)towlower((wint_t)c);
|
||||
}
|
||||
|
||||
char32_t
|
||||
toc32upper(char32_t c)
|
||||
{
|
||||
return (char32_t)towupper((wint_t)c);
|
||||
}
|
||||
|
||||
bool
|
||||
isc32space(char32_t c32)
|
||||
{
|
||||
return iswspace((wint_t)c32);
|
||||
}
|
||||
|
||||
bool
|
||||
isc32print(char32_t c32)
|
||||
{
|
||||
return iswprint((wint_t)c32);
|
||||
}
|
||||
|
||||
bool
|
||||
isc32graph(char32_t c32)
|
||||
{
|
||||
return iswgraph((wint_t)c32);
|
||||
}
|
||||
|
||||
int
|
||||
c32width(char32_t c)
|
||||
{
|
||||
return wcwidth((wchar_t)c);
|
||||
}
|
||||
|
||||
int
|
||||
c32swidth(const char32_t *s, size_t n)
|
||||
{
|
||||
return wcswidth((const wchar_t *)s, n);
|
||||
}
|
||||
35
char32.h
Normal file
35
char32.h
Normal 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);
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
86
config.c
86
config.c
|
|
@ -20,6 +20,7 @@
|
|||
#define LOG_MODULE "config"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "char32.h"
|
||||
#include "debug.h"
|
||||
#include "input.h"
|
||||
#include "macros.h"
|
||||
|
|
@ -484,10 +485,10 @@ done:
|
|||
}
|
||||
|
||||
static int
|
||||
wccmp(const void *_a, const void *_b)
|
||||
c32cmp_single(const void *_a, const void *_b)
|
||||
{
|
||||
const wchar_t *a = _a;
|
||||
const wchar_t *b = _b;
|
||||
const char32_t *a = _a;
|
||||
const char32_t *b = _b;
|
||||
return *a - *b;
|
||||
}
|
||||
|
||||
|
|
@ -619,17 +620,16 @@ value_to_str(struct context *ctx, char **res)
|
|||
}
|
||||
|
||||
static bool NOINLINE
|
||||
value_to_wchars(struct context *ctx, wchar_t **res)
|
||||
value_to_wchars(struct context *ctx, char32_t **res)
|
||||
{
|
||||
size_t chars = mbstowcs(NULL, ctx->value, 0);
|
||||
if (chars == (size_t)-1) {
|
||||
LOG_CONTEXTUAL_ERR("not a valid string value");
|
||||
char32_t *s = ambstoc32(ctx->value);
|
||||
if (s == NULL) {
|
||||
LOG_CONTEXTUAL_ERR("not a valie string value");
|
||||
return false;
|
||||
}
|
||||
|
||||
free(*res);
|
||||
*res = xmalloc((chars + 1) * sizeof(wchar_t));
|
||||
mbstowcs(*res, ctx->value, chars + 1);
|
||||
*res = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1171,7 +1171,7 @@ parse_section_url(struct context *ctx)
|
|||
len--;
|
||||
prot[len] = '\0';
|
||||
|
||||
size_t chars = mbstowcs(NULL, prot, 0);
|
||||
size_t chars = mbsntoc32(NULL, prot, len, 0);
|
||||
if (chars == (size_t)-1) {
|
||||
ctx->value = prot;
|
||||
LOG_CONTEXTUAL_ERRNO("invalid protocol");
|
||||
|
|
@ -1184,9 +1184,9 @@ parse_section_url(struct context *ctx)
|
|||
conf->url.prot_count * sizeof(conf->url.protocols[0]));
|
||||
|
||||
size_t idx = conf->url.prot_count - 1;
|
||||
conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(wchar_t));
|
||||
mbstowcs(conf->url.protocols[idx], prot, chars + 1);
|
||||
wcscpy(&conf->url.protocols[idx][chars], L"://");
|
||||
conf->url.protocols[idx] = xmalloc((chars + 1 + 3) * sizeof(char32_t));
|
||||
mbsntoc32(conf->url.protocols[idx], prot, len, chars + 1);
|
||||
c32cpy(&conf->url.protocols[idx][chars], U"://");
|
||||
|
||||
chars += 3; /* Include the "://" */
|
||||
if (chars > conf->url.max_prot_len)
|
||||
|
|
@ -1203,9 +1203,9 @@ parse_section_url(struct context *ctx)
|
|||
|
||||
qsort(
|
||||
conf->url.uri_characters,
|
||||
wcslen(conf->url.uri_characters),
|
||||
c32len(conf->url.uri_characters),
|
||||
sizeof(conf->url.uri_characters[0]),
|
||||
&wccmp);
|
||||
&c32cmp_single);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2748,7 +2748,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.shell = get_shell(),
|
||||
.title = xstrdup("foot"),
|
||||
.app_id = xstrdup("foot"),
|
||||
.word_delimiters = xwcsdup(L",│`|:\"'()[]{}<>"),
|
||||
.word_delimiters = xc32dup(U",│`|:\"'()[]{}<>"),
|
||||
.size = {
|
||||
.type = CONF_SIZE_PX,
|
||||
.width = 700,
|
||||
|
|
@ -2779,8 +2779,8 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.command_focused = false,
|
||||
},
|
||||
.url = {
|
||||
.label_letters = xwcsdup(L"sadfjklewcmpgh"),
|
||||
.uri_characters = xwcsdup(L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+=\"'()[]"),
|
||||
.label_letters = xc32dup(U"sadfjklewcmpgh"),
|
||||
.uri_characters = xc32dup(U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+=\"'()[]"),
|
||||
.osc8_underline = OSC8_UNDERLINE_URL_MODE,
|
||||
},
|
||||
.can_shape_grapheme = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING,
|
||||
|
|
@ -2789,7 +2789,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.indicator = {
|
||||
.position = SCROLLBACK_INDICATOR_POSITION_RELATIVE,
|
||||
.format = SCROLLBACK_INDICATOR_FORMAT_TEXT,
|
||||
.text = wcsdup(L""),
|
||||
.text = xc32dup(U""),
|
||||
},
|
||||
.multiplier = 3.,
|
||||
},
|
||||
|
|
@ -2872,16 +2872,16 @@ config_load(struct config *conf, const char *conf_path,
|
|||
&conf->notify.argv.args);
|
||||
tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args);
|
||||
|
||||
static const wchar_t *url_protocols[] = {
|
||||
L"http://",
|
||||
L"https://",
|
||||
L"ftp://",
|
||||
L"ftps://",
|
||||
L"file://",
|
||||
L"gemini://",
|
||||
L"gopher://",
|
||||
L"irc://",
|
||||
L"ircs://",
|
||||
static const char32_t *url_protocols[] = {
|
||||
U"http://",
|
||||
U"https://",
|
||||
U"ftp://",
|
||||
U"ftps://",
|
||||
U"file://",
|
||||
U"gemini://",
|
||||
U"gopher://",
|
||||
U"irc://",
|
||||
U"ircs://",
|
||||
};
|
||||
conf->url.protocols = xmalloc(
|
||||
ALEN(url_protocols) * sizeof(conf->url.protocols[0]));
|
||||
|
|
@ -2889,17 +2889,17 @@ config_load(struct config *conf, const char *conf_path,
|
|||
conf->url.max_prot_len = 0;
|
||||
|
||||
for (size_t i = 0; i < ALEN(url_protocols); i++) {
|
||||
size_t len = wcslen(url_protocols[i]);
|
||||
size_t len = c32len(url_protocols[i]);
|
||||
if (len > conf->url.max_prot_len)
|
||||
conf->url.max_prot_len = len;
|
||||
conf->url.protocols[i] = xwcsdup(url_protocols[i]);
|
||||
conf->url.protocols[i] = xc32dup(url_protocols[i]);
|
||||
}
|
||||
|
||||
qsort(
|
||||
conf->url.uri_characters,
|
||||
wcslen(conf->url.uri_characters),
|
||||
c32len(conf->url.uri_characters),
|
||||
sizeof(conf->url.uri_characters[0]),
|
||||
&wccmp);
|
||||
&c32cmp_single);
|
||||
|
||||
tll_foreach(*initial_user_notifications, it) {
|
||||
tll_push_back(conf->notifications, it->item);
|
||||
|
|
@ -3101,8 +3101,8 @@ config_clone(const struct config *old)
|
|||
conf->shell = xstrdup(old->shell);
|
||||
conf->title = xstrdup(old->title);
|
||||
conf->app_id = xstrdup(old->app_id);
|
||||
conf->word_delimiters = xwcsdup(old->word_delimiters);
|
||||
conf->scrollback.indicator.text = xwcsdup(old->scrollback.indicator.text);
|
||||
conf->word_delimiters = xc32dup(old->word_delimiters);
|
||||
conf->scrollback.indicator.text = xc32dup(old->scrollback.indicator.text);
|
||||
conf->server_socket_path = xstrdup(old->server_socket_path);
|
||||
spawn_template_clone(&conf->bell.command, &old->bell.command);
|
||||
spawn_template_clone(&conf->notify, &old->notify);
|
||||
|
|
@ -3111,13 +3111,13 @@ config_clone(const struct config *old)
|
|||
config_font_list_clone(&conf->fonts[i], &old->fonts[i]);
|
||||
config_font_list_clone(&conf->csd.font, &old->csd.font);
|
||||
|
||||
conf->url.label_letters = xwcsdup(old->url.label_letters);
|
||||
conf->url.uri_characters = xwcsdup(old->url.uri_characters);
|
||||
conf->url.label_letters = xc32dup(old->url.label_letters);
|
||||
conf->url.uri_characters = xc32dup(old->url.uri_characters);
|
||||
spawn_template_clone(&conf->url.launch, &old->url.launch);
|
||||
conf->url.protocols = xmalloc(
|
||||
old->url.prot_count * sizeof(conf->url.protocols[0]));
|
||||
for (size_t i = 0; i < old->url.prot_count; i++)
|
||||
conf->url.protocols[i] = xwcsdup(old->url.protocols[i]);
|
||||
conf->url.protocols[i] = xc32dup(old->url.protocols[i]);
|
||||
|
||||
key_binding_list_clone(&conf->bindings.key, &old->bindings.key);
|
||||
key_binding_list_clone(&conf->bindings.search, &old->bindings.search);
|
||||
|
|
@ -3237,13 +3237,13 @@ check_if_font_is_monospaced(const char *pattern,
|
|||
if (f == NULL)
|
||||
return true;
|
||||
|
||||
static const wchar_t chars[] = {L'a', L'i', L'l', L'M', L'W'};
|
||||
static const char32_t chars[] = {U'a', U'i', U'l', U'M', U'W'};
|
||||
|
||||
bool is_monospaced = true;
|
||||
int last_width = -1;
|
||||
|
||||
for (size_t i = 0; i < sizeof(chars) / sizeof(chars[0]); i++) {
|
||||
const struct fcft_glyph *g = fcft_glyph_rasterize(
|
||||
const struct fcft_glyph *g = fcft_rasterize_char_utf32(
|
||||
f, chars[i], FCFT_SUBPIXEL_NONE);
|
||||
|
||||
if (g == NULL)
|
||||
|
|
@ -3253,15 +3253,15 @@ check_if_font_is_monospaced(const char *pattern,
|
|||
LOG_WARN("%s: font does not appear to be monospace; "
|
||||
"check your config, or disable this warning by "
|
||||
"setting [tweak].font-monospace-warn=no",
|
||||
pattern);
|
||||
f->name);
|
||||
|
||||
static const char fmt[] =
|
||||
"%s: font does not appear to be monospace; "
|
||||
"check your config, or disable this warning by "
|
||||
"setting \033[1m[tweak].font-monospace-warn=no\033[22m";
|
||||
|
||||
user_notification_add_fmt(notifications, USER_NOTIFICATION_WARNING,
|
||||
fmt, pattern);
|
||||
user_notification_add_fmt(
|
||||
notifications, USER_NOTIFICATION_WARNING, fmt, f->name);
|
||||
|
||||
is_monospaced = false;
|
||||
break;
|
||||
|
|
|
|||
11
config.h
11
config.h
|
|
@ -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
3
csi.c
|
|
@ -14,6 +14,7 @@
|
|||
#define LOG_MODULE "csi"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "char32.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "grid.h"
|
||||
|
|
@ -742,7 +743,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
int count = vt_param_get(term, 0, 1);
|
||||
LOG_DBG("REP: '%lc' %d times", (wint_t)term->vt.last_printed, count);
|
||||
|
||||
const int width = wcwidth(term->vt.last_printed);
|
||||
const int width = c32width(term->vt.last_printed);
|
||||
if (width > 0) {
|
||||
for (int i = 0; i < count; i++)
|
||||
term_print(term, term->vt.last_printed, width);
|
||||
|
|
|
|||
60
extract.c
60
extract.c
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
24
ime.c
|
|
@ -9,6 +9,7 @@
|
|||
#define LOG_MODULE "ime"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "char32.h"
|
||||
#include "render.h"
|
||||
#include "search.h"
|
||||
#include "terminal.h"
|
||||
|
|
@ -159,26 +160,29 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
|||
/* 4. Calculate surrounding text to send - not supported */
|
||||
|
||||
/* 5. Insert new pre-edit text */
|
||||
size_t wchars = seat->ime.preedit.pending.text != NULL
|
||||
? mbstowcs(NULL, seat->ime.preedit.pending.text, 0)
|
||||
: 0;
|
||||
char32_t *allocated_preedit_text = NULL;
|
||||
|
||||
if (wchars == 0 || wchars == (size_t)-1) {
|
||||
if (seat->ime.preedit.pending.text == NULL ||
|
||||
seat->ime.preedit.pending.text[0] == '\0' ||
|
||||
(allocated_preedit_text = ambstoc32(seat->ime.preedit.pending.text)) == NULL)
|
||||
{
|
||||
ime_reset_pending_preedit(seat);
|
||||
return;
|
||||
}
|
||||
|
||||
/* First, convert to unicode */
|
||||
seat->ime.preedit.text = xmalloc((wchars + 1) * sizeof(wchar_t));
|
||||
mbstowcs(seat->ime.preedit.text, seat->ime.preedit.pending.text, wchars);
|
||||
seat->ime.preedit.text[wchars] = L'\0';
|
||||
xassert(seat->ime.preedit.pending.text != NULL);
|
||||
xassert(allocated_preedit_text != NULL);
|
||||
|
||||
seat->ime.preedit.text = allocated_preedit_text;
|
||||
|
||||
size_t wchars = c32len(seat->ime.preedit.text);
|
||||
|
||||
/* Next, count number of cells needed */
|
||||
size_t cell_count = 0;
|
||||
size_t widths[wchars + 1];
|
||||
|
||||
for (size_t i = 0; i < wchars; i++) {
|
||||
int width = max(wcwidth(seat->ime.preedit.text[i]), 1);
|
||||
int width = max(c32width(seat->ime.preedit.text[i]), 1);
|
||||
widths[i] = width;
|
||||
cell_count += width;
|
||||
}
|
||||
|
|
@ -238,7 +242,7 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
|||
*
|
||||
* To do this, we use mblen() to step though the utf-8
|
||||
* pre-edit string, advancing a unicode character index as
|
||||
* we go, *and* advancing a *cell* index using wcwidth()
|
||||
* we go, *and* advancing a *cell* index using c32width()
|
||||
* of the unicode character.
|
||||
*
|
||||
* When we find the matching *byte* index, we at the same
|
||||
|
|
|
|||
15
input.c
15
input.c
|
|
@ -1123,16 +1123,15 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term,
|
|||
}
|
||||
|
||||
else if (term->meta.eight_bit && count == 1) {
|
||||
const wchar_t wc = 0x80 | utf8[0];
|
||||
const char32_t wc = 0x80 | utf8[0];
|
||||
|
||||
char wc_as_utf8[8];
|
||||
mbstate_t ps = {0};
|
||||
size_t chars = wcrtomb(wc_as_utf8, wc, &ps);
|
||||
char utf8_meta[MB_CUR_MAX];
|
||||
size_t chars = c32rtomb(utf8_meta, wc, &(mbstate_t){0});
|
||||
|
||||
if (chars != (size_t)-1)
|
||||
term_to_slave(term, wc_as_utf8, chars);
|
||||
term_to_slave(term, utf8_meta, chars);
|
||||
else
|
||||
term_to_slave(term, wc_as_utf8, count);
|
||||
term_to_slave(term, utf8, count);
|
||||
}
|
||||
|
||||
else {
|
||||
|
|
@ -1585,8 +1584,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
xkb_compose_state_get_utf8(
|
||||
seat->kbd.xkb_compose_state, (char *)utf8, count + 1);
|
||||
|
||||
wchar_t wc;
|
||||
if (mbtowc(&wc, (const char *)utf8, count) == count)
|
||||
char32_t wc;
|
||||
if (mbrtoc32(&wc, (const char *)utf8, count, &(mbstate_t){0}) == count)
|
||||
utf32 = wc;
|
||||
} else {
|
||||
xkb_state_key_get_utf8(
|
||||
|
|
|
|||
38
main.c
38
main.c
|
|
@ -34,6 +34,12 @@
|
|||
#include "xmalloc.h"
|
||||
#include "xsnprintf.h"
|
||||
|
||||
#include "char32.h"
|
||||
|
||||
#if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__
|
||||
#error "char32_t does not use UTF-32"
|
||||
#endif
|
||||
|
||||
static bool
|
||||
fdm_sigint(struct fdm *fdm, int signo, void *data)
|
||||
{
|
||||
|
|
@ -90,16 +96,14 @@ print_usage(const char *prog_name)
|
|||
bool
|
||||
locale_is_utf8(void)
|
||||
{
|
||||
xassert(strlen(u8"ö") == 2);
|
||||
static const char u8[] = u8"ö";
|
||||
xassert(strlen(u8) == 2);
|
||||
|
||||
wchar_t w;
|
||||
if (mbtowc(&w, u8"ö", 2) != 2)
|
||||
char32_t w;
|
||||
if (mbrtoc32(&w, u8, 2, &(mbstate_t){0}) != 2)
|
||||
return false;
|
||||
|
||||
if (w != U'ö')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return w == U'ö';
|
||||
}
|
||||
|
||||
struct shutdown_context {
|
||||
|
|
@ -408,15 +412,6 @@ main(int argc, char *const *argv)
|
|||
log_init(log_colorize, as_server && log_syslog,
|
||||
as_server ? LOG_FACILITY_DAEMON : LOG_FACILITY_USER, log_level);
|
||||
|
||||
_Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR,
|
||||
"fcft log level enum offset");
|
||||
_Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS,
|
||||
"fcft colorize enum mismatch");
|
||||
fcft_log_init(
|
||||
(enum fcft_log_colorize)log_colorize,
|
||||
as_server && log_syslog,
|
||||
(enum fcft_log_class)log_level);
|
||||
|
||||
if (argc > 0) {
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
|
@ -499,6 +494,14 @@ main(int argc, char *const *argv)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
_Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR,
|
||||
"fcft log level enum offset");
|
||||
_Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS,
|
||||
"fcft colorize enum mismatch");
|
||||
fcft_init(
|
||||
(enum fcft_log_colorize)log_colorize,
|
||||
as_server && log_syslog,
|
||||
(enum fcft_log_class)log_level);
|
||||
fcft_set_scaling_filter(conf.tweak.fcft_filter);
|
||||
|
||||
if (conf_term != NULL) {
|
||||
|
|
@ -646,6 +649,8 @@ main(int argc, char *const *argv)
|
|||
if (aborted || tll_length(wayl->terms) == 0)
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
LOG_INFO("%zu", c32len(U"foobar"));
|
||||
|
||||
out:
|
||||
free(_cwd);
|
||||
server_destroy(server);
|
||||
|
|
@ -665,6 +670,7 @@ out:
|
|||
unlink(pid_file);
|
||||
|
||||
LOG_INFO("goodbye");
|
||||
fcft_fini();
|
||||
log_deinit();
|
||||
return ret == EXIT_SUCCESS && !as_server ? shutdown_ctx.exit_code : ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
11
misc.c
|
|
@ -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
4
misc.h
|
|
@ -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
149
render.c
|
|
@ -29,6 +29,7 @@
|
|||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "box-drawing.h"
|
||||
#include "char32.h"
|
||||
#include "config.h"
|
||||
#include "grid.h"
|
||||
#include "hsl.h"
|
||||
|
|
@ -566,7 +567,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
const struct fcft_glyph **glyphs = NULL;
|
||||
unsigned glyph_count = 0;
|
||||
|
||||
wchar_t base = cell->wc;
|
||||
char32_t base = cell->wc;
|
||||
int cell_cols = 1;
|
||||
|
||||
if (base != 0) {
|
||||
|
|
@ -637,9 +638,8 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
base = composed->chars[0];
|
||||
|
||||
if (term->conf->can_shape_grapheme && term->conf->tweak.grapheme_shaping) {
|
||||
grapheme = fcft_grapheme_rasterize(
|
||||
font, composed->count, composed->chars,
|
||||
0, NULL, term->font_subpixel);
|
||||
grapheme = fcft_rasterize_grapheme_utf32(
|
||||
font, composed->count, composed->chars, term->font_subpixel);
|
||||
}
|
||||
|
||||
if (grapheme != NULL) {
|
||||
|
|
@ -654,7 +654,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
|
||||
if (single == NULL && grapheme == NULL) {
|
||||
xassert(base != 0);
|
||||
single = fcft_glyph_rasterize(font, base, term->font_subpixel);
|
||||
single = fcft_rasterize_char_utf32(font, base, term->font_subpixel);
|
||||
if (single == NULL) {
|
||||
glyph_count = 0;
|
||||
cell_cols = 1;
|
||||
|
|
@ -717,7 +717,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
if (has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus)
|
||||
draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols);
|
||||
|
||||
if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == L'\t' ||
|
||||
if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == U'\t' ||
|
||||
(unlikely(cell->attrs.conceal) && !is_selected))
|
||||
{
|
||||
goto draw_cursor;
|
||||
|
|
@ -758,7 +758,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
assert(glyph_count == 1);
|
||||
|
||||
for (size_t i = 1; i < composed->count; i++) {
|
||||
const struct fcft_glyph *g = fcft_glyph_rasterize(
|
||||
const struct fcft_glyph *g = fcft_rasterize_char_utf32(
|
||||
font, composed->chars[i], term->font_subpixel);
|
||||
|
||||
if (g == NULL)
|
||||
|
|
@ -1414,7 +1414,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
|||
if (cell->wc >= CELL_SPACER)
|
||||
continue;
|
||||
|
||||
int width = max(1, wcwidth(cell->wc));
|
||||
int width = max(1, c32width(cell->wc));
|
||||
if (col_idx + i + width > term->cols)
|
||||
break;
|
||||
|
||||
|
|
@ -1626,7 +1626,7 @@ static void
|
|||
render_osd(struct terminal *term,
|
||||
struct wl_surface *surf, struct wl_subsurface *sub_surf,
|
||||
struct fcft_font *font, struct buffer *buf,
|
||||
const wchar_t *text, uint32_t _fg, uint32_t _bg,
|
||||
const char32_t *text, uint32_t _fg, uint32_t _bg,
|
||||
unsigned x, unsigned y)
|
||||
{
|
||||
pixman_region32_t clip;
|
||||
|
|
@ -1643,14 +1643,15 @@ render_osd(struct terminal *term,
|
|||
pixman_color_t fg = color_hex_to_pixman(_fg);
|
||||
const int x_ofs = term->font_x_ofs;
|
||||
|
||||
const size_t len = wcslen(text);
|
||||
const size_t len = c32len(text);
|
||||
struct fcft_text_run *text_run = NULL;
|
||||
const struct fcft_glyph **glyphs = NULL;
|
||||
const struct fcft_glyph *_glyphs[len];
|
||||
size_t glyph_count = 0;
|
||||
|
||||
if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) {
|
||||
text_run = fcft_text_run_rasterize(font, len, text, term->font_subpixel);
|
||||
text_run = fcft_rasterize_text_run_utf32(
|
||||
font, len, (const char32_t *)text, term->font_subpixel);
|
||||
|
||||
if (text_run != NULL) {
|
||||
glyphs = text_run->glyphs;
|
||||
|
|
@ -1660,7 +1661,7 @@ render_osd(struct terminal *term,
|
|||
|
||||
if (glyphs == NULL) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
|
||||
const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(
|
||||
font, text[i], term->font_subpixel);
|
||||
|
||||
if (glyph == NULL)
|
||||
|
|
@ -1740,20 +1741,15 @@ render_csd_title(struct terminal *term, const struct csd_data *info,
|
|||
fg = color_dim(term, fg);
|
||||
}
|
||||
|
||||
const wchar_t *title_text = L"";
|
||||
wchar_t *_title_text = NULL;
|
||||
|
||||
int chars = mbstowcs(NULL, term->window_title, 0);
|
||||
if (chars >= 0) {
|
||||
_title_text = xmalloc((chars + 1) * sizeof(wchar_t));
|
||||
mbstowcs(_title_text, term->window_title, chars + 1);
|
||||
title_text = _title_text;
|
||||
}
|
||||
char32_t *_title_text = ambstoc32(term->window_title);
|
||||
const char32_t *title_text = _title_text != NULL ? _title_text : U"";
|
||||
|
||||
struct wl_window *win = term->window;
|
||||
const int margin = win->csd.font->space_advance.x > 0
|
||||
? win->csd.font->space_advance.x
|
||||
: win->csd.font->max_advance.x;
|
||||
|
||||
const struct fcft_glyph *M = fcft_rasterize_char_utf32(
|
||||
win->csd.font, U'M', term->font_subpixel);
|
||||
|
||||
const int margin = M != NULL ? M->advance.x : win->csd.font->max_advance.x;
|
||||
|
||||
render_osd(term, surf->surf, surf->sub, win->csd.font,
|
||||
buf, title_text, fg, bg, margin,
|
||||
|
|
@ -2197,25 +2193,31 @@ render_scrollback_position(struct terminal *term)
|
|||
? 1.0
|
||||
: (double)rebased_view / (populated_rows - term->rows);
|
||||
|
||||
wchar_t _text[64];
|
||||
const wchar_t *text = _text;
|
||||
char32_t _text[64];
|
||||
const char32_t *text = _text;
|
||||
int cell_count = 0;
|
||||
|
||||
/* *What* to render */
|
||||
switch (term->conf->scrollback.indicator.format) {
|
||||
case SCROLLBACK_INDICATOR_FORMAT_PERCENTAGE:
|
||||
swprintf(_text, sizeof(_text) / sizeof(_text[0]), L"%u%%", (int)(100 * percent));
|
||||
case SCROLLBACK_INDICATOR_FORMAT_PERCENTAGE: {
|
||||
char percent_str[8];
|
||||
snprintf(percent_str, sizeof(percent_str), "%u%%", (int)(100 * percent));
|
||||
mbstoc32(_text, percent_str, ALEN(_text));
|
||||
cell_count = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case SCROLLBACK_INDICATOR_FORMAT_LINENO:
|
||||
swprintf(_text, sizeof(_text) / sizeof(_text[0]), L"%d", rebased_view + 1);
|
||||
cell_count = 1 + (int)log10(term->grid->num_rows);
|
||||
case SCROLLBACK_INDICATOR_FORMAT_LINENO: {
|
||||
char lineno_str[64];
|
||||
snprintf(lineno_str, sizeof(lineno_str), "%d", rebased_view + 1);
|
||||
mbstoc32(_text, lineno_str, ALEN(_text));
|
||||
cell_count = ceil(log10(term->grid->num_rows));
|
||||
break;
|
||||
}
|
||||
|
||||
case SCROLLBACK_INDICATOR_FORMAT_TEXT:
|
||||
text = term->conf->scrollback.indicator.text;
|
||||
cell_count = wcslen(text);
|
||||
cell_count = c32len(text);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -2281,7 +2283,7 @@ render_scrollback_position(struct terminal *term)
|
|||
win->scrollback_indicator.sub,
|
||||
term->fonts[0], buf, text,
|
||||
fg, 0xffu << 24 | bg,
|
||||
width - margin - wcslen(text) * term->cell_width, margin);
|
||||
width - margin - c32len(text) * term->cell_width, margin);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -2289,12 +2291,15 @@ render_render_timer(struct terminal *term, struct timespec render_time)
|
|||
{
|
||||
struct wl_window *win = term->window;
|
||||
|
||||
wchar_t text[256];
|
||||
char usecs_str[256];
|
||||
double usecs = render_time.tv_sec * 1000000 + render_time.tv_nsec / 1000.0;
|
||||
swprintf(text, sizeof(text) / sizeof(text[0]), L"%.2f µs", usecs);
|
||||
snprintf(usecs_str, sizeof(usecs_str), "%.2f µs", usecs);
|
||||
|
||||
char32_t text[256];
|
||||
mbstoc32(text, usecs_str, ALEN(text));
|
||||
|
||||
const int scale = term->scale;
|
||||
const int cell_count = wcslen(text);
|
||||
const int cell_count = c32len(text);
|
||||
const int margin = 3 * scale;
|
||||
const int width =
|
||||
(2 * margin + cell_count * term->cell_width + scale - 1) / scale * scale;
|
||||
|
|
@ -2846,34 +2851,34 @@ render_search_box(struct terminal *term)
|
|||
|
||||
size_t text_len = term->search.len;
|
||||
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
|
||||
text_len += wcslen(ime_seat->ime.preedit.text);
|
||||
text_len += c32len(ime_seat->ime.preedit.text);
|
||||
|
||||
wchar_t *text = xmalloc((text_len + 1) * sizeof(wchar_t));
|
||||
text[0] = L'\0';
|
||||
char32_t *text = xmalloc((text_len + 1) * sizeof(char32_t));
|
||||
text[0] = U'\0';
|
||||
|
||||
/* Copy everything up to the cursor */
|
||||
wcsncpy(text, term->search.buf, term->search.cursor);
|
||||
text[term->search.cursor] = L'\0';
|
||||
c32ncpy(text, term->search.buf, term->search.cursor);
|
||||
text[term->search.cursor] = U'\0';
|
||||
|
||||
/* Insert pre-edit text at cursor */
|
||||
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
|
||||
wcscat(text, ime_seat->ime.preedit.text);
|
||||
c32cat(text, ime_seat->ime.preedit.text);
|
||||
|
||||
/* And finally everything after the cursor */
|
||||
wcsncat(text, &term->search.buf[term->search.cursor],
|
||||
c32ncat(text, &term->search.buf[term->search.cursor],
|
||||
term->search.len - term->search.cursor);
|
||||
#else
|
||||
const wchar_t *text = term->search.buf;
|
||||
const char32_t *text = term->search.buf;
|
||||
const size_t text_len = term->search.len;
|
||||
#endif
|
||||
|
||||
/* Calculate the width of each character */
|
||||
int widths[text_len + 1];
|
||||
for (size_t i = 0; i < text_len; i++)
|
||||
widths[i] = max(0, wcwidth(text[i]));
|
||||
widths[i] = max(0, c32width(text[i]));
|
||||
widths[text_len] = 0;
|
||||
|
||||
const size_t total_cells = wcswidth(text, text_len);
|
||||
const size_t total_cells = c32swidth(text, text_len);
|
||||
const size_t wanted_visible_cells = max(20, total_cells);
|
||||
|
||||
xassert(term->scale >= 1);
|
||||
|
|
@ -3070,7 +3075,7 @@ render_search_box(struct terminal *term)
|
|||
continue;
|
||||
}
|
||||
|
||||
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
|
||||
const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(
|
||||
font, text[i], term->font_subpixel);
|
||||
|
||||
if (glyph == NULL) {
|
||||
|
|
@ -3195,7 +3200,7 @@ render_urls(struct terminal *term)
|
|||
/* Positioning data + label contents */
|
||||
struct {
|
||||
const struct wl_url *url;
|
||||
wchar_t *text;
|
||||
char32_t *text;
|
||||
int x;
|
||||
int y;
|
||||
} info[tll_length(win->urls)];
|
||||
|
|
@ -3208,8 +3213,8 @@ render_urls(struct terminal *term)
|
|||
|
||||
tll_foreach(win->urls, it) {
|
||||
const struct url *url = it->item.url;
|
||||
const wchar_t *key = url->key;
|
||||
const size_t entered_key_len = wcslen(term->url_keys);
|
||||
const char32_t *key = url->key;
|
||||
const size_t entered_key_len = c32len(term->url_keys);
|
||||
|
||||
if (key == NULL) {
|
||||
/* TODO: if we decide to use the .text field, we cannot
|
||||
|
|
@ -3232,9 +3237,9 @@ render_urls(struct terminal *term)
|
|||
|
||||
if (_row < view_start || _row > view_end)
|
||||
hide = true;
|
||||
if (wcslen(key) <= entered_key_len)
|
||||
if (c32len(key) <= entered_key_len)
|
||||
hide = true;
|
||||
if (wcsncasecmp(term->url_keys, key, entered_key_len) != 0)
|
||||
if (c32ncasecmp(term->url_keys, key, entered_key_len) != 0)
|
||||
hide = true;
|
||||
|
||||
if (hide) {
|
||||
|
|
@ -3264,32 +3269,34 @@ render_urls(struct terminal *term)
|
|||
term->width - term->margins.left - term->margins.right - x;
|
||||
const int max_cols = max_width / term->cell_width;
|
||||
|
||||
const size_t key_len = wcslen(key);
|
||||
const size_t key_len = c32len(key);
|
||||
|
||||
size_t url_len = mbstowcs(NULL, url->url, 0);
|
||||
size_t url_len = mbstoc32(NULL, url->url, 0);
|
||||
if (url_len == (size_t)-1)
|
||||
url_len = 0;
|
||||
|
||||
wchar_t url_wchars[url_len + 1];
|
||||
mbstowcs(url_wchars, url->url, url_len + 1);
|
||||
char32_t url_wchars[url_len + 1];
|
||||
mbstoc32(url_wchars, url->url, url_len + 1);
|
||||
|
||||
/* Format label, not yet subject to any size limitations */
|
||||
size_t chars = key_len + (show_url ? (2 + url_len) : 0);
|
||||
wchar_t label[chars + 1];
|
||||
label[chars] = L'\0';
|
||||
char32_t label[chars + 1];
|
||||
label[chars] = U'\0';
|
||||
|
||||
if (show_url)
|
||||
swprintf(label, chars + 1, L"%ls: %ls", key, url_wchars);
|
||||
else
|
||||
wcsncpy(label, key, chars);
|
||||
if (show_url) {
|
||||
c32cpy(label, key);
|
||||
c32cat(label, U": ");
|
||||
c32cat(label, url_wchars);
|
||||
} else
|
||||
c32ncpy(label, key, chars);
|
||||
|
||||
/* Upper case the key characters */
|
||||
for (size_t i = 0; i < wcslen(key); i++)
|
||||
label[i] = towupper(label[i]);
|
||||
for (size_t i = 0; i < c32len(key); i++)
|
||||
label[i] = toc32upper(label[i]);
|
||||
|
||||
/* Blank already entered key characters */
|
||||
for (size_t i = 0; i < entered_key_len; i++)
|
||||
label[i] = L' ';
|
||||
label[i] = U' ';
|
||||
|
||||
/*
|
||||
* Don’t extend outside our window
|
||||
|
|
@ -3303,16 +3310,16 @@ render_urls(struct terminal *term)
|
|||
|
||||
int cols = 0;
|
||||
|
||||
for (size_t i = 0; i <= wcslen(label); i++) {
|
||||
int _cols = wcswidth(label, i);
|
||||
for (size_t i = 0; i <= c32len(label); i++) {
|
||||
int _cols = c32swidth(label, i);
|
||||
|
||||
if (_cols == (size_t)-1)
|
||||
continue;
|
||||
|
||||
if (_cols >= max_cols) {
|
||||
if (i > 0)
|
||||
label[i - 1] = L'…';
|
||||
label[i] = L'\0';
|
||||
label[i - 1] = U'…';
|
||||
label[i] = U'\0';
|
||||
cols = max_cols;
|
||||
break;
|
||||
}
|
||||
|
|
@ -3328,7 +3335,7 @@ render_urls(struct terminal *term)
|
|||
(2 * y_margin + term->cell_height + scale - 1) / scale * scale;
|
||||
|
||||
info[render_count].url = &it->item;
|
||||
info[render_count].text = xwcsdup(label);
|
||||
info[render_count].text = xc32dup(label);
|
||||
info[render_count].x = x;
|
||||
info[render_count].y = y;
|
||||
|
||||
|
|
@ -3353,7 +3360,7 @@ render_urls(struct terminal *term)
|
|||
struct wl_surface *surf = info[i].url->surf.surf;
|
||||
struct wl_subsurface *sub_surf = info[i].url->surf.sub;
|
||||
|
||||
const wchar_t *label = info[i].text;
|
||||
const char32_t *label = info[i].text;
|
||||
const int x = info[i].x;
|
||||
const int y = info[i].y;
|
||||
|
||||
|
|
|
|||
82
search.c
82
search.c
|
|
@ -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)
|
||||
|
|
|
|||
18
selection.c
18
selection.c
|
|
@ -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;
|
||||
|
|
|
|||
34
terminal.c
34
terminal.c
|
|
@ -671,16 +671,12 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
|
|||
|
||||
const struct config *conf = term->conf;
|
||||
|
||||
const struct fcft_glyph *M = fcft_glyph_rasterize(
|
||||
term->fonts[0], L'M', term->font_subpixel);
|
||||
const struct fcft_glyph *M = fcft_rasterize_char_utf32(
|
||||
fonts[0], U'M', term->font_subpixel);
|
||||
int advance = M != NULL ? M->advance.x : term->fonts[0]->max_advance.x;
|
||||
|
||||
term->cell_width =
|
||||
(M != NULL
|
||||
? M->advance.x
|
||||
: (term->fonts[0]->space_advance.x > 0
|
||||
? term->fonts[0]->space_advance.x
|
||||
: term->fonts[0]->max_advance.x))
|
||||
+ term_pt_or_px_as_pixels(term, &conf->letter_spacing);
|
||||
term->cell_width = advance +
|
||||
term_pt_or_px_as_pixels(term, &conf->letter_spacing);
|
||||
|
||||
term->cell_height = term->font_line_height.px >= 0
|
||||
? term_pt_or_px_as_pixels(term, &term->font_line_height)
|
||||
|
|
@ -3208,7 +3204,7 @@ print_spacer(struct terminal *term, int col, int remaining)
|
|||
}
|
||||
|
||||
void
|
||||
term_print(struct terminal *term, wchar_t wc, int width)
|
||||
term_print(struct terminal *term, char32_t wc, int width)
|
||||
{
|
||||
xassert(width > 0);
|
||||
|
||||
|
|
@ -3218,11 +3214,11 @@ term_print(struct terminal *term, wchar_t wc, int width)
|
|||
wc >= 0x60 && wc <= 0x7e)
|
||||
{
|
||||
/* 0x60 - 0x7e */
|
||||
static const wchar_t vt100_0[] = {
|
||||
L'◆', L'▒', L'␉', L'␌', L'␍', L'␊', L'°', L'±', /* ` - g */
|
||||
L'', L'␋', L'┘', L'┐', L'┌', L'└', L'┼', L'⎺', /* h - o */
|
||||
L'⎻', L'─', L'⎼', L'⎽', L'├', L'┤', L'┴', L'┬', /* p - w */
|
||||
L'│', L'≤', L'≥', L'π', L'≠', L'£', L'·', /* x - ~ */
|
||||
static const char32_t vt100_0[] = {
|
||||
U'◆', U'▒', U'␉', U'␌', U'␍', U'␊', U'°', U'±', /* ` - g */
|
||||
U'', U'␋', U'┘', U'┐', U'┌', U'└', U'┼', U'⎺', /* h - o */
|
||||
U'⎻', U'─', U'⎼', U'⎽', U'├', U'┤', U'┴', U'┬', /* p - w */
|
||||
U'│', U'≤', U'≥', U'π', U'≠', U'£', U'·', /* x - ~ */
|
||||
};
|
||||
|
||||
xassert(width == 1);
|
||||
|
|
@ -3291,13 +3287,13 @@ term_print(struct terminal *term, wchar_t wc, int width)
|
|||
}
|
||||
|
||||
static void
|
||||
ascii_printer_generic(struct terminal *term, wchar_t wc)
|
||||
ascii_printer_generic(struct terminal *term, char32_t wc)
|
||||
{
|
||||
term_print(term, wc, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
ascii_printer_fast(struct terminal *term, wchar_t wc)
|
||||
ascii_printer_fast(struct terminal *term, char32_t wc)
|
||||
{
|
||||
struct grid *grid = term->grid;
|
||||
|
||||
|
|
@ -3333,7 +3329,7 @@ ascii_printer_fast(struct terminal *term, wchar_t wc)
|
|||
}
|
||||
|
||||
static void
|
||||
ascii_printer_single_shift(struct terminal *term, wchar_t wc)
|
||||
ascii_printer_single_shift(struct terminal *term, char32_t wc)
|
||||
{
|
||||
ascii_printer_generic(term, wc);
|
||||
term->charsets.selected = term->charsets.saved;
|
||||
|
|
@ -3343,7 +3339,7 @@ ascii_printer_single_shift(struct terminal *term, wchar_t wc)
|
|||
void
|
||||
term_update_ascii_printer(struct terminal *term)
|
||||
{
|
||||
void (*new_printer)(struct terminal *term, wchar_t wc) =
|
||||
void (*new_printer)(struct terminal *term, char32_t wc) =
|
||||
unlikely(tll_length(term->grid->sixel_images) > 0 ||
|
||||
term->vt.osc8.uri != NULL ||
|
||||
term->charsets.set[term->charsets.selected] == CHARSET_GRAPHIC ||
|
||||
|
|
|
|||
19
terminal.h
19
terminal.h
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
|
|||
110
url-mode.c
110
url-mode.c
|
|
@ -11,6 +11,7 @@
|
|||
#define LOG_MODULE "url-mode"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "char32.h"
|
||||
#include "grid.h"
|
||||
#include "render.h"
|
||||
#include "selection.h"
|
||||
|
|
@ -150,11 +151,11 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key,
|
|||
}
|
||||
}
|
||||
|
||||
size_t seq_len = wcslen(term->url_keys);
|
||||
size_t seq_len = c32len(term->url_keys);
|
||||
|
||||
if (sym == XKB_KEY_BackSpace) {
|
||||
if (seq_len > 0) {
|
||||
term->url_keys[seq_len - 1] = L'\0';
|
||||
term->url_keys[seq_len - 1] = U'\0';
|
||||
render_refresh_urls(term);
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +165,7 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key,
|
|||
if (mods & ~consumed)
|
||||
return;
|
||||
|
||||
wchar_t wc = xkb_state_key_get_utf32(seat->kbd.xkb_state, key);
|
||||
char32_t wc = xkb_state_key_get_utf32(seat->kbd.xkb_state, key);
|
||||
|
||||
/*
|
||||
* Determine if this is a “valid” key. I.e. if there is an URL
|
||||
|
|
@ -180,11 +181,11 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key,
|
|||
continue;
|
||||
|
||||
const struct url *url = &it->item;
|
||||
const size_t key_len = wcslen(it->item.key);
|
||||
const size_t key_len = c32len(it->item.key);
|
||||
|
||||
if (key_len >= seq_len + 1 &&
|
||||
wcsncasecmp(url->key, term->url_keys, seq_len) == 0 &&
|
||||
towlower(url->key[seq_len]) == towlower(wc))
|
||||
c32ncasecmp(url->key, term->url_keys, seq_len) == 0 &&
|
||||
toc32lower(url->key[seq_len]) == toc32lower(wc))
|
||||
{
|
||||
is_valid = true;
|
||||
if (key_len == seq_len + 1) {
|
||||
|
|
@ -207,10 +208,10 @@ urls_input(struct seat *seat, struct terminal *term, uint32_t key,
|
|||
}
|
||||
|
||||
static int
|
||||
wccmp(const void *_a, const void *_b)
|
||||
c32cmp_single(const void *_a, const void *_b)
|
||||
{
|
||||
const wchar_t *a = _a;
|
||||
const wchar_t *b = _b;
|
||||
const char32_t *a = _a;
|
||||
const char32_t *b = _b;
|
||||
return *a - *b;
|
||||
}
|
||||
|
||||
|
|
@ -220,16 +221,16 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
{
|
||||
const struct config *conf = term->conf;
|
||||
|
||||
const wchar_t *uri_characters = conf->url.uri_characters;
|
||||
const char32_t *uri_characters = conf->url.uri_characters;
|
||||
if (uri_characters == NULL)
|
||||
return;
|
||||
|
||||
const size_t uri_characters_count = wcslen(uri_characters);
|
||||
const size_t uri_characters_count = c32len(uri_characters);
|
||||
if (uri_characters_count == 0)
|
||||
return;
|
||||
|
||||
size_t max_prot_len = conf->url.max_prot_len;
|
||||
wchar_t proto_chars[max_prot_len];
|
||||
char32_t proto_chars[max_prot_len];
|
||||
struct coord proto_start[max_prot_len];
|
||||
size_t proto_char_count = 0;
|
||||
|
||||
|
|
@ -239,7 +240,7 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
} state = STATE_PROTOCOL;
|
||||
|
||||
struct coord start = {-1, -1};
|
||||
wchar_t url[term->cols * term->rows + 1];
|
||||
char32_t url[term->cols * term->rows + 1];
|
||||
size_t len = 0;
|
||||
|
||||
ssize_t parenthesis = 0;
|
||||
|
|
@ -251,7 +252,7 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
|
||||
for (int c = 0; c < term->cols; c++) {
|
||||
const struct cell *cell = &row->cells[c];
|
||||
wchar_t wc = cell->wc;
|
||||
char32_t wc = cell->wc;
|
||||
|
||||
switch (state) {
|
||||
case STATE_PROTOCOL:
|
||||
|
|
@ -268,18 +269,18 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
proto_char_count++;
|
||||
|
||||
for (size_t i = 0; i < conf->url.prot_count; i++) {
|
||||
size_t prot_len = wcslen(conf->url.protocols[i]);
|
||||
size_t prot_len = c32len(conf->url.protocols[i]);
|
||||
|
||||
if (proto_char_count < prot_len)
|
||||
continue;
|
||||
|
||||
const wchar_t *proto = &proto_chars[max_prot_len - prot_len];
|
||||
const char32_t *proto = &proto_chars[max_prot_len - prot_len];
|
||||
|
||||
if (wcsncasecmp(conf->url.protocols[i], proto, prot_len) == 0) {
|
||||
if (c32ncasecmp(conf->url.protocols[i], proto, prot_len) == 0) {
|
||||
state = STATE_URL;
|
||||
start = proto_start[max_prot_len - prot_len];
|
||||
|
||||
wcsncpy(url, proto, prot_len);
|
||||
c32ncpy(url, proto, prot_len);
|
||||
len = prot_len;
|
||||
|
||||
parenthesis = brackets = ltgts = 0;
|
||||
|
|
@ -289,12 +290,12 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
break;
|
||||
|
||||
case STATE_URL: {
|
||||
const wchar_t *match = bsearch(
|
||||
const char32_t *match = bsearch(
|
||||
&wc,
|
||||
uri_characters,
|
||||
uri_characters_count,
|
||||
sizeof(uri_characters[0]),
|
||||
&wccmp);
|
||||
&c32cmp_single);
|
||||
|
||||
bool emit_url = false;
|
||||
|
||||
|
|
@ -313,36 +314,36 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
url[len++] = wc;
|
||||
break;
|
||||
|
||||
case L'(':
|
||||
case U'(':
|
||||
parenthesis++;
|
||||
url[len++] = wc;
|
||||
break;
|
||||
|
||||
case L'[':
|
||||
case U'[':
|
||||
brackets++;
|
||||
url[len++] = wc;
|
||||
break;
|
||||
|
||||
case L'<':
|
||||
case U'<':
|
||||
ltgts++;
|
||||
url[len++] = wc;
|
||||
break;
|
||||
|
||||
case L')':
|
||||
case U')':
|
||||
if (--parenthesis < 0)
|
||||
emit_url = true;
|
||||
else
|
||||
url[len++] = wc;
|
||||
break;
|
||||
|
||||
case L']':
|
||||
case U']':
|
||||
if (--brackets < 0)
|
||||
emit_url = true;
|
||||
else
|
||||
url[len++] = wc;
|
||||
break;
|
||||
|
||||
case L'>':
|
||||
case U'>':
|
||||
if (--ltgts < 0)
|
||||
emit_url = true;
|
||||
else
|
||||
|
|
@ -374,8 +375,8 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
bool done = false;
|
||||
do {
|
||||
switch (url[len - 1]) {
|
||||
case L'.': case L',': case L':': case L';': case L'?':
|
||||
case L'!': case L'"': case L'\'': case L'%':
|
||||
case U'.': case U',': case U':': case U';': case U'?':
|
||||
case U'!': case U'"': case U'\'': case U'%':
|
||||
len--;
|
||||
end.col--;
|
||||
if (end.col < 0) {
|
||||
|
|
@ -390,16 +391,13 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
}
|
||||
} while (!done);
|
||||
|
||||
url[len] = L'\0';
|
||||
url[len] = U'\0';
|
||||
|
||||
start.row += term->grid->view;
|
||||
end.row += term->grid->view;
|
||||
|
||||
size_t chars = wcstombs(NULL, url, 0);
|
||||
if (chars != (size_t)-1) {
|
||||
char *url_utf8 = xmalloc((chars + 1) * sizeof(wchar_t));
|
||||
wcstombs(url_utf8, url, chars + 1);
|
||||
|
||||
char *url_utf8 = ac32tombs(url);
|
||||
if (url_utf8 != NULL) {
|
||||
tll_push_back(
|
||||
*urls,
|
||||
((struct url){
|
||||
|
|
@ -527,40 +525,40 @@ urls_collect(const struct terminal *term, enum url_action action, url_list_t *ur
|
|||
}
|
||||
|
||||
static int
|
||||
wcscmp_qsort_wrapper(const void *_a, const void *_b)
|
||||
c32cmp_qsort_wrapper(const void *_a, const void *_b)
|
||||
{
|
||||
const wchar_t *a = *(const wchar_t **)_a;
|
||||
const wchar_t *b = *(const wchar_t **)_b;
|
||||
return wcscmp(a, b);
|
||||
const char32_t *a = *(const char32_t **)_a;
|
||||
const char32_t *b = *(const char32_t **)_b;
|
||||
return c32cmp(a, b);
|
||||
}
|
||||
|
||||
static void
|
||||
generate_key_combos(const struct config *conf,
|
||||
size_t count, wchar_t *combos[static count])
|
||||
size_t count, char32_t *combos[static count])
|
||||
{
|
||||
const wchar_t *alphabet = conf->url.label_letters;
|
||||
const size_t alphabet_len = wcslen(alphabet);
|
||||
const char32_t *alphabet = conf->url.label_letters;
|
||||
const size_t alphabet_len = c32len(alphabet);
|
||||
|
||||
size_t hints_count = 1;
|
||||
wchar_t **hints = xmalloc(hints_count * sizeof(hints[0]));
|
||||
char32_t **hints = xmalloc(hints_count * sizeof(hints[0]));
|
||||
|
||||
hints[0] = xwcsdup(L"");
|
||||
hints[0] = xc32dup(U"");
|
||||
|
||||
size_t offset = 0;
|
||||
do {
|
||||
const wchar_t *prefix = hints[offset++];
|
||||
const size_t prefix_len = wcslen(prefix);
|
||||
const char32_t *prefix = hints[offset++];
|
||||
const size_t prefix_len = c32len(prefix);
|
||||
|
||||
hints = xrealloc(hints, (hints_count + alphabet_len) * sizeof(hints[0]));
|
||||
|
||||
const wchar_t *wc = &alphabet[0];
|
||||
const char32_t *wc = &alphabet[0];
|
||||
for (size_t i = 0; i < alphabet_len; i++, wc++) {
|
||||
wchar_t *hint = xmalloc((prefix_len + 1 + 1) * sizeof(wchar_t));
|
||||
char32_t *hint = xmalloc((prefix_len + 1 + 1) * sizeof(char32_t));
|
||||
hints[hints_count + i] = hint;
|
||||
|
||||
/* Will be reversed later */
|
||||
hint[0] = *wc;
|
||||
wcscpy(&hint[1], prefix);
|
||||
c32cpy(&hint[1], prefix);
|
||||
}
|
||||
hints_count += alphabet_len;
|
||||
} while (hints_count - offset < count);
|
||||
|
|
@ -578,13 +576,13 @@ generate_key_combos(const struct config *conf,
|
|||
|
||||
/* Sorting is a kind of shuffle, since we’re sorting on the
|
||||
* *reversed* strings */
|
||||
qsort(combos, count, sizeof(wchar_t *), &wcscmp_qsort_wrapper);
|
||||
qsort(combos, count, sizeof(char32_t *), &c32cmp_qsort_wrapper);
|
||||
|
||||
/* Reverse all strings */
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const size_t len = wcslen(combos[i]);
|
||||
const size_t len = c32len(combos[i]);
|
||||
for (size_t j = 0; j < len / 2; j++) {
|
||||
wchar_t tmp = combos[i][j];
|
||||
char32_t tmp = combos[i][j];
|
||||
combos[i][j] = combos[i][len - j - 1];
|
||||
combos[i][len - j - 1] = tmp;
|
||||
}
|
||||
|
|
@ -599,7 +597,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
|||
return;
|
||||
|
||||
uint64_t seen_ids[count];
|
||||
wchar_t *combos[count];
|
||||
char32_t *combos[count];
|
||||
generate_key_combos(conf, count, combos);
|
||||
|
||||
size_t combo_idx = 0;
|
||||
|
|
@ -629,7 +627,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
|||
break;
|
||||
|
||||
if (strcmp(it->item.url, it2->item.url) == 0) {
|
||||
it->item.key = xwcsdup(it2->item.key);
|
||||
it->item.key = xc32dup(it2->item.key);
|
||||
url_already_seen = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -648,9 +646,11 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
|||
if (it->item.key == NULL)
|
||||
continue;
|
||||
|
||||
char key[32];
|
||||
wcstombs(key, it->item.key, sizeof(key) - 1);
|
||||
char *key = ac32tombs(it->item.key);
|
||||
xassert(key != NULL);
|
||||
|
||||
LOG_DBG("URL: %s (%s)", it->item.url, key);
|
||||
free(key);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
27
vt.c
27
vt.c
|
|
@ -11,6 +11,7 @@
|
|||
#define LOG_MODULE "vt"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "char32.h"
|
||||
#include "config.h"
|
||||
#include "csi.h"
|
||||
#include "dcs.h"
|
||||
|
|
@ -182,14 +183,14 @@ action_execute(struct terminal *term, uint8_t c)
|
|||
struct row *row = term->grid->cur_row;
|
||||
|
||||
bool emit_tab_char = (row->cells[start_col].wc == 0 ||
|
||||
row->cells[start_col].wc == L' ');
|
||||
row->cells[start_col].wc == U' ');
|
||||
|
||||
/* Check if all cells from here until the next tab stop are empty */
|
||||
for (const struct cell *cell = &row->cells[start_col + 1];
|
||||
cell < &row->cells[new_col];
|
||||
cell++)
|
||||
{
|
||||
if (!(cell->wc == 0 || cell->wc == L' ')) {
|
||||
if (!(cell->wc == 0 || cell->wc == U' ')) {
|
||||
emit_tab_char = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -202,14 +203,14 @@ action_execute(struct terminal *term, uint8_t c)
|
|||
if (emit_tab_char) {
|
||||
row->dirty = true;
|
||||
|
||||
row->cells[start_col].wc = '\t';
|
||||
row->cells[start_col].wc = U'\t';
|
||||
row->cells[start_col].attrs.clean = 0;
|
||||
|
||||
for (struct cell *cell = &row->cells[start_col + 1];
|
||||
cell < &row->cells[new_col];
|
||||
cell++)
|
||||
{
|
||||
cell->wc = L' ';
|
||||
cell->wc = U' ';
|
||||
cell->attrs.clean = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -549,7 +550,7 @@ action_esc_dispatch(struct terminal *term, uint8_t final)
|
|||
for (int r = 0; r < term->rows; r++) {
|
||||
struct row *row = grid_row(term->grid, r);
|
||||
for (int c = 0; c < term->cols; c++) {
|
||||
row->cells[c].wc = L'E';
|
||||
row->cells[c].wc = U'E';
|
||||
row->cells[c].attrs = (struct attributes){0};
|
||||
}
|
||||
row->dirty = true;
|
||||
|
|
@ -617,9 +618,9 @@ chain_key(uint32_t old_key, uint32_t new_wc)
|
|||
}
|
||||
|
||||
static void
|
||||
action_utf8_print(struct terminal *term, wchar_t wc)
|
||||
action_utf8_print(struct terminal *term, char32_t wc)
|
||||
{
|
||||
int width = wcwidth(wc);
|
||||
int width = c32width(wc);
|
||||
const bool grapheme_clustering = term->conf->tweak.grapheme_shaping;
|
||||
|
||||
#if !defined(FOOT_GRAPHEME_CLUSTERING)
|
||||
|
|
@ -640,8 +641,8 @@ action_utf8_print(struct terminal *term, wchar_t wc)
|
|||
col--;
|
||||
|
||||
xassert(col >= 0 && col < term->cols);
|
||||
wchar_t base = row->cells[col].wc;
|
||||
wchar_t UNUSED last = base;
|
||||
char32_t base = row->cells[col].wc;
|
||||
char32_t UNUSED last = base;
|
||||
|
||||
/* Is base cell already a cluster? */
|
||||
const struct composed *composed =
|
||||
|
|
@ -672,7 +673,7 @@ action_utf8_print(struct terminal *term, wchar_t wc)
|
|||
}
|
||||
#endif
|
||||
|
||||
int base_width = wcwidth(base);
|
||||
int base_width = c32width(base);
|
||||
if (base_width > 0) {
|
||||
term->grid->cursor.point.col = col;
|
||||
term->grid->cursor.lcf = false;
|
||||
|
|
@ -682,11 +683,11 @@ action_utf8_print(struct terminal *term, wchar_t wc)
|
|||
bool comb_from_primary;
|
||||
bool pre_from_primary;
|
||||
|
||||
wchar_t precomposed = fcft_precompose(
|
||||
char32_t precomposed = fcft_precompose(
|
||||
term->fonts[0], base, wc, &base_from_primary,
|
||||
&comb_from_primary, &pre_from_primary);
|
||||
|
||||
int precomposed_width = wcwidth(precomposed);
|
||||
int precomposed_width = c32width(precomposed);
|
||||
|
||||
/*
|
||||
* Only use the pre-composed character if:
|
||||
|
|
@ -698,7 +699,7 @@ action_utf8_print(struct terminal *term, wchar_t wc)
|
|||
* font
|
||||
*/
|
||||
|
||||
if (precomposed != (wchar_t)-1 &&
|
||||
if (precomposed != (char32_t)-1 &&
|
||||
precomposed_width == base_width &&
|
||||
(pre_from_primary ||
|
||||
!base_from_primary ||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
13
xmalloc.c
13
xmalloc.c
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue