Add an option to use a modern wcwidth() instead of the system one

This commit is contained in:
Kovid Goyal 2017-01-12 19:28:51 +05:30
parent a8408a1ce4
commit cb5d6547e5
8 changed files with 1364 additions and 15 deletions

View File

@ -109,6 +109,7 @@ type_map = {
'cursor_blink_interval': float, 'cursor_blink_interval': float,
'cursor_stop_blinking_after': float, 'cursor_stop_blinking_after': float,
'enabled_layouts': to_layout_names, 'enabled_layouts': to_layout_names,
'use_system_wcwidth': to_bool,
} }
for name in 'foreground background cursor active_border_color inactive_border_color selection_foreground selection_background'.split(): for name in 'foreground background cursor active_border_color inactive_border_color selection_foreground selection_background'.split():

View File

@ -20,6 +20,17 @@ drain_read(PyObject UNUSED *self, PyObject *fd) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject*
wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {
return PyLong_FromUnsignedLong(safe_wcwidth(PyLong_AsLong(chr)));
}
static PyObject*
change_wcwidth_wrap(PyObject UNUSED *self, PyObject *use9) {
change_wcwidth(PyObject_IsTrue(use9));
Py_RETURN_NONE;
}
static PyMethodDef module_methods[] = { static PyMethodDef module_methods[] = {
GL_METHODS GL_METHODS
{"drain_read", (PyCFunction)drain_read, METH_O, ""}, {"drain_read", (PyCFunction)drain_read, METH_O, ""},
@ -27,6 +38,8 @@ static PyMethodDef module_methods[] = {
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""}, {"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
{"read_bytes", (PyCFunction)read_bytes, METH_VARARGS, ""}, {"read_bytes", (PyCFunction)read_bytes, METH_VARARGS, ""},
{"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""}, {"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""},
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
GLFW_FUNC_WRAPPERS GLFW_FUNC_WRAPPERS
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };

View File

@ -355,6 +355,8 @@ void historybuf_add_line(HistoryBuf *self, const Line *line);
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other); void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other);
void historybuf_init_line(HistoryBuf *self, index_type num, Line *l); void historybuf_init_line(HistoryBuf *self, index_type num, Line *l);
unsigned int safe_wcwidth(uint32_t ch);
void change_wcwidth(bool use9);
void screen_align(Screen*); void screen_align(Screen*);
void screen_restore_cursor(Screen *); void screen_restore_cursor(Screen *);
void screen_save_cursor(Screen *); void screen_save_cursor(Screen *);

View File

@ -73,6 +73,15 @@ open_url_modifiers ctrl+shift
# use the operating system's default URL handler. # use the operating system's default URL handler.
open_url_with default open_url_with default
# Choose whether to use the system implementation of wcwidth() (used to
# control how many cells a character is rendered in). If you use the system
# implementation, then kitty and any programs running in it will agree. The
# problem is that system implementations often are based on outdated unicode
# standards and get the width of many characters, such as emoji, wrong. So if
# you are using kitty with programs that have their own up-to-date wcwidth()
# implementation, set this option to no.
use_system_wcwidth yes
# The value of the TERM environment variable to set # The value of the TERM environment variable to set
term xterm-kitty term xterm-kitty

View File

@ -21,7 +21,7 @@ from .fast_data_types import (
GLFW_CONTEXT_VERSION_MINOR, GLFW_OPENGL_PROFILE, GLFW_CONTEXT_VERSION_MINOR, GLFW_OPENGL_PROFILE,
GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_CORE_PROFILE, GLFW_SAMPLES, GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_CORE_PROFILE, GLFW_SAMPLES,
glfw_set_error_callback, glfw_init, glfw_terminate, glfw_window_hint, glfw_set_error_callback, glfw_init, glfw_terminate, glfw_window_hint,
glfw_swap_interval, glfw_wait_events, Window glfw_swap_interval, glfw_wait_events, Window, change_wcwidth
) )
from .utils import safe_print from .utils import safe_print
@ -137,6 +137,7 @@ def main():
main(args.replay_commands) main(args.replay_commands)
return return
opts = load_config(args.config) opts = load_config(args.config)
change_wcwidth(not opts.use_system_wcwidth)
glfw_set_error_callback(on_glfw_error) glfw_set_error_callback(on_glfw_error)
enable_automatic_opengl_error_checking(False) enable_automatic_opengl_error_checking(False)
if not glfw_init(): if not glfw_init():

View File

@ -10,6 +10,7 @@
#include "unicode-data.h" #include "unicode-data.h"
#include "tracker.h" #include "tracker.h"
#include "modes.h" #include "modes.h"
#include "wcwidth9.h"
static const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true, .mDECARM=true}; static const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true, .mDECARM=true};
@ -195,13 +196,21 @@ screen_designate_charset(Screen *self, uint32_t which, uint32_t as) {
} }
} }
static inline unsigned int static int (*wcwidth_impl)(wchar_t) = wcwidth;
unsigned int
safe_wcwidth(uint32_t ch) { safe_wcwidth(uint32_t ch) {
int ans = wcwidth(ch); int ans = wcwidth_impl(ch);
if (ans < 0) ans = 1; if (ans < 0) ans = 1;
return MIN(2, ans); return MIN(2, ans);
} }
void
change_wcwidth(bool use9) {
wcwidth_impl = (use9) ? wcwidth9 : wcwidth;
}
void void
screen_draw(Screen *self, uint32_t och) { screen_draw(Screen *self, uint32_t och) {
if (is_ignored_char(och)) return; if (is_ignored_char(och)) return;

View File

@ -7,7 +7,6 @@ import os
import signal import signal
import shlex import shlex
import subprocess import subprocess
import ctypes
import math import math
from collections import namedtuple from collections import namedtuple
from contextlib import contextmanager from contextlib import contextmanager
@ -15,13 +14,7 @@ from functools import lru_cache
from time import monotonic from time import monotonic
from .constants import isosx from .constants import isosx
from .fast_data_types import glfw_get_physical_dpi from .fast_data_types import glfw_get_physical_dpi, wcwidth as wcwidth_impl
libc = ctypes.CDLL(None)
wcwidth_native = libc.wcwidth
del libc
wcwidth_native.argtypes = [ctypes.c_wchar]
wcwidth_native.restype = ctypes.c_int
def safe_print(*a, **k): def safe_print(*a, **k):
@ -37,10 +30,10 @@ def ceil_int(x):
@lru_cache(maxsize=2**13) @lru_cache(maxsize=2**13)
def wcwidth(c: str) -> int: def wcwidth(c: str) -> int:
ans = min(2, wcwidth_native(c)) try:
if ans == -1: return wcwidth_impl(ord(c))
ans = 1 except TypeError:
return ans return wcwidth_impl(ord(c[0]))
@contextmanager @contextmanager

1321
kitty/wcwidth9.h Normal file

File diff suppressed because it is too large Load Diff