Compare commits

...

44 Commits

Author SHA1 Message Date
Kovid Goyal
17e4995e93 version 0.27.1 2023-02-07 16:10:29 +05:30
Kovid Goyal
e161b5a4de Merge branch 'completion' of https://github.com/page-down/kitty 2023-02-04 13:35:58 +05:30
pagedown
52b643b6c6 Completion: Handle kitty +complete setup fish2
Provide the currently supported fish completion script when requesting
version 2.
2023-02-04 15:55:22 +08:00
Kovid Goyal
9bdb647454 kitty @ shell: Fix global options being ignored
Also no need to exec a separate process for every command
2023-02-04 12:54:49 +05:30
Kovid Goyal
0cabc3e109 Indicate when caps lock is on while reading password 2023-02-04 11:06:24 +05:30
Kovid Goyal
d06d6d7646 Add the command that can be used to get the default config file 2023-02-03 19:29:45 +05:30
Kovid Goyal
f1dc072045 Clean up previous PR 2023-02-03 16:14:24 +05:30
Kovid Goyal
9adc474e3c Merge branch 'completion' of https://github.com/page-down/kitty 2023-02-03 16:03:03 +05:30
pagedown
370aa3aaa6 Completion: Delegate kitty +complete to kitten
Implement `kitten __complete__ setup` in Go.
Fix zsh completion script to check `kitten`.
2023-02-03 18:16:04 +08:00
Kovid Goyal
bed4f33be8 Remove unused code 2023-02-03 09:51:54 +05:30
Kovid Goyal
8ce80d8962 Switch to using Go stdlib for ECDH crypto 2023-02-03 09:50:42 +05:30
Kovid Goyal
27ae9104ac Bump required Go version to 1.20
This allows us to use the stdlib for ECDH crypto used by remote control.
Fixes #5976
2023-02-03 09:32:56 +05:30
Kovid Goyal
331f1b7f2b Merge branch 'ksi' of https://github.com/page-down/kitty 2023-02-03 09:30:14 +05:30
pagedown
df1a99a974 Shell integration: More builtin commands 2023-02-03 10:56:19 +08:00
Kovid Goyal
7ea4270c88 ... 2023-02-03 08:02:28 +05:30
Kovid Goyal
a8480a4ca6 Update changelog for previous PR 2023-02-02 17:51:25 +05:30
Kovid Goyal
783bfb2823 Merge branch 'master' of https://github.com/shimt/kitty 2023-02-02 17:50:02 +05:30
Shinichi MOTOKI
a88164e3a2 Fix function key definitions in terminfo/termcap 2023-02-02 20:01:56 +09:00
Kovid Goyal
a7cbe3776d Wayland GNOME: Fix for ibus not working when using XWayland
See 8ce25208c3

I dont know what it is with GNOME. Every single release they break
backward compatibility somewhere, somehow. They must have special
talents.

Fixes #5967
2023-02-02 10:25:33 +05:30
Kovid Goyal
78d0cc40a3 Fix readSelectionFromPasteboard not actually inserting the text 2023-02-02 06:14:34 +05:30
Kovid Goyal
01720a8d4f Fix typo seems to have no actual effect, but... 2023-02-02 06:07:30 +05:30
Kovid Goyal
1d45cf4f91 Use crypto/rand rather than math/rand
Who knows how random math/rand actually is
2023-02-02 06:04:17 +05:30
Kovid Goyal
a9da57d9b3 Forgot to use builtin for alias 2023-02-01 19:27:39 +05:30
Kovid Goyal
a280328731 kitty->kitten typo 2023-02-01 19:11:39 +05:30
Kovid Goyal
17d2315d0c Merge branch 'docs' of https://github.com/page-down/kitty 2023-02-01 19:11:13 +05:30
pagedown
e27920527c Docs: Remove the text role target that is no longer needed 2023-02-01 19:26:38 +08:00
Kovid Goyal
960f5ff065 Merge branch 'docs' of https://github.com/page-down/kitty 2023-02-01 15:46:51 +05:30
pagedown
8fe936882d Docs: Improve usage and help documents for kitten 2023-02-01 17:14:54 +08:00
Kovid Goyal
682428fb54 Optimize the services implementation
Dont construct the selection string when we are merely checking if a
selection exists.
2023-02-01 12:46:19 +05:30
Kovid Goyal
1c6bae636b Only accept service requests when we actually have a selection 2023-02-01 12:17:59 +05:30
Kovid Goyal
c201bac900 ... 2023-02-01 12:17:15 +05:30
Kovid Goyal
5eaa935ede icat: Dont try to further compress PNG images when using stream based transmission 2023-02-01 11:45:01 +05:30
Kovid Goyal
a73f09cf89 Clarify that a=f is needed for chunked transmission of animation frame data 2023-02-01 11:43:15 +05:30
Kovid Goyal
092dc3d01f ... 2023-02-01 11:28:52 +05:30
Kovid Goyal
5c0d477a18 icat kitten: Fix transmission of frame data in direct mode
Sometimes frame data is > 2048 but does not compress smaller, which
broke the if statement checking for first loop.

Fixes #5958
2023-02-01 10:51:59 +05:30
Kovid Goyal
414ca86e3f Remaining fixes from #5962
Fixes #5962
2023-02-01 10:26:53 +05:30
Kovid Goyal
fbbfb25702 Better fix for kitten not being in PATH
Add it to PATH just as we add the kitty dir to PATH. Ensures the correct
kitten is in PATH, corresponding to the correct kitty.
2023-02-01 10:16:50 +05:30
Kovid Goyal
6ea812679f Fix modify_font not working for strikethrough position
Fixes #5946
2023-02-01 08:14:54 +05:30
Kovid Goyal
5a997a5f7a grammar 2023-01-31 21:02:58 +05:30
Kovid Goyal
47641456da Ensure edit-in-kitty works even if kitten is not in PATH
Still needs to be implemented for fish shell
2023-01-31 20:41:39 +05:30
Kovid Goyal
077f71cad5 Another place to update that talks about symlinking to PATH 2023-01-31 20:31:33 +05:30
Kovid Goyal
8f71f6112a Update installation instructions to note that kitty and kitten both need to be added to PATH 2023-01-31 20:29:41 +05:30
Kovid Goyal
8bdd4d0596 ssh kitten: Install kitty bootstrap on systems other than linux/darwin as now we have kitten which works on more types of systems 2023-01-31 20:16:20 +05:30
Kovid Goyal
df45a4e759 Add a note that --detach is not available on macOS 2023-01-31 17:52:36 +05:30
56 changed files with 428 additions and 197 deletions

View File

@@ -5,7 +5,7 @@ env:
ASAN_OPTIONS: leak_check_at_exit=0
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
GO_INSTALL_VERSION: ">=1.19.0"
GO_INSTALL_VERSION: ">=1.20.0"
permissions:
contents: read # to fetch code (actions/checkout)

View File

@@ -26,7 +26,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ">=1.19.0"
go-version: ">=1.20.0"
- name: Checkout repository
uses: actions/checkout@v3

View File

@@ -22,7 +22,8 @@ simply re-run the command.
.. warning::
**Do not** copy the kitty binary out of the installation folder. If you want
to add it to your :envvar:`PATH`, create a symlink in :file:`~/.local/bin` or
:file:`/usr/bin` or wherever.
:file:`/usr/bin` or wherever. You should create a symlink for the :file:`kitten`
binary as well.
Manually installing
@@ -46,9 +47,9 @@ particular desktop, but it should work for most major desktop environments.
.. code-block:: sh
# Create a symbolic link to add kitty to PATH (assuming ~/.local/bin is in
# Create symbolic links to add kitty and kitten to PATH (assuming ~/.local/bin is in
# your system-wide PATH)
ln -s ~/.local/kitty.app/bin/kitty ~/.local/bin/
ln -sf ~/.local/kitty.app/bin/kitty ~/.local/kitty.app/bin/kitten ~/.local/bin/
# Place the kitty.desktop file somewhere it can be found by the OS
cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications/
# If you want to open text files and images in kitty via your file manager also add the kitty-open.desktop file

View File

