Merge branch 'master' of https://github.com/kovidgoyal/kitty into fix-rectircle-subpixel-rendering
This commit is contained in:
@@ -102,6 +102,12 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
- Improve rendering of rounded corners by using a rectircle equation rather
|
||||
than a cubic bezier (:iss:`3409`)
|
||||
|
||||
- Graphics protocol: Add a control to allow clients to specify that the cursor
|
||||
should not move when displaying an image (:iss:`3411`)
|
||||
|
||||
- Fix marking of text not working on lines that contain zero cells
|
||||
(:iss:`3403`)
|
||||
|
||||
|
||||
0.19.3 [2020-12-19]
|
||||
-------------------
|
||||
@@ -138,6 +144,10 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
- Wayland: Fix key repeat being stopped by the release of an unrelated key
|
||||
(:iss:`2191`)
|
||||
|
||||
- Wayland: Add support for the text input protocol (:iss:`3410`)
|
||||
|
||||
- Wayland: Fix mouse handling when using client side decorations
|
||||
|
||||
- Add an option, :opt:`detect_urls` to control whether kitty will detect URLs
|
||||
when the mouse moves over them (:pull:`3118`)
|
||||
|
||||
|
||||
@@ -401,6 +401,12 @@ colors.
|
||||
number of rows in the image placement rectangle. If either of these cause
|
||||
the cursor to leave either the screen or the scroll area, the exact
|
||||
positioning of the cursor is undefined, and up to implementations.
|
||||
The client can ask the terminal emulator to not move the cursor at all
|
||||
by specifying ``C=1`` in the command, which sets the cursor movement policy
|
||||
to no movement for placing the current image.
|
||||
|
||||
.. versionadded:: 0.20.0
|
||||
Support for the C=1 cursor movement policy
|
||||
|
||||
|
||||
Deleting images
|
||||
@@ -680,6 +686,8 @@ Key Value Default Description
|
||||
``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image
|
||||
``c`` Positive integer ``0`` The number of columns to display the image over
|
||||
``r`` Positive integer ``0`` The number of rows to display the image over
|
||||
``C`` Positive integer ``0`` Cursor movement policy. ``0`` is the default, to move the cursor to after the image.
|
||||
``1`` is to not move the cursor at all when placing the image.
|
||||
``z`` 32-bit integer ``0`` The *z-index* vertical stacking order of the image
|
||||
|
||||
**Keys for animation frame loading**
|
||||
|
||||
@@ -273,6 +273,7 @@ def graphics_parser() -> None:
|
||||
'X': ('cell_x_offset', 'uint'),
|
||||
'Y': ('cell_y_offset', 'uint'),
|
||||
'z': ('z_index', 'int'),
|
||||
'C': ('cursor_movement', 'uint'),
|
||||
}
|
||||
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
|
||||
write_header(text, 'kitty/parse-graphics-command.h')
|
||||
|
||||
@@ -1073,7 +1073,7 @@ is_ascii_control_char(char x) {
|
||||
if (input_source_changed) {
|
||||
debug_key(@"Input source changed, clearing pre-edit text and resetting deadkey state\n");
|
||||
glfw_keyevent.text = NULL;
|
||||
glfw_keyevent.ime_state = 1;
|
||||
glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED;
|
||||
window->ns.deadKeyState = 0;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text
|
||||
}
|
||||
@@ -1110,14 +1110,14 @@ is_ascii_control_char(char x) {
|
||||
// 0x75 is the delete key which needs to be ignored during a compose sequence
|
||||
debug_key(@"Sending pre-edit text for dead key (text: %@ markedText: %@).\n", @(format_text(_glfw.ns.text)), markedText);
|
||||
glfw_keyevent.text = [[markedText string] UTF8String];
|
||||
glfw_keyevent.ime_state = 1;
|
||||
glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // update pre-edit text
|
||||
return;
|
||||
}
|
||||
if (in_compose_sequence) {
|
||||
debug_key(@"Clearing pre-edit text at end of compose sequence\n");
|
||||
glfw_keyevent.text = NULL;
|
||||
glfw_keyevent.ime_state = 1;
|
||||
glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text
|
||||
}
|
||||
}
|
||||
@@ -1127,11 +1127,11 @@ is_ascii_control_char(char x) {
|
||||
if (!window->ns.deadKeyState) {
|
||||
if ([self hasMarkedText]) {
|
||||
glfw_keyevent.text = [[markedText string] UTF8String];
|
||||
glfw_keyevent.ime_state = 1;
|
||||
glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // update pre-edit text
|
||||
} else if (previous_has_marked_text) {
|
||||
glfw_keyevent.text = NULL;
|
||||
glfw_keyevent.ime_state = 1;
|
||||
glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text
|
||||
}
|
||||
if (([self hasMarkedText] || previous_has_marked_text) && !_glfw.ns.text[0]) {
|
||||
@@ -1140,7 +1140,7 @@ is_ascii_control_char(char x) {
|
||||
}
|
||||
}
|
||||
glfw_keyevent.text = _glfw.ns.text;
|
||||
glfw_keyevent.ime_state = 0;
|
||||
glfw_keyevent.ime_state = GLFW_IME_NONE;
|
||||
add_alternate_keys(&glfw_keyevent, event);
|
||||
_glfwInputKeyboard(window, &glfw_keyevent);
|
||||
}
|
||||
@@ -1295,11 +1295,11 @@ is_ascii_control_char(char x) {
|
||||
[[markedText mutableString] setString:@""];
|
||||
}
|
||||
|
||||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) {
|
||||
[w->ns.view updateIMEStateFor: which left:(CGFloat)a top:(CGFloat)b cellWidth:(CGFloat)c cellHeight:(CGFloat)d];
|
||||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
[w->ns.view updateIMEStateFor: ev->type left:(CGFloat)ev->cursor.left top:(CGFloat)ev->cursor.top cellWidth:(CGFloat)ev->cursor.width cellHeight:(CGFloat)ev->cursor.height];
|
||||
}
|
||||
|
||||
- (void)updateIMEStateFor:(int)which
|
||||
- (void)updateIMEStateFor:(GLFWIMEUpdateType)which
|
||||
left:(CGFloat)left
|
||||
top:(CGFloat)top
|
||||
cellWidth:(CGFloat)cellWidth
|
||||
|
||||
36
glfw/glfw3.h
vendored
36
glfw/glfw3.h
vendored
@@ -1179,12 +1179,34 @@ typedef struct GLFWwindow GLFWwindow;
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef struct GLFWcursor GLFWcursor;
|
||||
|
||||
typedef enum {
|
||||
GLFW_RELEASE = 0,
|
||||
GLFW_PRESS = 1,
|
||||
GLFW_REPEAT = 2
|
||||
} GLFWKeyAction;
|
||||
|
||||
typedef enum {
|
||||
GLFW_IME_NONE,
|
||||
GLFW_IME_PREEDIT_CHANGED,
|
||||
GLFW_IME_COMMIT_TEXT
|
||||
} GLFWIMEState;
|
||||
|
||||
typedef enum {
|
||||
GLFW_IME_UPDATE_FOCUS = 1,
|
||||
GLFW_IME_UPDATE_CURSOR_POSITION = 2
|
||||
} GLFWIMEUpdateType;
|
||||
|
||||
typedef struct GLFWIMEUpdateEvent {
|
||||
GLFWIMEUpdateType type;
|
||||
const char *before_text, *at_text, *after_text;
|
||||
bool focused;
|
||||
struct {
|
||||
int left, top, width, height;
|
||||
} cursor;
|
||||
} GLFWIMEUpdateEvent;
|
||||
|
||||
|
||||
typedef struct GLFWkeyevent
|
||||
{
|
||||
// The [keyboard key](@ref keys) that was pressed or released.
|
||||
@@ -1203,9 +1225,9 @@ typedef struct GLFWkeyevent
|
||||
const char *text;
|
||||
|
||||
// Used for Input Method events. Zero for normal key events.
|
||||
// A value of 1 means the pre-edit text for the input event has been changed.
|
||||
// A value of 2 means the text should be committed.
|
||||
int ime_state;
|
||||
// A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed.
|
||||
// A value of GLFW_IME_COMMIT_TEXT means the text should be committed.
|
||||
GLFWIMEState ime_state;
|
||||
} GLFWkeyevent;
|
||||
|
||||
/*! @brief The function pointer type for error callbacks.
|
||||
@@ -4533,16 +4555,12 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboard
|
||||
* Used to notify the IME system of changes in state such as focus gained/lost
|
||||
* and text cursor position.
|
||||
*
|
||||
* @param which: What data to notify. 1 means focus and 2 means cursor position.
|
||||
* @param a, b, c, d: Interpreted based on the value of which. When which is 1
|
||||
* a is interpreted as a boolean indicating focus gained/lost. When which is 2
|
||||
* a, b, c, d are the cursor x, y, width and height values (in the window co-ordinate
|
||||
* system).
|
||||
* @param ev: What data to notify.
|
||||
*
|
||||
* @ingroup input
|
||||
* @since Added in version 4.0
|
||||
*/
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, int which, int a, int b, int c, int d);
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, const GLFWIMEUpdateEvent *ev);
|
||||
|
||||
|
||||
/*! @brief Sets the mouse button callback.
|
||||
|
||||
6
glfw/ibus_glfw.c
vendored
6
glfw/ibus_glfw.c
vendored
@@ -107,7 +107,7 @@ get_ibus_text_from_message(DBusMessage *msg) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, int ime_state) {
|
||||
send_text(const char *text, GLFWIMEState ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
@@ -130,11 +130,11 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
|
||||
case 0:
|
||||
text = get_ibus_text_from_message(msg);
|
||||
debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
|
||||
send_text(text, 2);
|
||||
send_text(text, GLFW_IME_COMMIT_TEXT);
|
||||
break;
|
||||
case 1:
|
||||
text = get_ibus_text_from_message(msg);
|
||||
send_text(text, 1);
|
||||
send_text(text, GLFW_IME_PREEDIT_CHANGED);
|
||||
debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
|
||||
break;
|
||||
case 2:
|
||||
|
||||
4
glfw/input.c
vendored
4
glfw/input.c
vendored
@@ -1010,13 +1010,13 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboard
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, int which, int a, int b, int c, int d) {
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, const GLFWIMEUpdateEvent *ev) {
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(_GLFW_COCOA)
|
||||
_glfwPlatformUpdateIMEState(window, which, a, b, c, d);
|
||||
_glfwPlatformUpdateIMEState(window, ev);
|
||||
#else
|
||||
(void)window; (void)which; (void)a; (void)b; (void)c; (void)d;
|
||||
#endif
|
||||
|
||||
2
glfw/internal.h
vendored
2
glfw/internal.h
vendored
@@ -728,7 +728,7 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled);
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled);
|
||||
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled);
|
||||
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
|
||||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d);
|
||||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev);
|
||||
|
||||
void _glfwPlatformPollEvents(void);
|
||||
void _glfwPlatformWaitEvents(void);
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"wl_platform.h",
|
||||
"posix_thread.h",
|
||||
"wl_cursors.h",
|
||||
"wl_text_input.h",
|
||||
"xkb_glfw.h",
|
||||
"dbus_glfw.h",
|
||||
"ibus_glfw.h",
|
||||
@@ -73,13 +74,15 @@
|
||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
|
||||
"unstable/xdg-decoration/xdg-decoration-unstable-v1.xml",
|
||||
"unstable/primary-selection/primary-selection-unstable-v1.xml"
|
||||
"unstable/primary-selection/primary-selection-unstable-v1.xml",
|
||||
"unstable/text-input/text-input-unstable-v3.xml"
|
||||
],
|
||||
"sources": [
|
||||
"wl_init.c",
|
||||
"wl_monitor.c",
|
||||
"wl_window.c",
|
||||
"wl_cursors.c",
|
||||
"wl_text_input.c",
|
||||
"posix_thread.c",
|
||||
"xkb_glfw.c",
|
||||
"dbus_glfw.c",
|
||||
|
||||
31
glfw/wl_init.c
vendored
31
glfw/wl_init.c
vendored
@@ -173,6 +173,8 @@ static void setCursor(GLFWCursorShape shape, _GLFWwindow* window)
|
||||
_glfw.wl.cursorPreviousShape = shape;
|
||||
}
|
||||
|
||||
#define x window->wl.allCursorPosX
|
||||
#define y window->wl.allCursorPosY
|
||||
static void pointerHandleMotion(void* data UNUSED,
|
||||
struct wl_pointer* pointer UNUSED,
|
||||
uint32_t time UNUSED,
|
||||
@@ -181,15 +183,14 @@ static void pointerHandleMotion(void* data UNUSED,
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
GLFWCursorShape cursorShape = GLFW_ARROW_CURSOR;
|
||||
double x, y;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
return;
|
||||
x = wl_fixed_to_double(sx);
|
||||
y = wl_fixed_to_double(sy);
|
||||
window->wl.allCursorPosX = wl_fixed_to_double(sx);
|
||||
window->wl.allCursorPosY = wl_fixed_to_double(sy);
|
||||
|
||||
switch (window->wl.decorations.focus)
|
||||
{
|
||||
@@ -252,7 +253,7 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
case mainWindow:
|
||||
break;
|
||||
case topDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
|
||||
else
|
||||
{
|
||||
@@ -261,21 +262,21 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
}
|
||||
break;
|
||||
case leftDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
|
||||
break;
|
||||
case rightDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
|
||||
break;
|
||||
case bottomDecoration:
|
||||
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||
if (x < _GLFW_DECORATION_WIDTH)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
||||
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
else if (x > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
|
||||
@@ -293,10 +294,7 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
{
|
||||
if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel)
|
||||
{
|
||||
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
|
||||
_glfw.wl.seat, serial,
|
||||
(int32_t)window->wl.cursorPosX,
|
||||
(int32_t)window->wl.cursorPosY);
|
||||
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, _glfw.wl.seat, serial, (int32_t)x, (int32_t)y - _GLFW_DECORATION_TOP);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -318,6 +316,8 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
: GLFW_RELEASE,
|
||||
_glfw.wl.xkb.states.modifiers);
|
||||
}
|
||||
#undef x
|
||||
#undef y
|
||||
|
||||
static void pointerHandleAxis(void* data UNUSED,
|
||||
struct wl_pointer* pointer UNUSED,
|
||||
@@ -584,6 +584,7 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
|
||||
_glfwSetupWaylandPrimarySelectionDevice();
|
||||
}
|
||||
_glfwWaylandInitTextInput();
|
||||
}
|
||||
}
|
||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||
@@ -617,6 +618,11 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
&zwp_pointer_constraints_v1_interface,
|
||||
1);
|
||||
}
|
||||
else if (strcmp(interface, GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME) == 0)
|
||||
{
|
||||
_glfwWaylandBindTextInput(registry, name);
|
||||
_glfwWaylandInitTextInput();
|
||||
}
|
||||
else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0)
|
||||
{
|
||||
_glfw.wl.idleInhibitManager =
|
||||
@@ -847,6 +853,7 @@ void _glfwPlatformTerminate(void)
|
||||
zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);
|
||||
if (_glfw.wl.pointerConstraints)
|
||||
zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
|
||||
_glfwWaylandDestroyTextInput();
|
||||
if (_glfw.wl.idleInhibitManager)
|
||||
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
|
||||
if (_glfw.wl.dataSourceForClipboard)
|
||||
|
||||
3
glfw/wl_platform.h
vendored
3
glfw/wl_platform.h
vendored
@@ -59,6 +59,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-primary-selection-unstable-v1-client-protocol.h"
|
||||
#include "wl_text_input.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
@@ -131,7 +132,7 @@ typedef struct _GLFWwindowWayland
|
||||
} xdg;
|
||||
|
||||
_GLFWcursor* currentCursor;
|
||||
double cursorPosX, cursorPosY;
|
||||
double cursorPosX, cursorPosY, allCursorPosX, allCursorPosY;
|
||||
|
||||
char* title;
|
||||
char appId[256];
|
||||
|
||||
129
glfw/wl_text_input.c
vendored
Normal file
129
glfw/wl_text_input.c
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* wl_text_input.c
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "wl_text_input.h"
|
||||
#include "internal.h"
|
||||
#include "wayland-text-input-unstable-v3-client-protocol.h"
|
||||
#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
|
||||
|
||||
static struct zwp_text_input_v3* text_input;
|
||||
static struct zwp_text_input_manager_v3* text_input_manager;
|
||||
|
||||
static void commit(void) { if (text_input) zwp_text_input_v3_commit (text_input); }
|
||||
|
||||
static void
|
||||
text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) {
|
||||
debug("text-input: enter event\n");
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_enable(text_input);
|
||||
zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) {
|
||||
debug("text-input: leave event\n");
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_disable(text_input);
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, GLFWIMEState ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.text = text;
|
||||
fake_ev.ime_state = ime_state;
|
||||
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_preedit_string(
|
||||
void *data UNUSED,
|
||||
struct zwp_text_input_v3 *text_input UNUSED,
|
||||
const char *text,
|
||||
int32_t cursor_begin,
|
||||
int32_t cursor_end
|
||||
) {
|
||||
debug("text-input: preedit_string event: text: %s cursor_begin: %d cursor_end: %d\n", text, cursor_begin, cursor_end);
|
||||
send_text(text, GLFW_IME_PREEDIT_CHANGED);
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text) {
|
||||
debug("text-input: commit_string event: text: %s\n", text);
|
||||
send_text(text, GLFW_IME_COMMIT_TEXT);
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_delete_surrounding_text(
|
||||
void *data UNUSED,
|
||||
struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED,
|
||||
uint32_t before_length,
|
||||
uint32_t after_length) {
|
||||
debug("text-input: delete_surrounding_text event: before_length: %u after_length: %u\n", before_length, after_length);
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_done(void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, uint32_t serial UNUSED) {
|
||||
debug("text-input: done event: serial: %u\n", serial);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandInitTextInput(void) {
|
||||
static const struct zwp_text_input_v3_listener text_input_listener = {
|
||||
.enter = text_input_enter,
|
||||
.leave = text_input_leave,
|
||||
.preedit_string = text_input_preedit_string,
|
||||
.commit_string = text_input_commit_string,
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandDestroyTextInput(void) {
|
||||
if (text_input) zwp_text_input_v3_destroy(text_input);
|
||||
if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager);
|
||||
text_input = NULL; text_input_manager = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
if (!text_input) return;
|
||||
switch(ev->type) {
|
||||
case GLFW_IME_UPDATE_FOCUS:
|
||||
debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused);
|
||||
if (ev->focused) zwp_text_input_v3_enable(text_input); else zwp_text_input_v3_disable(text_input);
|
||||
commit();
|
||||
break;
|
||||
case GLFW_IME_UPDATE_CURSOR_POSITION: {
|
||||
const int scale = w->wl.scale;
|
||||
const int left = ev->cursor.left / scale, top = ev->cursor.top / scale, width = ev->cursor.width / scale, height = ev->cursor.height / scale;
|
||||
debug("\ntext-input: updating cursor position: left=%d top=%d width=%d height=%d\n", left, top, width, height);
|
||||
zwp_text_input_v3_set_cursor_rectangle(text_input, left, top, width, height);
|
||||
commit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
14
glfw/wl_text_input.h
vendored
Normal file
14
glfw/wl_text_input.h
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <wayland-client.h>
|
||||
|
||||
#define GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME "zwp_text_input_manager_v3"
|
||||
|
||||
void _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name);
|
||||
void _glfwWaylandInitTextInput(void);
|
||||
void _glfwWaylandDestroyTextInput(void);
|
||||
5
glfw/wl_window.c
vendored
5
glfw/wl_window.c
vendored
@@ -2104,11 +2104,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) {
|
||||
glfw_xkb_update_ime_state(w, &_glfw.wl.xkb, which, a, b, c, d);
|
||||
}
|
||||
|
||||
static void
|
||||
frame_handle_redraw(void *data, struct wl_callback *callback, uint32_t time UNUSED) {
|
||||
_GLFWwindow* window = (_GLFWwindow*) data;
|
||||
|
||||
4
glfw/x11_window.c
vendored
4
glfw/x11_window.c
vendored
@@ -3085,8 +3085,8 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) {
|
||||
glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, which, a, b, c, d);
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, ev);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
20
glfw/xkb_glfw.c
vendored
20
glfw/xkb_glfw.c
vendored
@@ -349,7 +349,9 @@ glfw_xkb_create_context(_GLFWXKBData *xkb) {
|
||||
"Failed to initialize XKB context");
|
||||
return false;
|
||||
}
|
||||
#ifndef _GLFW_WAYLAND
|
||||
glfw_connect_to_ibus(&xkb->ibus);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -555,16 +557,16 @@ format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) {
|
||||
}
|
||||
|
||||
void
|
||||
glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d) {
|
||||
glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev) {
|
||||
int x = 0, y = 0;
|
||||
switch(which) {
|
||||
case 1:
|
||||
glfw_ibus_set_focused(&xkb->ibus, a ? true : false);
|
||||
switch(ev->type) {
|
||||
case GLFW_IME_UPDATE_FOCUS:
|
||||
glfw_ibus_set_focused(&xkb->ibus, ev->focused);
|
||||
break;
|
||||
case 2:
|
||||
case GLFW_IME_UPDATE_CURSOR_POSITION:
|
||||
_glfwPlatformGetWindowPos(w, &x, &y);
|
||||
x += a; y += b;
|
||||
glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, c, d);
|
||||
x += ev->cursor.left; y += ev->cursor.top;
|
||||
glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, ev->cursor.width, ev->cursor.height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -575,7 +577,7 @@ glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) {
|
||||
if (failed && window && window->callbacks.keyboard) {
|
||||
// notify application to remove any existing pre-edit text
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.ime_state = 1;
|
||||
fake_ev.ime_state = GLFW_IME_PREEDIT_CHANGED;
|
||||
window->callbacks.keyboard((GLFWwindow*) window, &fake_ev);
|
||||
}
|
||||
static xkb_keycode_t last_handled_press_keycode = 0;
|
||||
@@ -594,7 +596,7 @@ glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) {
|
||||
format_mods(ev->glfw_ev.mods), ev->glfw_ev.text
|
||||
);
|
||||
|
||||
ev->glfw_ev.ime_state = 0;
|
||||
ev->glfw_ev.ime_state = GLFW_IME_NONE;
|
||||
_glfwInputKeyboard(window, &ev->glfw_ev);
|
||||
} else debug("↳ discarded\n");
|
||||
if (!is_release && handled_by_ime)
|
||||
|
||||
2
glfw/xkb_glfw.h
vendored
2
glfw/xkb_glfw.h
vendored
@@ -92,5 +92,5 @@ const char* glfw_xkb_keysym_name(xkb_keysym_t sym);
|
||||
xkb_keysym_t glfw_xkb_sym_for_key(uint32_t key);
|
||||
void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t keycode, int action);
|
||||
int glfw_xkb_keysym_from_name(const char *name, bool case_sensitive);
|
||||
void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d);
|
||||
void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev);
|
||||
void glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed);
|
||||
|
||||
@@ -961,7 +961,7 @@ shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells
|
||||
bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));
|
||||
bool is_empty = is_special && is_empty_glyph(glyph_id, font);
|
||||
uint32_t num_codepoints_used_by_glyph = 0;
|
||||
bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1;
|
||||
bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1, is_first_glyph = G(glyph_idx) == 0;
|
||||
Group *current_group = G(groups) + G(group_idx);
|
||||
if (is_last_glyph) {
|
||||
num_codepoints_used_by_glyph = UINT32_MAX;
|
||||
@@ -977,7 +977,8 @@ shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells
|
||||
else add_to_current_group = ligature_type == INFINITE_LIGATURE_MIDDLE || ligature_type == INFINITE_LIGATURE_END || is_empty;
|
||||
} else {
|
||||
if (is_special) {
|
||||
if (font->spacer_strategy == SPACERS_BEFORE) add_to_current_group = G(prev_was_empty);
|
||||
if (!is_first_glyph && !current_group->num_cells) add_to_current_group = true;
|
||||
else if (font->spacer_strategy == SPACERS_BEFORE) add_to_current_group = G(prev_was_empty);
|
||||
else add_to_current_group = is_empty;
|
||||
} else {
|
||||
add_to_current_group = !G(prev_was_special);
|
||||
|
||||
@@ -464,7 +464,8 @@ def rectircle_equations(
|
||||
xexp = radius / cell_width
|
||||
pow = math.pow
|
||||
left_quadrants, lower_quadrants = {'╭': (True, False), '╮': (False, False), '╰': (True, True), '╯': (False, True)}[which]
|
||||
adjust_left_quadrant = (cell_width // supersample_factor % 2) * supersample_factor
|
||||
cell_width_is_odd = (cell_width // supersample_factor) % 2
|
||||
adjust_x = cell_width_is_odd * supersample_factor
|
||||
|
||||
if lower_quadrants:
|
||||
def y(t: float) -> float: # 0 -> top of cell, 1 -> middle of cell
|
||||
@@ -478,7 +479,7 @@ def rectircle_equations(
|
||||
if left_quadrants:
|
||||
def x(t: float) -> float:
|
||||
xterm = 1 - pow(t, yexp)
|
||||
return math.floor(cell_width - abs(a * pow(xterm, xexp)) - adjust_left_quadrant)
|
||||
return math.floor(cell_width - abs(a * pow(xterm, xexp)) - adjust_x)
|
||||
else:
|
||||
def x(t: float) -> float:
|
||||
xterm = 1 - pow(t, yexp)
|
||||
|
||||
30
kitty/glfw-wrapper.h
generated
30
kitty/glfw-wrapper.h
generated
@@ -917,12 +917,34 @@ typedef struct GLFWwindow GLFWwindow;
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef struct GLFWcursor GLFWcursor;
|
||||
|
||||
typedef enum {
|
||||
GLFW_RELEASE = 0,
|
||||
GLFW_PRESS = 1,
|
||||
GLFW_REPEAT = 2
|
||||
} GLFWKeyAction;
|
||||
|
||||
typedef enum {
|
||||
GLFW_IME_NONE,
|
||||
GLFW_IME_PREEDIT_CHANGED,
|
||||
GLFW_IME_COMMIT_TEXT
|
||||
} GLFWIMEState;
|
||||
|
||||
typedef enum {
|
||||
GLFW_IME_UPDATE_FOCUS = 1,
|
||||
GLFW_IME_UPDATE_CURSOR_POSITION = 2
|
||||
} GLFWIMEUpdateType;
|
||||
|
||||
typedef struct GLFWIMEUpdateEvent {
|
||||
GLFWIMEUpdateType type;
|
||||
const char *before_text, *at_text, *after_text;
|
||||
bool focused;
|
||||
struct {
|
||||
int left, top, width, height;
|
||||
} cursor;
|
||||
} GLFWIMEUpdateEvent;
|
||||
|
||||
|
||||
typedef struct GLFWkeyevent
|
||||
{
|
||||
// The [keyboard key](@ref keys) that was pressed or released.
|
||||
@@ -941,9 +963,9 @@ typedef struct GLFWkeyevent
|
||||
const char *text;
|
||||
|
||||
// Used for Input Method events. Zero for normal key events.
|
||||
// A value of 1 means the pre-edit text for the input event has been changed.
|
||||
// A value of 2 means the text should be committed.
|
||||
int ime_state;
|
||||
// A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed.
|
||||
// A value of GLFW_IME_COMMIT_TEXT means the text should be committed.
|
||||
GLFWIMEState ime_state;
|
||||
} GLFWkeyevent;
|
||||
|
||||
/*! @brief The function pointer type for error callbacks.
|
||||
@@ -1920,7 +1942,7 @@ typedef GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboar
|
||||
GFW_EXTERN glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl;
|
||||
#define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl
|
||||
|
||||
typedef void (*glfwUpdateIMEState_func)(GLFWwindow*, int, int, int, int, int);
|
||||
typedef void (*glfwUpdateIMEState_func)(GLFWwindow*, const GLFWIMEUpdateEvent*);
|
||||
GFW_EXTERN glfwUpdateIMEState_func glfwUpdateIMEState_impl;
|
||||
#define glfwUpdateIMEState glfwUpdateIMEState_impl
|
||||
|
||||
|
||||
@@ -343,7 +343,8 @@ window_focus_callback(GLFWwindow *w, int focused) {
|
||||
global_state.callback_os_window->cursor_blink_zero_time = now;
|
||||
if (is_window_ready_for_callbacks()) {
|
||||
WINDOW_CALLBACK(on_focus, "O", focused ? Py_True : Py_False);
|
||||
glfwUpdateIMEState(global_state.callback_os_window->handle, 1, focused, 0, 0, 0);
|
||||
GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_FOCUS, .focused = focused };
|
||||
glfwUpdateIMEState(global_state.callback_os_window->handle, &ev);
|
||||
}
|
||||
request_tick_callback();
|
||||
global_state.callback_os_window = NULL;
|
||||
|
||||
@@ -699,7 +699,9 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
||||
update_src_rect(ref, img);
|
||||
update_dest_rect(ref, g->num_cells, g->num_lines, cell);
|
||||
// Move the cursor, the screen will take care of ensuring it is in bounds
|
||||
c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1;
|
||||
if (g->cursor_movement != 1) {
|
||||
c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1;
|
||||
}
|
||||
return img->client_id;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
typedef struct {
|
||||
unsigned char action, transmission_type, compressed, delete_action;
|
||||
uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet;
|
||||
uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet, cursor_movement;
|
||||
uint32_t width, height, x_offset, y_offset, data_height, data_width, num_cells, num_lines, cell_x_offset, cell_y_offset;
|
||||
int32_t z_index;
|
||||
size_t payload_sz;
|
||||
|
||||
@@ -278,7 +278,7 @@ __str__(HistoryBuf *self) {
|
||||
if (lines == NULL) return PyErr_NoMemory();
|
||||
for (index_type i = 0; i < self->count; i++) {
|
||||
init_line(self, index_of(self, i), self->line);
|
||||
PyObject *t = line_as_unicode(self->line);
|
||||
PyObject *t = line_as_unicode(self->line, false);
|
||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||
PyTuple_SET_ITEM(lines, i, t);
|
||||
}
|
||||
|
||||
10
kitty/keys.c
10
kitty/keys.c
@@ -85,7 +85,9 @@ update_ime_position(OSWindow *os_window, Window* w, Screen *screen) {
|
||||
unsigned int left = w->geometry.left, top = w->geometry.top;
|
||||
left += screen->cursor->x * cell_width;
|
||||
top += screen->cursor->y * cell_height;
|
||||
glfwUpdateIMEState(global_state.callback_os_window->handle, 2, left, top, cell_width, cell_height);
|
||||
GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION };
|
||||
ev.cursor.left = left; ev.cursor.top = top; ev.cursor.width = cell_width; ev.cursor.height = cell_height;
|
||||
glfwUpdateIMEState(global_state.callback_os_window->handle, &ev);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -105,19 +107,19 @@ on_key_input(GLFWkeyevent *ev) {
|
||||
id_type active_window_id = w->id;
|
||||
|
||||
switch(ev->ime_state) {
|
||||
case 1: // update pre-edit text
|
||||
case GLFW_IME_PREEDIT_CHANGED:
|
||||
update_ime_position(global_state.callback_os_window, w, screen);
|
||||
screen_draw_overlay_text(screen, text);
|
||||
debug("updated pre-edit text: '%s'\n", text);
|
||||
return;
|
||||
case 2: // commit text
|
||||
case GLFW_IME_COMMIT_TEXT:
|
||||
if (*text) {
|
||||
schedule_write_to_child(w->id, 1, text, strlen(text));
|
||||
debug("committed pre-edit text: %s\n", text);
|
||||
} else debug("committed pre-edit text: (null)\n");
|
||||
screen_draw_overlay_text(screen, NULL);
|
||||
return;
|
||||
case 0:
|
||||
case GLFW_IME_NONE:
|
||||
// for macOS, update ime position on every key input
|
||||
// because the position is required before next input
|
||||
#if defined(__APPLE__)
|
||||
|
||||
@@ -470,7 +470,7 @@ __str__(LineBuf *self) {
|
||||
if (lines == NULL) return PyErr_NoMemory();
|
||||
for (index_type i = 0; i < self->ynum; i++) {
|
||||
init_line(self, self->line, self->line_map[i]);
|
||||
PyObject *t = line_as_unicode(self->line);
|
||||
PyObject *t = line_as_unicode(self->line, false);
|
||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||
PyTuple_SET_ITEM(lines, i, t);
|
||||
}
|
||||
|
||||
23
kitty/line.c
23
kitty/line.c
@@ -229,7 +229,7 @@ cell_as_utf8_for_fallback(CPUCell *cell, char *buf) {
|
||||
|
||||
|
||||
PyObject*
|
||||
unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char) {
|
||||
unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const char leading_char, const bool skip_zero_cells) {
|
||||
size_t n = 0;
|
||||
static Py_UCS4 buf[4096];
|
||||
if (leading_char) buf[n++] = leading_char;
|
||||
@@ -238,6 +238,7 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
|
||||
char_type ch = self->cpu_cells[i].ch;
|
||||
if (ch == 0) {
|
||||
if (previous_width == 2) { previous_width = 0; continue; };
|
||||
if (skip_zero_cells) continue;
|
||||
}
|
||||
if (ch == '\t') {
|
||||
buf[n++] = '\t';
|
||||
@@ -255,8 +256,8 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
|
||||
}
|
||||
|
||||
PyObject *
|
||||
line_as_unicode(Line* self) {
|
||||
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0);
|
||||
line_as_unicode(Line* self, bool skip_zero_cells) {
|
||||
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0, skip_zero_cells);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
@@ -380,13 +381,19 @@ is_continued(Line* self, PyObject *a UNUSED) {
|
||||
|
||||
static PyObject*
|
||||
__repr__(Line* self) {
|
||||
PyObject *s = line_as_unicode(self);
|
||||
PyObject *s = line_as_unicode(self, false);
|
||||
if (s == NULL) return NULL;
|
||||
PyObject *ans = PyObject_Repr(s);
|
||||
Py_CLEAR(s);
|
||||
return ans;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
__str__(Line* self) {
|
||||
return line_as_unicode(self, false);
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
width(Line *self, PyObject *val) {
|
||||
#define width_doc "width(x) -> the width of the character at x"
|
||||
@@ -729,8 +736,8 @@ apply_mark(Line *line, const attrs_type mark, index_type *cell_pos, unsigned int
|
||||
#define MARK { line->gpu_cells[x].attrs &= ATTRS_MASK_WITHOUT_MARK; line->gpu_cells[x].attrs |= mark; }
|
||||
index_type x = *cell_pos;
|
||||
MARK;
|
||||
(*match_pos)++;
|
||||
if (line->cpu_cells[x].ch) {
|
||||
(*match_pos)++;
|
||||
if (line->cpu_cells[x].ch == '\t') {
|
||||
unsigned num_cells_to_skip_for_tab = line->cpu_cells[x].cc_idx[0];
|
||||
while (num_cells_to_skip_for_tab && x + 1 < line->xnum && line->cpu_cells[x+1].ch == ' ') {
|
||||
@@ -784,7 +791,7 @@ mark_text_in_line(PyObject *marker, Line *line) {
|
||||
for (index_type i = 0; i < line->xnum; i++) line->gpu_cells[i].attrs &= ATTRS_MASK_WITHOUT_MARK;
|
||||
return;
|
||||
}
|
||||
PyObject *text = line_as_unicode(line);
|
||||
PyObject *text = line_as_unicode(line, false);
|
||||
if (PyUnicode_GET_LENGTH(text) > 0) {
|
||||
apply_marker(marker, line, text);
|
||||
} else {
|
||||
@@ -827,7 +834,7 @@ as_text_generic(PyObject *args, void *container, get_line_func get_line, index_t
|
||||
Py_CLEAR(ret);
|
||||
}
|
||||
} else {
|
||||
t = line_as_unicode(line);
|
||||
t = line_as_unicode(line, false);
|
||||
}
|
||||
if (t == NULL) goto end;
|
||||
ret = PyObject_CallFunctionObjArgs(callback, t, NULL);
|
||||
@@ -906,7 +913,7 @@ PyTypeObject Line_Type = {
|
||||
.tp_basicsize = sizeof(Line),
|
||||
.tp_dealloc = (destructor)dealloc,
|
||||
.tp_repr = (reprfunc)__repr__,
|
||||
.tp_str = (reprfunc)line_as_unicode,
|
||||
.tp_str = (reprfunc)__str__,
|
||||
.tp_as_sequence = &sequence_methods,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_richcompare = richcmp,
|
||||
|
||||
@@ -83,8 +83,8 @@ size_t cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type);
|
||||
size_t cell_as_unicode_for_fallback(CPUCell *cell, Py_UCS4 *buf);
|
||||
size_t cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type);
|
||||
size_t cell_as_utf8_for_fallback(CPUCell *cell, char *buf);
|
||||
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
||||
PyObject* line_as_unicode(Line *);
|
||||
PyObject* unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const char leading_char, const bool skip_zero_cells);
|
||||
PyObject* line_as_unicode(Line *, bool);
|
||||
|
||||
void linebuf_init_line(LineBuf *, index_type);
|
||||
void linebuf_clear(LineBuf *, char_type ch);
|
||||
|
||||
46
kitty/parse-graphics-command.h
generated
46
kitty/parse-graphics-command.h
generated
@@ -39,7 +39,8 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
num_lines = 'r',
|
||||
cell_x_offset = 'X',
|
||||
cell_y_offset = 'Y',
|
||||
z_index = 'z'
|
||||
z_index = 'z',
|
||||
cursor_movement = 'C'
|
||||
};
|
||||
|
||||
enum KEYS key = 'a';
|
||||
@@ -121,6 +122,9 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
case z_index:
|
||||
value_state = INT;
|
||||
break;
|
||||
case cursor_movement:
|
||||
value_state = UINT;
|
||||
break;
|
||||
default:
|
||||
REPORT_ERROR("Malformed GraphicsCommand control block, invalid key "
|
||||
"character: 0x%x",
|
||||
@@ -144,9 +148,9 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
|
||||
case action: {
|
||||
g.action = screen->parser_buf[pos++] & 0xff;
|
||||
if (g.action != 'q' && g.action != 'd' && g.action != 'p' &&
|
||||
g.action != 't' && g.action != 'T' && g.action != 'f' &&
|
||||
g.action != 'a') {
|
||||
if (g.action != 't' && g.action != 'a' && g.action != 'T' &&
|
||||
g.action != 'f' && g.action != 'd' && g.action != 'p' &&
|
||||
g.action != 'q') {
|
||||
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
||||
"value for action: 0x%x",
|
||||
g.action);
|
||||
@@ -156,16 +160,16 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
|
||||
case delete_action: {
|
||||
g.delete_action = screen->parser_buf[pos++] & 0xff;
|
||||
if (g.delete_action != 'F' && g.delete_action != 'n' &&
|
||||
g.delete_action != 'x' && g.delete_action != 'I' &&
|
||||
g.delete_action != 'p' && g.delete_action != 'i' &&
|
||||
g.delete_action != 'C' && g.delete_action != 'Q' &&
|
||||
g.delete_action != 'A' && g.delete_action != 'z' &&
|
||||
g.delete_action != 'y' && g.delete_action != 'c' &&
|
||||
g.delete_action != 'Z' && g.delete_action != 'N' &&
|
||||
g.delete_action != 'X' && g.delete_action != 'q' &&
|
||||
g.delete_action != 'P' && g.delete_action != 'f' &&
|
||||
g.delete_action != 'Y' && g.delete_action != 'a') {
|
||||
if (g.delete_action != 'Q' && g.delete_action != 'N' &&
|
||||
g.delete_action != 'z' && g.delete_action != 'p' &&
|
||||
g.delete_action != 'X' && g.delete_action != 'a' &&
|
||||
g.delete_action != 'y' && g.delete_action != 'f' &&
|
||||
g.delete_action != 'Z' && g.delete_action != 'F' &&
|
||||
g.delete_action != 'Y' && g.delete_action != 'n' &&
|
||||
g.delete_action != 'C' && g.delete_action != 'q' &&
|
||||
g.delete_action != 'c' && g.delete_action != 'i' &&
|
||||
g.delete_action != 'A' && g.delete_action != 'P' &&
|
||||
g.delete_action != 'I' && g.delete_action != 'x') {
|
||||
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
||||
"value for delete_action: 0x%x",
|
||||
g.delete_action);
|
||||
@@ -175,8 +179,8 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
|
||||
case transmission_type: {
|
||||
g.transmission_type = screen->parser_buf[pos++] & 0xff;
|
||||
if (g.transmission_type != 's' && g.transmission_type != 'f' &&
|
||||
g.transmission_type != 'd' && g.transmission_type != 't') {
|
||||
if (g.transmission_type != 's' && g.transmission_type != 't' &&
|
||||
g.transmission_type != 'd' && g.transmission_type != 'f') {
|
||||
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
|
||||
"value for transmission_type: 0x%x",
|
||||
g.transmission_type);
|
||||
@@ -264,6 +268,7 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
U(num_lines);
|
||||
U(cell_x_offset);
|
||||
U(cell_y_offset);
|
||||
U(cursor_movement);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -322,8 +327,8 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
}
|
||||
|
||||
REPORT_VA_COMMAND(
|
||||
"s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI si "
|
||||
"sI} y#",
|
||||
"s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI "
|
||||
"si sI} y#",
|
||||
"graphics_command", "action", g.action, "delete_action", g.delete_action,
|
||||
"transmission_type", g.transmission_type, "compressed", g.compressed,
|
||||
"format", (unsigned int)g.format, "more", (unsigned int)g.more, "id",
|
||||
@@ -336,8 +341,9 @@ static inline void parse_graphics_code(Screen *screen,
|
||||
(unsigned int)g.data_sz, "data_offset", (unsigned int)g.data_offset,
|
||||
"num_cells", (unsigned int)g.num_cells, "num_lines",
|
||||
(unsigned int)g.num_lines, "cell_x_offset", (unsigned int)g.cell_x_offset,
|
||||
"cell_y_offset", (unsigned int)g.cell_y_offset, "z_index", (int)g.z_index,
|
||||
"payload_sz", g.payload_sz, payload, g.payload_sz);
|
||||
"cell_y_offset", (unsigned int)g.cell_y_offset, "cursor_movement",
|
||||
(unsigned int)g.cursor_movement, "z_index", (int)g.z_index, "payload_sz",
|
||||
g.payload_sz, payload, g.payload_sz);
|
||||
|
||||
screen_handle_graphics_command(screen, &g, payload);
|
||||
}
|
||||
|
||||
@@ -1978,7 +1978,7 @@ text_for_range(Screen *self, const Selection *sel, bool insert_newlines) {
|
||||
Line *line = range_line_(self, y);
|
||||
XRange xr = xrange_for_iteration(&idata, y, line);
|
||||
char leading_char = (i > 0 && insert_newlines && !line->continued) ? '\n' : 0;
|
||||
PyObject *text = unicode_in_range(line, xr.x, xr.x_limit, true, leading_char);
|
||||
PyObject *text = unicode_in_range(line, xr.x, xr.x_limit, true, leading_char, false);
|
||||
if (text == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); }
|
||||
PyTuple_SET_ITEM(ans, i, text);
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@ class Rendering(BaseTest):
|
||||
self.ae(groups('|\U0001F601|\U0001F64f|\U0001F63a|'), [(1, 1), (2, 1), (1, 1), (2, 1), (1, 1), (2, 1), (1, 1)])
|
||||
self.ae(groups('He\u0347\u0305llo\u0337,', font='LiberationMono-Regular.ttf'),
|
||||
[(1, 1), (1, 3), (1, 1), (1, 1), (1, 2), (1, 1)])
|
||||
self.ae(groups('i\u0332\u0308', font='LiberationMono-Regular.ttf'), [(1, 2)])
|
||||
|
||||
def test_emoji_presentation(self):
|
||||
s = self.create_screen()
|
||||
|
||||
@@ -124,9 +124,15 @@ def put_helpers(self, cw, ch):
|
||||
s = self.create_screen(10, 5, cell_width=cw, cell_height=ch)
|
||||
return s, 2 / s.columns, 2 / s.lines
|
||||
|
||||
def put_cmd(z=0, num_cols=0, num_lines=0, x_off=0, y_off=0, width=0, height=0, cell_x_off=0, cell_y_off=0, placement_id=0):
|
||||
return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d' % (
|
||||
z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off, cell_y_off, placement_id)
|
||||
def put_cmd(
|
||||
z=0, num_cols=0, num_lines=0, x_off=0, y_off=0, width=0,
|
||||
height=0, cell_x_off=0, cell_y_off=0, placement_id=0,
|
||||
cursor_movement=0
|
||||
):
|
||||
return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d,C=%d' % (
|
||||
z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off,
|
||||
cell_y_off, placement_id, cursor_movement
|
||||
)
|
||||
|
||||
def put_image(screen, w, h, **kw):
|
||||
nonlocal iid
|
||||
@@ -510,6 +516,8 @@ class TestGraphics(BaseTest):
|
||||
rect_eq(l2[1]['dest_rect'], -1, 1, -1 + dx, 1 - dy)
|
||||
self.ae(l2[0]['group_count'], 1), self.ae(l2[1]['group_count'], 1)
|
||||
self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1)
|
||||
self.ae(put_image(s, 10, 20, cursor_movement=1)[1], 'OK')
|
||||
self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1)
|
||||
s.reset()
|
||||
self.assertEqual(s.grman.disk_cache.total_size, 0)
|
||||
|
||||
|
||||
@@ -381,7 +381,7 @@ class TestParser(BaseTest):
|
||||
k[p] = v.encode('ascii')
|
||||
for f in 'action delete_action transmission_type compressed'.split():
|
||||
k.setdefault(f, b'\0')
|
||||
for f in ('format more id data_sz data_offset width height x_offset y_offset data_height data_width'
|
||||
for f in ('format more id data_sz data_offset width height x_offset y_offset data_height data_width cursor_movement'
|
||||
' num_cells num_lines cell_x_offset cell_y_offset z_index placement_id image_number quiet').split():
|
||||
k.setdefault(f, 0)
|
||||
p = k.pop('payload', '').encode('utf-8')
|
||||
|
||||
@@ -647,6 +647,13 @@ class TestScreen(BaseTest):
|
||||
self.ae(s.marked_cells(), cells(8))
|
||||
s.set_marker(marker_from_regex('\t', 3))
|
||||
self.ae(s.marked_cells(), cells(*range(8)))
|
||||
s = self.create_screen()
|
||||
s.cursor.x = 2
|
||||
s.draw('x')
|
||||
s.cursor.x += 1
|
||||
s.draw('x')
|
||||
s.set_marker(marker_from_function(mark_x))
|
||||
self.ae(s.marked_cells(), [(2, 0, 1), (4, 0, 2)])
|
||||
|
||||
def test_hyperlinks(self):
|
||||
s = self.create_screen()
|
||||
|
||||
Reference in New Issue
Block a user