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