@@ -46,7 +46,7 @@ Run-time dependencies:
Build-time dependencies:
* ``gcc`` or ``clang``
* ``go >= 1.19`` (see :file:`go.mod` for go packages used during building)
* ``go >= 1.20`` (see :file:`go.mod` for go packages used during building)
* ``pkg-config``
* For building on Linux in addition to the above dependencies you might also
need to install the following packages, if they are not already installed by

View File

@@ -35,6 +35,25 @@ mouse anywhere in the current command to move the cursor there. See
Detailed list of changes
-------------------------------------
0.27.1 [2023-02-07]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Fix :opt:`modify_font` not working for strikethrough position (:iss:`5946`)
- Fix a regression causing the ``edit-in-kitty`` command not working if :file:`kitten` is not added
to PATH (:iss:`5956`)
- icat kitten: Fix a regression that broke display of animated GIFs over SSH (:iss:`5958`)
- Wayland GNOME: Fix for ibus not working when using XWayland (:iss:`5967`)
- Fix regression in previous release that caused incorrect entries in terminfo for modifier+F3 key combinations (:pull:`5970`)
- Bring back the deprecated and removed ``kitty +complete`` and delegate it to :program:`kitten` for backward compatibility (:pull:`5977`)
- Bump the version of Go needed to build kitty to ``1.20`` so we can use the Go stdlib ecdh package for crypto.
0.27.0 [2023-01-31]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -67,7 +86,7 @@ Detailed list of changes
- A new option :opt:`undercurl_style` to control the rendering of undercurls (:pull:`5883`)
- Bash integration: Fix ``clone-in-kitty`` not working on bash >= 5.2 if an environment variable values contain newlines or other special characters (:iss:`5629`)
- Bash integration: Fix ``clone-in-kitty`` not working on bash >= 5.2 if environment variable values contain newlines or other special characters (:iss:`5629`)
- A new :ac:`sleep` action useful in combine based mappings to make kitty sleep before executing the next action

View File

@@ -68,6 +68,11 @@ Sample kitty.conf
pre-existing :file:`kitty.conf`, then that will be used instead, delete it to
see the sample file.
A default configuration file can also be generated by running::
kitty +runpy 'from kitty.config import *; print(commented_out_default_config())'
This will print the commented out default config file to :file:`STDOUT`.
All mappable actions
------------------------

View File

@@ -330,12 +330,13 @@ sequence of escape codes to the terminal emulator::
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
Note that only the first escape code needs to have the full set of control
codes such as width, height, format etc. Subsequent chunks **must** have
only the ``m`` key. The client **must** finish sending all chunks for a single image
before sending any other graphics related escape codes. Note that the cursor
position used to display the image **must** be the position when the final chunk is
received. Finally, terminals must not display anything, until the entire sequence is
received and validated.
codes such as width, height, format, etc. Subsequent chunks **must** have only
the ``m`` and optionally ``q`` keys. When sending animation frame data, subsequent
chunks **must** also specify the ``a=f`` key. The client **must** finish sending
all chunks for a single image before sending any other graphics related escape
codes. Note that the cursor position used to display the image **must** be the
position when the final chunk is received. Finally, terminals must not display
anything, until the entire sequence is received and validated.
Querying support and available transmission mediums

View File

@@ -1581,26 +1581,33 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
// Support services receiving "public.utf8-plain-text" and "NSStringPboardType"
- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
{
if ([sendType isEqual:NSPasteboardTypeString] || [sendType isEqual:@"NSStringPboardType"]) {
return self;
if (
(!sendType || [sendType isEqual:NSPasteboardTypeString] || [sendType isEqual:@"NSStringPboardType"]) &&
(!returnType || [returnType isEqual:NSPasteboardTypeString] || [returnType isEqual:@"NSStringPboardType"])
) {
if (_glfw.callbacks.has_current_selection && _glfw.callbacks.has_current_selection()) return self;
}
return nil;
return [super validRequestorForSendType:sendType returnType:returnType];
}
// Selected text as input to be sent to Services
- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types
{
NSString *text = [self accessibilitySelectedText];
if (text && [text length] > 0) {
if (!_glfw.callbacks.get_current_selection) return NO;
char *text = _glfw.callbacks.get_current_selection();
if (!text) return NO;
BOOL ans = NO;
if (text[0]) {
if ([types containsObject:NSPasteboardTypeString] == YES) {
[pboard declareTypes:@[NSPasteboardTypeString] owner:self];
return [pboard setString:text forType:NSPasteboardTypeString];
ans = [pboard setString:@(text) forType:NSPasteboardTypeString];
} else if ([types containsObject:@"NSStringPboardType"] == YES) {
[pboard declareTypes:@[@"NSStringPboardType"] owner:self];
return [pboard setString:text forType:NSPasteboardTypeString];
ans = [pboard setString:@(text) forType:@"NSStringPboardType"];
}
free(text);
}
return NO;
return ans;
}
// Service output to be handled
@@ -1625,7 +1632,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
_glfwInputKeyboard(window, &glfw_keyevent);
}
debug_key("Sending text received in readSelectionFromPasteboard as key event\n");
GLFWkeyevent glfw_keyevent = {.text=utf8};
GLFWkeyevent glfw_keyevent = {.text=utf8, .ime_state=GLFW_IME_COMMIT_TEXT};
_glfwInputKeyboard(window, &glfw_keyevent);
return YES;
}

2
glfw/glfw3.h vendored
View File

