Compare commits

..

34 Commits

Author SHA1 Message Date
Kovid Goyal
15ac33a058 version 0.36.2 2024-09-06 08:09:44 +05:30
Kovid Goyal
b9051d8288 Merge branch 'achalmers/typo' of https://github.com/adamchalmers/kitty 2024-09-04 19:44:37 +05:30
Adam Chalmers
b5fc63cbb5 Fix typo in docs
s/firt/first
2024-09-04 08:06:06 -05:00
Kovid Goyal
913ce58fe3 Make shlex_split always return a token
Matches behavior of split() so is therefore more intuitive
2024-09-02 17:30:18 +05:30
Kovid Goyal
d9b1c8c04f Fix a harmless error being printed to stderr if shell integration sends an empty cmdline
Fixes #7813
2024-09-02 11:38:31 +05:30
Kovid Goyal
ccc3bee9af Add a simple function for listing basic font data 2024-09-01 10:50:52 +05:30
Kovid Goyal
8a607fa34c kitten run-shell: Forward KSI env var for no-rc
This is useless, since if you are doing manual shell integration
anyway there is no point in running via the run-shell kitten.
But, let's keep the pedants at bay.

Fixes #7809
2024-09-01 10:29:51 +05:30
Kovid Goyal
0105536665 Merge branch 'master' of https://github.com/rbong/kitty 2024-08-30 13:02:00 +05:30
Kovid Goyal
8eb0b556b7 diff kitten: Fix a regression that broke diffing against remote files
Fixes #7797
2024-08-28 11:00:42 +05:30
Roger Bongers
de25e4b8f5 Add more special branch drawing commit symbols
These symbols allow drawing branches merging into or forking out of
commits directly rather than requiring that merges/forks are rendered on
their own line.
2024-08-27 22:23:54 -04:00
Roger Bongers
21f25ee5a9 Make commit symbol circles slightly larger
These look unnaturally small on some screen sizes.
2024-08-27 22:23:44 -04:00
Kovid Goyal
d363513884 Fix listen_on with IPv6 address 2024-08-27 18:42:51 +05:30
Kovid Goyal
fdc3c3d7c1 kitten @: Fix a regression connecting to TCP sockets using plain IP addresses rather than hostnames
Fixes #7794
2024-08-27 18:30:51 +05:30
Kovid Goyal
0515dc1957 Update FAQ on NERD fonts to reflect the fact that kitty now has builtin NERD fonts 2024-08-27 16:31:55 +05:30
Kovid Goyal
627360e5ad Bind the idle inhibit protocol even though it is currently unused 2024-08-27 11:32:06 +05:30
Kovid Goyal
57ba0f4d87 Add a note about X11 specificity of preshow_callback 2024-08-27 10:39:52 +05:30
Kovid Goyal
3e8abaac58 ... 2024-08-27 10:30:35 +05:30
Kovid Goyal
68401eddba Wayland: Don't do two stage window creation
We cant make window visible after createwindow has returned because we
want to loop till the compositor fully creates the window and that loop
requires attaching a single pixel buffer to the window surface which
cannot be done once an OpenGL context is created for the window.

As far as I can see the preshow callback is not used on Wayland for
anything, so this should be OK to do.
2024-08-27 10:25:18 +05:30
Kovid Goyal
9843b5c210 Remove unused code 2024-08-26 19:13:09 +05:30
Kovid Goyal
96f2861c2a ... 2024-08-26 19:08:51 +05:30
Kovid Goyal
7a4f0318ed Remote control: When listening on a UNIX domain socket only allow connections from processes having the same user id
Fixes #7777
2024-08-26 19:07:18 +05:30
Kovid Goyal
eedf01fbe3 Make getpeerid re-useable 2024-08-26 18:55:10 +05:30
Kovid Goyal
0594033b31 Wayland: Move creation of OpenGL context to end of window creation function
Apparently with explicit sync it is now not possible to attach a
temporary buffer to a surface that also has an OpenGL context. So we
first attach the temporary buffer, and only after we are done waiting
for window creation do we attach the OpenGL context to the window
surface.

