Merge branch 'ime' of https://github.com/page-down/kitty
This commit is contained in:
commit
dba8d278cb
@ -46,7 +46,7 @@ Detailed list of changes
|
|||||||
|
|
||||||
- When changing the cursor color via escape codes or remote control to a fixed color, do not reset cursor_text_color (:iss:`5994`)
|
- When changing the cursor color via escape codes or remote control to a fixed color, do not reset cursor_text_color (:iss:`5994`)
|
||||||
|
|
||||||
- Input Method Extensions: Fix incorrect rendering of IME in-progress text in some situations (:pull:`6002`)
|
- Input Method Extensions: Fix incorrect rendering of IME in-progress and commited text in some situations (:pull:`6049`)
|
||||||
|
|
||||||
- Linux: Reduce minimum required OpenGL version from 3.3 to 3.1 + extensions (:iss:`2790`)
|
- Linux: Reduce minimum required OpenGL version from 3.3 to 3.1 + extensions (:iss:`2790`)
|
||||||
|
|
||||||
|
|||||||
@ -1454,15 +1454,11 @@ is_ascii_control_char(char x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||||
[w->ns.view updateIMEStateFor: ev->type focused:(bool)ev->focused left:(CGFloat)ev->cursor.left top:(CGFloat)ev->cursor.top cellWidth:(CGFloat)ev->cursor.width cellHeight:(CGFloat)ev->cursor.height];
|
[w->ns.view updateIMEStateFor: ev->type focused:(bool)ev->focused];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateIMEStateFor:(GLFWIMEUpdateType)which
|
- (void)updateIMEStateFor:(GLFWIMEUpdateType)which
|
||||||
focused:(bool)focused
|
focused:(bool)focused
|
||||||
left:(CGFloat)left
|
|
||||||
top:(CGFloat)top
|
|
||||||
cellWidth:(CGFloat)cellWidth
|
|
||||||
cellHeight:(CGFloat)cellHeight
|
|
||||||
{
|
{
|
||||||
if (which == GLFW_IME_UPDATE_FOCUS && !focused && [self hasMarkedText] && window) {
|
if (which == GLFW_IME_UPDATE_FOCUS && !focused && [self hasMarkedText] && window) {
|
||||||
[input_context discardMarkedText];
|
[input_context discardMarkedText];
|
||||||
@ -1472,16 +1468,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
|||||||
_glfw.ns.text[0] = 0;
|
_glfw.ns.text[0] = 0;
|
||||||
}
|
}
|
||||||
if (which != GLFW_IME_UPDATE_CURSOR_POSITION) return;
|
if (which != GLFW_IME_UPDATE_CURSOR_POSITION) return;
|
||||||
left /= window->ns.xscale;
|
|
||||||
top /= window->ns.yscale;
|
|
||||||
cellWidth /= window->ns.xscale;
|
|
||||||
cellHeight /= window->ns.yscale;
|
|
||||||
debug_key("updateIMEPosition: left=%f, top=%f, width=%f, height=%f\n", left, top, cellWidth, cellHeight);
|
|
||||||
const NSRect frame = [window->ns.view frame];
|
|
||||||
const NSRect rectInView = NSMakeRect(left,
|
|
||||||
frame.size.height - top - cellHeight,
|
|
||||||
cellWidth, cellHeight);
|
|
||||||
markedRect = [window->ns.object convertRectToScreen: rectInView];
|
|
||||||
if (_glfwPlatformWindowFocused(window)) [[window->ns.view inputContext] invalidateCharacterCoordinates];
|
if (_glfwPlatformWindowFocused(window)) [[window->ns.view inputContext] invalidateCharacterCoordinates];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1507,6 +1494,21 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
|||||||
actualRange:(NSRangePointer)actualRange
|
actualRange:(NSRangePointer)actualRange
|
||||||
{
|
{
|
||||||
(void)range; (void)actualRange;
|
(void)range; (void)actualRange;
|
||||||
|
if (_glfw.callbacks.get_ime_cursor_position) {
|
||||||
|
GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION };
|
||||||
|
if (_glfw.callbacks.get_ime_cursor_position((GLFWwindow*)window, &ev)) {
|
||||||
|
const CGFloat left = (CGFloat)ev.cursor.left / window->ns.xscale;
|
||||||
|
const CGFloat top = (CGFloat)ev.cursor.top / window->ns.yscale;
|
||||||
|
const CGFloat cellWidth = (CGFloat)ev.cursor.width / window->ns.xscale;
|
||||||
|
const CGFloat cellHeight = (CGFloat)ev.cursor.height / window->ns.yscale;
|
||||||
|
debug_key("updateIMEPosition: left=%f, top=%f, width=%f, height=%f\n", left, top, cellWidth, cellHeight);
|
||||||
|
const NSRect frame = [window->ns.view frame];
|
||||||
|
const NSRect rectInView = NSMakeRect(left,
|
||||||
|
frame.size.height - top - cellHeight,
|
||||||
|
cellWidth, cellHeight);
|
||||||
|
markedRect = [window->ns.object convertRectToScreen: rectInView];
|
||||||
|
}
|
||||||
|
}
|
||||||
return markedRect;
|
return markedRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
glfw/glfw3.h
vendored
2
glfw/glfw3.h
vendored
@ -1732,6 +1732,7 @@ typedef enum {
|
|||||||
} GLFWClipboardType;
|
} GLFWClipboardType;
|
||||||
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
|
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
|
||||||
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
|
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
|
||||||
|
typedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev);
|
||||||
|
|
||||||
/*! @brief Video mode type.
|
/*! @brief Video mode type.
|
||||||
*
|
*
|
||||||
@ -1891,6 +1892,7 @@ GLFWAPI void glfwRemoveTimer(unsigned long long);
|
|||||||
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);
|
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);
|
||||||
GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback);
|
GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback);
|
||||||
GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback);
|
GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback);
|
||||||
|
GLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun callback);
|
||||||
|
|
||||||
/*! @brief Terminates the GLFW library.
|
/*! @brief Terminates the GLFW library.
|
||||||
*
|
*
|
||||||
|
|||||||
7
glfw/init.c
vendored
7
glfw/init.c
vendored
@ -402,3 +402,10 @@ GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascur
|
|||||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun);
|
_GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun);
|
||||||
return cbfun;
|
return cbfun;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun cbfun)
|
||||||
|
{
|
||||||
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
|
_GLFW_SWAP_POINTERS(_glfw.callbacks.get_ime_cursor_position, cbfun);
|
||||||
|
return cbfun;
|
||||||
|
}
|
||||||
|
|||||||
1
glfw/internal.h
vendored
1
glfw/internal.h
vendored
@ -635,6 +635,7 @@ struct _GLFWlibrary
|
|||||||
GLFWdrawtextfun draw_text;
|
GLFWdrawtextfun draw_text;
|
||||||
GLFWcurrentselectionfun get_current_selection;
|
GLFWcurrentselectionfun get_current_selection;
|
||||||
GLFWhascurrentselectionfun has_current_selection;
|
GLFWhascurrentselectionfun has_current_selection;
|
||||||
|
GLFWimecursorpositionfun get_ime_cursor_position;
|
||||||
} callbacks;
|
} callbacks;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -626,8 +626,16 @@ cursor_needs_render(Window *w) {
|
|||||||
static bool
|
static bool
|
||||||
collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow *os_window) {
|
collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow *os_window) {
|
||||||
ScreenRenderData *rd = &w->render_data;
|
ScreenRenderData *rd = &w->render_data;
|
||||||
Cursor *cursor = rd->screen->cursor;
|
Cursor *cursor;
|
||||||
|
if (screen_is_overlay_active(rd->screen)) {
|
||||||
|
// Do not force the cursor to be visible here for the sake of some programs that prefer it hidden
|
||||||
|
cursor = &(rd->screen->overlay_line.original_line.cursor);
|
||||||
|
ans->x = rd->screen->overlay_line.cursor_x;
|
||||||
|
ans->y = rd->screen->overlay_line.ynum;
|
||||||
|
} else {
|
||||||
|
cursor = rd->screen->cursor;
|
||||||
ans->x = cursor->x; ans->y = cursor->y;
|
ans->x = cursor->x; ans->y = cursor->y;
|
||||||
|
}
|
||||||
ans->is_visible = false;
|
ans->is_visible = false;
|
||||||
if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) return cursor_needs_render(w);
|
if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) return cursor_needs_render(w);
|
||||||
monotonic_t time_since_start_blink = now - os_window->cursor_blink_zero_time;
|
monotonic_t time_since_start_blink = now - os_window->cursor_blink_zero_time;
|
||||||
|
|||||||
3
kitty/glfw-wrapper.c
generated
3
kitty/glfw-wrapper.c
generated
@ -44,6 +44,9 @@ load_glfw(const char* path) {
|
|||||||
*(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback");
|
*(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback");
|
||||||
if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s", dlerror());
|
if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s", dlerror());
|
||||||
|
|
||||||
|
*(void **) (&glfwSetIMECursorPositionCallback_impl) = dlsym(handle, "glfwSetIMECursorPositionCallback");
|
||||||
|
if (glfwSetIMECursorPositionCallback_impl == NULL) fail("Failed to load glfw function glfwSetIMECursorPositionCallback with error: %s", dlerror());
|
||||||
|
|
||||||
*(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate");
|
*(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate");
|
||||||
if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror());
|
if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror());
|
||||||
|
|
||||||
|
|||||||
5
kitty/glfw-wrapper.h
generated
5
kitty/glfw-wrapper.h
generated
@ -1470,6 +1470,7 @@ typedef enum {
|
|||||||
} GLFWClipboardType;
|
} GLFWClipboardType;
|
||||||
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
|
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
|
||||||
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
|
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
|
||||||
|
typedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev);
|
||||||
|
|
||||||
/*! @brief Video mode type.
|
/*! @brief Video mode type.
|
||||||
*
|
*
|
||||||
@ -1667,6 +1668,10 @@ typedef GLFWhascurrentselectionfun (*glfwSetHasCurrentSelectionCallback_func)(GL
|
|||||||
GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl;
|
GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl;
|
||||||
#define glfwSetHasCurrentSelectionCallback glfwSetHasCurrentSelectionCallback_impl
|
#define glfwSetHasCurrentSelectionCallback glfwSetHasCurrentSelectionCallback_impl
|
||||||
|
|
||||||
|
typedef GLFWimecursorpositionfun (*glfwSetIMECursorPositionCallback_func)(GLFWimecursorpositionfun);
|
||||||
|
GFW_EXTERN glfwSetIMECursorPositionCallback_func glfwSetIMECursorPositionCallback_impl;
|
||||||
|
#define glfwSetIMECursorPositionCallback glfwSetIMECursorPositionCallback_impl
|
||||||
|
|
||||||
typedef void (*glfwTerminate_func)(void);
|
typedef void (*glfwTerminate_func)(void);
|
||||||
GFW_EXTERN glfwTerminate_func glfwTerminate_impl;
|
GFW_EXTERN glfwTerminate_func glfwTerminate_impl;
|
||||||
#define glfwTerminate glfwTerminate_impl
|
#define glfwTerminate glfwTerminate_impl
|
||||||
|
|||||||
21
kitty/glfw.c
21
kitty/glfw.c
@ -507,6 +507,26 @@ has_current_selection(void) {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev);
|
||||||
|
|
||||||
|
static bool
|
||||||
|
get_ime_cursor_position(GLFWwindow *glfw_window, GLFWIMEUpdateEvent *ev) {
|
||||||
|
if (!set_callback_window(glfw_window)) return false;
|
||||||
|
bool ans = false;
|
||||||
|
OSWindow *osw = global_state.callback_os_window;
|
||||||
|
if (osw && osw->is_focused && is_window_ready_for_callbacks()) {
|
||||||
|
Tab *tab = osw->tabs + osw->active_tab;
|
||||||
|
Window *w = tab->windows + tab->active_window;
|
||||||
|
Screen *screen = w->render_data.screen;
|
||||||
|
if (screen) {
|
||||||
|
prepare_ime_position_update_event(osw, w, screen, ev);
|
||||||
|
ans = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global_state.callback_os_window = NULL;
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void get_window_dpi(GLFWwindow *w, double *x, double *y);
|
static void get_window_dpi(GLFWwindow *w, double *x, double *y);
|
||||||
|
|
||||||
@ -832,6 +852,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
|||||||
glfwSetApplicationCloseCallback(application_close_requested_callback);
|
glfwSetApplicationCloseCallback(application_close_requested_callback);
|
||||||
glfwSetCurrentSelectionCallback(get_current_selection);
|
glfwSetCurrentSelectionCallback(get_current_selection);
|
||||||
glfwSetHasCurrentSelectionCallback(has_current_selection);
|
glfwSetHasCurrentSelectionCallback(has_current_selection);
|
||||||
|
glfwSetIMECursorPositionCallback(get_ime_cursor_position);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
|
cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
|
||||||
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
|
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
|
||||||
|
|||||||
24
kitty/keys.c
24
kitty/keys.c
@ -102,13 +102,25 @@ update_ime_focus(OSWindow *osw, bool focused) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
update_ime_position(Window* w, Screen *screen) {
|
prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev) {
|
||||||
unsigned int cell_width = global_state.callback_os_window->fonts_data->cell_width, cell_height = global_state.callback_os_window->fonts_data->cell_height;
|
unsigned int cell_width = osw->fonts_data->cell_width, cell_height = osw->fonts_data->cell_height;
|
||||||
unsigned int left = w->geometry.left, top = w->geometry.top;
|
unsigned int left = w->geometry.left, top = w->geometry.top;
|
||||||
|
if (screen_is_overlay_active(screen)) {
|
||||||
|
left += screen->overlay_line.cursor_x * cell_width;
|
||||||
|
top += MIN(screen->overlay_line.ynum + screen->scrolled_by, screen->lines - 1) * cell_height;
|
||||||
|
} else {
|
||||||
left += screen->cursor->x * cell_width;
|
left += screen->cursor->x * cell_width;
|
||||||
top += screen->cursor->y * cell_height;
|
top += screen->cursor->y * cell_height;
|
||||||
|
}
|
||||||
|
ev->cursor.left = left; ev->cursor.top = top; ev->cursor.width = cell_width; ev->cursor.height = cell_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update_ime_position(Window* w UNUSED, Screen *screen UNUSED) {
|
||||||
GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION };
|
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;
|
#ifndef __APPLE__
|
||||||
|
prepare_ime_position_update_event(global_state.callback_os_window, w, screen, &ev);
|
||||||
|
#endif
|
||||||
glfwUpdateIMEState(global_state.callback_os_window->handle, &ev);
|
glfwUpdateIMEState(global_state.callback_os_window->handle, &ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,12 +166,12 @@ on_key_input(GLFWkeyevent *ev) {
|
|||||||
case GLFW_IME_WAYLAND_DONE_EVENT:
|
case GLFW_IME_WAYLAND_DONE_EVENT:
|
||||||
// If we update IME position here it sends GNOME's text input system into
|
// If we update IME position here it sends GNOME's text input system into
|
||||||
// an infinite loop. See https://github.com/kovidgoyal/kitty/issues/5105
|
// an infinite loop. See https://github.com/kovidgoyal/kitty/issues/5105
|
||||||
screen_draw_overlay_text(screen, NULL);
|
screen_update_overlay_text(screen, NULL);
|
||||||
debug("handled wayland IME done event\n");
|
debug("handled wayland IME done event\n");
|
||||||
return;
|
return;
|
||||||
case GLFW_IME_PREEDIT_CHANGED:
|
case GLFW_IME_PREEDIT_CHANGED:
|
||||||
|
screen_update_overlay_text(screen, text);
|
||||||
update_ime_position(w, screen);
|
update_ime_position(w, screen);
|
||||||
screen_draw_overlay_text(screen, text);
|
|
||||||
debug("updated pre-edit text: '%s'\n", text);
|
debug("updated pre-edit text: '%s'\n", text);
|
||||||
return;
|
return;
|
||||||
case GLFW_IME_COMMIT_TEXT:
|
case GLFW_IME_COMMIT_TEXT:
|
||||||
@ -167,7 +179,7 @@ on_key_input(GLFWkeyevent *ev) {
|
|||||||
schedule_write_to_child(w->id, 1, text, strlen(text));
|
schedule_write_to_child(w->id, 1, text, strlen(text));
|
||||||
debug("committed pre-edit text: %s\n", text);
|
debug("committed pre-edit text: %s\n", text);
|
||||||
} else debug("committed pre-edit text: (null)\n");
|
} else debug("committed pre-edit text: (null)\n");
|
||||||
screen_draw_overlay_text(screen, NULL);
|
screen_update_overlay_text(screen, NULL);
|
||||||
return;
|
return;
|
||||||
case GLFW_IME_NONE:
|
case GLFW_IME_NONE:
|
||||||
// for macOS, update ime position on every key input
|
// for macOS, update ime position on every key input
|
||||||
|
|||||||
306
kitty/screen.c
306
kitty/screen.c
@ -23,6 +23,7 @@
|
|||||||
#include "unicode-data.h"
|
#include "unicode-data.h"
|
||||||
#include "modes.h"
|
#include "modes.h"
|
||||||
#include "wcwidth-std.h"
|
#include "wcwidth-std.h"
|
||||||
|
#include "wcswidth.h"
|
||||||
#include "control-codes.h"
|
#include "control-codes.h"
|
||||||
#include "charsets.h"
|
#include "charsets.h"
|
||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
@ -49,25 +50,37 @@ init_tabstops(bool *tabstops, index_type count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
init_overlay_line(Screen *self, index_type columns) {
|
init_overlay_line(Screen *self, index_type columns, bool keep_active) {
|
||||||
PyMem_Free(self->overlay_line.cpu_cells);
|
PyMem_Free(self->overlay_line.cpu_cells);
|
||||||
PyMem_Free(self->overlay_line.gpu_cells);
|
PyMem_Free(self->overlay_line.gpu_cells);
|
||||||
|
PyMem_Free(self->overlay_line.original_line.cpu_cells);
|
||||||
|
PyMem_Free(self->overlay_line.original_line.gpu_cells);
|
||||||
self->overlay_line.cpu_cells = PyMem_Calloc(columns, sizeof(CPUCell));
|
self->overlay_line.cpu_cells = PyMem_Calloc(columns, sizeof(CPUCell));
|
||||||
self->overlay_line.gpu_cells = PyMem_Calloc(columns, sizeof(GPUCell));
|
self->overlay_line.gpu_cells = PyMem_Calloc(columns, sizeof(GPUCell));
|
||||||
if (!self->overlay_line.cpu_cells || !self->overlay_line.gpu_cells) {
|
self->overlay_line.original_line.cpu_cells = PyMem_Calloc(columns, sizeof(CPUCell));
|
||||||
|
self->overlay_line.original_line.gpu_cells = PyMem_Calloc(columns, sizeof(GPUCell));
|
||||||
|
if (!self->overlay_line.cpu_cells || !self->overlay_line.gpu_cells ||
|
||||||
|
!self->overlay_line.original_line.cpu_cells || !self->overlay_line.original_line.gpu_cells) {
|
||||||
PyErr_NoMemory(); return false;
|
PyErr_NoMemory(); return false;
|
||||||
}
|
}
|
||||||
|
if (!keep_active) {
|
||||||
self->overlay_line.is_active = false;
|
self->overlay_line.is_active = false;
|
||||||
self->overlay_line.xnum = 0;
|
self->overlay_line.xnum = 0;
|
||||||
|
}
|
||||||
|
self->overlay_line.is_dirty = true;
|
||||||
self->overlay_line.ynum = 0;
|
self->overlay_line.ynum = 0;
|
||||||
self->overlay_line.xstart = 0;
|
self->overlay_line.xstart = 0;
|
||||||
|
self->overlay_line.cursor_x = 0;
|
||||||
|
self->overlay_line.last_ime_pos.x = 0;
|
||||||
|
self->overlay_line.last_ime_pos.y = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void save_overlay_line(Screen *self, const char* func_name);
|
|
||||||
static void restore_overlay_line(Screen *self);
|
|
||||||
static void deactivate_overlay_line(Screen *self);
|
static void deactivate_overlay_line(Screen *self);
|
||||||
static void clear_saved_overlay_line(Screen *self);
|
static void update_overlay_position(Screen *self);
|
||||||
|
static void render_overlay_line(Screen *self, Line *line, FONTS_DATA_HANDLE fonts_data);
|
||||||
|
static void update_overlay_line_data(Screen *self, uint8_t *data);
|
||||||
|
|
||||||
#define RESET_CHARSETS \
|
#define RESET_CHARSETS \
|
||||||
self->g0_charset = translation_table(0); \
|
self->g0_charset = translation_table(0); \
|
||||||
@ -144,8 +157,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
init_tabstops(self->main_tabstops, self->columns);
|
init_tabstops(self->main_tabstops, self->columns);
|
||||||
init_tabstops(self->alt_tabstops, self->columns);
|
init_tabstops(self->alt_tabstops, self->columns);
|
||||||
self->key_encoding_flags = self->main_key_encoding_flags;
|
self->key_encoding_flags = self->main_key_encoding_flags;
|
||||||
if (!init_overlay_line(self, self->columns)) { Py_CLEAR(self); return NULL; }
|
if (!init_overlay_line(self, self->columns, false)) { Py_CLEAR(self); return NULL; }
|
||||||
clear_saved_overlay_line(self);
|
|
||||||
self->hyperlink_pool = alloc_hyperlink_pool();
|
self->hyperlink_pool = alloc_hyperlink_pool();
|
||||||
if (!self->hyperlink_pool) { Py_CLEAR(self); return PyErr_NoMemory(); }
|
if (!self->hyperlink_pool) { Py_CLEAR(self); return PyErr_NoMemory(); }
|
||||||
self->as_ansi_buf.hyperlink_pool = self->hyperlink_pool;
|
self->as_ansi_buf.hyperlink_pool = self->hyperlink_pool;
|
||||||
@ -158,8 +170,11 @@ static Line* range_line_(Screen *self, int y);
|
|||||||
void
|
void
|
||||||
screen_reset(Screen *self) {
|
screen_reset(Screen *self) {
|
||||||
if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self, true, true);
|
if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self, true, true);
|
||||||
if (self->overlay_line.is_active) deactivate_overlay_line(self);
|
if (screen_is_overlay_active(self)) {
|
||||||
clear_saved_overlay_line(self);
|
deactivate_overlay_line(self);
|
||||||
|
// Cancel IME composition
|
||||||
|
update_ime_position_for_window(self->window_id, false, -1);
|
||||||
|
}
|
||||||
Py_CLEAR(self->last_reported_cwd);
|
Py_CLEAR(self->last_reported_cwd);
|
||||||
self->render_unfocused_cursor = false;
|
self->render_unfocused_cursor = false;
|
||||||
memset(self->main_key_encoding_flags, 0, sizeof(self->main_key_encoding_flags));
|
memset(self->main_key_encoding_flags, 0, sizeof(self->main_key_encoding_flags));
|
||||||
@ -336,7 +351,6 @@ found:
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
||||||
if (self->overlay_line.is_active) save_overlay_line(self, __func__);
|
|
||||||
lines = MAX(1u, lines); columns = MAX(1u, columns);
|
lines = MAX(1u, lines); columns = MAX(1u, columns);
|
||||||
|
|
||||||
bool is_main = self->linebuf == self->main_linebuf;
|
bool is_main = self->linebuf == self->main_linebuf;
|
||||||
@ -361,7 +375,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
|||||||
which.num_content_lines = num_content_lines_after; \
|
which.num_content_lines = num_content_lines_after; \
|
||||||
}
|
}
|
||||||
// Resize overlay line
|
// Resize overlay line
|
||||||
if (!init_overlay_line(self, columns)) return false;
|
if (!init_overlay_line(self, columns, true)) return false;
|
||||||
|
|
||||||
// Resize main linebuf
|
// Resize main linebuf
|
||||||
HistoryBuf *nh = realloc_hb(self->historybuf, self->historybuf->ynum, columns, &self->as_ansi_buf);
|
HistoryBuf *nh = realloc_hb(self->historybuf, self->historybuf->ynum, columns, &self->as_ansi_buf);
|
||||||
@ -426,7 +440,6 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
|||||||
self->linebuf->line->cpu_cells[0].ch = 0;
|
self->linebuf->line->cpu_cells[0].ch = 0;
|
||||||
self->cursor->x = 0;
|
self->cursor->x = 0;
|
||||||
}
|
}
|
||||||
restore_overlay_line(self);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +476,9 @@ dealloc(Screen* self) {
|
|||||||
Py_CLEAR(self->marker);
|
Py_CLEAR(self->marker);
|
||||||
PyMem_Free(self->overlay_line.cpu_cells);
|
PyMem_Free(self->overlay_line.cpu_cells);
|
||||||
PyMem_Free(self->overlay_line.gpu_cells);
|
PyMem_Free(self->overlay_line.gpu_cells);
|
||||||
Py_CLEAR(self->overlay_line.save.overlay_text);
|
PyMem_Free(self->overlay_line.original_line.cpu_cells);
|
||||||
|
PyMem_Free(self->overlay_line.original_line.gpu_cells);
|
||||||
|
Py_CLEAR(self->overlay_line.overlay_text);
|
||||||
PyMem_Free(self->main_tabstops);
|
PyMem_Free(self->main_tabstops);
|
||||||
free(self->pending_mode.buf);
|
free(self->pending_mode.buf);
|
||||||
free(self->selections.items);
|
free(self->selections.items);
|
||||||
@ -661,7 +676,6 @@ draw_combining_char(Screen *self, char_type ch) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
draw_codepoint(Screen *self, char_type och, bool from_input_stream) {
|
draw_codepoint(Screen *self, char_type och, bool from_input_stream) {
|
||||||
if (is_ignored_char(och)) return;
|
if (is_ignored_char(och)) return;
|
||||||
@ -713,87 +727,11 @@ draw_codepoint(Screen *self, char_type och, bool from_input_stream) {
|
|||||||
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
|
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
screen_draw_overlay_text(Screen *self, const char *utf8_text) {
|
|
||||||
if (self->overlay_line.is_active) deactivate_overlay_line(self);
|
|
||||||
if (self->overlay_line.save.overlay_text) clear_saved_overlay_line(self);
|
|
||||||
if (!utf8_text || !utf8_text[0]) return;
|
|
||||||
Line *line = range_line_(self, self->cursor->y);
|
|
||||||
if (!line) return;
|
|
||||||
line_save_cells(line, 0, self->columns, self->overlay_line.gpu_cells, self->overlay_line.cpu_cells);
|
|
||||||
self->overlay_line.is_active = true;
|
|
||||||
self->overlay_line.ynum = self->cursor->y;
|
|
||||||
self->overlay_line.xstart = self->cursor->x;
|
|
||||||
self->overlay_line.xnum = 0;
|
|
||||||
uint32_t codepoint = 0; UTF8State state = UTF8_ACCEPT;
|
|
||||||
bool orig_line_wrap_mode = self->modes.mDECAWM;
|
|
||||||
self->modes.mDECAWM = false;
|
|
||||||
self->cursor->reverse ^= true;
|
|
||||||
index_type before;
|
|
||||||
while (*utf8_text) {
|
|
||||||
switch(decode_utf8(&state, &codepoint, *(utf8_text++))) {
|
|
||||||
case UTF8_ACCEPT:
|
|
||||||
before = self->cursor->x;
|
|
||||||
draw_codepoint(self, codepoint, false);
|
|
||||||
self->overlay_line.xnum += self->cursor->x - before;
|
|
||||||
break;
|
|
||||||
case UTF8_REJECT:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self->cursor->reverse ^= true;
|
|
||||||
self->modes.mDECAWM = orig_line_wrap_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
get_overlay_text(Screen *self) {
|
|
||||||
#define ol self->overlay_line
|
|
||||||
if (ol.ynum >= self->lines || ol.xnum >= self->columns || !ol.xnum) return NULL;
|
|
||||||
Line *line = range_line_(self, ol.ynum);
|
|
||||||
if (!line) return NULL;
|
|
||||||
return unicode_in_range(line, ol.xstart, ol.xstart + ol.xnum, true, false, true);
|
|
||||||
#undef ol
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
save_overlay_line(Screen *self, const char* func_name) {
|
|
||||||
if (self->overlay_line.is_active && screen_is_cursor_visible(self)) {
|
|
||||||
Py_XDECREF(self->overlay_line.save.overlay_text);
|
|
||||||
self->overlay_line.save.overlay_text = get_overlay_text(self);
|
|
||||||
self->overlay_line.save.func_name = func_name;
|
|
||||||
deactivate_overlay_line(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
restore_overlay_line(Screen *self) {
|
|
||||||
if (self->overlay_line.save.overlay_text && screen_is_cursor_visible(self)) {
|
|
||||||
debug("Received input from child (%s) while overlay active. Overlay contents: %s\n", self->overlay_line.save.func_name, PyUnicode_AsUTF8(self->overlay_line.save.overlay_text));
|
|
||||||
screen_draw_overlay_text(self, PyUnicode_AsUTF8(self->overlay_line.save.overlay_text));
|
|
||||||
clear_saved_overlay_line(self);
|
|
||||||
update_ime_position_for_window(self->window_id, false, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
clear_saved_overlay_line(Screen *self) {
|
|
||||||
Py_CLEAR(self->overlay_line.save.overlay_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
restore_overlay_line_from_cleanup(Screen **self) {
|
|
||||||
restore_overlay_line(*self);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MOVE_OVERLAY_LINE_WITH_CURSOR Screen __attribute__ ((__cleanup__(restore_overlay_line_from_cleanup))) *_sol_ = self; save_overlay_line(_sol_, __func__);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_draw(Screen *self, uint32_t och, bool from_input_stream) {
|
screen_draw(Screen *self, uint32_t och, bool from_input_stream) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
draw_codepoint(self, och, from_input_stream);
|
draw_codepoint(self, och, from_input_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_align(Screen *self) {
|
screen_align(Screen *self) {
|
||||||
self->margin_top = 0; self->margin_bottom = self->lines - 1;
|
self->margin_top = 0; self->margin_bottom = self->lines - 1;
|
||||||
@ -819,7 +757,6 @@ screen_alignment_display(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
select_graphic_rendition(Screen *self, int *params, unsigned int count, Region *region_) {
|
select_graphic_rendition(Screen *self, int *params, unsigned int count, Region *region_) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR; // needed in case colors have changed
|
|
||||||
if (region_) {
|
if (region_) {
|
||||||
Region region = *region_;
|
Region region = *region_;
|
||||||
if (!region.top) region.top = 1;
|
if (!region.top) region.top = 1;
|
||||||
@ -1018,14 +955,7 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) {
|
|||||||
self->modes.mDECCKM = val;
|
self->modes.mDECCKM = val;
|
||||||
break;
|
break;
|
||||||
case DECTCEM:
|
case DECTCEM:
|
||||||
if(!val) {
|
|
||||||
save_overlay_line(self, __func__);
|
|
||||||
self->modes.mDECTCEM = val;
|
self->modes.mDECTCEM = val;
|
||||||
} else {
|
|
||||||
self->modes.mDECTCEM = val;
|
|
||||||
if (self->overlay_line.is_active && !self->overlay_line.save.overlay_text) save_overlay_line(self, __func__);
|
|
||||||
restore_overlay_line(self);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case DECSCNM:
|
case DECSCNM:
|
||||||
// Render screen in reverse video
|
// Render screen in reverse video
|
||||||
@ -1191,7 +1121,6 @@ screen_backspace(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_tab(Screen *self) {
|
screen_tab(Screen *self) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Move to the next tab space, or the end of the screen if there aren't anymore left.
|
// Move to the next tab space, or the end of the screen if there aren't anymore left.
|
||||||
unsigned int found = 0;
|
unsigned int found = 0;
|
||||||
for (unsigned int i = self->cursor->x + 1; i < self->columns; i++) {
|
for (unsigned int i = self->cursor->x + 1; i < self->columns; i++) {
|
||||||
@ -1223,7 +1152,6 @@ screen_tab(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_backtab(Screen *self, unsigned int count) {
|
screen_backtab(Screen *self, unsigned int count) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Move back count tabs
|
// Move back count tabs
|
||||||
if (!count) count = 1;
|
if (!count) count = 1;
|
||||||
int i;
|
int i;
|
||||||
@ -1261,7 +1189,6 @@ screen_set_tab_stop(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_cursor_back(Screen *self, unsigned int count/*=1*/, int move_direction/*=-1*/) {
|
screen_cursor_back(Screen *self, unsigned int count/*=1*/, int move_direction/*=-1*/) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
if (count == 0) count = 1;
|
if (count == 0) count = 1;
|
||||||
if (move_direction < 0 && count > self->cursor->x) self->cursor->x = 0;
|
if (move_direction < 0 && count > self->cursor->x) self->cursor->x = 0;
|
||||||
else self->cursor->x += move_direction * count;
|
else self->cursor->x += move_direction * count;
|
||||||
@ -1275,13 +1202,12 @@ screen_cursor_forward(Screen *self, unsigned int count/*=1*/) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_cursor_up(Screen *self, unsigned int count/*=1*/, bool do_carriage_return/*=false*/, int move_direction/*=-1*/) {
|
screen_cursor_up(Screen *self, unsigned int count/*=1*/, bool do_carriage_return/*=false*/, int move_direction/*=-1*/) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
bool in_margins = cursor_within_margins(self);
|
bool in_margins = cursor_within_margins(self);
|
||||||
if (count == 0) count = 1;
|
if (count == 0) count = 1;
|
||||||
if (move_direction < 0 && count > self->cursor->y) self->cursor->y = 0;
|
if (move_direction < 0 && count > self->cursor->y) self->cursor->y = 0;
|
||||||
else self->cursor->y += move_direction * count;
|
else self->cursor->y += move_direction * count;
|
||||||
screen_ensure_bounds(self, true, in_margins);
|
|
||||||
if (do_carriage_return) self->cursor->x = 0;
|
if (do_carriage_return) self->cursor->x = 0;
|
||||||
|
screen_ensure_bounds(self, true, in_margins);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1301,7 +1227,6 @@ screen_cursor_down1(Screen *self, unsigned int count/*=1*/) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_cursor_to_column(Screen *self, unsigned int column) {
|
screen_cursor_to_column(Screen *self, unsigned int column) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
unsigned int x = MAX(column, 1u) - 1;
|
unsigned int x = MAX(column, 1u) - 1;
|
||||||
if (x != self->cursor->x) {
|
if (x != self->cursor->x) {
|
||||||
self->cursor->x = x;
|
self->cursor->x = x;
|
||||||
@ -1328,7 +1253,6 @@ screen_cursor_to_column(Screen *self, unsigned int column) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_index(Screen *self) {
|
screen_index(Screen *self) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Move cursor down one line, scrolling screen if needed
|
// Move cursor down one line, scrolling screen if needed
|
||||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||||
if (self->cursor->y == bottom) {
|
if (self->cursor->y == bottom) {
|
||||||
@ -1338,7 +1262,6 @@ screen_index(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_scroll(Screen *self, unsigned int count) {
|
screen_scroll(Screen *self, unsigned int count) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Scroll the screen up by count lines, not moving the cursor
|
// Scroll the screen up by count lines, not moving the cursor
|
||||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
@ -1349,7 +1272,6 @@ screen_scroll(Screen *self, unsigned int count) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_reverse_index(Screen *self) {
|
screen_reverse_index(Screen *self) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Move cursor up one line, scrolling screen if needed
|
// Move cursor up one line, scrolling screen if needed
|
||||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||||
if (self->cursor->y == top) {
|
if (self->cursor->y == top) {
|
||||||
@ -1359,7 +1281,6 @@ screen_reverse_index(Screen *self) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
_reverse_scroll(Screen *self, unsigned int count, bool fill_from_scrollback) {
|
_reverse_scroll(Screen *self, unsigned int count, bool fill_from_scrollback) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Scroll the screen down by count lines, not moving the cursor
|
// Scroll the screen down by count lines, not moving the cursor
|
||||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||||
fill_from_scrollback = fill_from_scrollback && self->linebuf == self->main_linebuf;
|
fill_from_scrollback = fill_from_scrollback && self->linebuf == self->main_linebuf;
|
||||||
@ -1389,7 +1310,6 @@ screen_reverse_scroll_and_fill_from_scrollback(Screen *self, unsigned int count)
|
|||||||
void
|
void
|
||||||
screen_carriage_return(Screen *self) {
|
screen_carriage_return(Screen *self) {
|
||||||
if (self->cursor->x != 0) {
|
if (self->cursor->x != 0) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
self->cursor->x = 0;
|
self->cursor->x = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1449,7 +1369,7 @@ copy_specific_mode(Screen *self, unsigned int mode, const ScreenModes *src, Scre
|
|||||||
SIMPLE_MODE(BRACKETED_PASTE)
|
SIMPLE_MODE(BRACKETED_PASTE)
|
||||||
SIMPLE_MODE(FOCUS_TRACKING)
|
SIMPLE_MODE(FOCUS_TRACKING)
|
||||||
SIMPLE_MODE(DECCKM)
|
SIMPLE_MODE(DECCKM)
|
||||||
SIDE_EFFECTS(DECTCEM) // side effect: redraw IME overlay line
|
SIMPLE_MODE(DECTCEM)
|
||||||
SIMPLE_MODE(DECAWM)
|
SIMPLE_MODE(DECAWM)
|
||||||
case MOUSE_BUTTON_TRACKING: case MOUSE_MOTION_TRACKING: case MOUSE_MOVE_TRACKING:
|
case MOUSE_BUTTON_TRACKING: case MOUSE_MOTION_TRACKING: case MOUSE_MOVE_TRACKING:
|
||||||
dest->mouse_tracking_mode = src->mouse_tracking_mode; break;
|
dest->mouse_tracking_mode = src->mouse_tracking_mode; break;
|
||||||
@ -1504,7 +1424,6 @@ screen_save_modes(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_restore_cursor(Screen *self) {
|
screen_restore_cursor(Screen *self) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
Savepoint *sp = self->linebuf == self->main_linebuf ? &self->main_savepoint : &self->alt_savepoint;
|
Savepoint *sp = self->linebuf == self->main_linebuf ? &self->main_savepoint : &self->alt_savepoint;
|
||||||
if (!sp->is_valid) {
|
if (!sp->is_valid) {
|
||||||
screen_cursor_position(self, 1, 1);
|
screen_cursor_position(self, 1, 1);
|
||||||
@ -1542,7 +1461,6 @@ screen_ensure_bounds(Screen *self, bool force_use_margins/*=false*/, bool in_mar
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_cursor_position(Screen *self, unsigned int line, unsigned int column) {
|
screen_cursor_position(Screen *self, unsigned int line, unsigned int column) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
bool in_margins = cursor_within_margins(self);
|
bool in_margins = cursor_within_margins(self);
|
||||||
line = (line == 0 ? 1 : line) - 1;
|
line = (line == 0 ? 1 : line) - 1;
|
||||||
column = (column == 0 ? 1: column) - 1;
|
column = (column == 0 ? 1: column) - 1;
|
||||||
@ -1737,6 +1655,7 @@ screen_insert_lines(Screen *self, unsigned int count) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
screen_scroll_until_cursor_prompt(Screen *self) {
|
screen_scroll_until_cursor_prompt(Screen *self) {
|
||||||
|
bool in_margins = cursor_within_margins(self);
|
||||||
int q = screen_cursor_at_a_shell_prompt(self);
|
int q = screen_cursor_at_a_shell_prompt(self);
|
||||||
unsigned int y = q > -1 ? (unsigned int)q : self->cursor->y;
|
unsigned int y = q > -1 ? (unsigned int)q : self->cursor->y;
|
||||||
unsigned int num_lines_to_scroll = MIN(self->margin_bottom, y);
|
unsigned int num_lines_to_scroll = MIN(self->margin_bottom, y);
|
||||||
@ -1744,6 +1663,7 @@ screen_scroll_until_cursor_prompt(Screen *self) {
|
|||||||
self->cursor->y = self->margin_bottom;
|
self->cursor->y = self->margin_bottom;
|
||||||
while (num_lines_to_scroll--) screen_index(self);
|
while (num_lines_to_scroll--) screen_index(self);
|
||||||
self->cursor->y = final_y;
|
self->cursor->y = final_y;
|
||||||
|
screen_ensure_bounds(self, false, in_margins);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1785,7 +1705,6 @@ screen_repeat_character(Screen *self, unsigned int count) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_delete_characters(Screen *self, unsigned int count) {
|
screen_delete_characters(Screen *self, unsigned int count) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Delete characters, later characters are moved left
|
// Delete characters, later characters are moved left
|
||||||
const unsigned int bottom = self->lines ? self->lines - 1 : 0;
|
const unsigned int bottom = self->lines ? self->lines - 1 : 0;
|
||||||
if (count == 0) count = 1;
|
if (count == 0) count = 1;
|
||||||
@ -1803,7 +1722,6 @@ screen_delete_characters(Screen *self, unsigned int count) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_erase_characters(Screen *self, unsigned int count) {
|
screen_erase_characters(Screen *self, unsigned int count) {
|
||||||
MOVE_OVERLAY_LINE_WITH_CURSOR;
|
|
||||||
// Delete characters replacing them by spaces
|
// Delete characters replacing them by spaces
|
||||||
if (count == 0) count = 1;
|
if (count == 0) count = 1;
|
||||||
unsigned int x = self->cursor->x;
|
unsigned int x = self->cursor->x;
|
||||||
@ -2280,11 +2198,13 @@ screen_has_marker(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_data, bool cursor_has_moved) {
|
screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_data, bool cursor_has_moved) {
|
||||||
|
const bool is_overlay_active = screen_is_overlay_active(self);
|
||||||
unsigned int history_line_added_count = self->history_line_added_count;
|
unsigned int history_line_added_count = self->history_line_added_count;
|
||||||
index_type lnum;
|
index_type lnum;
|
||||||
bool was_dirty = self->is_dirty;
|
bool was_dirty = self->is_dirty;
|
||||||
if (self->scrolled_by) self->scrolled_by = MIN(self->scrolled_by + history_line_added_count, self->historybuf->count);
|
|
||||||
screen_reset_dirty(self);
|
screen_reset_dirty(self);
|
||||||
|
update_overlay_position(self);
|
||||||
|
if (self->scrolled_by) self->scrolled_by = MIN(self->scrolled_by + history_line_added_count, self->historybuf->count);
|
||||||
self->scroll_changed = false;
|
self->scroll_changed = false;
|
||||||
for (index_type y = 0; y < MIN(self->lines, self->scrolled_by); y++) {
|
for (index_type y = 0; y < MIN(self->lines, self->scrolled_by); y++) {
|
||||||
lnum = self->scrolled_by - 1 - y;
|
lnum = self->scrolled_by - 1 - y;
|
||||||
@ -2303,11 +2223,18 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
|
|||||||
(cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor_y == lnum))) {
|
(cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor_y == lnum))) {
|
||||||
render_line(fonts_data, self->linebuf->line, lnum, self->cursor, self->disable_ligatures);
|
render_line(fonts_data, self->linebuf->line, lnum, self->cursor, self->disable_ligatures);
|
||||||
if (self->linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(self->marker, self->linebuf->line);
|
if (self->linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(self->marker, self->linebuf->line);
|
||||||
|
if (is_overlay_active && lnum == self->overlay_line.ynum) render_overlay_line(self, self->linebuf->line, fonts_data);
|
||||||
linebuf_mark_line_clean(self->linebuf, lnum);
|
linebuf_mark_line_clean(self->linebuf, lnum);
|
||||||
}
|
}
|
||||||
update_line_data(self->linebuf->line, y, address);
|
update_line_data(self->linebuf->line, y, address);
|
||||||
}
|
}
|
||||||
|
if (is_overlay_active && self->overlay_line.ynum + self->scrolled_by < self->lines) {
|
||||||
|
if (self->overlay_line.is_dirty) {
|
||||||
|
linebuf_init_line(self->linebuf, self->overlay_line.ynum);
|
||||||
|
render_overlay_line(self, self->linebuf->line, fonts_data);
|
||||||
|
}
|
||||||
|
update_overlay_line_data(self, address);
|
||||||
|
}
|
||||||
if (was_dirty) clear_selection(&self->url_ranges);
|
if (was_dirty) clear_selection(&self->url_ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2696,22 +2623,6 @@ screen_open_url(Screen *self) {
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
deactivate_overlay_line(Screen *self) {
|
|
||||||
if (self->overlay_line.is_active && self->overlay_line.xnum && self->overlay_line.ynum < self->lines) {
|
|
||||||
Line *line = range_line_(self, self->overlay_line.ynum);
|
|
||||||
line_reset_cells(line, self->overlay_line.xstart, self->overlay_line.xnum, self->overlay_line.gpu_cells, self->overlay_line.cpu_cells);
|
|
||||||
if (self->cursor->y == self->overlay_line.ynum) self->cursor->x = self->overlay_line.xstart;
|
|
||||||
self->is_dirty = true;
|
|
||||||
linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);
|
|
||||||
}
|
|
||||||
self->overlay_line.is_active = false;
|
|
||||||
self->overlay_line.ynum = 0;
|
|
||||||
self->overlay_line.xnum = 0;
|
|
||||||
self->overlay_line.xstart = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// URLs {{{
|
// URLs {{{
|
||||||
@ -2792,7 +2703,136 @@ screen_detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
|||||||
return has_url ? -1 : 0;
|
return has_url ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// IME Overlay {{{
|
||||||
|
bool
|
||||||
|
screen_is_overlay_active(Screen *self) {
|
||||||
|
return self->overlay_line.is_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
deactivate_overlay_line(Screen *self) {
|
||||||
|
if (self->overlay_line.is_active && self->overlay_line.xnum && self->overlay_line.ynum < self->lines) {
|
||||||
|
self->is_dirty = true;
|
||||||
|
linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);
|
||||||
|
}
|
||||||
|
self->overlay_line.is_active = false;
|
||||||
|
self->overlay_line.is_dirty = true;
|
||||||
|
self->overlay_line.ynum = 0;
|
||||||
|
self->overlay_line.xstart = 0;
|
||||||
|
self->overlay_line.cursor_x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
screen_update_overlay_text(Screen *self, const char *utf8_text) {
|
||||||
|
if (screen_is_overlay_active(self)) deactivate_overlay_line(self);
|
||||||
|
if (!utf8_text || !utf8_text[0]) return;
|
||||||
|
PyObject *text = PyUnicode_FromString(utf8_text);
|
||||||
|
if (!text) return;
|
||||||
|
Py_XDECREF(self->overlay_line.overlay_text);
|
||||||
|
// Calculate the total number of cells for initial overlay cursor position
|
||||||
|
PyObject *text_len = wcswidth_std(NULL, text);
|
||||||
|
self->overlay_line.overlay_text = text;
|
||||||
|
self->overlay_line.is_active = true;
|
||||||
|
self->overlay_line.is_dirty = true;
|
||||||
|
self->overlay_line.xstart = self->cursor->x;
|
||||||
|
self->overlay_line.xnum = !text_len ? 0 : PyLong_AsLong(text_len);
|
||||||
|
self->overlay_line.cursor_x = MIN(self->overlay_line.xstart + self->overlay_line.xnum, self->columns);
|
||||||
|
self->overlay_line.ynum = self->cursor->y;
|
||||||
|
cursor_copy_to(self->cursor, &(self->overlay_line.original_line.cursor));
|
||||||
|
linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);
|
||||||
|
self->is_dirty = true;
|
||||||
|
// Since we are typing, scroll to the bottom
|
||||||
|
if (self->scrolled_by != 0) {
|
||||||
|
self->scrolled_by = 0;
|
||||||
|
self->scroll_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
screen_draw_overlay_line(Screen *self) {
|
||||||
|
if (!self->overlay_line.overlay_text) return;
|
||||||
|
const char *utf8_text = PyUnicode_AsUTF8(self->overlay_line.overlay_text);
|
||||||
|
if (!utf8_text || !utf8_text[0]) return;
|
||||||
|
self->overlay_line.xnum = 0;
|
||||||
|
uint32_t codepoint = 0; UTF8State state = UTF8_ACCEPT;
|
||||||
|
bool orig_line_wrap_mode = self->modes.mDECAWM;
|
||||||
|
bool orig_cursor_enable_mode = self->modes.mDECTCEM;
|
||||||
|
bool orig_insert_replace_mode = self->modes.mIRM;
|
||||||
|
self->modes.mDECAWM = false;
|
||||||
|
self->modes.mDECTCEM = false;
|
||||||
|
self->modes.mIRM = false;
|
||||||
|
Cursor *orig_cursor = self->cursor;
|
||||||
|
self->cursor = &(self->overlay_line.original_line.cursor);
|
||||||
|
self->cursor->reverse ^= true;
|
||||||
|
self->cursor->x = self->overlay_line.xstart;
|
||||||
|
self->cursor->y = self->overlay_line.ynum;
|
||||||
|
self->overlay_line.xnum = 0;
|
||||||
|
index_type before;
|
||||||
|
while (*utf8_text) {
|
||||||
|
switch(decode_utf8(&state, &codepoint, *(utf8_text++))) {
|
||||||
|
case UTF8_ACCEPT:
|
||||||
|
before = self->cursor->x;
|
||||||
|
draw_codepoint(self, codepoint, false);
|
||||||
|
self->overlay_line.xnum += self->cursor->x - before;
|
||||||
|
break;
|
||||||
|
case UTF8_REJECT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->overlay_line.cursor_x = self->cursor->x;
|
||||||
|
self->cursor->reverse ^= true;
|
||||||
|
self->cursor = orig_cursor;
|
||||||
|
self->modes.mDECAWM = orig_line_wrap_mode;
|
||||||
|
self->modes.mDECTCEM = orig_cursor_enable_mode;
|
||||||
|
self->modes.mIRM = orig_insert_replace_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_overlay_position(Screen *self) {
|
||||||
|
if (screen_is_overlay_active(self) && screen_is_cursor_visible(self)) {
|
||||||
|
bool cursor_update = false;
|
||||||
|
if (self->cursor->x != self->overlay_line.xstart) {
|
||||||
|
cursor_update = true;
|
||||||
|
self->overlay_line.xstart = self->cursor->x;
|
||||||
|
self->overlay_line.cursor_x = MIN(self->overlay_line.xstart + self->overlay_line.xnum, self->columns);
|
||||||
|
}
|
||||||
|
if (self->cursor->y != self->overlay_line.ynum) {
|
||||||
|
cursor_update = true;
|
||||||
|
linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);
|
||||||
|
self->overlay_line.ynum = self->cursor->y;
|
||||||
|
}
|
||||||
|
if (cursor_update) {
|
||||||
|
linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);
|
||||||
|
self->overlay_line.is_dirty = true;
|
||||||
|
self->is_dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
render_overlay_line(Screen *self, Line *line, FONTS_DATA_HANDLE fonts_data) {
|
||||||
|
#define ol self->overlay_line
|
||||||
|
line_save_cells(line, 0, line->xnum, ol.original_line.gpu_cells, ol.original_line.cpu_cells);
|
||||||
|
screen_draw_overlay_line(self);
|
||||||
|
render_line(fonts_data, line, ol.ynum, self->cursor, self->disable_ligatures);
|
||||||
|
line_save_cells(line, 0, line->xnum, ol.gpu_cells, ol.cpu_cells);
|
||||||
|
line_reset_cells(line, 0, line->xnum, ol.original_line.gpu_cells, ol.original_line.cpu_cells);
|
||||||
|
ol.is_dirty = false;
|
||||||
|
const index_type y = MIN(ol.ynum + self->scrolled_by, self->lines - 1);
|
||||||
|
if (ol.last_ime_pos.x != ol.cursor_x || ol.last_ime_pos.y != y) {
|
||||||
|
ol.last_ime_pos.x = ol.cursor_x; ol.last_ime_pos.y = y;
|
||||||
|
update_ime_position_for_window(self->window_id, false, 0);
|
||||||
|
}
|
||||||
|
#undef ol
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_overlay_line_data(Screen *self, uint8_t *data) {
|
||||||
|
const size_t base = sizeof(GPUCell) * (self->overlay_line.ynum + self->scrolled_by) * self->columns;
|
||||||
|
memcpy(data + base, self->overlay_line.gpu_cells, self->columns * sizeof(GPUCell));
|
||||||
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
@ -3921,7 +3961,7 @@ focus_changed(Screen *self, PyObject *has_focus_) {
|
|||||||
if (has_focus != previous) {
|
if (has_focus != previous) {
|
||||||
self->has_focus = has_focus;
|
self->has_focus = has_focus;
|
||||||
if (has_focus) self->has_activity_since_last_focus = false;
|
if (has_focus) self->has_activity_since_last_focus = false;
|
||||||
else if (self->overlay_line.is_active) deactivate_overlay_line(self);
|
else if (screen_is_overlay_active(self)) deactivate_overlay_line(self);
|
||||||
if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, CSI, has_focus ? "I" : "O");
|
if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, CSI, has_focus ? "I" : "O");
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,15 +68,20 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
PyObject *overlay_text;
|
||||||
CPUCell *cpu_cells;
|
CPUCell *cpu_cells;
|
||||||
GPUCell *gpu_cells;
|
GPUCell *gpu_cells;
|
||||||
|
index_type xstart, ynum, xnum, cursor_x;
|
||||||
bool is_active;
|
bool is_active;
|
||||||
index_type xstart, ynum, xnum;
|
bool is_dirty;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
PyObject *overlay_text;
|
CPUCell *cpu_cells;
|
||||||
const char *func_name;
|
GPUCell *gpu_cells;
|
||||||
} save;
|
Cursor cursor;
|
||||||
|
} original_line;
|
||||||
|
struct {
|
||||||
|
index_type x, y;
|
||||||
|
} last_ime_pos;
|
||||||
} OverlayLine;
|
} OverlayLine;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -264,7 +269,8 @@ void screen_dirty_sprite_positions(Screen *self);
|
|||||||
void screen_rescale_images(Screen *self);
|
void screen_rescale_images(Screen *self);
|
||||||
void screen_report_size(Screen *, unsigned int which);
|
void screen_report_size(Screen *, unsigned int which);
|
||||||
void screen_manipulate_title_stack(Screen *, unsigned int op, unsigned int which);
|
void screen_manipulate_title_stack(Screen *, unsigned int op, unsigned int which);
|
||||||
void screen_draw_overlay_text(Screen *self, const char *utf8_text);
|
bool screen_is_overlay_active(Screen *self);
|
||||||
|
void screen_update_overlay_text(Screen *self, const char *utf8_text);
|
||||||
void screen_set_key_encoding_flags(Screen *self, uint32_t val, uint32_t how);
|
void screen_set_key_encoding_flags(Screen *self, uint32_t val, uint32_t how);
|
||||||
void screen_push_key_encoding_flags(Screen *self, uint32_t val);
|
void screen_push_key_encoding_flags(Screen *self, uint32_t val);
|
||||||
void screen_pop_key_encoding_flags(Screen *self, uint32_t num);
|
void screen_pop_key_encoding_flags(Screen *self, uint32_t num);
|
||||||
|
|||||||
@ -329,7 +329,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
|||||||
// Cursor position
|
// Cursor position
|
||||||
enum { BLOCK_IDX = 0, BEAM_IDX = NUM_UNDERLINE_STYLES + 3, UNDERLINE_IDX = NUM_UNDERLINE_STYLES + 4, UNFOCUSED_IDX = NUM_UNDERLINE_STYLES + 5 };
|
enum { BLOCK_IDX = 0, BEAM_IDX = NUM_UNDERLINE_STYLES + 3, UNDERLINE_IDX = NUM_UNDERLINE_STYLES + 4, UNFOCUSED_IDX = NUM_UNDERLINE_STYLES + 5 };
|
||||||
if (cursor->is_visible) {
|
if (cursor->is_visible) {
|
||||||
rd->cursor_x = screen->cursor->x, rd->cursor_y = screen->cursor->y;
|
rd->cursor_x = cursor->x, rd->cursor_y = cursor->y;
|
||||||
if (cursor->is_focused) {
|
if (cursor->is_focused) {
|
||||||
switch(cursor->shape) {
|
switch(cursor->shape) {
|
||||||
default:
|
default:
|
||||||
@ -341,11 +341,11 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
|||||||
}
|
}
|
||||||
} else rd->cursor_fg_sprite_idx = UNFOCUSED_IDX;
|
} else rd->cursor_fg_sprite_idx = UNFOCUSED_IDX;
|
||||||
color_type cell_fg = rd->default_fg, cell_bg = rd->default_bg;
|
color_type cell_fg = rd->default_fg, cell_bg = rd->default_bg;
|
||||||
index_type cell_color_x = screen->cursor->x;
|
index_type cell_color_x = cursor->x;
|
||||||
bool cursor_ok = screen->cursor->x < screen->columns && screen->cursor->y < screen->lines;
|
bool cursor_ok = cursor->x < screen->columns && cursor->y < screen->lines;
|
||||||
bool reversed = false;
|
bool reversed = false;
|
||||||
if (cursor_ok) {
|
if (cursor_ok) {
|
||||||
linebuf_init_line(screen->linebuf, screen->cursor->y);
|
linebuf_init_line(screen->linebuf, cursor->y);
|
||||||
colors_for_cell(screen->linebuf->line, screen->color_profile, &cell_color_x, &cell_fg, &cell_bg, &reversed);
|
colors_for_cell(screen->linebuf->line, screen->color_profile, &cell_color_x, &cell_fg, &cell_bg, &reversed);
|
||||||
}
|
}
|
||||||
if (IS_SPECIAL_COLOR(cursor_color)) {
|
if (IS_SPECIAL_COLOR(cursor_color)) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user