@@ -1719,6 +1719,7 @@ typedef void (* GLFWtickcallback)(void*);
typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
typedef char* (* GLFWcurrentselectionfun)(void);
typedef bool (* GLFWhascurrentselectionfun)(void);
typedef void (* GLFWclipboarddatafreefun)(void* data);
typedef struct GLFWDataChunk {
const char *data;
@@ -1889,6 +1890,7 @@ GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval,
GLFWAPI void glfwRemoveTimer(unsigned long long);
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);
GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback);
GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback);
/*! @brief Terminates the GLFW library.
*

40
glfw/ibus_glfw.c vendored
View File

@@ -283,29 +283,35 @@ static const char*
get_ibus_address_file_name(void) {
const char *addr;
static char ans[PATH_MAX];
static char display[64] = {0};
addr = getenv("IBUS_ADDRESS");
int offset = 0;
if (addr && addr[0]) {
memcpy(ans, addr, GLFW_MIN(strlen(addr), sizeof(ans)));
return ans;
}
const char *de = getenv("DISPLAY");
if (!de || !de[0]) de = ":0.0";
char *display = _glfw_strdup(de);
const char *host = display;
char *disp_num = strrchr(display, ':');
char *screen_num = strrchr(display, '.');
if (!disp_num) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as DISPLAY env var has no colon");
free(display);
return NULL;
const char* disp_num = NULL;
const char *host = "unix";
// See https://github.com/ibus/ibus/commit/8ce25208c3f4adfd290a032c6aa739d2b7580eb1 for why we need this dance.
const char *de = getenv("WAYLAND_DISPLAY");
if (de) {
disp_num = de;
} else {
const char *de = getenv("DISPLAY");
if (!de || !de[0]) de = ":0.0";
strncpy(display, de, sizeof(display) - 1);
char *dnum = strrchr(display, ':');
if (!dnum) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as DISPLAY env var has no colon");
return NULL;
}
char *screen_num = strrchr(display, '.');
*dnum = 0;
dnum++;
if (screen_num) *screen_num = 0;
if (*display) host = display;
disp_num = dnum;
}
*disp_num = 0;
disp_num++;
if (screen_num) *screen_num = 0;
if (!*host) host = "unix";
memset(ans, 0, sizeof(ans));
const char *conf_env = getenv("XDG_CONFIG_HOME");
@@ -315,7 +321,6 @@ get_ibus_address_file_name(void) {
conf_env = getenv("HOME");
if (!conf_env || !conf_env[0]) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as no HOME env var is set");
free(display);
return NULL;
}
offset = snprintf(ans, sizeof(ans), "%s/.config", conf_env);
@@ -323,7 +328,6 @@ get_ibus_address_file_name(void) {
char *key = dbus_get_local_machine_id();
snprintf(ans + offset, sizeof(ans) - offset, "/ibus/bus/%s-%s-%s", key, host, disp_num);
dbus_free(key);
free(display);
return ans;
}

7
glfw/init.c vendored
View File

@@ -395,3 +395,10 @@ GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselec
_GLFW_SWAP_POINTERS(_glfw.callbacks.get_current_selection, cbfun);
return cbfun;
}
GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun cbfun)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun);
return cbfun;
}

1
glfw/internal.h vendored
View File

@@ -634,6 +634,7 @@ struct _GLFWlibrary
GLFWapplicationclosefun application_close;
GLFWdrawtextfun draw_text;
GLFWcurrentselectionfun get_current_selection;
GLFWhascurrentselectionfun has_current_selection;
} callbacks;

13
go.mod
View File

@@ -1,6 +1,6 @@
module kitty
go 1.19
go 1.20
require (
github.com/ALTree/bigfloat v0.0.0-20220102081255-38c8b72a9924
@@ -8,11 +8,10 @@ require (
github.com/google/go-cmp v0.5.8
github.com/google/uuid v1.3.0
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f
github.com/seancfoley/ipaddress-go v1.2.1
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7
golang.org/x/exp v0.0.0-20220921164117-439092de6870
golang.org/x/image v0.2.0
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
github.com/seancfoley/ipaddress-go v1.5.3
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
golang.org/x/image v0.3.0
golang.org/x/sys v0.4.0
)
require github.com/seancfoley/bintree v1.1.0 // indirect
require github.com/seancfoley/bintree v1.2.1 // indirect

24
go.sum
View File

@@ -8,20 +8,18 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f h1:Ko4+g6K16vSyUrtd/pPXuQnWsiHe5BYptEtTxfwYwCc=
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f/go.mod h1:eHzfhOKbTGJEGPSdMHzU6jft192tHHt2Bu2vIZArvC0=
github.com/seancfoley/bintree v1.1.0 h1:6J0rj9hLNLIcWSsfYdZ4ZHkMHokaK/PHkak8qyBO/mc=
github.com/seancfoley/bintree v1.1.0/go.mod h1:CtE6qO6/n9H3V2CAGEC0lpaYr6/OijhNaMG/dt7P70c=
github.com/seancfoley/ipaddress-go v1.2.1 h1:yEZxnyC6NQEDDPflyQm4KkWozffx1vHWsx+knKBr/n0=
github.com/seancfoley/ipaddress-go v1.2.1/go.mod h1:/UEVHyrBg1ASVap2ffdY2cq5UMYIX9f3QW3uWSVqpbo=
github.com/seancfoley/bintree v1.2.1 h1:Z/iNjRKkXnn0CTW7jDQYtjW5fz2GH1yWvOTJ4MrMvdo=
github.com/seancfoley/bintree v1.2.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
github.com/seancfoley/ipaddress-go v1.5.3 h1:fLnn4nsatd2rp3IJsVWriXv5gXn2Qiy8uxjxe4iZtTg=
github.com/seancfoley/ipaddress-go v1.5.3/go.mod h1:fpvVPC+Jso+YEhNcNiww8HQmBgKP8T4T6BTp1SLxxIo=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 h1:WJywXQVIb56P2kAvXeMGTIgQ1ZHQxR60+F9dLsodECc=
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20220921164117-439092de6870 h1:j8b6j9gzSigH28O5SjSpQSSh9lFd6f5D/q0aHjNTulc=
golang.org/x/exp v0.0.0-20220921164117-439092de6870/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b h1:EqBVA+nNsObCwQoBEHy4wLU0pi7i8a4AL3pbItPdPkE=
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ=
golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI=
golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg=
golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -33,14 +31,14 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@@ -1985,6 +1985,12 @@ class Boss:
return w.text_for_selection()
return None
def has_active_selection(self) -> bool:
w = self.active_window
if w is not None and not w.destroyed:
return w.has_selection()
return False
@ac('cp', '''
Copy the selection from the active window to the specified buffer

View File

@@ -888,7 +888,7 @@ Change to the specified directory when launching.
--detach
type=bool-set
condition=not is_macos
Detach from the controlling terminal, if any.
Detach from the controlling terminal, if any. Not available on macOS.
--session

View File

@@ -22,7 +22,7 @@ class Version(NamedTuple):
appname: str = 'kitty'
kitty_face = '🐱'
version: Version = Version(0, 27, 0)
version: Version = Version(0, 27, 1)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat
@@ -79,6 +79,7 @@ def kitty_exe() -> str:
return os.path.join(rpath, 'kitty')
@run_once
def kitten_exe() -> str:
return os.path.join(os.path.dirname(kitty_exe()), 'kitten')

View File

@@ -31,6 +31,19 @@ def hold(args: List[str]) -> None:
os.execvp(kitten_exe(), args)
def complete(args: List[str]) -> None:
# Delegate to kitten to maintain backward compatibility
if len(args) < 2 or args[1] not in ('setup', 'zsh', 'fish2', 'bash'):
raise SystemExit(1)
if args[1] == 'fish2':
args[1:1] = ['fish', '_legacy_completion=fish2']
elif len(args) >= 3 and args [1:3] == ['setup', 'fish2']:
args[2] = 'fish'
from kitty.constants import kitten_exe
args = ['kitten', '__complete__'] + args[1:]
os.execvp(kitten_exe(), args)
def open_urls(args: List[str]) -> None:
setattr(sys, 'cmdline_args_for_open', True)
sys.argv = ['kitty'] + args[1:]
@@ -144,6 +157,7 @@ entry_points = {
}
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
namespaced_entry_points['hold'] = hold
namespaced_entry_points['complete'] = complete
namespaced_entry_points['runpy'] = runpy
namespaced_entry_points['launch'] = launch
namespaced_entry_points['open'] = open_urls

View File

@@ -387,7 +387,7 @@ calc_cell_metrics(FontGroup *fg) {
int adjustment = baseline - baseline_before;
baseline = adjust_ypos(baseline_before, cell_height, adjustment);
underline_position = adjust_ypos(underline_position, cell_height, adjustment);
strikethrough_position = adjust_ypos(underline_position, cell_height, adjustment);
strikethrough_position = adjust_ypos(strikethrough_position, cell_height, adjustment);
}
underline_position = MIN(cell_height - 1, underline_position);

3
kitty/glfw-wrapper.c generated
View File

@@ -41,6 +41,9 @@ load_glfw(const char* path) {
*(void **) (&glfwSetCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetCurrentSelectionCallback");
if (glfwSetCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetCurrentSelectionCallback with error: %s", dlerror());
*(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback");
if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s", dlerror());
*(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate");
if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror());

5
kitty/glfw-wrapper.h generated
View File

@@ -1457,6 +1457,7 @@ typedef void (* GLFWtickcallback)(void*);
typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
typedef char* (* GLFWcurrentselectionfun)(void);
typedef bool (* GLFWhascurrentselectionfun)(void);
typedef void (* GLFWclipboarddatafreefun)(void* data);
typedef struct GLFWDataChunk {
const char *data;
@@ -1662,6 +1663,10 @@ typedef GLFWcurrentselectionfun (*glfwSetCurrentSelectionCallback_func)(GLFWcurr
GFW_EXTERN glfwSetCurrentSelectionCallback_func glfwSetCurrentSelectionCallback_impl;
#define glfwSetCurrentSelectionCallback glfwSetCurrentSelectionCallback_impl
typedef GLFWhascurrentselectionfun (*glfwSetHasCurrentSelectionCallback_func)(GLFWhascurrentselectionfun);
GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl;
#define glfwSetHasCurrentSelectionCallback glfwSetHasCurrentSelectionCallback_impl
typedef void (*glfwTerminate_func)(void);
GFW_EXTERN glfwTerminate_func glfwTerminate_impl;
#define glfwTerminate glfwTerminate_impl

View File

@@ -179,7 +179,7 @@ log_event(const char *format, ...) {
// callbacks {{{
void
update_os_window_references() {
update_os_window_references(void) {
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
if (w->handle) glfwSetWindowUserPointer(w->handle, w);
@@ -496,6 +496,16 @@ get_current_selection(void) {
return ans;
}
static bool
has_current_selection(void) {
if (!global_state.boss) return false;
PyObject *ret = PyObject_CallMethod(global_state.boss, "has_active_selection", NULL);
if (!ret) { PyErr_Print(); return false; }
bool ans = ret == Py_True;
Py_DECREF(ret);
return ans;
}
static void get_window_dpi(GLFWwindow *w, double *x, double *y);
@@ -800,6 +810,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
if (OPT(hide_window_decorations) & 1) glfwWindowHint(GLFW_DECORATED, false);
glfwSetApplicationCloseCallback(application_close_requested_callback);
glfwSetCurrentSelectionCallback(get_current_selection);
glfwSetHasCurrentSelectionCallback(has_current_selection);
#ifdef __APPLE__
cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
@@ -1433,7 +1444,7 @@ swap_window_buffers(OSWindow *os_window) {
}
void
wakeup_main_loop() {
wakeup_main_loop(void) {
glfwPostEmptyEvent();
}

View File

@@ -58,7 +58,7 @@ Where to launch the child process:
:code:`tab`
A new :term:`tab` in the current OS window
:code:`os-window <os_window>`
:code:`os-window`
A new :term:`operating system window <os_window>`
:code:`overlay`

View File

@@ -23,6 +23,7 @@ from .constants import (
glfw_path,
is_macos,
is_wayland,
kitten_exe,
kitty_exe,
logo_png_file,
running_in_kitty,
@@ -382,6 +383,15 @@ def ensure_kitty_in_path() -> None:
os.environ['PATH'] = prepend_if_not_present(rpath, env_path)
def ensure_kitten_in_path() -> None:
correct_kitten = kitten_exe()
existing = shutil.which('kitten')
if existing and safe_samefile(existing, correct_kitten):
return
env_path = os.environ.get('PATH', '')
os.environ['PATH'] = prepend_if_not_present(os.path.dirname(correct_kitten), env_path)
def setup_manpath(env: Dict[str, str]) -> None:
# Ensure kitty manpages are available in frozen builds
if not getattr(sys, 'frozen', False):
@@ -407,6 +417,7 @@ def setup_environment(opts: Options, cli_opts: CLIOptions) -> None:
cli_opts.listen_on = expand_listen_on(cli_opts.listen_on, from_config_file)
env = opts.env.copy()
ensure_kitty_in_path()
ensure_kitten_in_path()
kitty_path = shutil.which('kitty')
if kitty_path:
child_path = env.get('PATH')

View File

@@ -2785,7 +2785,12 @@ screen_detect_url(Screen *screen, unsigned int x, unsigned int y) {
#define WRAP2B(name) static PyObject* name(Screen *self, PyObject *args) { unsigned int a, b; int p; if(!PyArg_ParseTuple(args, "IIp", &a, &b, &p)) return NULL; screen_##name(self, a, b, (bool)p); Py_RETURN_NONE; }
WRAP0(garbage_collect_hyperlink_pool)
WRAP0x(has_selection)
static PyObject*
has_selection(Screen *self, PyObject *a UNUSED) {
if (screen_has_selection(self)) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static PyObject*
hyperlinks_as_list(Screen *self, PyObject *args UNUSED) {
@@ -4076,7 +4081,7 @@ static PyMethodDef methods[] = {
MND(cursor_down1, METH_VARARGS)
MND(cursor_forward, METH_VARARGS)
{"index", (PyCFunction)xxx_index, METH_VARARGS, ""},
{"has_selection", (PyCFunction)xxx_has_selection, METH_VARARGS, ""},
{"has_selection", (PyCFunction)has_selection, METH_VARARGS, ""},
MND(set_pending_timeout, METH_O)
MND(as_text, METH_VARARGS)
MND(as_text_non_visual, METH_VARARGS)

View File

@@ -284,11 +284,18 @@ string_capabilities = {
}
string_capabilities.update({
f'kf{offset + n}':
encode_keystring(modify_key_bytes(b'\033' + value, mod))
for offset, mod in {0: 0, 12: 2, 24: 5, 36: 6, 48: 3, 60: 4}.items()
f'kf{n}':
encode_keystring(modify_key_bytes(b'\033' + value, 0))
for n, value in zip(range(1, 13),
b'OP OQ OR OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())
})
string_capabilities.update({
f'kf{offset + n}':
encode_keystring(modify_key_bytes(b'\033' + value, mod))
for offset, mod in {12: 2, 24: 5, 36: 6, 48: 3, 60: 4}.items()
for n, value in zip(range(1, 13),
b'OP OQ [13~ OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())
if offset + n < 64
})

View File

@@ -1354,6 +1354,9 @@ class Window:
lines = self.screen.text_for_selection(as_ansi, strip_trailing_spaces)
return ''.join(lines)
def has_selection(self) -> bool:
return self.screen.has_selection()
def call_watchers(self, which: Iterable[Watcher], data: Dict[str, Any]) -> None:
boss = get_boss()
for w in which:

View File

@@ -13,7 +13,7 @@ if [[ -n "$KITTY_BASH_INJECT" ]]; then
if [[ -z "$KITTY_BASH_ETC_LOCATION" ]]; then KITTY_BASH_ETC_LOCATION="/etc"; fi
_ksi_sourceable() {
[[ -f "$1" && -r "$1" ]] && return 0; return 1;
[[ -f "$1" && -r "$1" ]] && builtin return 0; builtin return 1;
}
if [[ "$kitty_bash_inject" == *"posix"* ]]; then
@@ -209,7 +209,8 @@ _ksi_main() {
_ksi_prompt[ps0]+="\[\e]133;C\a\]"
fi
alias edit-in-kitty="kitten edit-in-kitty"
builtin alias edit-in-kitty="kitten edit-in-kitty"
if [[ "${_ksi_prompt[complete]}" == "y" ]]; then
_ksi_completions() {
builtin local src
@@ -272,8 +273,8 @@ _ksi_main() {
builtin local venv="${VIRTUAL_ENV}/bin/activate"
builtin local sourced=""
_ksi_s_is_ok() {
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && return 0
return 1
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && builtin return 0
builtin return 1
}
if _ksi_s_is_ok "venv" && [ -n "${VIRTUAL_ENV}" -a -r "$venv" ]; then
@@ -339,7 +340,7 @@ clone-in-kitty() {
"") break;;
-h|--help)
builtin printf "%s\n\n%s\n" "Clone the current bash session into a new kitty window." "For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell"
return
builtin return
;;
*) data="$data,a=$(builtin printf "%s" "$1" | builtin command base64)";;
esac

View File

@@ -156,7 +156,7 @@ function __ksi_schedule --on-event fish_prompt -d "Setup kitty integration after
end
end
function edit-in-kitty --wraps "kitten edit-in-kitty"
function edit-in-kitty --wraps "kitten edit-in-kitty" -d "Edit the specified file in a kitty overlay window with your locally installed editor"
kitten edit-in-kitty $argv
end

View File

@@ -177,11 +177,6 @@ fi' > "$sh_script"
}
install_kitty_bootstrap() {
case "$(command uname)" in
Linux) ;;
Darwin) ;;
*) return ;;
esac
kitty_exists="n"
command -v kitty 2> /dev/null > /dev/null && kitty_exists="y"
if [ "$kitty_remote" = "yes" -o "$kitty_remote-$kitty_exists" = "if-needed-n" ]; then

View File

@@ -261,8 +261,6 @@ def exec_with_shell_integration():
def install_kitty_bootstrap():
kitty_remote = os.environ.pop('KITTY_REMOTE', '')
if os.uname().sysname not in ('Linux', 'Darwin'):
return
kitty_exists = shutil.which('kitty')
if kitty_remote == 'yes' or (kitty_remote == 'if-needed' and not kitty_exists):
kitty_dir = os.path.join(data_dir, 'kitty', 'bin')

View File

@@ -1,6 +1,6 @@
#compdef kitty
(( ${+commands[kitty]} )) || builtin return
(( ${+commands[kitten]} )) || builtin return
builtin local src cmd=${(F)words:0:$CURRENT}
# Send all words up to the word the cursor is currently on.
src=$(builtin command kitten __complete__ zsh "_matcher=$_matcher" <<<$cmd) || builtin return

View File

@@ -362,8 +362,8 @@ _ksi_deferred_init() {
builtin local venv="${VIRTUAL_ENV}/bin/activate"
builtin local sourced=""
_ksi_s_is_ok() {
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && return 0
return 1
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && builtin return 0
builtin return 1
}
if _ksi_s_is_ok "venv" && [[ -n "${VIRTUAL_ENV}" && -r "$venv" ]]; then
@@ -386,7 +386,7 @@ _ksi_deferred_init() {
fi
builtin unset KITTY_IS_CLONE_LAUNCH KITTY_CLONE_SOURCE_STRATEGIES
alias edit-in-kitty="kitten edit-in-kitty"
builtin alias edit-in-kitty="kitten edit-in-kitty"
# Map alt+left/right to move by word if not already mapped. This is expected behavior on macOS and I am tired
# of answering questions about it.
@@ -421,7 +421,7 @@ clone-in-kitty() {
"") break;;
-h|--help)
builtin printf "%s\n\n%s\n" "Clone the current zsh session into a new kitty window." "For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell"
return
builtin return
;;
*) data="$data,a=$(builtin printf "%s" "$1" | builtin command base64)";;
esac

View File

@@ -1 +1 @@
xterm-kitty|KovIdTTY:5i:NP:am:cc:hs:km:mi:ms:xn:Co#256:co#80:it#8:li#24:pa#32767:#2=\E[1;2H:#3=\E[2;2~:#4=\E[1;2D:%1=:%c=\E[6;2~:%e=\E[5;2~:%i=\E[1;2C:&8=:&9=\E[1;2E:*4=\E[3;2~:*7=\E[1;2F:@1=\EOE:@7=\EOF:AB=\E[48;5;%dm:AF=\E[38;5;%dm:AL=\E[%dL:DC=\E[%dP:DL=\E[%dM:DO=\E[%dB:F1=\E[23~:F2=\E[24~:F3=\E[1;2P:F4=\E[1;2Q:F5=\E[1;2R:F6=\E[1;2S:F7=\E[15;2~:F8=\E[17;2~:F9=\E[18;2~:FA=\E[19;2~:FB=\E[20;2~:FC=\E[21;2~:FD=\E[23;2~:FE=\E[24;2~:FF=\E[1;5P:FG=\E[1;5Q:FH=\E[1;5R:FI=\E[1;5S:FJ=\E[15;5~:FK=\E[17;5~:FL=\E[18;5~:FM=\E[19;5~:FN=\E[20;5~:FO=\E[21;5~:FP=\E[23;5~:FQ=\E[24;5~:FR=\E[1;6P:FS=\E[1;6Q:FT=\E[1;6R:FU=\E[1;6S:FV=\E[15;6~:FW=\E[17;6~:FX=\E[18;6~:FY=\E[19;6~:FZ=\E[20;6~:Fa=\E[21;6~:Fb=\E[23;6~:Fc=\E[24;6~:Fd=\E[1;3P:Fe=\E[1;3Q:Ff=\E[1;3R:Fg=\E[1;3S:Fh=\E[15;3~:Fi=\E[17;3~:Fj=\E[18;3~:Fk=\E[19;3~:Fl=\E[20;3~:Fm=\E[21;3~:Fn=\E[23;3~:Fo=\E[24;3~:Fp=\E[1;4P:Fq=\E[1;4Q:Fr=\E[1;4R:IC=\E[%d@:..Ic=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\:K1=:K3=:K4=:K5=:Km=\E[M:LE=\E[%dD:RA=\E[?7l:RI=\E[%dC:SA=\E[?7h:SF=\E[%dS:SR=\E[%dT:UP=\E[%dA:ZH=\E[3m:ZR=\E[23m:ac=++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~:ae=\E(B:al=\E[L:as=\E(0:bl=^G:bt=\E[Z:cb=\E[1K:cd=\E[J:ce=\E[K:ch=\E[%i%dG:cl=\E[H\E[2J:cm=\E[%i%d;%dH:cr=\r:cs=\E[%i%d;%dr:ct=\E[3g:cv=\E[%i%dd:dc=\E[P:dl=\E[M:do=\n:ds=\E]2;\007:ec=\E[%dX:ei=\E[4l:fs=^G:ho=\E[H:im=\E[4h:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:k5=\E[15~:k6=\E[17~:k7=\E[18~:k8=\E[19~:k9=\E[20~:k;=\E[21~:kB=\E[Z:kD=\E[3~:kF=\E[1;2B:kI=\E[2~:kN=\E[6~:kP=\E[5~:kR=\E[1;2A:kb=\177:kd=\EOB:ke=\E[?1l:kh=\EOH:kl=\EOD:kr=\EOC:ks=\E[?1h:ku=\EOA:le=^H:md=\E[1m:me=\E[0m:mh=\E[2m:mr=\E[7m:nd=\E[C:oc=\E]104\007:op=\E[39;49m:r1=\E]\E\\\Ec:rc=\E8:..rp=%p1%c\E[%p2%{1}%-%db:..sa=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m:sc=\E7:se=\E[27m:sf=\n:so=\E[7m:sr=\EM:st=\EH:ta=^I:te=\E[?1049l:ti=\E[?1049h:ts=\E]2;:u6=\E[%i%d;%dR:u7=\E[6n:..u8=\E[?%[;0123456789]c:u9=\E[c:ue=\E[24m:up=\E[A:us=\E[4m:vb=\E[?5h\E[?5l:ve=\E[?12h\E[?25h:vi=\E[?25l:vs=\E[?12;25h:
xterm-kitty|KovIdTTY:5i:NP:am:cc:hs:km:mi:ms:xn:Co#256:co#80:it#8:li#24:pa#32767:#2=\E[1;2H:#3=\E[2;2~:#4=\E[1;2D:%1=:%c=\E[6;2~:%e=\E[5;2~:%i=\E[1;2C:&8=:&9=\E[1;2E:*4=\E[3;2~:*7=\E[1;2F:@1=\EOE:@7=\EOF:AB=\E[48;5;%dm:AF=\E[38;5;%dm:AL=\E[%dL:DC=\E[%dP:DL=\E[%dM:DO=\E[%dB:F1=\E[23~:F2=\E[24~:F3=\E[1;2P:F4=\E[1;2Q:F5=\E[13;2~:F6=\E[1;2S:F7=\E[15;2~:F8=\E[17;2~:F9=\E[18;2~:FA=\E[19;2~:FB=\E[20;2~:FC=\E[21;2~:FD=\E[23;2~:FE=\E[24;2~:FF=\E[1;5P:FG=\E[1;5Q:FH=\E[13;5~:FI=\E[1;5S:FJ=\E[15;5~:FK=\E[17;5~:FL=\E[18;5~:FM=\E[19;5~:FN=\E[20;5~:FO=\E[21;5~:FP=\E[23;5~:FQ=\E[24;5~:FR=\E[1;6P:FS=\E[1;6Q:FT=\E[13;6~:FU=\E[1;6S:FV=\E[15;6~:FW=\E[17;6~:FX=\E[18;6~:FY=\E[19;6~:FZ=\E[20;6~:Fa=\E[21;6~:Fb=\E[23;6~:Fc=\E[24;6~:Fd=\E[1;3P:Fe=\E[1;3Q:Ff=\E[13;3~:Fg=\E[1;3S:Fh=\E[15;3~:Fi=\E[17;3~:Fj=\E[18;3~:Fk=\E[19;3~:Fl=\E[20;3~:Fm=\E[21;3~:Fn=\E[23;3~:Fo=\E[24;3~:Fp=\E[1;4P:Fq=\E[1;4Q:Fr=\E[13;4~:IC=\E[%d@:..Ic=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\:K1=:K3=:K4=:K5=:Km=\E[M:LE=\E[%dD:RA=\E[?7l:RI=\E[%dC:SA=\E[?7h:SF=\E[%dS:SR=\E[%dT:UP=\E[%dA:ZH=\E[3m:ZR=\E[23m:ac=++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~:ae=\E(B:al=\E[L:as=\E(0:bl=^G:bt=\E[Z:cb=\E[1K:cd=\E[J:ce=\E[K:ch=\E[%i%dG:cl=\E[H\E[2J:cm=\E[%i%d;%dH:cr=\r:cs=\E[%i%d;%dr:ct=\E[3g:cv=\E[%i%dd:dc=\E[P:dl=\E[M:do=\n:ds=\E]2;\007:ec=\E[%dX:ei=\E[4l:fs=^G:ho=\E[H:im=\E[4h:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:k5=\E[15~:k6=\E[17~:k7=\E[18~:k8=\E[19~:k9=\E[20~:k;=\E[21~:kB=\E[Z:kD=\E[3~:kF=\E[1;2B:kI=\E[2~:kN=\E[6~:kP=\E[5~:kR=\E[1;2A:kb=\177:kd=\EOB:ke=\E[?1l:kh=\EOH:kl=\EOD:kr=\EOC:ks=\E[?1h:ku=\EOA:le=^H:md=\E[1m:me=\E[0m:mh=\E[2m:mr=\E[7m:nd=\E[C:oc=\E]104\007:op=\E[39;49m:r1=\E]\E\\\Ec:rc=\E8:..rp=%p1%c\E[%p2%{1}%-%db:..sa=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m:sc=\E7:se=\E[27m:sf=\n:so=\E[7m:sr=\EM:st=\EH:ta=^I:te=\E[?1049l:ti=\E[?1049h:ts=\E]2;:u6=\E[%i%d;%dR:u7=\E[6n:..u8=\E[?%[;0123456789]c:u9=\E[c:ue=\E[24m:up=\E[A:us=\E[4m:vb=\E[?5h\E[?5l:ve=\E[?12h\E[?25h:vi=\E[?25l:vs=\E[?12;25h:

View File

@@ -144,7 +144,7 @@ xterm-kitty|KovIdTTY,
kf12=\E[24~,
kf13=\E[1;2P,
kf14=\E[1;2Q,
kf15=\E[1;2R,
kf15=\E[13;2~,
kf16=\E[1;2S,
kf17=\E[15;2~,
kf18=\E[17;2~,
@@ -157,7 +157,7 @@ xterm-kitty|KovIdTTY,
kf24=\E[24;2~,
kf25=\E[1;5P,
kf26=\E[1;5Q,
kf27=\E[1;5R,
kf27=\E[13;5~,
kf28=\E[1;5S,
kf29=\E[15;5~,
kf3=\EOR,
@@ -170,7 +170,7 @@ xterm-kitty|KovIdTTY,
kf36=\E[24;5~,
kf37=\E[1;6P,
kf38=\E[1;6Q,
kf39=\E[1;6R,
kf39=\E[13;6~,
kf4=\EOS,
kf40=\E[1;6S,
kf41=\E[15;6~,
@@ -184,7 +184,7 @@ xterm-kitty|KovIdTTY,
kf49=\E[1;3P,
kf5=\E[15~,
kf50=\E[1;3Q,
kf51=\E[1;3R,
kf51=\E[13;3~,
kf52=\E[1;3S,
kf53=\E[15;3~,
kf54=\E[17;3~,
@@ -197,7 +197,7 @@ xterm-kitty|KovIdTTY,
kf60=\E[24;3~,
kf61=\E[1;4P,
kf62=\E[1;4Q,
kf63=\E[1;4R,
kf63=\E[13;4~,
kf7=\E[18~,
kf8=\E[19~,
kf9=\E[20~,

Binary file not shown.

View File

@@ -11,6 +11,25 @@ import (
var _ = fmt.Print
func bash_completion_script(commands []string) (string, error) {
return `_ksi_completions() {
builtin local src
builtin local limit
# Send all words up to the word the cursor is currently on
builtin let limit=1+$COMP_CWORD
src=$(builtin printf "%s\n" "${COMP_WORDS[@]:0:$limit}" | builtin command kitten __complete__ bash)
if [[ $? == 0 ]]; then
builtin eval "${src}"
fi
}
builtin complete -F _ksi_completions kitty
builtin complete -F _ksi_completions edit-in-kitty
builtin complete -F _ksi_completions clone-in-kitty
builtin complete -F _ksi_completions kitten
`, nil
}
func bash_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
output := strings.Builder{}
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
@@ -51,6 +70,7 @@ func bash_init_completions(completions *Completions) {
}
func init() {
completion_scripts["bash"] = bash_completion_script
input_parsers["bash"] = shell_input_parser
output_serializers["bash"] = bash_output_serializer
init_completions["bash"] = bash_init_completions

View File

@@ -520,28 +520,24 @@ func (self *Command) GetOptionValues(pointer_to_options_struct any) error {
return nil
}
func (self *Command) Exec(args ...string) {
func (self *Command) ExecArgs(args []string) (exit_code int) {
root := self
for root.Parent != nil {
root = root.Parent
}
if len(args) == 0 {
args = os.Args
}
cmd, err := root.ParseArgs(args)
if err != nil {
ShowError(err)
os.Exit(1)
return 1
}
help_opt := cmd.option_map["Help"]
version_opt := root.option_map["Version"]
exit_code := 0
if help_opt != nil && help_opt.parsed_value().(bool) {
cmd.ShowHelp()
os.Exit(exit_code)
return
} else if version_opt != nil && version_opt.parsed_value().(bool) {
root.ShowVersion()
os.Exit(exit_code)
return
} else if cmd.Run != nil {
exit_code, err = cmd.Run(cmd, cmd.Args)
if err != nil {
@@ -551,7 +547,14 @@ func (self *Command) Exec(args ...string) {
}
}
}
os.Exit(exit_code)
return
}
func (self *Command) Exec(args ...string) {
if len(args) == 0 {
args = os.Args
}
os.Exit(self.ExecArgs(args))
}
func (self *Command) GetCompletions(argv []string, init_completions func(*Completions)) *Completions {

View File

@@ -30,9 +30,11 @@ func json_output_serializer(completions []*Completions, shell_state map[string]s
return json.Marshal(completions)
}
type completion_script_func func(commands []string) (string, error)
type parser_func func(data []byte, shell_state map[string]string) ([][]string, error)
type serializer_func func(completions []*Completions, shell_state map[string]string) ([]byte, error)
var completion_scripts = make(map[string]completion_script_func, 4)
var input_parsers = make(map[string]parser_func, 4)
var output_serializers = make(map[string]serializer_func, 4)
var init_completions = make(map[string]func(*Completions), 4)
@@ -61,6 +63,22 @@ func GenerateCompletions(args []string) error {
if n < 1 {
n = 1
}
if output_type == "setup" {
if len(args) == 0 {
return fmt.Errorf("The shell must be specified")
}
shell_name := args[0]
args = args[1:]
completion_script := completion_scripts[shell_name]
if completion_script == nil {
return fmt.Errorf("Unsupported shell: %s", shell_name)
}
output, err := completion_script(args)
if err == nil {
_, err = os.Stdout.WriteString(output)
}
return err
}
shell_state := make(map[string]string, n)
for _, arg := range args {
k, v, found := utils.Cut(arg, "=")

View File

@@ -12,12 +12,51 @@ import (
var _ = fmt.Print
func fish_completion_script(commands []string) (string, error) {
// One command in fish requires one completion script.
// Usage: kitten __complete__ setup fish [kitty|kitten|clone-in-kitty]
all_commands := map[string]bool{
"kitty": true,
"clone-in-kitty": true,
"kitten": true,
}
if len(commands) == 0 {
commands = append(commands, utils.Keys(all_commands)...)
}
script := strings.Builder{}
script.WriteString(`function __ksi_completions
set --local ct (commandline --current-token)
set --local tokens (commandline --tokenize --cut-at-cursor --current-process)
printf "%s\n" $tokens $ct | command kitten __complete__ fish | source -
end
`)
for _, cmd := range commands {
if all_commands[cmd] {
fmt.Fprintf(&script, "complete -f -c %s -a \"(__ksi_completions)\"\n", cmd)
} else if strings.Contains(cmd, "=") {
// Reserved for `setup SHELL [KEY=VALUE ...]`, not used now.
continue
} else {
return "", fmt.Errorf("No fish completion script for command: %s", cmd)
}
}
return script.String(), nil
}
func fish_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
output := strings.Builder{}
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
n := completions[0].Delegate.NumToRemove
fm := markup.New(false) // fish freaks out if there are escape codes in the description strings
if n > 0 {
legacy_completion := shell_state["_legacy_completion"]
if legacy_completion == "fish2" {
for _, mg := range completions[0].Groups {
for _, m := range mg.Matches {
f("%s", strings.ReplaceAll(m.Word+"\t"+fm.Prettify(m.Description), "\n", " "))
}
}
} else if n > 0 {
words := make([]string, len(completions[0].AllWords)-n+1)
words[0] = completions[0].Delegate.Command
copy(words[1:], completions[0].AllWords[n:])
@@ -40,6 +79,7 @@ func fish_output_serializer(completions []*Completions, shell_state map[string]s
}
func init() {
completion_scripts["fish"] = fish_completion_script
input_parsers["fish"] = shell_input_parser
output_serializers["fish"] = fish_output_serializer
}

View File

@@ -15,6 +15,25 @@ import (
var _ = fmt.Print
func zsh_completion_script(commands []string) (string, error) {
return `#compdef kitty
_kitty() {
(( ${+commands[kitten]} )) || builtin return
builtin local src cmd=${(F)words:0:$CURRENT}
# Send all words up to the word the cursor is currently on.
src=$(builtin command kitten __complete__ zsh "_matcher=$_matcher" <<<$cmd) || builtin return
builtin eval "$src"
}
if (( $+functions[compdef] )); then
compdef _kitty kitty
compdef _kitty clone-in-kitty
compdef _kitty kitten
fi
`, nil
}
func shell_input_parser(data []byte, shell_state map[string]string) ([][]string, error) {
raw := string(data)
new_word := strings.HasSuffix(raw, "\n\n")
@@ -150,6 +169,7 @@ func zsh_output_serializer(completions []*Completions, shell_state map[string]st
}
func init() {
completion_scripts["zsh"] = zsh_completion_script
input_parsers["zsh"] = zsh_input_parser
output_serializers["zsh"] = zsh_output_serializer
}

View File

@@ -36,6 +36,7 @@ var ProtocolVersion [3]int = [3]int{0, 26, 0}
type GlobalOptions struct {
to_network, to_address, password string
to_address_is_from_env_var bool
already_setup bool
}
var global_options GlobalOptions
@@ -367,6 +368,9 @@ func register_at_cmd(f func(*cli.Command) *cli.Command) {
}
func setup_global_options(cmd *cli.Command) (err error) {
if global_options.already_setup {
return nil
}
err = cmd.GetOptionValues(&rc_global_opts)
if err != nil {
return err
@@ -385,6 +389,7 @@ func setup_global_options(cmd *cli.Command) (err error) {
}
q, err := get_password(rc_global_opts.Password, rc_global_opts.PasswordFile, rc_global_opts.PasswordEnv, rc_global_opts.UsePassword)
global_options.password = q
global_options.already_setup = true
return err
}

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
@@ -110,6 +109,8 @@ func show_basic_help() {
fmt.Fprintln(&output, " ", sc.ShortDescription)
}
}
fmt.Fprintln(&output, " ", formatter.Green("help"))
fmt.Fprintln(&output, " ", "Show this help")
fmt.Fprintln(&output, " ", formatter.Green("exit"))
fmt.Fprintln(&output, " ", "Exit this shell")
cli.ShowHelpInPager(output.String())
@@ -159,26 +160,12 @@ func exec_command(at_root_command *cli.Command, rl *readline.Readline, cmdline s
fmt.Fprintln(os.Stderr, "No command named", formatter.BrightRed(parsed_cmdline[0])+". Type help for a list of commands")
return true
}
exe, err := os.Executable()
if err != nil {
exe, err = exec.LookPath("kitten")
if err != nil {
fmt.Fprintln(os.Stderr, "Could not find the kitten executable")
return false
}
}
cmdline := []string{"kitten", "@"}
cmdline = append(cmdline, parsed_cmdline...)
cmd := exec.Cmd{Path: exe, Args: cmdline, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr}
err = cmd.Run()
root := cli.NewRootCommand()
EntryPoint(root)
hi.ExitCode = root.ExecArgs(cmdline)
hi.Duration = time.Now().Sub(hi.Timestamp)
hi.ExitCode = 0
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
hi.ExitCode = exitError.ExitCode()
}
fmt.Fprintln(os.Stderr, err)
}
rl.AddHistoryItem(hi)
}
return true
@@ -212,6 +199,10 @@ func completions(before_cursor, after_cursor string) (ans *cli.Completions) {
}
func shell_main(cmd *cli.Command, args []string) (int, error) {
err := setup_global_options(cmd)
if err != nil {
return 1, err
}
formatter = markup.New(true)
fmt.Println("Welcome to the kitty shell!")
fmt.Println("Use", formatter.Green("help"), "for assistance or", formatter.Green("exit"), "to quit.")

View File

@@ -87,7 +87,7 @@ func EntryPoint(tool_root *cli.Command) {
Name: "__complete__", Hidden: true,
Usage: "output_type [shell state...]",
ShortDescription: "Generate completions for kitty commands",
HelpText: "Generate completion candidates for kitty commands. The command line is read from STDIN. output_type can be one of the supported shells or 'json' for JSON output.",
HelpText: "Generate completion candidates for kitty commands. The command line is read from STDIN. output_type can be one of the supported shells: :code:`zsh`, :code:`fish`, :code:`bash`, or :code:`setup` for completion setup script following with the shell name, or :code:`json` for JSON output.",
Run: func(cmd *cli.Command, args []string) (ret int, err error) {
return ret, cli.GenerateCompletions(args)
},

View File

@@ -235,7 +235,7 @@ type Options struct {
func EntryPoint(parent *cli.Command) *cli.Command {
sc := parent.AddSubCommand(&cli.Command{
Name: "edit-in-kitty",
Usage: "edit-in-kitty [options] file-to-edit",
Usage: "[options] file-to-edit",
ShortDescription: "Edit a file in a kitty overlay window",
HelpText: "Edit the specified file in a kitty overlay window. Works over SSH as well.\n\n" +
"For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#edit-file",

View File

@@ -4,11 +4,13 @@ package icat
import (
"bytes"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"math/rand"
not_rand "math/rand"
"os"
"path/filepath"
@@ -202,6 +204,19 @@ func place_cursor(imgd *image_data) {
}
}
func next_random() (ans uint32) {
for ans == 0 {
b := make([]byte, 4)
_, err := rand.Read(b)
if err == nil {
ans = binary.LittleEndian.Uint32(b[:])
} else {
ans = not_rand.Uint32()
}
}
return ans
}
func transmit_image(imgd *image_data) {
defer func() {
for _, frame := range imgd.frames {
@@ -239,7 +254,7 @@ func transmit_image(imgd *image_data) {
}
if len(imgd.frames) > 1 {
for imgd.image_number == 0 {
imgd.image_number = rand.Uint32()
imgd.image_number = next_random()
}
}
place_cursor(imgd)

View File

@@ -18,7 +18,7 @@ var _ = fmt.Print
func KittyToolEntryPoints(root *cli.Command) {
root.Add(cli.OptionSpec{
Name: "--version", Type: "bool-set", Help: "The current kitty version."})
Name: "--version", Type: "bool-set", Help: "The current kitten version."})
// @
at.EntryPoint(root)
// update-self

View File

@@ -71,7 +71,7 @@ func update_self(version string) (err error) {
func EntryPoint(root *cli.Command) *cli.Command {
sc := root.AddSubCommand(&cli.Command{
Name: "update-self",
Usage: "update-self [options ...]",
Usage: "[options]",
ShortDescription: "Update this kitten binary",
HelpText: "Update this kitten binary in place to the latest available version.",
Run: func(cmd *cli.Command, args []string) (ret int, err error) {
@@ -89,7 +89,7 @@ func EntryPoint(root *cli.Command) *cli.Command {
sc.Add(cli.OptionSpec{
Name: "--fetch-version",
Default: "latest",
Help: "The version to fetch. The special words :code:`latest` and :code:`nightly` fetch the latest stable and nightly release respectively. Other values can be, for example: 0.27.1.",
Help: fmt.Sprintf("The version to fetch. The special words :code:`latest` and :code:`nightly` fetch the latest stable and nightly release respectively. Other values can be, for example: :code:`%s`.", kitty.VersionString),
})
return sc
}

View File

@@ -6,27 +6,39 @@ package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"crypto/sha256"
"encoding/json"
"fmt"
"github.com/jamesruan/go-rfc1924/base85"
"golang.org/x/crypto/curve25519"
"kitty/tools/utils"
"time"
)
func curve25519_key_pair() (private_key []byte, public_key []byte, err error) {
private_key = make([]byte, 32)
_, err = rand.Read(private_key)
curve := ecdh.X25519()
privkey, err := curve.GenerateKey(rand.Reader)
if err == nil {
public_key, err = curve25519.X25519(private_key[:], curve25519.Basepoint)
pubkey := privkey.PublicKey()
return privkey.Bytes(), pubkey.Bytes(), nil
}
return
return nil, nil, err
}
func curve25519_derive_shared_secret(private_key []byte, public_key []byte) (secret []byte, err error) {
secret, err = curve25519.X25519(private_key[:], public_key[:])
prkey, err := ecdh.X25519().NewPrivateKey(private_key)
if err != nil {
return nil, fmt.Errorf("Invalid X25519 private key: %w", err)
}
pubkey, err := ecdh.X25519().NewPublicKey(public_key)
if err != nil {
return nil, fmt.Errorf("Invalid X25519 public key: %w", err)
}
secret, err = prkey.ECDH(pubkey)
if err != nil {
err = fmt.Errorf("Failed to perform ECDH shared secret derivation: %w", err)
}
return
}

View File

@@ -544,11 +544,12 @@ func (self *GraphicsCommand) WriteWithPayloadTo(o io.StringWriter, payload []byt
return self.serialize_to(o, base64.StdEncoding.EncodeToString(payload))
}
gc := *self
compressed := compress_with_zlib(payload)
if len(compressed) < len(payload) {
gc.SetCompression(GRT_compression_zlib)
gc.SetDataSize(uint64(len(payload)))
payload = compressed
if self.Format() != GRT_format_png {
compressed := compress_with_zlib(payload)
if len(compressed) < len(payload) {
gc.SetCompression(GRT_compression_zlib)
payload = compressed
}
}
data := base64.StdEncoding.EncodeToString(payload)
for len(data) > 0 && err == nil {
@@ -565,9 +566,10 @@ func (self *GraphicsCommand) WriteWithPayloadTo(o io.StringWriter, payload []byt
gc.m = GRT_more_nomore
}
err = gc.serialize_to(o, chunk)
if gc.DataSize() > 0 {
gc = GraphicsCommand{}
if err != nil {
return err
}
gc = GraphicsCommand{q: self.q, a: self.a}
}
return
}

View File

@@ -93,6 +93,10 @@ func (self KeyModifiers) String() string {
return strings.Join(ans, "+")
}
func (self KeyModifiers) HasCapsLock() bool {
return self&CAPS_LOCK != 0
}
type KeyEvent struct {
Type KeyEventType
Mods KeyModifiers
@@ -121,6 +125,10 @@ func (self *KeyEvent) String() string {
return ans + "}"
}
func (self *KeyEvent) HasCapsLock() bool {
return self.Mods.HasCapsLock()
}
func KeyEventFromCSI(csi string) *KeyEvent {
if len(csi) == 0 {
return nil

View File

@@ -26,10 +26,28 @@ func ReadPassword(prompt string, kill_if_signaled bool) (password string, err er
if err != nil {
return
}
capspress_was_locked := false
has_caps_lock := false
redraw_prompt := func() {
text := prompt + shadow
lp.QueueWriteString("\r")
lp.ClearToEndOfLine()
if has_caps_lock {
lp.QueueWriteString("\x1b[31m[CapsLock on!]\x1b[39m ")
}
lp.QueueWriteString(text)
}
lp.OnInitialize = func() (string, error) {
lp.QueueWriteString(prompt)
return "\r\n", nil
lp.SetCursorShape(loop.BAR_CURSOR, true)
return "", nil
}
lp.OnFinalize = func() string {
lp.SetCursorShape(loop.BLOCK_CURSOR, true)
return "\r\n"
}
lp.OnText = func(text string, from_key_event bool, in_bracketed_paste bool) error {
@@ -45,6 +63,22 @@ func ReadPassword(prompt string, kill_if_signaled bool) (password string, err er
}
lp.OnKeyEvent = func(event *loop.KeyEvent) error {
has_caps := false
if strings.ToLower(event.Key) == "caps_lock" {
if event.Type == loop.RELEASE {
has_caps = !capspress_was_locked
capspress_was_locked = false
} else {
capspress_was_locked = event.HasCapsLock()
has_caps = true
}
} else {
has_caps = event.HasCapsLock()
}
if has_caps_lock != has_caps {
has_caps_lock = has_caps
redraw_prompt()
}
if event.MatchesPressOrRepeat("backspace") || event.MatchesPressOrRepeat("delete") {
event.Handled = true
if len(password) > 0 {
@@ -80,7 +114,7 @@ func ReadPassword(prompt string, kill_if_signaled bool) (password string, err er
}
lp.OnResumeFromStop = func() error {
lp.QueueWriteString("\r" + prompt + shadow)
redraw_prompt()
return nil
}

View File

@@ -3,9 +3,11 @@
package shm
import (
"crypto/rand"
"encoding/base32"
"errors"
"fmt"
"math/rand"
not_rand "math/rand"
"os"
"strconv"
"strings"
@@ -42,8 +44,12 @@ func prefix_and_suffix(pattern string) (prefix, suffix string, err error) {
}
func next_random() string {
num := rand.Uint32()
return strconv.FormatUint(uint64(num), 16)
b := make([]byte, 8)
_, err := rand.Read(b)
if err != nil {
return strconv.FormatUint(uint64(not_rand.Uint32()), 16)
}
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)
}
type MMap interface {

View File

@@ -3,10 +3,10 @@
package shm
import (
"crypto/rand"
"errors"
"fmt"
"io/fs"
"math/rand"
"os"
"reflect"
"testing"

View File

@@ -21,6 +21,5 @@ func UnsafeStringToBytes(s string) (b []byte) {
// If you modify b, then s will also be modified. This violates the
// property that strings are immutable.
func UnsafeBytesToString(b []byte) (s string) {
unsafe.String(unsafe.SliceData(b), len(b))
return s
return unsafe.String(unsafe.SliceData(b), len(b))
}

View File

@@ -1,46 +0,0 @@
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
//go:build !go1.20
package utils
import (
"unsafe"
)
// stringHeader is the runtime representation of a string.
// It should be identical to reflect.StringHeader
type stringHeader struct {
data unsafe.Pointer
stringLen int
}
// sliceHeader is the runtime representation of a slice.
// It should be identical to reflect.sliceHeader
type sliceHeader struct {
data unsafe.Pointer
sliceLen int
sliceCap int
}
// Unsafely converts s into a byte slice.
// If you modify b, then s will also be modified. This violates the
// property that strings are immutable.
func UnsafeStringToBytes(s string) (b []byte) {
stringHeader := (*stringHeader)(unsafe.Pointer(&s))
sliceHeader := (*sliceHeader)(unsafe.Pointer(&b))
sliceHeader.data = stringHeader.data
sliceHeader.sliceLen = len(s)
sliceHeader.sliceCap = len(s)
return b
}
// Unsafely converts b into a string.
// If you modify b, then s will also be modified. This violates the
// property that strings are immutable.
func UnsafeBytesToString(b []byte) (s string) {
sliceHeader := (*sliceHeader)(unsafe.Pointer(&b))
stringHeader := (*stringHeader)(unsafe.Pointer(&s))
stringHeader.data = sliceHeader.data
stringHeader.stringLen = len(b)
return s
}