Fixes #7767 (maybe, not installing sway-git to check)
2024-08-26 15:36:27 +05:30
Kovid Goyal
75f5908009 Allow jumping to the nth previous active tab via goto_tab
This matches the docs for the goto_tab action and there is no reason not
to have this. For backwards compat 0 and -1 both go to the first previously
active tab as before. However -2 goes to the second previously active
tab, -3 to the third and so on.
2024-08-26 12:09:54 +05:30
Kovid Goyal
4ea2b92c8c Merge branch 'dependabot/go_modules/all-go-deps-257ddc7ff2' of https://github.com/kovidgoyal/kitty 2024-08-26 11:43:50 +05:30
dependabot[bot]
7e891d5287 Bump github.com/seancfoley/ipaddress-go in the all-go-deps group
Bumps the all-go-deps group with 1 update: [github.com/seancfoley/ipaddress-go](https://github.com/seancfoley/ipaddress-go).


Updates `github.com/seancfoley/ipaddress-go` from 1.6.0 to 1.7.0
- [Release notes](https://github.com/seancfoley/ipaddress-go/releases)
- [Commits](https://github.com/seancfoley/ipaddress-go/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/seancfoley/ipaddress-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-go-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 03:49:14 +00:00
Kovid Goyal
2c01405d85 ... 2024-08-25 18:57:50 +05:30
Kovid Goyal
ab3cefd81f Output font features in debug info 2024-08-25 11:19:36 +05:30
Kovid Goyal
0d57253300 ... 2024-08-24 17:18:05 +05:30
Kovid Goyal
611d8baf28 Linux: Fix a regression in 0.36.0 that caused font features defined via fontconfig to be ignored
Fixes #7773
2024-08-24 17:17:37 +05:30
Kovid Goyal
2560a024b9 ... 2024-08-24 11:20:16 +05:30
Kovid Goyal
e5321f96a3 never return zero cell width for a font 2024-08-24 11:19:44 +05:30
Kovid Goyal
60601a6dba Fix #7772 2024-08-24 11:12:37 +05:30
Kovid Goyal
d36a64087e Bump Go to 1.23
We need this because Go < 1.23 produces binaries that dont work on
modern OpenBSD because OpenBSD decided to remove syscall() from their
libc. Mad buggers, who removes functions from libc breaking all
binaries!!

Also increase minimum macOS version to 11.0 as Go 1.23 requires that
2024-08-24 08:06:02 +05:30
37 changed files with 300 additions and 146 deletions

View File

@@ -1,8 +1,8 @@
# Requires installation of XCode 10.3 and go 1.19 and Python 3 and
# Requires installation of XCode >= 10.3 and go 1.23 and Python 3 and
# python3 -m pip install certifi
vm_name 'macos-kitty'
root '/Users/Shared/kitty-build'
python '/usr/local/bin/python3'
universal 'true'
deploy_target '10.14'
deploy_target '11.0'

View File

@@ -74,6 +74,21 @@ consumption to do the same tasks.
Detailed list of changes
-------------------------------------
0.36.2 [2024-09-06]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Linux: Fix a regression in 0.36.0 that caused font features defined via fontconfig to be ignored (:iss:`7773`)
- :ac:`goto_tab`: Allow numbers less than ``-1`` to go to the Nth previously active tab
- Wayland: Fix for upcoming explicit sync changes in Wayland compositors breaking kitty (:iss:`7767`)
- Remote control: When listening on a UNIX domain socket only allow connections from processes having the same user id (:pull:`7777`)
- kitten @: Fix a regression connecting to TCP sockets using plain IP addresses rather than hostnames (:iss:`7794`)
- diff kitten: Fix a regression that broke diffing against remote files (:iss:`7797`)
0.36.1 [2024-08-24]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -257,11 +257,11 @@ fonts to be freely resizable, so it does not support bitmapped fonts.
.. note::
If you are trying to use a font patched with `Nerd Fonts
<https://nerdfonts.com/>`__ symbols, don't do that as patching destroys
fonts. There is no need, simply install the standalone ``Symbols Nerd Font Mono``
(the file :file:`NerdFontsSymbolsOnly.tar.xz` from the `Nerd Fonts releases page
<https://github.com/ryanoasis/nerd-fonts/releases>`__). kitty should pick up
symbols from it automatically, and you can tell it to do so explicitly in
case it doesn't with the :opt:`symbol_map` directive::
fonts. There is no need, kitty has a builtin NERD font and will use it for
symbols not found in any other font on your system.
If you have patched fonts on your system they might be used instead for NERD
symbols, so to force kitty to use the pure NERD font for NERD symbols,
add the following line to :file:`kitty.conf`::
# Nerd Fonts v3.2.0

View File

@@ -56,7 +56,7 @@ Programs implementing this protocol:
* The `far2l file manager <https://github.com/elfmz/far2l/commit/e1f2ee0ef2b8332e5fa3ad7f2e4afefe7c96fc3b>`__
* The `Yazi file manager <https://github.com/sxyazi/yazi>`__
* The `awrit web browser <https://github.com/chase/awrit>`__
* The `Turbo Vision <https://github.com/magiblot/tvision/commit/6e5a7b46c6634079feb2ac98f0b890bbed59f1ba>`/`Free Vision <https://gitlab.com/freepascal.org/fpc/source/-/issues/40673#note_2061428120>`__ IDEs
* The `Turbo Vision <https://github.com/magiblot/tvision/commit/6e5a7b46c6634079feb2ac98f0b890bbed59f1ba>`__/`Free Vision <https://gitlab.com/freepascal.org/fpc/source/-/issues/40673#note_2061428120>`__ IDEs
Shells implementing this protocol:

View File

@@ -177,7 +177,7 @@ option in :file:`kitty.conf`. An example, showing all available commands:
launch emacs
# Create a complex layout using multiple splits. Creates two columns of
# windows with two windows in each column. The windows in the firt column are
# windows with two windows in each column. The windows in the first column are
# split 50:50. In the second column the windows are not evenly split.
new_tab complex tab
layout splits

View File

@@ -82,6 +82,7 @@
"staging/cursor-shape/cursor-shape-v1.xml",
"staging/fractional-scale/fractional-scale-v1.xml",
"staging/single-pixel-buffer/single-pixel-buffer-v1.xml",
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
"kwin-blur-v1.xml",
"wlr-layer-shell-unstable-v1.xml"

6
glfw/wl_init.c vendored
View File

@@ -599,6 +599,9 @@ static void registryHandleGlobal(void* data UNUSED,
_glfw.wl.zwlr_layer_shell_v1 = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version);
}
}
else if (is(zwp_idle_inhibit_manager_v1)) {
_glfw.wl.idle_inhibit_manager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
}
#undef is
}
@@ -679,6 +682,7 @@ get_compositor_missing_capabilities(void) {
C(blur, org_kde_kwin_blur_manager); C(server_side_decorations, decorationManager);
C(cursor_shape, wp_cursor_shape_manager_v1); C(layer_shell, zwlr_layer_shell_v1);
C(single_pixel_buffer, wp_single_pixel_buffer_manager_v1); C(preferred_scale, has_preferred_buffer_scale);
C(idle_inhibit, idle_inhibit_manager);
#undef C
return buf;
}
@@ -861,6 +865,8 @@ void _glfwPlatformTerminate(void)
org_kde_kwin_blur_manager_destroy(_glfw.wl.org_kde_kwin_blur_manager);
if (_glfw.wl.zwlr_layer_shell_v1)
zwlr_layer_shell_v1_destroy(_glfw.wl.zwlr_layer_shell_v1);
if (_glfw.wl.idle_inhibit_manager)
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idle_inhibit_manager);
if (_glfw.wl.registry)
wl_registry_destroy(_glfw.wl.registry);

2
glfw/wl_platform.h vendored
View File

