mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-15 21:17:49 +02:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15ac33a058 | ||
|
|
b9051d8288 | ||
|
|
b5fc63cbb5 | ||
|
|
913ce58fe3 | ||
|
|
d9b1c8c04f | ||
|
|
ccc3bee9af | ||
|
|
8a607fa34c | ||
|
|
0105536665 | ||
|
|
8eb0b556b7 | ||
|
|
de25e4b8f5 | ||
|
|
21f25ee5a9 | ||
|
|
d363513884 | ||
|
|
fdc3c3d7c1 | ||
|
|
0515dc1957 | ||
|
|
627360e5ad | ||
|
|
57ba0f4d87 | ||
|
|
3e8abaac58 | ||
|
|
68401eddba | ||
|
|
9843b5c210 | ||
|
|
96f2861c2a | ||
|
|
7a4f0318ed | ||
|
|
eedf01fbe3 | ||
|
|
0594033b31 | ||
|
|
75f5908009 | ||
|
|
4ea2b92c8c | ||
|
|
7e891d5287 | ||
|
|
2c01405d85 | ||
|
|
ab3cefd81f | ||
|
|
0d57253300 | ||
|
|
611d8baf28 | ||
|
|
2560a024b9 | ||
|
|
e5321f96a3 | ||
|
|
60601a6dba | ||
|
|
d36a64087e |
@@ -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'
|
||||
|
||||
@@ -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]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
10
docs/faq.rst
10
docs/faq.rst
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
6
glfw/wl_init.c
vendored
@@ -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
2
glfw/wl_platform.h
vendored
@@ -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
53
glfw/wl_window.c
vendored
@@ -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
4
go.mod
@@ -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
4
go.sum
@@ -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=
|
||||
|
||||
@@ -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", "/")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
// }}}
|
||||
|
||||
|
||||
|
||||
@@ -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, ""},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 ''
|
||||
|
||||
@@ -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))
|
||||
|
||||
2
setup.py
2
setup.py
@@ -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='????',
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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") != "") {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user