diff --git a/docs/changelog.rst b/docs/changelog.rst index 67ae96ae6..9caf054b7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -85,6 +85,8 @@ Detailed list of changes - Wayland: Support preferred integer scales +- Wayland: A new option :opt:`wayland_enable_ime` to turn off Input Method Extensions which add latency and create bugs + 0.33.1 [2024-03-21] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 4c5b8da42..925098267 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1168,6 +1168,7 @@ typedef enum { * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). */ #define GLFW_COCOA_MENUBAR 0x00051002 +#define GLFW_WAYLAND_IME 0x00051003 /*! @} */ #define GLFW_DONT_CARE -1 diff --git a/glfw/init.c b/glfw/init.c index 8d09106a1..470eef49a 100644 --- a/glfw/init.c +++ b/glfw/init.c @@ -57,7 +57,10 @@ static _GLFWinitconfig _glfwInitHints = { .ns = { .menubar = true, // macOS menu bar .chdir = true // macOS bundle chdir - } + }, + .wl = { + .ime = true, // Wayland IME support + }, }; // Terminate the library @@ -296,6 +299,9 @@ GLFWAPI void glfwInitHint(int hint, int value) case GLFW_COCOA_MENUBAR: _glfwInitHints.ns.menubar = value; return; + case GLFW_WAYLAND_IME: + _glfwInitHints.wl.ime = value; + return; } _glfwInputError(GLFW_INVALID_ENUM, diff --git a/glfw/internal.h b/glfw/internal.h index 381251df9..6a505c0fd 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -286,6 +286,9 @@ struct _GLFWinitconfig bool menubar; bool chdir; } ns; + struct { + bool ime; + } wl; }; // Window configuration diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 4befdab82..bf50c1b8d 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -671,7 +671,6 @@ static void registryHandleGlobal(void* data UNUSED, if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) { _glfwSetupWaylandPrimarySelectionDevice(); } - _glfwWaylandInitTextInput(); } } else if (is(xdg_wm_base)) @@ -703,7 +702,6 @@ static void registryHandleGlobal(void* data UNUSED, else if (is(zwp_text_input_manager_v3)) { _glfwWaylandBindTextInput(registry, name); - _glfwWaylandInitTextInput(); } else if (is(wl_data_device_manager)) { @@ -873,6 +871,7 @@ int _glfwPlatformInit(void) // Sync so we got all registry objects wl_display_roundtrip(_glfw.wl.display); + _glfwWaylandInitTextInput(); // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); diff --git a/glfw/wl_text_input.c b/glfw/wl_text_input.c index 4608d3bfd..76b30f9bd 100644 --- a/glfw/wl_text_input.c +++ b/glfw/wl_text_input.c @@ -117,7 +117,7 @@ text_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, u void _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) { - if (!text_input_manager) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); + if (!text_input_manager && _glfw.hints.init.wl.ime) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); } void @@ -130,12 +130,9 @@ _glfwWaylandInitTextInput(void) { .delete_surrounding_text = text_input_delete_surrounding_text, .done = text_input_done, }; - if (!text_input) { - if (text_input_manager && _glfw.wl.seat) { - text_input = zwp_text_input_manager_v3_get_text_input( - text_input_manager, _glfw.wl.seat); - if (text_input) zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL); - } + if (_glfw.hints.init.wl.ime && !text_input && text_input_manager && _glfw.wl.seat) { + text_input = zwp_text_input_manager_v3_get_text_input(text_input_manager, _glfw.wl.seat); + if (text_input) zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL); } } diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 3bd8de3e3..c7c226313 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -587,7 +587,10 @@ def glfw_terminate() -> None: pass -def glfw_init(path: str, edge_spacing_func: Callable[[EdgeLiteral], float], debug_keyboard: bool = False, debug_rendering: bool = False) -> bool: +def glfw_init( + path: str, edge_spacing_func: Callable[[EdgeLiteral], float], debug_keyboard: bool = False, debug_rendering: bool = False, + wayland_enable_ime: bool = True +) -> bool: pass diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 3058d282e..ab6303488 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -906,6 +906,7 @@ typedef enum { * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). */ #define GLFW_COCOA_MENUBAR 0x00051002 +#define GLFW_WAYLAND_IME 0x00051003 /*! @} */ #define GLFW_DONT_CARE -1 diff --git a/kitty/glfw.c b/kitty/glfw.c index ea5c67a71..3c425fc40 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1422,9 +1422,9 @@ dbus_user_notification_activated(uint32_t notification_id, const char* action) { static PyObject* glfw_init(PyObject UNUSED *self, PyObject *args) { const char* path; - int debug_keyboard = 0, debug_rendering = 0; + int debug_keyboard = 0, debug_rendering = 0, wayland_enable_ime = 0; PyObject *edge_sf; - if (!PyArg_ParseTuple(args, "sO|pp", &path, &edge_sf, &debug_keyboard, &debug_rendering)) return NULL; + if (!PyArg_ParseTuple(args, "sO|ppp", &path, &edge_sf, &debug_keyboard, &debug_rendering, &wayland_enable_ime)) return NULL; if (!PyCallable_Check(edge_sf)) { PyErr_SetString(PyExc_TypeError, "edge_spacing_func must be a callable"); return NULL; } Py_CLEAR(edge_spacing_func); #ifdef __APPLE__ @@ -1436,6 +1436,7 @@ glfw_init(PyObject UNUSED *self, PyObject *args) { glfwInitHint(GLFW_DEBUG_KEYBOARD, debug_keyboard); glfwInitHint(GLFW_DEBUG_RENDERING, debug_rendering); OPT(debug_keyboard) = debug_keyboard != 0; + glfwInitHint(GLFW_WAYLAND_IME, wayland_enable_ime != 0); #ifdef __APPLE__ glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, 0); glfwInitHint(GLFW_COCOA_MENUBAR, 0); diff --git a/kitty/main.py b/kitty/main.py index c4237a38f..20b86f9b3 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -134,14 +134,14 @@ def load_all_shaders(semi_transparent: bool = False) -> None: raise SystemExit(err) -def init_glfw_module(glfw_module: str, debug_keyboard: bool = False, debug_rendering: bool = False) -> None: - if not glfw_init(glfw_path(glfw_module), edge_spacing, debug_keyboard, debug_rendering): +def init_glfw_module(glfw_module: str, debug_keyboard: bool = False, debug_rendering: bool = False, wayland_enable_ime: bool = True) -> None: + if not glfw_init(glfw_path(glfw_module), edge_spacing, debug_keyboard, debug_rendering, wayland_enable_ime): raise SystemExit('GLFW initialization failed') def init_glfw(opts: Options, debug_keyboard: bool = False, debug_rendering: bool = False) -> str: glfw_module = 'cocoa' if is_macos else ('wayland' if is_wayland(opts) else 'x11') - init_glfw_module(glfw_module, debug_keyboard, debug_rendering) + init_glfw_module(glfw_module, debug_keyboard, debug_rendering, wayland_enable_ime=opts.wayland_enable_ime) return glfw_module diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 29d03f548..f93f31f77 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -3411,6 +3411,14 @@ based on the system state is chosen automatically. Set it to :code:`x11` or config is not supported. ''' ) + +opt('wayland_enable_ime', 'yes', option_type='to_bool', ctype='bool', long_text=''' +Enable Input Method Extension on Wayland. This is typically used for +inputting text in East Asian languages. However, its implementation in +Wayland is often buggy and introduces latency into the input loop, +so disable this if you know you dont need it. Changing this option +by reloading the config is not supported, it will not have any effect. +''') egr() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 32af7520e..b08b460fc 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1358,6 +1358,9 @@ class Parser: for k, v in store_multiple(val, ans["watcher"]): ans["watcher"][k] = v + def wayland_enable_ime(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['wayland_enable_ime'] = to_bool(val) + def wayland_titlebar_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['wayland_titlebar_color'] = titlebar_color(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 82ac22744..eba012444 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -1123,6 +1123,19 @@ convert_from_opts_macos_colorspace(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_wayland_enable_ime(PyObject *val, Options *opts) { + opts->wayland_enable_ime = PyObject_IsTrue(val); +} + +static void +convert_from_opts_wayland_enable_ime(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "wayland_enable_ime"); + if (ret == NULL) return; + convert_from_python_wayland_enable_ime(ret, opts); + Py_DECREF(ret); +} + static bool convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { convert_from_opts_font_size(py_opts, opts); @@ -1297,5 +1310,7 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_macos_colorspace(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_wayland_enable_ime(py_opts, opts); + if (PyErr_Occurred()) return false; return true; } diff --git a/kitty/options/types.py b/kitty/options/types.py index 94e4b67db..dac33a8d6 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -450,6 +450,7 @@ option_names = ( # {{{ 'visual_bell_duration', 'visual_window_select_characters', 'watcher', + 'wayland_enable_ime', 'wayland_titlebar_color', 'wheel_scroll_min_lines', 'wheel_scroll_multiplier', @@ -609,6 +610,7 @@ class Options: visual_bell_color: typing.Optional[kitty.fast_data_types.Color] = None visual_bell_duration: float = 0 visual_window_select_characters: str = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ' + wayland_enable_ime: bool = True wayland_titlebar_color: int = 0 wheel_scroll_min_lines: int = 1 wheel_scroll_multiplier: float = 5.0 diff --git a/kitty/state.h b/kitty/state.h index a800b84b9..ec7c74035 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -101,6 +101,7 @@ typedef struct { long macos_titlebar_color; unsigned long wayland_titlebar_color; struct { struct MenuItem *entries; size_t count; } global_menu; + bool wayland_enable_ime; } Options; typedef struct WindowLogoRenderData {