@@ -65,6 +65,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#include "wayland-kwin-blur-v1-client-protocol.h"
#include "wayland-wlr-layer-shell-unstable-v1-client-protocol.h"
#include "wayland-single-pixel-buffer-v1-client-protocol.h"
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define _glfw_dlclose(handle) dlclose(handle)
@@ -338,6 +339,7 @@ typedef struct _GLFWlibraryWayland
struct org_kde_kwin_blur_manager *org_kde_kwin_blur_manager;
struct zwlr_layer_shell_v1* zwlr_layer_shell_v1; uint32_t zwlr_layer_shell_v1_version;
struct wp_single_pixel_buffer_manager_v1 *wp_single_pixel_buffer_manager_v1;
struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager;
int compositorVersion;
int seatVersion;

53
glfw/wl_window.c vendored
View File

@@ -371,8 +371,8 @@ wait_for_swap_to_commit(_GLFWwindow *window) {
static void
resizeFramebuffer(_GLFWwindow* window) {
GLFWwindow *ctx = glfwGetCurrentContext();
const bool ctx_changed = ctx != (GLFWwindow*)window;
if (ctx_changed) glfwMakeContextCurrent((GLFWwindow*)window);
bool ctx_changed = false;
if (ctx != (GLFWwindow*)window && window->context.client != GLFW_NO_API) { ctx_changed = true; glfwMakeContextCurrent((GLFWwindow*)window); }
double scale = _glfwWaylandWindowScale(window);
int scaled_width = (int)round(window->wl.width * scale);
int scaled_height = (int)round(window->wl.height * scale);
@@ -1320,30 +1320,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
window->swaps_disallowed = true;
if (!createSurface(window, wndconfig)) return false;
if (ctxconfig->client != GLFW_NO_API)
{
if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
{
if (!_glfwInitEGL())
return false;
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
return false;
}
else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
{
if (!_glfwInitOSMesa())
return false;
if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
return false;
}
}
if (wndconfig->title)
window->wl.title = _glfw_strdup(wndconfig->title);
if (wndconfig->maximized)
window->wl.maximize_on_first_show = true;
if (wndconfig->title) window->wl.title = _glfw_strdup(wndconfig->title);
if (wndconfig->maximized) window->wl.maximize_on_first_show = true;
if (wndconfig->visible)
{
@@ -1366,7 +1344,29 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
window->wl.monitorsCount = 0;
window->wl.monitorsSize = 1;
// looping till window fully created attaches a single pixel buffer to the window,
// this cannot be done once a OpenGL context is created for the window. So first loop
// and only then create the OpenGL context.
if (window->wl.visible) loop_till_window_fully_created(window);
debug("Creating OpenGL context and attaching it to window\n");
if (ctxconfig->client != GLFW_NO_API)
{
if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
{
if (!_glfwInitEGL())
return false;
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
return false;
}
else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
{
if (!_glfwInitOSMesa())
return false;
if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
return false;
}
}
return true;
}
@@ -1598,7 +1598,6 @@ void _glfwPlatformShowWindow(_GLFWwindow* window)
if (!window->wl.visible) {
create_window_desktop_surface(window);
window->wl.visible = true;
loop_till_window_fully_created(window);
}
}

4
go.mod
View File

@@ -1,6 +1,6 @@
module kitty
go 1.22
go 1.23
require (
github.com/ALTree/bigfloat v0.2.0
@@ -11,7 +11,7 @@ require (
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/kovidgoyal/imaging v1.6.3
github.com/seancfoley/ipaddress-go v1.6.0
github.com/seancfoley/ipaddress-go v1.7.0
github.com/shirou/gopsutil/v3 v3.24.5
github.com/zeebo/xxh3 v1.0.2
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b

4
go.sum
View File

@@ -40,8 +40,8 @@ github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3EXGI=
github.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
github.com/seancfoley/ipaddress-go v1.6.0 h1:9z7yGmOnV4P2ML/dlR/kCJiv5tp8iHOOetJvxJh/R5w=
github.com/seancfoley/ipaddress-go v1.6.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
github.com/seancfoley/ipaddress-go v1.7.0 h1:vWp3SR3k+HkV3aKiNO2vEe6xbVxS0x/Ixw6hgyP238s=
github.com/seancfoley/ipaddress-go v1.7.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=

View File

@@ -58,7 +58,7 @@ func get_ssh_file(hostname, rpath string) (string, error) {
for strings.HasPrefix(rpath, "/") {
rpath = rpath[1:]
}
cmd := []string{ssh.SSHExe(), hostname, "tar", "-c", "-f", "-"}
cmd := []string{ssh.SSHExe(), hostname, "tar", "--dereference", "--create", "--file", "-"}
if is_abs {
cmd = append(cmd, "-C", "/")
}

View File

@@ -173,10 +173,9 @@ def listen_on(spec: str) -> tuple[int, str]:
atexit.register(remove_socket_file, s, socket_path)
s.bind(address)
s.listen()
if isinstance(address, tuple):
h, resolved_port = s.getsockname()
sfamily, host, port = spec.split(':', 2)
spec = f'{sfamily}:{host}:{resolved_port}'
if isinstance(address, tuple): # tcp socket
h, resolved_port = s.getsockname()[:2]
spec = spec.rpartition(':')[0] + f':{resolved_port}'
return s.fileno(), spec
@@ -362,7 +361,7 @@ class Boss:
self.child_monitor: ChildMonitor = ChildMonitor(
self.on_child_death,
DumpCommands(args) if args.dump_commands or args.dump_bytes else None,
talk_fd, listen_fd,
talk_fd, listen_fd, self.listening_on.startswith('unix:')
)
self.args: CLIOptions = args
self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None

View File

@@ -152,6 +152,8 @@ mask_kitty_signals_process_wide(PyObject *self UNUSED, PyObject *a UNUSED) {
Py_RETURN_NONE;
}
static int verify_peer_uid = false;
static PyObject *
new_childmonitor_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
ChildMonitor *self;
@@ -160,7 +162,7 @@ new_childmonitor_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwd
int ret;
if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
if (!PyArg_ParseTuple(args, "OO|ii", &death_notify, &dump_callback, &talk_fd, &listen_fd)) return NULL;
if (!PyArg_ParseTuple(args, "OO|iip", &death_notify, &dump_callback, &talk_fd, &listen_fd, &verify_peer_uid)) return NULL;
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
return NULL;
@@ -1655,6 +1657,20 @@ add_peer(int peer, bool is_remote_control_peer) {
return ans;
}
static bool
getpeerid(int fd, uid_t *euid, gid_t *egid) {
#ifdef __linux__
struct ucred cr;
socklen_t sz = sizeof(cr);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &sz) != 0) return false;
*euid = cr.uid; *egid = cr.gid;
#else
if (getpeereid(fd, euid, egid) != 0) return false;
#endif
return true;
}
static bool
accept_peer(int listen_fd, bool shutting_down, bool is_remote_control_peer) {
int peer = accept(listen_fd, NULL, NULL);
@@ -1663,6 +1679,21 @@ accept_peer(int listen_fd, bool shutting_down, bool is_remote_control_peer) {
if (!shutting_down) perror("accept() on talk socket failed!");
return false;
}
if (verify_peer_uid) {
uid_t peer_uid; gid_t peer_gid;
if (!getpeerid(peer, &peer_uid, &peer_gid)) {
log_error("Denying access to peer because failed to get uid and gid for peer: %d with error: %s", peer, strerror(errno));
shutdown(peer, SHUT_RDWR);
safe_close(peer, __FILE__, __LINE__);
return true;
}
if (peer_uid != geteuid()) {
log_error("Denying access to peer because its uid (%d) does not match our uid (%d)", peer_uid, geteuid());
shutdown(peer, SHUT_RDWR);
safe_close(peer, __FILE__, __LINE__);
return true;
}
}
add_peer(peer, is_remote_control_peer);
return true;
}

View File

@@ -23,7 +23,7 @@ class Version(NamedTuple):
appname: str = 'kitty'
kitty_face = '🐱'
version: Version = Version(0, 36, 1)
version: Version = Version(0, 36, 2)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat

View File

@@ -812,11 +812,13 @@ render_sample_text(CTFace *self, PyObject *args) {
if (!PyArg_ParseTuple(args, "Ukk|k", &ptext, &canvas_width, &canvas_height, &fg)) return NULL;
unsigned int cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
cell_metrics((PyObject*)self, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness);
RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height));
if (!pbuf) return NULL;
memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));
if (!cell_width || !cell_height) return Py_BuildValue("OII", pbuf, cell_width, cell_height);
size_t num_chars = PyUnicode_GET_LENGTH(ptext);
int num_chars_per_line = canvas_width / cell_width, num_of_lines = (int)ceil((float)num_chars / (float)num_chars_per_line);
canvas_height = MIN(canvas_height, num_of_lines * cell_height);
RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height));
if (!pbuf) return NULL;
__attribute__((cleanup(destroy_hb_buffer))) hb_buffer_t *hb_buffer = hb_buffer_create();
if (!hb_buffer_pre_allocate(hb_buffer, 4*num_chars)) { PyErr_NoMemory(); return NULL; }
@@ -1068,10 +1070,16 @@ get_variable_data(CTFace *self) {
static PyObject*
identify_for_debug(CTFace *self) {
return PyUnicode_FromFormat("%V: %V", self->postscript_name, "[psname]", self->path, "[path]");
RAII_PyObject(features, PyTuple_New(self->font_features.count)); if (!features) return NULL;
char buf[128];
for (unsigned i = 0; i < self->font_features.count; i++) {
hb_feature_to_string(self->font_features.features + i, buf, sizeof(buf));
PyObject *f = PyUnicode_FromString(buf); if (!f) return NULL;
PyTuple_SET_ITEM(features, i, f);
}
return PyUnicode_FromFormat("%V: %V\nFeatures: %S", self->postscript_name, "[psname]", self->path, "[path]", features);
}
// }}}

View File

@@ -360,23 +360,6 @@ locale_is_valid(PyObject *self UNUSED, PyObject *args) {
Py_RETURN_TRUE;
}
static PyObject*
py_getpeereid(PyObject *self UNUSED, PyObject *args) {
int fd;
if (!PyArg_ParseTuple(args, "i", &fd)) return NULL;
uid_t euid = 0; gid_t egid = 0;
#ifdef __linux__
struct ucred cr;
socklen_t sz = sizeof(cr);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &sz) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
euid = cr.uid; egid = cr.gid;
#else
if (getpeereid(fd, &euid, &egid) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
#endif
int u = euid, g = egid;
return Py_BuildValue("ii", u, g);
}
#include "docs_ref_map_generated.h"
static PyObject*
@@ -569,7 +552,6 @@ static PyMethodDef module_methods[] = {
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
{"expand_ansi_c_escapes", (PyCFunction)expand_ansi_c_escapes, METH_O, ""},
{"get_docs_ref_map", (PyCFunction)get_docs_ref_map, METH_NOARGS, ""},
{"getpeereid", (PyCFunction)py_getpeereid, METH_VARARGS, ""},
{"wcswidth", (PyCFunction)wcswidth_std, METH_O, ""},
{"unicode_database_version", (PyCFunction)unicode_database_version, METH_NOARGS, ""},
{"open_tty", open_tty, METH_VARARGS, ""},

View File

@@ -261,7 +261,10 @@ def debug_config(opts: KittyOpts) -> str:
p(green('Fonts:'))
for k, font in current_fonts().items():
if hasattr(font, 'identify_for_debug'):
p(yellow(f' {k}:'), font.identify_for_debug())
flines = font.identify_for_debug().splitlines()
p(yellow(f'{k.rjust(8)}:'), flines[0])
for fl in flines[1:]:
p(' ' * 9, fl)
p(green('Paths:'))
p(yellow(' kitty:'), os.path.realpath(kitty_exe()))
p(yellow(' base dir:'), kitty_base_dir)

View File

@@ -1366,6 +1366,7 @@ class ChildMonitor:
dump_callback: Optional[Callable[[int, str, Any], None]],
talk_fd: int = -1,
listen_fd: int = -1,
verify_peer_uid: bool = False,
):
pass
@@ -1589,10 +1590,6 @@ def remove_signal_handlers() -> None:
pass
def getpeereid(fd: int) -> Tuple[int, int]:
pass
X25519: int
SHA1_HASH: int
SHA224_HASH: int

View File

@@ -419,6 +419,9 @@ specialize_font_descriptor(PyObject *base_descriptor, double font_sz_in_pts, dou
FcPattern *pat = FcPatternCreate();
if (pat == NULL) return PyErr_NoMemory();
RAII_PyObject(features, PyList_New(0));
if (!features) return NULL;
RAII_PyObject(final_features, NULL);
RAII_PyObject(ans, NULL);
AP(FcPatternAddString, FC_FILE, (const FcChar8*)PyUnicode_AsUTF8(p), "path");
AP(FcPatternAddInteger, FC_INDEX, face_idx, "index");
@@ -445,10 +448,23 @@ specialize_font_descriptor(PyObject *base_descriptor, double font_sz_in_pts, dou
if (axes) {
if (PyDict_SetItemString(ans, "axes", axes) != 0) return NULL;
}
PyObject *features = PyDict_GetItemString(base_descriptor, "features");
if (features) {
if (PyDict_SetItemString(ans, "features", features) != 0) return NULL;
PyObject *ff = PyDict_GetItemString(ans, "fontfeatures");
if (ff && PyList_GET_SIZE(ff)) {
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(ff); i++) {
RAII_PyObject(pff, (PyObject*)parse_font_feature(PyUnicode_AsUTF8(PyList_GET_ITEM(ff, i))));
if (pff == NULL) {
PyErr_Print(); fprintf(stderr, "\n");
} else if (PyList_Append(features, pff) != 0) return NULL;
}
}
PyObject *base_features = PyDict_GetItemString(base_descriptor, "features");
final_features = PyTuple_New(PyList_GET_SIZE(features) + (base_features ? PyTuple_GET_SIZE(base_features) : 0));
if (!final_features) return NULL;
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(features); i++) { PyTuple_SET_ITEM(final_features, i, Py_NewRef(PyList_GET_ITEM(features, i))); }
if (base_features) {
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(base_features); i++) { PyTuple_SET_ITEM(final_features, i + PyList_GET_SIZE(features), Py_NewRef(PyTuple_GET_ITEM(base_features, i))); }
}
if (PyDict_SetItemString(ans, "features", final_features) != 0) return NULL;
Py_INCREF(ans);
return ans;
end:

View File

@@ -608,7 +608,7 @@ START_ALLOW_CASE_RANGE
case 0xe0b0 ... 0xe0bf: case 0xe0d6 ... 0xe0d7: // powerline box drawing
case 0xee00 ... 0xee0b: // fira code progress bar/spinner
case 0x1fb00 ... 0x1fbae: // symbols for legacy computing
case 0xf5d0 ... 0xf5fb: // branch drawing characters
case 0xf5d0 ... 0xf60d: // branch drawing characters
return BOX_FONT;
default:
*is_emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
@@ -649,8 +649,8 @@ START_ALLOW_CASE_RANGE
return 0xf00 + ch - 0x2800; // IDs from 0xf00 to 0xfff
case 0x1fb00 ... 0x1fbae:
return 0x1000 + ch - 0x1fb00; // IDs from 0x1000 to 0x10ae
case 0xf5d0 ... 0xf5fb:
return 0x2000 + ch - 0xf5d0; // IDs from 0x2000 to 0x202b
case 0xf5d0 ... 0xf60d:
return 0x2000 + ch - 0xf5d0; // IDs from 0x2000 to 0x203d
default:
return 0xffff;
}
@@ -1740,18 +1740,25 @@ free_font_data(PyObject *self UNUSED, PyObject *args UNUSED) {
Py_RETURN_NONE;
}
static PyObject *
parsed_font_feature_new(PyTypeObject *type, PyObject *args, PyObject *kwds UNUSED) {
const char *s;
if (!PyArg_ParseTuple(args, "s", &s)) return NULL;
ParsedFontFeature *self = (ParsedFontFeature *)type->tp_alloc(type, 0);
PyTypeObject ParsedFontFeature_Type;
ParsedFontFeature*
parse_font_feature(const char *spec) {
ParsedFontFeature *self = (ParsedFontFeature*)ParsedFontFeature_Type.tp_alloc(&ParsedFontFeature_Type, 0);
if (self != NULL) {
if (!hb_feature_from_string(s, -1, &self->feature)) {
PyErr_Format(PyExc_ValueError, "%s is not a valid font feature", s);
if (!hb_feature_from_string(spec, -1, &self->feature)) {
PyErr_Format(PyExc_ValueError, "%s is not a valid font feature", self);
Py_CLEAR(self);
}
}
return (PyObject*) self;
return self;
}
static PyObject *
parsed_font_feature_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds UNUSED) {
const char *s;
if (!PyArg_ParseTuple(args, "s", &s)) return NULL;
return (PyObject*)parse_font_feature(s);
}
static PyObject*
@@ -1768,8 +1775,6 @@ parsed_font_feature_repr(PyObject *self_) {
}
PyTypeObject ParsedFontFeature_Type;
static PyObject*
parsed_font_feature_cmp(PyObject *self, PyObject *other, int op) {
if (op != Py_EQ && op != Py_NE) return Py_NotImplemented;

View File

@@ -31,6 +31,7 @@ typedef struct ParsedFontFeature {
bool hash_computed;
} ParsedFontFeature;
ParsedFontFeature* parse_font_feature(const char *spec);
// API that font backends need to implement
unsigned int glyph_id_for_codepoint(const PyObject *, char_type);

View File

@@ -595,21 +595,22 @@ def draw_circle(buf: SSByteArray, width: int, height: int, scale: float = 1.0, g
@supersampled()
def commit(buf: SSByteArray, width: int, height: int, level: int = 1, scale: float = 0.75, line: str = 'none', solid: bool = True) -> None:
def commit(buf: SSByteArray, width: int, height: int, level: int = 1, scale: float = 0.9, lines: list[str] = [], solid: bool = True) -> None:
' Draw a circular commit with the given scale. Commits can either be solid or hollow and can have vertical, horizontal, up, down, left, or right line(s) '
factor = buf.supersample_factor
# Round half width/height to supersample factor to avoid misalignment with non-supersampled lines
hwidth, hheight = factor * (width // 2 // factor), factor * (height // 2 // factor)
if line == 'horizontal' or line == 'right':
draw_hline(buf, width, hwidth, width, hheight, level, supersample_factor=factor)
if line == 'horizontal' or line == 'left':
draw_hline(buf, width, 0, hwidth, hheight, level, supersample_factor=factor)
if line == 'vertical' or line == 'down':
draw_vline(buf, width, hheight, height, hwidth, level, supersample_factor=factor)
if line == 'vertical' or line == 'up':
draw_vline(buf, width, 0, hheight, hwidth, level, supersample_factor=factor)
for line in lines:
if line == 'horizontal' or line == 'right':
draw_hline(buf, width, hwidth, width, hheight, level, supersample_factor=factor)
if line == 'horizontal' or line == 'left':
draw_hline(buf, width, 0, hwidth, hheight, level, supersample_factor=factor)
if line == 'vertical' or line == 'down':
draw_vline(buf, width, hheight, height, hwidth, level, supersample_factor=factor)
if line == 'vertical' or line == 'up':
draw_vline(buf, width, 0, hheight, hwidth, level, supersample_factor=factor)
draw_circle(buf, width, height, scale=scale)
if not solid:
@@ -1332,18 +1333,36 @@ box_chars: dict[str, list[Callable[[BufType, int, int], Any]]] = {
'': [hline, p(rounded_corner, which=''), p(rounded_corner, which='')],
'': [commit],
'': [p(commit, solid=False)],
'': [p(commit, line='right')],
'': [p(commit, solid=False, line='right')],
'': [p(commit, line='left')],
'': [p(commit, solid=False, line='left')],
'': [p(commit, line='horizontal')],
'': [p(commit, solid=False, line='horizontal')],
'': [p(commit, line='down')],
'': [p(commit, solid=False, line='down')],
'': [p(commit, line='up')],
'': [p(commit, solid=False, line='up')],
'': [p(commit, line='vertical')],
'': [p(commit, solid=False, line='vertical')],
'': [p(commit, lines=['right'])],
'': [p(commit, solid=False, lines=['right'])],
'': [p(commit, lines=['left'])],
'': [p(commit, solid=False, lines=['left'])],
'': [p(commit, lines=['horizontal'])],
'': [p(commit, solid=False, lines=['horizontal'])],
'': [p(commit, lines=['down'])],
'': [p(commit, solid=False, lines=['down'])],
'': [p(commit, lines=['up'])],
'': [p(commit, solid=False, lines=['up'])],
'': [p(commit, lines=['vertical'])],
'': [p(commit, solid=False, lines=['vertical'])],
'': [p(commit, lines=['right', 'down'])],
'': [p(commit, solid=False, lines=['right', 'down'])],
'': [p(commit, lines=['left', 'down'])],
'': [p(commit, solid=False, lines=['left', 'down'])],
'': [p(commit, lines=['right', 'up'])],
'': [p(commit, solid=False, lines=['right', 'up'])],
'': [p(commit, lines=['left', 'up'])],
'': [p(commit, solid=False, lines=['left', 'up'])],
'': [p(commit, lines=['vertical', 'right'])],
'': [p(commit, solid=False, lines=['vertical', 'right'])],
'': [p(commit, lines=['vertical', 'left'])],
'': [p(commit, solid=False, lines=['vertical', 'left'])],
'': [p(commit, lines=['horizontal', 'down'])],
'': [p(commit, solid=False, lines=['horizontal', 'down'])],
'': [p(commit, lines=['horizontal', 'up'])],
'': [p(commit, solid=False, lines=['horizontal', 'up'])],
'': [p(commit, lines=['horizontal', 'vertical'])],
'': [p(commit, solid=False, lines=['horizontal', 'vertical'])],
}
t, f = 1, 3

View File

@@ -502,5 +502,14 @@ def develop(family: str = '') -> None:
s('Bold-Italic:', ff['bi'])
def list_fonts(monospaced: bool = True) -> dict[str, list[dict[str, str]]]:
ans: dict[str, list[dict[str, str]]] = {}
for key, descriptors in all_fonts_map(monospaced)['family_map'].items():
entries = ans.setdefault(key, [])
for d in descriptors:
entries.append({'family': d['family'], 'psname': d['postscript_name'], 'path': d['path'], 'style': d['style']})
return ans
if __name__ == '__main__':
develop()

View File

@@ -386,6 +386,7 @@ calc_cell_width(Face *self) {
ans = MAX(ans, (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
}
}
if (!ans) ans = MAX(1u, (unsigned int)ceilf(self->face->size->metrics.max_advance / 64.f));
return ans;
}
@@ -746,7 +747,14 @@ identify_for_debug(PyObject *s, PyObject *a UNUSED) {
Face *self = (Face*)s;
FaceIndex instance;
instance.val = self->face->face_index;
return PyUnicode_FromFormat("%s: %V:%d", FT_Get_Postscript_Name(self->face), self->path, "[path]", instance.val);
RAII_PyObject(features, PyTuple_New(self->font_features.count)); if (!features) return NULL;
char buf[128];
for (unsigned i = 0; i < self->font_features.count; i++) {
hb_feature_to_string(self->font_features.features + i, buf, sizeof(buf));
PyObject *f = PyUnicode_FromString(buf); if (!f) return NULL;
PyTuple_SET_ITEM(features, i, f);
}
return PyUnicode_FromFormat("%s: %V:%d\nFeatures: %S", FT_Get_Postscript_Name(self->face), self->path, "[path]", instance.val, features);
}
static PyObject*
@@ -970,11 +978,12 @@ render_sample_text(Face *self, PyObject *args) {
if (!PyArg_ParseTuple(args, "Ukk|k", &ptext, &canvas_width, &canvas_height, &fg)) return NULL;
unsigned int cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
cell_metrics((PyObject*)self, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness);
int num_chars_per_line = canvas_width / cell_width, num_of_lines = (int)ceil((float)PyUnicode_GET_LENGTH(ptext) / (float)num_chars_per_line);
canvas_height = MIN(canvas_height, num_of_lines * cell_height);
RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height));
if (!pbuf) return NULL;
memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));
if (!cell_width || !cell_height) return Py_BuildValue("OII", pbuf, cell_width, cell_height);
int num_chars_per_line = canvas_width / cell_width, num_of_lines = (int)ceil((float)PyUnicode_GET_LENGTH(ptext) / (float)num_chars_per_line);
canvas_height = MIN(canvas_height, num_of_lines * cell_height);
__attribute__((cleanup(destroy_hb_buffer))) hb_buffer_t *hb_buffer = hb_buffer_create();
if (!hb_buffer_pre_allocate(hb_buffer, 4*PyUnicode_GET_LENGTH(ptext))) { PyErr_NoMemory(); return NULL; }

View File

@@ -1155,7 +1155,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
// We use a temp window to avoid the need to set the window size after
// creation, which causes a resize event and all the associated processing.
// The temp window is used to get the DPI.
glfwWindowHint(GLFW_VISIBLE, false);
if (!global_state.is_wayland) glfwWindowHint(GLFW_VISIBLE, false);
GLFWwindow *common_context = global_state.num_os_windows ? global_state.os_windows[0].handle : NULL;
GLFWwindow *temp_window = NULL;
#ifdef __APPLE__
@@ -1213,7 +1213,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
bool is_apple = true;
#ifndef __APPLE__
is_apple = false;
glfwShowWindow(glfw_window);
if (!global_state.is_wayland) glfwShowWindow(glfw_window);
#endif
if (global_state.is_wayland || is_apple) {
float n_xscale, n_yscale;

View File

@@ -3968,8 +3968,9 @@ map('Set tab title',
egr('''
You can also create shortcuts to go to specific :term:`tabs <tab>`, with
:code:`1` being the first tab, :code:`2` the second tab and :code:`-1` being the
previously active tab, and any number larger than the last tab being the last
tab::
previously active tab, :code:`-2` being the tab active before the previously active tab and so on.
Any number larger than the number of tabs goes to the last tab and any number less
than the number of previously used tabs in the history goes to the oldest previously used tab in the history::
map ctrl+alt+1 goto_tab 1
map ctrl+alt+2 goto_tab 2

View File

@@ -145,8 +145,10 @@ def open_url_parse(func: str, rest: str) -> FuncArgsType:
@func_with_args('goto_tab')
def goto_tab_parse(func: str, rest: str) -> FuncArgsType:
args = (max(0, int(rest)), )
return func, args
n = int(rest)
if n < 0:
n += 1 # goto_tab subtracts 1 from its argument, this maps both zero and -1 to previous tab for backwards compat.
return func, (n,)
@func_with_args('detach_window')

View File

@@ -29,13 +29,13 @@ void FUNC(xor_data64)(const uint8_t key[64] UNUSED, uint8_t* data UNUSED, const
// Boilerplate {{{
START_IGNORE_DIAGNOSTIC("-Wfloat-conversion")
START_IGNORE_DIAGNOSTIC("-Wpedantic")
#if defined(__clang__) && __clang_major__ > 12
#if defined(__clang__) && __clang_major__ > 13
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wbitwise-instead-of-logical\"")
#endif
#include <simde/x86/avx2.h>
#include <simde/arm/neon.h>
#if defined(__clang__) && __clang_major__ > 12
#if defined(__clang__) && __clang_major__ > 13
_Pragma("clang diagnostic pop")
#endif
END_IGNORE_DIAGNOSTIC

View File

@@ -1034,11 +1034,11 @@ class TabManager: # {{{
tab_num = max(0, len(self.tabs) - 1)
if tab_num >= 0:
self.set_active_tab_idx(tab_num)
else:
elif self.active_tab_history:
try:
old_active_tab_id = self.active_tab_history[tab_num]
except IndexError:
return
old_active_tab_id = self.active_tab_history[0]
for idx, tab in enumerate(self.tabs):
if tab.id == old_active_tab_id:
self.set_active_tab_idx(idx)

View File

@@ -327,6 +327,10 @@ def end_startup_notification(ctx: Optional['StartupCtx']) -> None:
class startup_notification_handler:
# WARNING: This only works on X11 on other platforms extra_callback will be called
# after the window is shown, not before, as they do not do two stage window
# creation.
def __init__(self, do_notify: bool = True, startup_id: Optional[str] = None, extra_callback: Optional[Callable[[int], None]] = None):
self.do_notify = do_notify
self.startup_id = startup_id
@@ -488,7 +492,14 @@ def parse_address_spec(spec: str) -> tuple[AddressFamily, Union[tuple[str, int],
socket_path = address
elif protocol in ('tcp', 'tcp6'):
family = socket.AF_INET if protocol == 'tcp' else socket.AF_INET6
host, port = rest.rsplit(':', 1)
if rest.startswith('['): # ]
host = rest[1:]
host, sep, leftover = host.rpartition(']')
_, port = leftover.rsplit(':', 1)
if ':' in host and protocol == 'tcp':
family = socket.AF_INET6
else:
host, port = rest.rsplit(':', 1)
address = host, int(port)
else:
raise ValueError(f'Unknown protocol in listen-on value: {spec}')
@@ -1204,14 +1215,22 @@ def key_val_matcher(items: Iterable[tuple[str, str]], key_pat: 're.Pattern[str]'
def shlex_split(text: str, allow_ansi_quoted_strings: bool = False) -> Iterator[str]:
s = Shlex(text, allow_ansi_quoted_strings)
yielded = False
while (q := s.next_word())[0] > -1:
yield q[1]
yielded = True
if not yielded:
yield ''
def shlex_split_with_positions(text: str, allow_ansi_quoted_strings: bool = False) -> Iterator[tuple[int, str]]:
s = Shlex(text, allow_ansi_quoted_strings)
yielded = False
while (q := s.next_word())[0] > -1:
yield q
yielded = True
if not yielded:
yield 0, ''
def timed_debug_print(*a: Any, sep: str = ' ', end: str = '\n') -> None:

View File

@@ -214,7 +214,7 @@ def decode_cmdline(x: str) -> str:
ctype, sep, val = x.partition('=')
if ctype == 'cmdline':
return next(shlex_split(val, True))
if ctype == 'cmdline_url':
elif ctype == 'cmdline_url':
from urllib.parse import unquote
return unquote(val)
return ''

View File

@@ -629,7 +629,7 @@ class TestDataTypes(BaseTest):
r'x "ab"y \m': ((0, 'x'), (2, 'aby'), (8, 'm')),
r'''x'y"\z'1''': ((0, 'xy"\\z1'),),
r'\abc\ d': ((0, 'abc d'),),
'': (), ' ': (), ' \tabc\n\t\r ': ((2, 'abc'),),
'': ((0, ''),), ' ': ((0, ''),), ' \tabc\n\t\r ': ((2, 'abc'),),
"$'ab'": ((0, '$ab'),),
}.items():
actual = tuple(shlex_split_with_positions(q))

View File

@@ -1588,7 +1588,7 @@ def macos_info_plist() -> bytes:
NSHumanReadableCopyright=time.strftime('Copyright %Y, Kovid Goyal'),
CFBundleGetInfoString='kitty - The fast, feature-rich, GPU based terminal emulator. https://sw.kovidgoyal.net/kitty/',
# Operating System Version
LSMinimumSystemVersion='10.15.0',
LSMinimumSystemVersion='11.0.0',
# Categorization
CFBundlePackageType='APPL',
CFBundleSignature='????',

View File

@@ -174,8 +174,10 @@ func do_socket_io(io_data *rc_io_data) (serialized_response []byte, err error) {
}
defer f.Close()
} else {
conn, err = net.Dial(global_options.to_network, global_options.to_address)
network := utils.IfElse(global_options.to_network == "ip", "tcp", global_options.to_network)
conn, err = net.Dial(network, global_options.to_address)
if err != nil {
err = fmt.Errorf("Failed to connect to %s %s with error: %w", network, global_options.to_address, err)
return
}
}

View File

@@ -135,33 +135,51 @@ func get_shell_name(argv0 string) (ans string) {
return strings.TrimPrefix(ans, "-")
}
func rc_modification_allowed(ksi string) bool {
func rc_modification_allowed(ksi string) (allowed bool, set_ksi_env_var bool) {
allowed = ksi != ""
set_ksi_env_var = true
for _, x := range strings.Split(ksi, " ") {
switch x {
case "disabled", "no-rc":
return false
case "disabled":
allowed = false
set_ksi_env_var = false
break
case "no-rc":
allowed = false
break
}
}
return ksi != ""
return
}
func copy_os_env_as_dict() map[string]string {
oenv := os.Environ()
env := make(map[string]string, len(oenv))
for _, x := range oenv {
if k, v, found := strings.Cut(x, "="); found {
env[k] = v
}
}
return env
}
func RunShell(shell_cmd []string, shell_integration_env_var_val, cwd string) (err error) {
shell_name := get_shell_name(shell_cmd[0])
var shell_env map[string]string
if rc_modification_allowed(shell_integration_env_var_val) && shell_integration.IsSupportedShell(shell_name) {
oenv := os.Environ()
env := make(map[string]string, len(oenv))
for _, x := range oenv {
if k, v, found := strings.Cut(x, "="); found {
env[k] = v
if shell_integration.IsSupportedShell(shell_name) {
rc_mod_allowed, set_ksi_env_var := rc_modification_allowed(shell_integration_env_var_val)
if rc_mod_allowed {
// KITTY_SHELL_INTEGRATION is always set by this function
argv, env, err := shell_integration.Setup(shell_name, shell_integration_env_var_val, shell_cmd, copy_os_env_as_dict())
if err != nil {
return err
}
shell_cmd = argv
shell_env = env
} else if set_ksi_env_var {
shell_env = copy_os_env_as_dict()
shell_env["KITTY_SHELL_INTEGRATION"] = shell_integration_env_var_val
}
argv, env, err := shell_integration.Setup(shell_name, shell_integration_env_var_val, shell_cmd, env)
if err != nil {
return err
}
shell_cmd = argv
shell_env = env
}
exe := shell_cmd[0]
if runtime.GOOS == "darwin" && (os.Getenv("KITTY_RUNNING_SHELL_INTEGRATION_TEST") != "1" || os.Getenv("KITTY_RUNNING_BASH_INTEGRATION_TEST") != "") {

View File

@@ -48,6 +48,7 @@ func ExtractAllFromTar(tr *tar.Reader, dest_path string, optss ...TarExtractOpti
var hdr *tar.Header
hdr, err = tr.Next()
if errors.Is(err, io.EOF) {
err = nil
break
}
if err != nil {
@@ -70,6 +71,9 @@ func ExtractAllFromTar(tr *tar.Reader, dest_path string, optss ...TarExtractOpti
}
case tar.TypeReg, tar.TypeRegA:
var d *os.File
if err = os.MkdirAll(filepath.Dir(dest), 0o700); err != nil {
return
}
if d, err = os.Create(dest); err != nil {
return
}
@@ -82,6 +86,9 @@ func ExtractAllFromTar(tr *tar.Reader, dest_path string, optss ...TarExtractOpti
return
}
case tar.TypeLink:
if err = os.MkdirAll(filepath.Dir(dest), 0o700); err != nil {
return
}
if err = os.Link(hdr.Linkname, dest); err != nil {
return
}
@@ -89,6 +96,9 @@ func ExtractAllFromTar(tr *tar.Reader, dest_path string, optss ...TarExtractOpti
return
}
case tar.TypeSymlink:
if err = os.MkdirAll(filepath.Dir(dest), 0o700); err != nil {
return
}
if err = os.Symlink(hdr.Linkname, dest); err != nil {
return
}