Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kitty for openSUSE:Factory checked in at 2022-11-07 13:52:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitty (Old) and /work/SRC/openSUSE:Factory/.kitty.new.1597 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitty" Mon Nov 7 13:52:14 2022 rev:14 rq:1034148 version:0.26.5 Changes: -------- --- /work/SRC/openSUSE:Factory/kitty/kitty.changes 2022-10-19 13:18:01.449265271 +0200 +++ /work/SRC/openSUSE:Factory/.kitty.new.1597/kitty.changes 2022-11-07 13:52:24.340042445 +0100 @@ -1,0 +2,20 @@ +Mon Nov 7 10:50:41 UTC 2022 - Michael Vetter <mvet...@suse.com> + +- Update to 0.26.5: + * Splits layout: Add a new mappable action to move the active + window to the screen edge (#5643) + * ssh kitten: Allow using absolute paths for the location of + transferred data (#5607) + * Fix a regression in the previous release that caused a + resize_draw_strategy of static to not work (#5601) + * Wayland KDE: Fix abort when pasting into Firefox (#5603) + * Wayland GNOME: Fix ghosting when using background_tint (#5605) + * Fix cursor position at x=0 changing to x=1 on resize (#5635) + * Wayland GNOME: Fix incorrect window size in some circumstances + when switching between windows with window decorations disabled (#4802) + * Wayland: Fix high CPU usage when using some input methods (#5369) + * Remote control: When matching window by state:focused and no window + currently has keyboard focus, match the window belonging to the OS + window that was last focused (#5602) + +------------------------------------------------------------------- Old: ---- kitty-0.26.4.tar.gz New: ---- kitty-0.26.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitty.spec ++++++ --- /var/tmp/diff_new_pack.SoFNOx/_old 2022-11-07 13:52:24.948045863 +0100 +++ /var/tmp/diff_new_pack.SoFNOx/_new 2022-11-07 13:52:24.952045885 +0100 @@ -19,7 +19,7 @@ # sphinx_copybutton not in Factory %bcond_with docs Name: kitty -Version: 0.26.4 +Version: 0.26.5 Release: 0 Summary: A GPU-based terminal emulator License: GPL-3.0-only ++++++ kitty-0.26.4.tar.gz -> kitty-0.26.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/.github/workflows/ci.yml new/kitty-0.26.5/.github/workflows/ci.yml --- old/kitty-0.26.4/.github/workflows/ci.yml 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/.github/workflows/ci.yml 2022-11-07 11:06:42.000000000 +0100 @@ -27,11 +27,11 @@ sanitize: 0 - python: b - pyver: "3.9" + pyver: "3.11" sanitize: 1 - python: c - pyver: "3.10" + pyver: "3.9" sanitize: 1 @@ -76,7 +76,7 @@ - name: Set up Python uses: actions/setup-python@v3 with: - python-version: 3.9 + python-version: "3.10" - name: Install build-only deps run: pip install -r docs/requirements.txt flake8 mypy types-requests types-docutils @@ -128,6 +128,11 @@ with: fetch-depth: 0 # needed for :commit: docs role + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Build kitty run: python3 .github/workflows/ci.py build diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/bypy/linux/__main__.py new/kitty-0.26.5/bypy/linux/__main__.py --- old/kitty-0.26.4/bypy/linux/__main__.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/bypy/linux/__main__.py 2022-11-07 11:06:42.000000000 +0100 @@ -220,7 +220,7 @@ print('Compressing archive...') ans = f'{dist.rpartition(".")[0]}.txz' start_time = time.time() - subprocess.check_call(['xz', '--threads=0', '-f', f'-{compression_level}', dist]) + subprocess.check_call(['xz', '--verbose', '--threads=0', '-f', f'-{compression_level}', dist]) secs = time.time() - start_time print('Compressed in {} minutes {} seconds'.format(secs // 60, secs % 60)) os.rename(f'{dist}.xz', ans) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/docs/changelog.rst new/kitty-0.26.5/docs/changelog.rst --- old/kitty-0.26.4/docs/changelog.rst 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/docs/changelog.rst 2022-11-07 11:06:42.000000000 +0100 @@ -35,6 +35,27 @@ Detailed list of changes ------------------------------------- +0.26.5 [2022-11-07] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Splits layout: Add a new mappable action to move the active window to the screen edge (:iss:`5643`) + +- ssh kitten: Allow using absolute paths for the location of transferred data (:iss:`5607`) + +- Fix a regression in the previous release that caused a :opt:`resize_draw_strategy` of ``static`` to not work (:iss:`5601`) + +- Wayland KDE: Fix abort when pasting into Firefox (:iss:`5603`) + +- Wayland GNOME: Fix ghosting when using :opt:`background_tint` (:iss:`5605`) + +- Fix cursor position at x=0 changing to x=1 on resize (:iss:`5635`) + +- Wayland GNOME: Fix incorrect window size in some circumstances when switching between windows with window decorations disabled (:iss:`4802`) + +- Wayland: Fix high CPU usage when using some input methods (:pull:`5369`) + +- Remote control: When matching window by `state:focused` and no window currently has keyboard focus, match the window belonging to the OS window that was last focused (:iss:`5602`) + 0.26.4 [2022-10-17] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/docs/conf.py new/kitty-0.26.5/docs/conf.py --- old/kitty-0.26.4/docs/conf.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/docs/conf.py 2022-11-07 11:06:42.000000000 +0100 @@ -277,7 +277,7 @@ from kitty.rc.base import ( RemoteCommand, all_command_names, command_for_name ) - field_pat = re.compile(r'\s*([a-zA-Z0-9_+/]+)\s*:\s*(.+)') + field_pat = re.compile(r'\s*([^:]+?)\s*:\s*(.+)') def format_cmd(p: Callable[..., None], name: str, cmd: RemoteCommand) -> None: p(name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/docs/layouts.rst new/kitty-0.26.5/docs/layouts.rst --- old/kitty-0.26.4/docs/layouts.rst 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/docs/layouts.rst 2022-11-07 11:06:42.000000000 +0100 @@ -167,6 +167,12 @@ map shift+right move_window right map shift+down move_window down + # Move the active window to the indicated screen edge + map ctrl+shift+up layout_action move_to_screen_edge top + map ctrl+shift+left layout_action move_to_screen_edge left + map ctrl+shift+right layout_action move_to_screen_edge right + map ctrl+shift+down layout_action move_to_screen_edge bottom + # Switch focus to the neighboring window in the indicated direction map ctrl+left neighboring_window left map ctrl+right neighboring_window right diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/glfw/context.c new/kitty-0.26.5/glfw/context.c --- old/kitty-0.26.4/glfw/context.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/glfw/context.c 2022-11-07 11:06:42.000000000 +0100 @@ -478,6 +478,9 @@ } window->context.swapBuffers(window); +#ifdef _GLFW_WAYLAND + _glfwWaylandAfterBufferSwap(window); +#endif } GLFWAPI void glfwSwapInterval(int interval) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/glfw/egl_context.c new/kitty-0.26.5/glfw/egl_context.c --- old/kitty-0.26.4/glfw/egl_context.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/glfw/egl_context.c 2022-11-07 11:06:42.000000000 +0100 @@ -326,6 +326,7 @@ glfw_dlsym(_glfw.egl.SwapBuffers, _glfw.egl.handle, "eglSwapBuffers"); glfw_dlsym(_glfw.egl.SwapInterval, _glfw.egl.handle, "eglSwapInterval"); glfw_dlsym(_glfw.egl.QueryString, _glfw.egl.handle, "eglQueryString"); + glfw_dlsym(_glfw.egl.QuerySurface, _glfw.egl.handle, "eglQuerySurface"); glfw_dlsym(_glfw.egl.GetProcAddress, _glfw.egl.handle, "eglGetProcAddress"); if (!_glfw.egl.GetConfigAttrib || diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/glfw/egl_context.h new/kitty-0.26.5/glfw/egl_context.h --- old/kitty-0.26.4/glfw/egl_context.h 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/glfw/egl_context.h 2022-11-07 11:06:42.000000000 +0100 @@ -132,6 +132,7 @@ typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); +typedef const char* (EGLAPIENTRY * PFN_eglQuerySurface)(EGLDisplay,EGLSurface,EGLint,EGLint*); typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs @@ -149,6 +150,7 @@ #define eglSwapBuffers _glfw.egl.SwapBuffers #define eglSwapInterval _glfw.egl.SwapInterval #define eglQueryString _glfw.egl.QueryString +#define eglQuerySurface _glfw.egl.QuerySurface #define eglGetProcAddress _glfw.egl.GetProcAddress typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum,void*,const EGLint*); @@ -211,6 +213,7 @@ PFN_eglSwapBuffers SwapBuffers; PFN_eglSwapInterval SwapInterval; PFN_eglQueryString QueryString; + PFN_eglQuerySurface QuerySurface; PFN_eglGetProcAddress GetProcAddress; PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/glfw/wl_platform.h new/kitty-0.26.5/glfw/wl_platform.h --- old/kitty-0.26.4/glfw/wl_platform.h 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/glfw/wl_platform.h 2022-11-07 11:06:42.000000000 +0100 @@ -152,6 +152,7 @@ bool hovered; bool transparent; struct wl_surface* surface; + bool waiting_for_swap_to_commit; struct wl_egl_window* native; struct wl_callback* callback; @@ -369,6 +370,7 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version); +void _glfwWaylandAfterBufferSwap(_GLFWwindow *window); void _glfwSetupWaylandDataDevice(void); void _glfwSetupWaylandPrimarySelectionDevice(void); void animateCursorImage(id_type timer_id, void *data); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/glfw/wl_text_input.c new/kitty-0.26.5/glfw/wl_text_input.c --- old/kitty-0.26.4/glfw/wl_text_input.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/glfw/wl_text_input.c 2022-11-07 11:06:42.000000000 +0100 @@ -9,12 +9,15 @@ #include "internal.h" #include "wayland-text-input-unstable-v3-client-protocol.h" #include <stdlib.h> +#include <string.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 char *pending_pre_edit = NULL; +static char *current_pre_edit = NULL; static char *pending_commit = NULL; +static int last_cursor_left = 0, last_cursor_top = 0, last_cursor_width = 0, last_cursor_height = 0; uint32_t commit_serial = 0; static void commit(void) { @@ -90,12 +93,20 @@ if (serial > commit_serial) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial); return; } - if (pending_pre_edit) { - send_text(pending_pre_edit, GLFW_IME_PREEDIT_CHANGED); + + if ((pending_pre_edit == NULL && current_pre_edit == NULL) || + (pending_pre_edit && current_pre_edit && strcmp(pending_pre_edit, current_pre_edit) == 0)) { free(pending_pre_edit); pending_pre_edit = NULL; } else { - // Clear pre-edit text - send_text(NULL, GLFW_IME_WAYLAND_DONE_EVENT); + free(current_pre_edit); + current_pre_edit = pending_pre_edit; + pending_pre_edit = NULL; + if (current_pre_edit) { + send_text(current_pre_edit, GLFW_IME_PREEDIT_CHANGED); + } else { + // Clear pre-edit text + send_text(NULL, GLFW_IME_WAYLAND_DONE_EVENT); + } } if (pending_commit) { send_text(pending_commit, GLFW_IME_COMMIT_TEXT); @@ -133,6 +144,7 @@ if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager); text_input = NULL; text_input_manager = NULL; free(pending_pre_edit); pending_pre_edit = NULL; + free(current_pre_edit); current_pre_edit = NULL; free(pending_commit); pending_commit = NULL; } @@ -146,10 +158,11 @@ 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); } else { - if (pending_pre_edit) { + free(pending_pre_edit); pending_pre_edit = NULL; + if (current_pre_edit) { // Clear pre-edit text send_text(NULL, GLFW_IME_PREEDIT_CHANGED); - free(pending_pre_edit); pending_pre_edit = NULL; + free(current_pre_edit); current_pre_edit = NULL; } if (pending_commit) { free(pending_commit); pending_commit = NULL; @@ -161,9 +174,15 @@ 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(); + if (left != last_cursor_left || top != last_cursor_top || width != last_cursor_width || height != last_cursor_height) { + last_cursor_left = left; + last_cursor_top = top; + last_cursor_width = width; + last_cursor_height = height; + 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; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/glfw/wl_window.c new/kitty-0.26.5/glfw/wl_window.c --- old/kitty-0.26.4/glfw/wl_window.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/glfw/wl_window.c 2022-11-07 11:06:42.000000000 +0100 @@ -251,6 +251,15 @@ return false; } +static void +commit_window_surface_if_safe(_GLFWwindow *window) { + // we only commit if the buffer attached to the surface is the correct size, + // which means that at least one frame is drawn after resizeFramebuffer() + if (!window->wl.waiting_for_swap_to_commit) { + wl_surface_commit(window->wl.surface); + } +} + // Makes the surface considered as XRGB instead of ARGB. static void setOpaqueRegion(_GLFWwindow* window, bool commit_surface) { @@ -262,7 +271,7 @@ wl_region_add(region, 0, 0, window->wl.width, window->wl.height); wl_surface_set_opaque_region(window->wl.surface, region); - if (commit_surface) wl_surface_commit(window->wl.surface); + if (commit_surface) commit_window_surface_if_safe(window); wl_region_destroy(region); } @@ -287,9 +296,20 @@ debug("Resizing framebuffer to: %dx%d at scale: %d\n", window->wl.width, window->wl.height, scale); wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); if (!window->wl.transparent) setOpaqueRegion(window, false); + window->wl.waiting_for_swap_to_commit = true; _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); } +void +_glfwWaylandAfterBufferSwap(_GLFWwindow* window) { + if (window->wl.waiting_for_swap_to_commit) { + debug("Waiting for swap to commit: swap has happened\n"); + window->wl.waiting_for_swap_to_commit = false; + // this is not really needed, since I think eglSwapBuffers() calls wl_surface_commit() + // but lets be safe. See https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/egl/drivers/dri2/platform_wayland.c#L1510 + wl_surface_commit(window->wl.surface); + } +} static const char* clipboard_mime(void) { @@ -301,9 +321,9 @@ } static bool -dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height, bool *scale_changed) { +dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) { bool size_changed = width != window->wl.width || height != window->wl.height; - *scale_changed = checkScaleChange(window); + bool scale_changed = checkScaleChange(window); if (size_changed) { _glfwInputWindowSize(window, width, height); @@ -311,7 +331,7 @@ resizeFramebuffer(window); } - if (*scale_changed) { + if (scale_changed) { debug("Scale changed to %d in dispatchChangesAfterConfigure\n", window->wl.scale); if (!size_changed) resizeFramebuffer(window); _glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale); @@ -319,7 +339,7 @@ _glfwInputWindowDamage(window); - return size_changed || *scale_changed; + return size_changed || scale_changed; } static void @@ -568,6 +588,15 @@ struct xdg_surface* surface, uint32_t serial) { + // The poorly documented pattern Wayland requires is: + // 1) ack the configure, + // 2) set the window geometry + // 3) attach a new buffer of the correct size to the surface + // 4) only then commit the surface. + // buffer is attached only by eglSwapBuffers, + // so we set a flag to not commit the surface till the next swapbuffers. Note that + // wl_egl_window_resize() does not actually resize the buffer until the next draw call + // or buffer state query. _GLFWwindow* window = data; xdg_surface_ack_configure(surface, serial); if (window->wl.pending_state & PENDING_STATE_TOPLEVEL) { @@ -600,11 +629,10 @@ } bool resized = false; - bool scale_changed = false; if (window->wl.pending_state) { int width = window->wl.pending.width, height = window->wl.pending.height; set_csd_window_geometry(window, &width, &height); - resized = dispatchChangesAfterConfigure(window, width, height, &scale_changed); + resized = dispatchChangesAfterConfigure(window, width, height); if (window->wl.decorations.serverSide) { free_csd_surfaces(window); } else { @@ -614,14 +642,7 @@ } inform_compositor_of_window_geometry(window, "configure"); - - // we need to swap buffers here to ensure the buffer attached to the surface is a multiple - // of the new scale. See https://github.com/kovidgoyal/kitty/issues/5467 - if (scale_changed) swap_buffers(window); - - // if a resize happened there will be a commit at the next render frame so - // dont commit here, GNOME doesnt like it and its not really needed anyway - if (!resized) wl_surface_commit(window->wl.surface); + commit_window_surface_if_safe(window); window->wl.pending_state = 0; } @@ -1021,7 +1042,7 @@ window->wl.width = w; window->wl.height = h; resizeFramebuffer(window); ensure_csd_resources(window); - wl_surface_commit(window->wl.surface); + commit_window_surface_if_safe(window); inform_compositor_of_window_geometry(window, "SetWindowSize"); } } @@ -1038,7 +1059,7 @@ maxwidth = maxheight = 0; xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); - wl_surface_commit(window->wl.surface); + commit_window_surface_if_safe(window); } } @@ -1273,7 +1294,7 @@ } else wl_surface_set_input_region(window->wl.surface, 0); - wl_surface_commit(window->wl.surface); + commit_window_surface_if_safe(window); } float _glfwPlatformGetWindowOpacity(_GLFWwindow* window UNUSED) @@ -1337,7 +1358,7 @@ zwp_locked_pointer_v1_set_cursor_position_hint( window->wl.pointerLock.lockedPointer, wl_fixed_from_double(x), wl_fixed_from_double(y)); - wl_surface_commit(window->wl.surface); + commit_window_surface_if_safe(window); } } @@ -1691,9 +1712,18 @@ zwp_primary_selection_source_v1_destroy(primary_selection_source); } +// KWin aborts if we don't define these even though they are not used for copy/paste +static void dummy_data_source_target(void* data UNUSED, struct wl_data_source* wl_data_source UNUSED, const char* mime_type UNUSED) { +} + +static void dummy_data_source_action(void* data UNUSED, struct wl_data_source* wl_data_source UNUSED, uint dnd_action UNUSED) { +} + static const struct wl_data_source_listener data_source_listener = { .send = _glfwSendClipboardText, .cancelled = data_source_canceled, + .target = dummy_data_source_target, + .action = dummy_data_source_action, }; static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = { @@ -2232,12 +2262,19 @@ _GLFWwindow* window = (_GLFWwindow*) handle; static const struct wl_callback_listener frame_listener = { .done = frame_handle_redraw }; if (window->wl.frameCallbackData.current_wl_callback) wl_callback_destroy(window->wl.frameCallbackData.current_wl_callback); - window->wl.frameCallbackData.id = id; - window->wl.frameCallbackData.callback = callback; - window->wl.frameCallbackData.current_wl_callback = wl_surface_frame(window->wl.surface); - if (window->wl.frameCallbackData.current_wl_callback) { - wl_callback_add_listener(window->wl.frameCallbackData.current_wl_callback, &frame_listener, window); - wl_surface_commit(window->wl.surface); + if (window->wl.waiting_for_swap_to_commit) { + callback(id); + window->wl.frameCallbackData.id = 0; + window->wl.frameCallbackData.callback = NULL; + window->wl.frameCallbackData.current_wl_callback = NULL; + } else { + window->wl.frameCallbackData.id = id; + window->wl.frameCallbackData.callback = callback; + window->wl.frameCallbackData.current_wl_callback = wl_surface_frame(window->wl.surface); + if (window->wl.frameCallbackData.current_wl_callback) { + wl_callback_add_listener(window->wl.frameCallbackData.current_wl_callback, &frame_listener, window); + commit_window_surface_if_safe(window); + } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kittens/ssh/options/definition.py new/kitty-0.26.5/kittens/ssh/options/definition.py --- old/kitty-0.26.4/kittens/ssh/options/definition.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kittens/ssh/options/definition.py 2022-11-07 11:06:42.000000000 +0100 @@ -40,10 +40,9 @@ available or broken, using an alternate interpreter can be useful. ''') -opt('remote_dir', '.local/share/kitty-ssh-kitten', option_type='relative_dir', long_text=''' +opt('remote_dir', '.local/share/kitty-ssh-kitten', long_text=''' The location on the remote host where the files needed for this kitten are -installed. The location is relative to the HOME directory. Absolute paths or -paths that resolve to a location outside the HOME are not allowed. +installed. Relative paths are resolved with respect to :code:`$HOME`. ''') opt('+copy', '', option_type='copy', add_to_default=False, long_text=f''' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kittens/ssh/options/parse.py new/kitty-0.26.5/kittens/ssh/options/parse.py --- old/kitty-0.26.4/kittens/ssh/options/parse.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kittens/ssh/options/parse.py 2022-11-07 11:06:42.000000000 +0100 @@ -1,7 +1,7 @@ # generated by gen-config.py DO NOT edit import typing -from kittens.ssh.options.utils import copy, env, hostname, relative_dir +from kittens.ssh.options.utils import copy, env, hostname from kitty.conf.utils import merge_dicts, to_bool @@ -39,7 +39,7 @@ ans['login_shell'] = str(val) def remote_dir(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['remote_dir'] = relative_dir(val) + ans['remote_dir'] = str(val) def remote_kitty(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: val = val.lower() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kittens/ssh/options/utils.py new/kitty-0.26.5/kittens/ssh/options/utils.py --- old/kitty-0.26.4/kittens/ssh/options/utils.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kittens/ssh/options/utils.py 2022-11-07 11:06:42.000000000 +0100 @@ -1,7 +1,6 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net> -import posixpath from typing import Any, Dict, Iterable, Optional, Tuple from ..copy import CopyInstruction, parse_copy_instructions @@ -9,16 +8,6 @@ DELETE_ENV_VAR = '_delete_this_env_var_' -def relative_dir(val: str) -> str: - if posixpath.isabs(val): - raise ValueError(f'Absolute paths not allowed. {val} is invalid.') - base = '/ffjdg' - q = posixpath.normpath(posixpath.join(base, val)) - if q == base or not q.startswith(base): - raise ValueError(f'Paths that escape their parent dir are not allowed. {val} is not valid') - return posixpath.normpath(val) - - def env(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, str]]: val = val.strip() if val: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/boss.py new/kitty-0.26.5/kitty/boss.py --- old/kitty-0.26.4/kitty/boss.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/boss.py 2022-11-07 11:06:42.000000000 +0100 @@ -12,8 +12,8 @@ from gettext import gettext as _ from time import monotonic from typing import ( - TYPE_CHECKING, Any, Callable, Container, Dict, Iterable, Iterator, List, - Optional, Set, Tuple, Union + TYPE_CHECKING, Any, Callable, Container, Dict, Iterable, Iterator, List, Optional, + Set, Tuple, Union, ) from weakref import WeakValueDictionary @@ -21,15 +21,15 @@ from .cli import create_opts, parse_args from .cli_stub import CLIOptions from .clipboard import ( - Clipboard, get_clipboard_string, get_primary_selection, - set_clipboard_string, set_primary_selection + Clipboard, get_clipboard_string, get_primary_selection, set_clipboard_string, + set_primary_selection, ) from .conf.utils import BadLine, KeyAction, to_cmdline from .config import common_opts_as_dict, prepare_config_file_for_editing from .constants import ( RC_ENCRYPTION_PROTOCOL_VERSION, appname, cache_dir, clear_handled_signals, - config_dir, handled_signals, is_macos, is_wayland, kitty_exe, - logo_png_file, supports_primary_selection, website_url + config_dir, handled_signals, is_macos, is_wayland, kitty_exe, logo_png_file, + supports_primary_selection, website_url, ) from .fast_data_types import ( CLOSE_BEING_CONFIRMED, GLFW_MOD_ALT, GLFW_MOD_CONTROL, GLFW_MOD_SHIFT, @@ -37,15 +37,15 @@ IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED, ChildMonitor, Color, EllipticCurveKey, KeyEvent, SingleKey, add_timer, apply_options_update, background_opacity_of, change_background_opacity, change_os_window_state, - cocoa_set_menubar_title, create_os_window, - current_application_quit_request, current_os_window, destroy_global_data, - focus_os_window, get_boss, get_options, get_os_window_size, - global_font_size, mark_os_window_for_close, os_window_font_size, - patch_global_colors, redirect_mouse_handling, ring_bell, - run_with_activation_token, safe_pipe, send_data_to_peer, - set_application_quit_request, set_background_image, set_boss, - set_in_sequence_mode, set_options, set_os_window_size, set_os_window_title, - thread_write, toggle_fullscreen, toggle_maximized, toggle_secure_input + cocoa_set_menubar_title, create_os_window, current_application_quit_request, + current_focused_os_window_id, current_os_window, destroy_global_data, + focus_os_window, get_boss, get_options, get_os_window_size, global_font_size, + last_focused_os_window_id, mark_os_window_for_close, os_window_font_size, + patch_global_colors, redirect_mouse_handling, ring_bell, run_with_activation_token, + safe_pipe, send_data_to_peer, set_application_quit_request, set_background_image, + set_boss, set_in_sequence_mode, set_options, set_os_window_size, + set_os_window_title, thread_write, toggle_fullscreen, toggle_maximized, + toggle_secure_input, ) from .key_encoding import get_name_to_functional_number_map from .keys import get_shortcut, shortcut_matches @@ -57,16 +57,14 @@ from .prewarm import PrewarmProcess from .rgb import color_from_int from .session import Session, create_sessions, get_os_window_sizing_data -from .tabs import ( - SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager -) +from .tabs import SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager from .types import _T, AsyncResponse, WindowSystemMouseEvent, ac from .typing import PopenType, TypedDict from .utils import ( cleanup_ssh_control_masters, func_name, get_editor, get_new_os_window_size, is_path_in_temp_dir, less_version, log_error, macos_version, open_url, parse_address_spec, parse_uri_list, platform_window_id, remove_socket_file, - safe_print, single_instance, startup_notification_handler, which + safe_print, single_instance, startup_notification_handler, which, ) from .window import CommandOutput, CwdRequest, Window @@ -80,6 +78,8 @@ id: int platform_window_id: Optional[int] is_focused: bool + is_active: bool + last_focused: bool tabs: List[TabDict] wm_class: str wm_name: str @@ -227,7 +227,7 @@ t = tm.tab_for_id(self.prev_tab_id) if t is not tm.active_tab and t is not None: tm.set_active_tab(t) - if current_os_window() != self.prev_os_window_id and self.prev_os_window_id is not None: + if current_focused_os_window_id() != self.prev_os_window_id and self.prev_os_window_id is not None: focus_os_window(self.prev_os_window_id, True) @@ -287,9 +287,7 @@ self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None self.update_keymap() if is_macos: - from .fast_data_types import ( - cocoa_set_notification_activated_callback - ) + from .fast_data_types import cocoa_set_notification_activated_callback cocoa_set_notification_activated_callback(notification_activated) def update_keymap(self) -> None: @@ -345,14 +343,15 @@ def list_os_windows(self, self_window: Optional[Window] = None) -> Iterator[OSWindowDict]: with cached_process_data(): - active_tab, active_window = self.active_tab, self.active_window active_tab_manager = self.active_tab_manager for os_window_id, tm in self.os_window_map.items(): yield { 'id': os_window_id, 'platform_window_id': platform_window_id(os_window_id), - 'is_focused': tm is active_tab_manager, - 'tabs': list(tm.list_tabs(active_tab, active_window, self_window)), + 'is_active': tm is active_tab_manager, + 'is_focused': current_focused_os_window_id() == os_window_id, + 'last_focused': os_window_id == last_focused_os_window_id(), + 'tabs': list(tm.list_tabs(self_window)), 'wm_class': tm.wm_class, 'wm_name': tm.wm_name } @@ -377,6 +376,10 @@ return from .search_query_parser import search tab = self.active_tab + if current_focused_os_window_id() <= 0: + tm = self.os_window_map.get(last_focused_os_window_id()) + if tm is not None: + tab = tm.active_tab def get_matches(location: str, query: str, candidates: Set[int]) -> Set[int]: return {wid for wid in candidates if self.window_id_map[wid].matches_query(location, query, tab)} @@ -398,6 +401,8 @@ return self.all_tabs from .search_query_parser import search tm = self.active_tab_manager + if current_focused_os_window_id() <= 0: + tm = self.os_window_map.get(last_focused_os_window_id()) or tm tim = {t.id: t for t in self.all_tabs} def get_matches(location: str, query: str, candidates: Set[int]) -> Set[int]: @@ -424,7 +429,7 @@ if tab is not self.active_tab: tm.set_active_tab(tab, for_keep_focus=window.tabref() if for_keep_focus else None) tab.set_active_window(w, for_keep_focus=window if for_keep_focus else None) - if switch_os_window_if_needed and current_os_window() != os_window_id: + if switch_os_window_if_needed and current_focused_os_window_id() != os_window_id: focus_os_window(os_window_id, True) return os_window_id return None @@ -534,9 +539,7 @@ return True def remote_cmd_permission_received(self, pcmd: Dict[str, Any], window_id: int, peer_id: int, choice: str) -> None: - from .remote_control import ( - encode_response_for_peer, set_user_password_allowed - ) + from .remote_control import encode_response_for_peer, set_user_password_allowed response: RCResponse = None window = self.window_id_map.get(window_id) choice = choice or 'r' @@ -587,9 +590,7 @@ self.show_error(_('remote_control mapping failed'), tb) def call_remote_control(self, active_window: Optional[Window], args: Tuple[str, ...]) -> 'ResponseType': - from .rc.base import ( - PayloadGetter, command_for_name, parse_subcommand_cli - ) + from .rc.base import PayloadGetter, command_for_name, parse_subcommand_cli from .remote_control import parse_rc_args aa = list(args) silent = False @@ -1088,8 +1089,14 @@ @property def active_tab_manager(self) -> Optional[TabManager]: - os_window_id = current_os_window() - return None if os_window_id is None else self.os_window_map.get(os_window_id) + os_window_id = current_focused_os_window_id() + if os_window_id <= 0: + os_window_id = last_focused_os_window_id() + if os_window_id <= 0: + q = current_os_window() + if q is not None: + os_window_id = q + return self.os_window_map.get(os_window_id) @property def active_tab(self) -> Optional[Tab]: @@ -2248,9 +2255,7 @@ log_error(f'Failed to process update check data {raw!r}, with error: {e}') def dbus_notification_callback(self, activated: bool, a: int, b: Union[int, str]) -> None: - from .notify import ( - dbus_notification_activated, dbus_notification_created - ) + from .notify import dbus_notification_activated, dbus_notification_created if activated: assert isinstance(b, str) dbus_notification_activated(a, b) @@ -2284,9 +2289,7 @@ map f5 set_colors --configured /path/to/some/config/file/colors.conf ''') def set_colors(self, *args: str) -> None: - from kitty.rc.base import ( - PayloadGetter, command_for_name, parse_subcommand_cli - ) + from kitty.rc.base import PayloadGetter, command_for_name, parse_subcommand_cli from kitty.remote_control import parse_rc_args c = command_for_name('set_colors') try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/child-monitor.c new/kitty-0.26.5/kitty/child-monitor.c --- old/kitty-0.26.4/kitty/child-monitor.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/child-monitor.c 2022-11-07 11:06:42.000000000 +0100 @@ -690,6 +690,11 @@ float x_ratio = 1, y_ratio = 1; draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window); br->is_dirty = false; + bool static_live_resize_in_progress = os_window->live_resize.in_progress && OPT(resize_draw_strategy) == RESIZE_DRAW_STATIC; + if (static_live_resize_in_progress) { + x_ratio = (float) os_window->viewport_width / (float) os_window->live_resize.width; + y_ratio = (float) os_window->viewport_height / (float) os_window->live_resize.height; + } if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, 0, &TD, x_ratio, y_ratio, os_window, true, false, NULL); for (unsigned int i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/constants.py new/kitty-0.26.5/kitty/constants.py --- old/kitty-0.26.4/kitty/constants.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/constants.py 2022-11-07 11:06:42.000000000 +0100 @@ -6,7 +6,7 @@ import pwd import sys from contextlib import suppress -from typing import TYPE_CHECKING, Any, Iterable, NamedTuple, Optional, Set +from typing import TYPE_CHECKING, Any, Iterator, NamedTuple, Optional, Set from .types import run_once @@ -22,7 +22,7 @@ appname: str = 'kitty' kitty_face = '????' -version: Version = Version(0, 26, 4) +version: Version = Version(0, 26, 5) str_version: str = '.'.join(map(str, version)) _plat = sys.platform.lower() is_macos: bool = 'darwin' in _plat @@ -219,15 +219,28 @@ return bool(getattr(running_in_kitty, 'ans', False)) -def list_kitty_resources(package: str = 'kitty') -> Iterable[str]: - from importlib.resources import contents - return contents(package) +def list_kitty_resources(package: str = 'kitty') -> Iterator[str]: + try: + if sys.version_info[:2] < (3, 10): + raise ImportError('importlib.resources.files() doesnt work with frozen builds on python 3.9') + from importlib.resources import files + except ImportError: + from importlib.resources import contents + return iter(contents(package)) + else: + return (path.name for path in files(package).iterdir()) def read_kitty_resource(name: str, package_name: str = 'kitty') -> bytes: - from importlib.resources import read_binary - - return read_binary(package_name, name) + try: + if sys.version_info[:2] < (3, 10): + raise ImportError('importlib.resources.files() doesnt work with frozen builds on python 3.9') + from importlib.resources import files + except ImportError: + from importlib.resources import read_binary + return read_binary(package_name, name) + else: + return (files(package_name) / name).read_bytes() def website_url(doc_name: str = '', website: str = 'https://sw.kovidgoyal.net/kitty/') -> str: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/fast_data_types.pyi new/kitty-0.26.5/kitty/fast_data_types.pyi --- old/kitty-0.26.4/kitty/fast_data_types.pyi 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/fast_data_types.pyi 2022-11-07 11:06:42.000000000 +0100 @@ -786,6 +786,14 @@ pass +def last_focused_os_window_id() -> int: + pass + + +def current_focused_os_window_id() -> int: + pass + + def cocoa_set_menubar_title(title: str) -> None: pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/glfw.c new/kitty-0.26.5/kitty/glfw.c --- old/kitty-0.26.4/kitty/glfw.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/glfw.c 2022-11-07 11:06:42.000000000 +0100 @@ -312,14 +312,6 @@ window->live_resize.last_resize_event_at = monotonic(); global_state.callback_os_window = NULL; request_tick_callback(); - if (global_state.is_wayland) { - // because of the stupidity of Wayland design, the GLFW wayland backend - // will need to swap buffers immediately after a scale change to ensure - // the buffer size is a multiple of the scale. So blank the new buffer to - // ensure we dont leak any unintialized pixels to the screen. The OpenGL viewport - // will already have been resized to its new size in framebuffer_size_callback - blank_os_window(window); - } } static void @@ -1539,8 +1531,8 @@ // Some Wayland compositors are too fragile to handle multiple // render frame requests, see https://github.com/kovidgoyal/kitty/issues/2329 if (w->render_state != RENDER_FRAME_REQUESTED) { - glfwRequestWaylandFrameEvent(w->handle, w->id, wayland_frame_request_callback); w->render_state = RENDER_FRAME_REQUESTED; + glfwRequestWaylandFrameEvent(w->handle, w->id, wayland_frame_request_callback); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/layout/splits.py new/kitty-0.26.5/kitty/layout/splits.py --- old/kitty-0.26.4/kitty/layout/splits.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/layout/splits.py 2022-11-07 11:06:42.000000000 +0100 @@ -414,6 +414,16 @@ def pairs_root(self, root: Pair) -> None: self._pairs_root = root + def remove_windows(self, *windows_to_remove: int) -> None: + root = self.pairs_root + for pair in root.self_and_descendants(): + pair.remove_windows(windows_to_remove) + root.collapse_redundant_pairs() + if root.one is None or root.two is None: + q = root.one or root.two + if isinstance(q, Pair): + self.pairs_root = q + def do_layout(self, all_windows: WindowList) -> None: groups = tuple(all_windows.iter_all_layoutable_groups()) window_count = len(groups) @@ -421,15 +431,8 @@ all_present_window_ids = frozenset(w.id for w in groups) already_placed_window_ids = frozenset(root.all_window_ids()) windows_to_remove = already_placed_window_ids - all_present_window_ids - if windows_to_remove: - for pair in root.self_and_descendants(): - pair.remove_windows(windows_to_remove) - root.collapse_redundant_pairs() - if root.one is None or root.two is None: - q = root.one or root.two - if isinstance(q, Pair): - root = self.pairs_root = q + self.remove_windows(*windows_to_remove) id_window_map = {w.id: w for w in groups} id_idx_map = {w.id: i for i, w in enumerate(groups)} windows_to_add = all_present_window_ids - already_placed_window_ids @@ -552,6 +555,23 @@ if swap: pair.one, pair.two = pair.two, pair.one return True + elif action_name == 'move_to_screen_edge': + args = args or ('left',) + which = args[0] + horizontal = which in ('left', 'right') + wg = all_windows.active_group + if wg is not None: + self.remove_windows(wg.id) + new_root = Pair(horizontal) + if which in ('left', 'top'): + new_root.balanced_add(wg.id) + new_root.two = self.pairs_root + else: + new_root.one = self.pairs_root + new_root.two = wg.id + self.pairs_root = new_root + return True + return None def layout_state(self) -> Dict[str, Any]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/multiprocessing.py new/kitty-0.26.5/kitty/multiprocessing.py --- old/kitty-0.26.4/kitty/multiprocessing.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/multiprocessing.py 2022-11-07 11:06:42.000000000 +0100 @@ -81,7 +81,7 @@ q = ctx.Queue() p = ctx.Process(target=q.put, args=('hello',)) p.start() - x = q.get(timeout=2) + x = q.get(timeout=8) assert x == 'hello' p.join() finally: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/prewarm.py new/kitty-0.26.5/kitty/prewarm.py --- old/kitty-0.26.4/kitty/prewarm.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/prewarm.py 2022-11-07 11:06:42.000000000 +0100 @@ -38,7 +38,7 @@ error_events = select.POLLERR | select.POLLNVAL | select.POLLHUP -TIMEOUT = 15.0 if os.environ.get('CI') == 'true' else 5.0 +TIMEOUT = 5.0 def restore_python_signal_handlers() -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/rc/base.py new/kitty-0.26.5/kitty/rc/base.py --- old/kitty-0.26.4/kitty/rc/base.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/rc/base.py 2022-11-07 11:06:42.000000000 +0100 @@ -100,7 +100,7 @@ The field :code:`state` matches on the state of the window. Supported states are: :code:`active`, :code:`focused`, :code:`needs_attention`, :code:`parent_active` and :code:`parent_focused`. Active windows are the windows that are active in their parent tab. There is only one focused window and it is the -window to which keyboard events are delivered. +window to which keyboard events are delivered. If no window is focused, the last focused window is matched. Note that you can use the :ref:`kitty @ ls <at-ls>` command to get a list of windows. ''' @@ -134,7 +134,7 @@ The field :code:`state` matches on the state of the tab. Supported states are: :code:`active`, :code:`focused`, :code:`needs_attention`, :code:`parent_active` and :code:`parent_focused`. Active tabs are the tabs that are active in their parent OS window. There is only one focused tab -and it is the tab to which keyboard events are delivered. +and it is the tab to which keyboard events are delivered. If no tab is focused, the last focused tab is matched. Note that you can use the :ref:`kitty @ ls <at-ls>` command to get a list of tabs. ''' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/rewrap.h new/kitty-0.26.5/kitty/rewrap.h --- old/kitty-0.26.4/kitty/rewrap.h 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/rewrap.h 2022-11-07 11:06:42.000000000 +0100 @@ -81,7 +81,7 @@ for (TrackCursor *t = track; !t->is_sentinel; t++) { if (t->is_tracked_line && src_x <= t->x && t->x < src_x + num) { t->y = dest_y; - t->x = dest_x + (t->x - src_x + 1); + t->x = dest_x + (t->x - src_x + (t->x > 0)); } } src_x += num; dest_x += num; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/screen.c new/kitty-0.26.5/kitty/screen.c --- old/kitty-0.26.4/kitty/screen.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/screen.c 2022-11-07 11:06:42.000000000 +0100 @@ -368,6 +368,7 @@ if (n == NULL) return false; Py_CLEAR(self->main_linebuf); self->main_linebuf = n; if (is_main) setup_cursor(cursor); + /* printf("old_cursor: (%u, %u) new_cursor: (%u, %u) beyond_content: %d\n", self->cursor->x, self->cursor->y, cursor.after.x, cursor.after.y, cursor.is_beyond_content); */ setup_cursor(main_saved_cursor); grman_resize(self->main_grman, self->lines, lines, self->columns, columns); @@ -396,7 +397,6 @@ clear_selection(&self->selections); clear_selection(&self->url_ranges); self->last_visited_prompt.is_set = false; - /* printf("old_cursor: (%u, %u) new_cursor: (%u, %u) beyond_content: %d\n", self->cursor->x, self->cursor->y, cursor_x, cursor_y, cursor_is_beyond_content); */ #define S(c, w) c->x = MIN(w.after.x, self->columns - 1); c->y = MIN(w.after.y, self->lines - 1); S(self->cursor, cursor); S((&(self->main_savepoint.cursor)), main_saved_cursor); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/shaders.c new/kitty-0.26.5/kitty/shaders.c --- old/kitty-0.26.4/kitty/shaders.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/shaders.c 2022-11-07 11:06:42.000000000 +0100 @@ -12,6 +12,7 @@ #include "window_logo.h" #define BLEND_ONTO_OPAQUE glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // blending onto opaque colors +#define BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); // blending onto opaque colors with final color having alpha 1 #define BLEND_PREMULT glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blending of pre-multiplied colors enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BLIT_PROGRAM, BGIMAGE_PROGRAM, TINT_PROGRAM, NUM_PROGRAMS }; @@ -531,7 +532,7 @@ static void draw_tint(bool premult, Screen *screen, const CellRenderData *crd) { - // On GNOME+Wayland this causes ghosting while rendering. Yet another GNOME bug, does not occur under KDE, Hyprland or Sway. + if (premult) { BLEND_PREMULT } else { BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT } bind_program(TINT_PROGRAM); color_type window_bg = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.default_bg, screen->color_profile->configured.default_bg).rgb; #define C(shift) ((((GLfloat)((window_bg >> shift) & 0xFF)) / 255.0f)) * premult_factor @@ -721,7 +722,6 @@ glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } else if (OPT(background_tint) > 0) { - BLEND_ONTO_OPAQUE; draw_tint(false, screen, crd); BLEND_ONTO_OPAQUE; } @@ -758,7 +758,6 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *os_window, const CellRenderData *crd, const WindowLogoRenderData *wl) { if (OPT(background_tint) > 0.f) { glEnable(GL_BLEND); - BLEND_PREMULT; draw_tint(true, screen, crd); glDisable(GL_BLEND); } @@ -982,7 +981,7 @@ BLEND_ONTO_OPAQUE; background_opacity = 1.0f; tint_opacity = OPT(background_tint); - tint_premult = 1.0f; + tint_premult = w->is_semi_transparent ? OPT(background_tint) : 1.0f; } if (num_border_rects) { @@ -1007,6 +1006,10 @@ color_type default_bg = (num_visible_windows > 1 && !all_windows_have_same_bg) ? OPT(background) : active_window_bg; glUniform3f(border_uniform_locations[BORDER_default_bg], CV3(default_bg)); #undef CV3 + if (has_bgimage(w)) { + if (w->is_semi_transparent) { BLEND_PREMULT; } + else { BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT; } + } glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, num_border_rects); unbind_vertex_array(); unbind_program(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/state.c new/kitty-0.26.5/kitty/state.c --- old/kitty-0.26.4/kitty/state.c 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/state.c 2022-11-07 11:06:42.000000000 +0100 @@ -96,7 +96,7 @@ OSWindow* -current_os_window() { +current_os_window(void) { if (global_state.callback_os_window) return global_state.callback_os_window; for (size_t i = 0; i < global_state.num_os_windows; i++) { if (global_state.os_windows[i].is_focused) return global_state.os_windows + i; @@ -104,6 +104,29 @@ return global_state.os_windows; } +static id_type +last_focused_os_window_id(void) { + id_type ans = 0, max_fc_count = 0; + for (size_t i = 0; i < global_state.num_os_windows; i++) { + OSWindow *w = &global_state.os_windows[i]; + if (w->last_focused_counter > max_fc_count) { + ans = w->id; max_fc_count = w->last_focused_counter; + } + } + return ans; +} + + +static id_type +current_focused_os_window_id(void) { + for (size_t i = 0; i < global_state.num_os_windows; i++) { + OSWindow *w = &global_state.os_windows[i]; + if (w->is_focused) { return w->id; } + } + return 0; +} + + OSWindow* os_window_for_kitty_window(id_type kitty_window_id) { for (size_t i = 0; i < global_state.num_os_windows; i++) { @@ -665,6 +688,15 @@ return PyLong_FromUnsignedLongLong(global_state.window_id_counter + 1); } +PYWRAP0(last_focused_os_window_id) { + return PyLong_FromUnsignedLongLong(last_focused_os_window_id()); +} + +PYWRAP0(current_focused_os_window_id) { + return PyLong_FromUnsignedLongLong(current_focused_os_window_id()); +} + + PYWRAP1(handle_for_window_id) { id_type os_window_id; PA("K", &os_window_id); @@ -1266,6 +1298,8 @@ static PyMethodDef module_methods[] = { MW(current_os_window, METH_NOARGS), MW(next_window_id, METH_NOARGS), + MW(last_focused_os_window_id, METH_NOARGS), + MW(current_focused_os_window_id, METH_NOARGS), MW(set_options, METH_VARARGS), MW(get_options, METH_NOARGS), MW(click_mouse_url, METH_VARARGS), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/tab_bar.py new/kitty-0.26.5/kitty/tab_bar.py --- old/kitty-0.26.4/kitty/tab_bar.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/tab_bar.py 2022-11-07 11:06:42.000000000 +0100 @@ -2,6 +2,7 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> import os +import re from functools import lru_cache, partial, wraps from string import Formatter as StringFormatter from typing import ( @@ -189,7 +190,7 @@ safe_builtins = { - 'max': max, 'min': min, 'str': str, 'repr': repr, 'abs': abs, 'len': len, + 'max': max, 'min': min, 'str': str, 'repr': repr, 'abs': abs, 'len': len, 're': re, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/tabs.py new/kitty-0.26.5/kitty/tabs.py --- old/kitty-0.26.4/kitty/tabs.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/tabs.py 2022-11-07 11:06:42.000000000 +0100 @@ -10,8 +10,8 @@ from operator import attrgetter from time import monotonic from typing import ( - Any, Deque, Dict, Generator, Iterable, Iterator, List, - NamedTuple, Optional, Sequence, Set, Tuple, Union + Any, Deque, Dict, Generator, Iterable, Iterator, List, NamedTuple, Optional, + Sequence, Set, Tuple, Union, ) from .borders import Border, Borders @@ -19,11 +19,11 @@ from .cli_stub import CLIOptions from .constants import appname, kitty_exe from .fast_data_types import ( - GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, GLFW_RELEASE, - add_tab, attach_window, current_os_window, detach_window, get_boss, - get_click_interval, get_options, mark_tab_bar_dirty, next_window_id, - remove_tab, remove_window, ring_bell, set_active_tab, set_active_window, - swap_tabs, sync_os_window_title + GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, GLFW_RELEASE, add_tab, + attach_window, current_focused_os_window_id, detach_window, get_boss, + get_click_interval, get_options, last_focused_os_window_id, mark_tab_bar_dirty, + next_window_id, remove_tab, remove_window, ring_bell, set_active_tab, + set_active_window, swap_tabs, sync_os_window_title, ) from .layout.base import Layout from .layout.interface import create_layout_object_for, evict_cached_layouts @@ -46,6 +46,7 @@ class TabDict(TypedDict): id: int is_focused: bool + is_active_tab: bool title: str layout: str layout_state: Dict[str, Any] @@ -698,9 +699,13 @@ def move_window_backward(self) -> None: self.move_window(-1) - def list_windows(self, active_window: Optional[Window], self_window: Optional[Window] = None) -> Generator[WindowDict, None, None]: + def list_windows(self, self_window: Optional[Window] = None) -> Generator[WindowDict, None, None]: + active_window = self.active_window for w in self: - yield w.as_dict(is_focused=w is active_window, is_self=w is self_window) + yield w.as_dict( + is_active_window=w is active_window, + is_focused=w.os_window_id == current_focused_os_window_id() and w is active_window, + is_self=w is self_window) def matches_query(self, field: str, query: str, active_tab_manager: Optional['TabManager'] = None) -> bool: if field == 'title': @@ -724,9 +729,10 @@ return False if field == 'state': if query == 'active': - return active_tab_manager is not None and self is active_tab_manager.active_tab + tm = self.tab_manager_ref() + return tm is not None and self is tm.active_tab if query == 'focused': - return active_tab_manager is not None and self is active_tab_manager.active_tab and self.os_window_id == current_os_window() + return active_tab_manager is not None and self is active_tab_manager.active_tab and self.os_window_id == last_focused_os_window_id() if query == 'needs_attention': for w in self: if w.needs_attention: @@ -734,7 +740,7 @@ if query == 'parent_active': return active_tab_manager is not None and self.tab_manager_ref() is active_tab_manager if query == 'parent_focused': - return active_tab_manager is not None and self.tab_manager_ref() is active_tab_manager and self.os_window_id == current_os_window() + return active_tab_manager is not None and self.tab_manager_ref() is active_tab_manager and self.os_window_id == last_focused_os_window_id() return False return False @@ -758,7 +764,7 @@ self.windows = WindowList(self) def __repr__(self) -> str: - return f'Tab(title={self.effective_title}, id={hex(id(self))})' + return f'Tab(title={self.effective_title}, id={self.id})' def make_active(self) -> None: tm = self.tab_manager_ref() @@ -934,17 +940,19 @@ def __len__(self) -> int: return len(self.tabs) - def list_tabs(self, active_tab: Optional[Tab], active_window: Optional[Window], self_window: Optional[Window] = None) -> Generator[TabDict, None, None]: + def list_tabs(self, self_window: Optional[Window] = None) -> Generator[TabDict, None, None]: + active_tab = self.active_tab for tab in self: yield { 'id': tab.id, - 'is_focused': tab is active_tab, + 'is_focused': tab is active_tab and tab.os_window_id == current_focused_os_window_id(), + 'is_active_tab': tab is active_tab, 'title': tab.name or tab.title, 'layout': str(tab.current_layout.name), 'layout_state': tab.current_layout.layout_state(), 'layout_opts': tab.current_layout.layout_opts.serialized(), 'enabled_layouts': tab.enabled_layouts, - 'windows': list(tab.list_windows(active_window, self_window)), + 'windows': list(tab.list_windows(self_window)), 'active_window_history': list(tab.windows.active_window_history), } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty/window.py new/kitty-0.26.5/kitty/window.py --- old/kitty-0.26.4/kitty/window.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty/window.py 2022-11-07 11:06:42.000000000 +0100 @@ -36,11 +36,12 @@ NUM_UNDERLINE_STYLES, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, STRIKETHROUGH, TINT_PROGRAM, Color, KeyEvent, Screen, add_timer, add_window, cell_size_for_window, click_mouse_cmd_output, click_mouse_url, compile_program, - current_os_window, encode_key_for_tty, get_boss, get_click_interval, get_options, - init_cell_program, mark_os_window_dirty, mouse_selection, - move_cursor_to_mouse_if_in_prompt, pt_to_px, set_titlebar_color, set_window_logo, - set_window_padding, set_window_render_data, update_ime_position_for_window, - update_window_title, update_window_visibility, wakeup_main_loop, + current_focused_os_window_id, encode_key_for_tty, get_boss, get_click_interval, + get_options, init_cell_program, last_focused_os_window_id, mark_os_window_dirty, + mouse_selection, move_cursor_to_mouse_if_in_prompt, pt_to_px, set_titlebar_color, + set_window_logo, set_window_padding, set_window_render_data, + update_ime_position_for_window, update_window_title, update_window_visibility, + wakeup_main_loop, ) from .keys import keyboard_mode_name, mod_mask from .notify import ( @@ -138,6 +139,7 @@ class WindowDict(TypedDict): id: int is_focused: bool + is_active_window: bool title: str pid: Optional[int] cwd: str @@ -614,10 +616,11 @@ def __repr__(self) -> str: return f'Window(title={self.title}, id={self.id})' - def as_dict(self, is_focused: bool = False, is_self: bool = False) -> WindowDict: + def as_dict(self, is_focused: bool = False, is_self: bool = False, is_active_window: bool = False) -> WindowDict: return dict( id=self.id, is_focused=is_focused, + is_active_window=is_active_window, title=self.title, pid=self.child.pid, cwd=self.child.current_cwd or self.child.cwd, @@ -705,15 +708,20 @@ return False if field == 'state': if query == 'active': - return active_tab is not None and self is active_tab.active_window + tab = self.tabref() + return tab is not None and tab.active_window is self if query == 'focused': - return active_tab is not None and self is active_tab.active_window and current_os_window() == self.os_window_id + return active_tab is not None and self is active_tab.active_window and last_focused_os_window_id() == self.os_window_id if query == 'needs_attention': return self.needs_attention if query == 'parent_active': - return active_tab is not None and self.tabref() is active_tab + tab = self.tabref() + if tab is not None: + tm = tab.tab_manager_ref() + return tm is not None and tm.active_tab is tab + return False if query == 'parent_focused': - return active_tab is not None and self.tabref() is active_tab and current_os_window() == self.os_window_id + return active_tab is not None and self.tabref() is active_tab and last_focused_os_window_id() == self.os_window_id return False pat = compile_match_query(query, field != 'env') return self.matches(field, pat) @@ -919,7 +927,7 @@ tab = self.tabref() if tab is not None: tab.relayout_borders() - elif self.os_window_id == current_os_window(): + elif self.os_window_id == current_focused_os_window_id(): # Cancel IME composition after loses focus update_ime_position_for_window(self.id, False, -1) @@ -1354,7 +1362,7 @@ self.destroyed = True del self.kitten_result_processors if hasattr(self, 'screen'): - if self.is_active and self.os_window_id == current_os_window(): + if self.is_active and self.os_window_id == current_focused_os_window_id(): # Cancel IME composition when window is destroyed update_ime_position_for_window(self.id, False, -1) # Remove cycles so that screen is de-allocated immediately diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty_tests/main.py new/kitty-0.26.5/kitty_tests/main.py --- old/kitty-0.26.4/kitty_tests/main.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty_tests/main.py 2022-11-07 11:06:42.000000000 +0100 @@ -4,9 +4,20 @@ import importlib import os import shutil +import sys import unittest -from importlib.resources import contents -from typing import Callable, Generator, NoReturn, Sequence, Set +from typing import Callable, Generator, Iterator, NoReturn, Sequence, Set + + +def contents(package: str) -> Iterator[str]: + try: + if sys.version_info[:2] < (3, 10): + raise ImportError('importlib.resources.files() doesnt work with frozen builds on python 3.9') + from importlib.resources import files + except ImportError: + from importlib.resources import contents + return iter(contents(package)) + return (path.name for path in files(package).iterdir()) def itertests(suite: unittest.TestSuite) -> Generator[unittest.TestCase, None, None]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty_tests/prewarm.py new/kitty-0.26.5/kitty_tests/prewarm.py --- old/kitty-0.26.4/kitty_tests/prewarm.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty_tests/prewarm.py 2022-11-07 11:06:42.000000000 +0100 @@ -50,7 +50,7 @@ 'pid': os.getpid(), 'font_family': get_options().font_family, 'stdin': sys.stdin.read(), - }, indent=2), "ALL_OUTPUT_PRESENT", sep="")"""], cwd=cwd, env=env, stdin_data=stdin_data) + }, indent=2), "ALL_OUTPUT_PRESENT", sep="")"""], cwd=cwd, env=env, stdin_data=stdin_data, timeout=15.0) self.assertFalse(pty.screen_contents().strip()) p.mark_child_as_ready(child.child_id) pty.wait_till(lambda: 'ALL_OUTPUT_PRESENT' in pty.screen_contents()) @@ -140,7 +140,7 @@ # macOS does not send SIGCHLD when child is continued # https://stackoverflow.com/questions/48487935/sigchld-is-sent-on-sigcont-on-linux-but-not-on-macos p.stdin.close() - p.wait(1) + p.wait(3) for fd, event in poll.poll(0): read_signals(signal_read_fd, lambda si: None) finally: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty_tests/screen.py new/kitty-0.26.5/kitty_tests/screen.py --- old/kitty-0.26.4/kitty_tests/screen.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty_tests/screen.py 2022-11-07 11:06:42.000000000 +0100 @@ -332,6 +332,15 @@ s.resize(s.lines - 1, s.columns) self.ae(x_before, s.cursor.x) + s = self.create_screen() + s.draw('abc') + b = s.cursor.x + s.resize(7, s.columns) + self.assertEqual(s.cursor.x, b) + s.cursor.x = 0 + s.resize(5, s.columns) + self.assertEqual(s.cursor.x, 0) + def test_scrollback_fill_after_resize(self): def prepare_screen(content=()): ans = self.create_screen(options={'scrollback_fill_enlarged_window': True}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/kitty_tests/ssh.py new/kitty-0.26.5/kitty_tests/ssh.py --- old/kitty-0.26.4/kitty_tests/ssh.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/kitty_tests/ssh.py 2022-11-07 11:06:42.000000000 +0100 @@ -275,13 +275,13 @@ if 'bzip2' in q: raise ValueError('Untarring failed with screen contents:\n' + q) return 'UNTAR_DONE' in q - pty.wait_till(check_untar_or_fail) + pty.wait_till(check_untar_or_fail, timeout=30) self.assertTrue(os.path.exists(os.path.join(home_dir, '.terminfo/kitty.terminfo'))) if SHELL_INTEGRATION_VALUE != 'enabled': - pty.wait_till(lambda: len(pty.screen_contents().splitlines()) > 1) + pty.wait_till(lambda: len(pty.screen_contents().splitlines()) > 1, timeout=30) self.assertEqual(pty.screen.cursor.shape, 0) else: - pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM) + pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM, timeout=30) return pty finally: with suppress(FileNotFoundError): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/shell-integration/ssh/bootstrap.py new/kitty-0.26.5/shell-integration/ssh/bootstrap.py --- old/kitty-0.26.4/shell-integration/ssh/bootstrap.py 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/shell-integration/ssh/bootstrap.py 2022-11-07 11:06:42.000000000 +0100 @@ -205,7 +205,10 @@ with open(tdir + '/data.sh') as f: env_vars = f.read() apply_env_vars(env_vars) - data_dir = os.path.join(HOME, os.environ.pop('KITTY_SSH_KITTEN_DATA_DIR')) + data_dir = os.environ.pop('KITTY_SSH_KITTEN_DATA_DIR') + if not os.path.isabs(data_dir): + data_dir = os.path.join(HOME, data_dir) + data_dir = os.path.abspath(data_dir) shell_integration_dir = os.path.join(data_dir, 'shell-integration') compile_terminfo(tdir + '/home') move(tdir + '/home', HOME) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.4/shell-integration/ssh/bootstrap.sh new/kitty-0.26.5/shell-integration/ssh/bootstrap.sh --- old/kitty-0.26.4/shell-integration/ssh/bootstrap.sh 2022-10-17 04:48:51.000000000 +0200 +++ new/kitty-0.26.5/shell-integration/ssh/bootstrap.sh 2022-11-07 11:06:42.000000000 +0100 @@ -103,7 +103,10 @@ . "$tdir/bootstrap-utils.sh" . "$tdir/data.sh" [ -z "$KITTY_SSH_KITTEN_DATA_DIR" ] && die "Failed to read SSH data from tty" - data_dir="$HOME/$KITTY_SSH_KITTEN_DATA_DIR" + case "$KITTY_SSH_KITTEN_DATA_DIR" in + /*) data_dir="$KITTY_SSH_KITTEN_DATA_DIR" ;; + *) data_dir="$HOME/$KITTY_SSH_KITTEN_DATA_DIR" + esac shell_integration_dir="$data_dir/shell-integration" unset KITTY_SSH_KITTEN_DATA_DIR login_shell="$KITTY_LOGIN_SHELL"