Compare commits

..

45 Commits

Author SHA1 Message Date
Kovid Goyal
13e59df1f8 version 0.22.2 2021-08-02 08:20:33 +05:30
Kovid Goyal
c797944923 Also set first underviced render frame request 2021-08-01 12:59:22 +05:30
Kovid Goyal
ade4e67b51 Possible fix for #3890
Try to ensure we have a functioning displaylink always.
GLFW skips over sleeping monitors during a poll and also
had a bug where the display link was not re-created for a monitor
that already had a glfw monitor entry.

Also add a bunch more debug reporting
2021-08-01 12:53:35 +05:30
Kovid Goyal
656f49f2ff ... 2021-08-01 07:56:55 +05:30
Kovid Goyal
0045244295 All option instances must not share the same color table 2021-07-31 18:44:39 +05:30
Kovid Goyal
6bfb704f6f When going from csd->no csd increase window height to compensate for titlebar.
Sway will send the next configure event (for example when focus changes)
with titlebar height added, so to avoid a sudden resize at that time,
bump the height by the titlebar.
2021-07-31 14:56:45 +05:30
Kovid Goyal
80cebdefcd Better fix for CSD toggling on Wayland
Draw CSD if the compositor wants us to. See #3888
2021-07-31 14:15:38 +05:30
Kovid Goyal
68cd863fe0 version 0.22.1 2021-07-31 12:03:42 +05:30
Kovid Goyal
65e5015d19 Allow linking to discussions in docs 2021-07-31 11:51:03 +05:30
Kovid Goyal
c26665ec4d Wayland: If the compositor turns off server side decorations after turning them on do not draw client side decorations
This matches the behavior of GNOME based clients and in any case sway
seems to get very confused if CSD is drawn.

Fixes #3888
2021-07-31 11:41:27 +05:30
Kovid Goyal
91d82ebf4b Remove unused import 2021-07-31 11:27:29 +05:30
Kovid Goyal
303e4baa58 Add GLFW API to set WM_COMMAND
Also have create_os_window take keyword arguments
2021-07-30 08:35:40 +05:30
Kovid Goyal
3c953d47ca Merge branch 'pr-fix-nix' of https://github.com/egnor/kitty 2021-07-29 14:23:08 +05:30
Kovid Goyal
5e629d5c09 Linux binary: Remove any RPATH build artifacts from bundled libraries
Thanks to Dan Egnor for pointing these out.
2021-07-29 13:12:49 +05:30
Kovid Goyal
c6ec2d4282 Update docs to reflect the fact that the shell is run with both --login and --interactive to read its env vars 2021-07-29 12:40:19 +05:30
Kovid Goyal
66b93a9af8 Merge branch 'pr-doc-editor-fix' of https://github.com/egnor/kitty 2021-07-29 12:24:17 +05:30
egnor
8d5479f55e minor tweaks to wording and formatting 2021-07-28 23:50:11 -07:00
egnor
96ce6c9504 Resolve some duplication in glossary.rst 2021-07-28 23:47:55 -07:00
egnor
0d55865545 more accurately describe editor behavior 2021-07-28 23:44:09 -07:00
Kovid Goyal
a1fc383e6f Cleanup the previous PR 2021-07-29 11:52:14 +05:30
Kovid Goyal
c3807e175d Merge branch 'pr-doc-editor' of https://github.com/egnor/kitty 2021-07-29 11:48:34 +05:30
Kovid Goyal
6f83f76d41 Make reading shell environment a bit more robust
Now ensure that --login flag is not duplicated. Also
run the shell in --interactive as well as --login mode.
2021-07-29 11:12:09 +05:30
egnor
9a56d619af clarify the text for the "editor" config variable slightly 2021-07-28 22:18:03 -07:00
Kovid Goyal
f692d586f7 Fix type checking in send_text 2021-07-29 09:04:07 +05:30
Kovid Goyal
d3d3e99979 broadcast kitten: Fix decoding of key events
No clue why mypy didnt catch this error
2021-07-29 08:55:34 +05:30
egnor
8e6a179efe fix shell.nix error with stdenv.lib.getLib 2021-07-28 17:16:52 -07:00
Kovid Goyal
17ff317d30 Allow specifying additional targets for detach_window 2021-07-28 21:50:09 +05:30
Kovid Goyal
faef9f0049 ... 2021-07-28 21:27:43 +05:30
Kovid Goyal
bb0c831601 Fix #3878 2021-07-28 18:22:08 +05:30
Kovid Goyal
9462654738 Update changelog 2021-07-28 15:52:54 +05:30
Kovid Goyal
e872169e4c Graphics protocol: Fix composing onto currently displayed frame not updating the frame on the GPU. Fixes #3874 2021-07-28 15:46:50 +05:30
Kovid Goyal
a22404abe6 Better names 2021-07-28 15:44:17 +05:30
Kovid Goyal
7c06313750 A couple more useful aliases 2021-07-28 15:40:48 +05:30
Kovid Goyal
6f265f448d DRYer 2021-07-28 15:39:21 +05:30
Kovid Goyal
acdc41bd03 Allow sending no payload 2021-07-28 13:39:15 +05:30
Kovid Goyal
bcff2a7fb6 Add aliases to GraphicsCommand to make it more useable 2021-07-28 13:28:21 +05:30
Kovid Goyal
94188fddce pep8-ify sample code 2021-07-28 11:13:29 +05:30
Kovid Goyal
3684e7f54a Merge branch 'cocoa-fix-m1' of https://github.com/horta/kitty 2021-07-28 07:45:50 +05:30
Danilo Horta
890181172f Fix "Cocoa: Failed to find service port...". 2021-07-28 00:39:50 +01:00
Kovid Goyal
9c9d68561e When calling the active tab idx setter do not re-record the current tab in the active tab history if the current tab will not actually change. Fixes #3871 2021-07-27 09:12:35 +05:30
Kovid Goyal
1bd39ff935 Use new URL scheme and define the kitty website centrally as much as possible 2021-07-27 09:03:11 +05:30
Kovid Goyal
17f3d2d581 DRYer 2021-07-27 08:44:50 +05:30
Kovid Goyal
55aa9e11db Dont use globals for role mapping 2021-07-27 08:39:04 +05:30
Kovid Goyal
bc895eacc5 Also prevent unknown roles from causing formatting to error 2021-07-27 08:07:11 +05:30
Kovid Goyal
6c8eb4a19a Fix #3869 2021-07-27 08:05:55 +05:30
45 changed files with 452 additions and 167 deletions

View File

@@ -22,7 +22,7 @@ If applicable, add screenshots to help explain your problem.
**Environment details**
```
Press Ctrl+Shift+F6 (cmd+option+, on macOS) in kitty, to copy debug output about kitty and its
Press Ctrl+Shift+F6 (cmd+option+comma on macOS) in kitty, to copy debug output about kitty and its
configuration to the clipboard and paste it here.
On older versions of kitty, run kitty --debug-config instead

View File

@@ -1 +1 @@
See https://sw.kovidgoyal.net/kitty/changelog.html
See https://sw.kovidgoyal.net/kitty/changelog/

View File

@@ -10,9 +10,9 @@ config to reproduce the issue with).
### Contributing code
Install [the dependencies](https://sw.kovidgoyal.net/kitty/build.html#dependencies)
Install [the dependencies](https://sw.kovidgoyal.net/kitty/build/#dependencies)
using your favorite package manager. Build and run kitty [from
source](https://sw.kovidgoyal.net/kitty/build.html#install-and-run-from-source).
source](https://sw.kovidgoyal.net/kitty/build/#install-and-run-from-source).
Make a fork, submit your Pull Request. If it's a large/controversial change, open an issue
beforehand to discuss it, so that you don't waste your time making a pull

View File

@@ -1,3 +1,3 @@
To build from source: <https://sw.kovidgoyal.net/kitty/build.html>
To build from source: <https://sw.kovidgoyal.net/kitty/build/>
Pre-built binaries: <https://sw.kovidgoyal.net/kitty/binary.html>
Pre-built binaries: <https://sw.kovidgoyal.net/kitty/binary/>

View File

@@ -4,7 +4,7 @@ See https://sw.kovidgoyal.net/kitty/[the kitty website].
image:https://github.com/kovidgoyal/kitty/workflows/CI/badge.svg["Build status", link="https://github.com/kovidgoyal/kitty/actions?query=workflow%3ACI"]
https://sw.kovidgoyal.net/kitty/faq.html[Frequently Asked Questions]
https://sw.kovidgoyal.net/kitty/faq/[Frequently Asked Questions]
To ask other questions about kitty usage, use either the https://github.com/kovidgoyal/kitty/discussions/[discussions on GitHub] or the
https://www.reddit.com/r/KittyTerminal[Reddit community]

View File

@@ -88,6 +88,8 @@ def copy_libs(env):
for x in binary_includes():
dest = env.bin_dir if '/bin/' in x else env.lib_dir
shutil.copy2(x, dest)
dest = os.path.join(dest, os.path.basename(x))
subprocess.check_call(['chrpath', '-d', dest])
def copy_python(env):

View File

@@ -83,6 +83,10 @@ move it to another tab or another OS window::
map ctrl+f2 detach_window
# moves the window into a new Tab
map ctrl+f3 detach_window new-tab
# moves the window into the previously active tab
map ctrl+f3 detach_window tab-prev
# moves the window into the tab at the left of the active tab
map ctrl+f3 detach_window tab-left
# asks which tab to move the window into
map ctrl+f4 detach_window ask

View File

@@ -4,6 +4,40 @@ Changelog
|kitty| is a feature-rich, cross-platform, *fast*, GPU based terminal.
To update |kitty|, :doc:`follow the instructions <binary>`.
0.22.2 [2021-08-02]
----------------------
- macOS: Fix a long standing bug that could cause kitty windows to stop
updating, that got worse in the previous release (:iss:`3890` and
:iss:`2016`)
- Wayland: A better fix for compositors like sway that can toggle client side
decorations on and off (:iss:`3888`)
0.22.1 [2021-07-31]
----------------------
- Fix a regression in the previous release that broke ``kitty --help`` (:iss:`3869`)
- Graphics protocol: Fix composing onto currently displayed frame not updating the frame on the GPU (:iss:`3874`)
- Fix switching to previously active tab after detaching a tab not working (:pull:`3871`)
- macOS: Fix an error on Apple silicon when enumerating monitors (:pull:`3875`)
- detach_window: Allow specifying the previously active tab or the tab to the left/right of
the active tab (:disc:`3877`)
- broadcast kitten: Fix a regression in ``0.20.0`` that broke sending of some
keys, such as backspace
- Linux binary: Remove any RPATH build artifacts from bundled libraries
- Wayland: If the compositor turns off server side decorations after turning
them on do not draw client side decorations (:iss:`3888`)
0.22.0 [2021-07-26]
----------------------

View File

@@ -29,7 +29,7 @@ if kitty_src not in sys.path:
sys.path.insert(0, kitty_src)
from kitty.conf.types import Definition # noqa
from kitty.constants import str_version # noqa
from kitty.constants import str_version, website_url # noqa
# config {{{
# -- Project information -----------------------------------------------------
@@ -65,7 +65,7 @@ extensions = [
]
# URL for OpenGraph tags
ogp_site_url = 'https://sw.kovidgoyal.net/kitty/'
ogp_site_url = website_url()
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -516,4 +516,5 @@ def setup(app: Any) -> None:
app.add_role('link', link_role)
app.add_role('iss', partial(num_role, 'issues'))
app.add_role('pull', partial(num_role, 'pull'))
app.add_role('disc', partial(num_role, 'discussions'))
app.add_role('commit', commit_role)

View File

@@ -53,6 +53,17 @@ Variables that influence kitty behavior
directory lookup mechanism see, :option:`kitty --config`.
.. envvar:: VISUAL
The terminal editor (such as ``vi`` or ``nano``) kitty uses, when, for
instance, opening :file:`kitty.conf` in response to :sc:`edit_config_file`.
.. envvar:: EDITOR
Same as :envvar:`VISUAL`. Used if :envvar:`VISUAL` is not set.
Variables that kitty sets when running child programs
.. envvar:: KITTY_WINDOW_ID

View File

@@ -97,32 +97,36 @@ features of the graphics protocol:
.. code-block:: python
import sys
from base64 import standard_b64encode
import sys
from base64 import standard_b64encode
def serialize_gr_command(**cmd):
payload = cmd.pop('payload', None)
cmd = ','.join('{}={}'.format(k, v) for k, v in cmd.items())
ans = []
w = ans.append
w(b'\033_G'), w(cmd.encode('ascii'))
if payload:
w(b';')
w(payload)
w(b'\033\\')
return b''.join(ans)
def write_chunked(**cmd):
data = standard_b64encode(cmd.pop('data'))
while data:
chunk, data = data[:4096], data[4096:]
m = 1 if data else 0
sys.stdout.buffer.write(serialize_gr_command(payload=chunk, m=m, **cmd))
sys.stdout.flush()
cmd.clear()
def serialize_gr_command(**cmd):
payload = cmd.pop('payload', None)
cmd = ','.join('{}={}'.format(k, v) for k, v in cmd.items())
ans = []
w = ans.append
w(b'\033_G'), w(cmd.encode('ascii'))
if payload:
w(b';')
w(payload)
w(b'\033\\')
return b''.join(ans)
with open(sys.argv[-1], 'rb') as f:
write_chunked(a='T', f=100, data=f.read())
def write_chunked(**cmd):
data = standard_b64encode(cmd.pop('data'))
while data:
chunk, data = data[:4096], data[4096:]
m = 1 if data else 0
sys.stdout.buffer.write(serialize_gr_command(payload=chunk, m=m,
**cmd))
sys.stdout.flush()
cmd.clear()
with open(sys.argv[-1], 'rb') as f:
write_chunked(a='T', f=100, data=f.read())
Save this script as :file:`png.py`, then you can use it to display any PNG
@@ -693,7 +697,7 @@ take, and the default value they take when missing. All integers are 32-bit.
Key Value Default Description
======= ==================== ========= =================
``a`` Single character. ``t`` The overall action this graphics command is performing.
``(a, c, d, f, ` ``t`` - transmit data, ``T`` - transmit data and display image,
``(a, c, d, f, `` ``t`` - transmit data, ``T`` - transmit data and display image,
``p, q, t, T)`` ``q`` - query terminal, ``p`` - put (display) previous transmitted image,
``d`` - delete image, ``f`` - transmit data for animation frames,
``a`` - control animation, ``c`` - compose animation frames
@@ -753,7 +757,7 @@ Key Value Default Description
**Keys for animation frame composition**
-----------------------------------------------------------
``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the base data
``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the overlaid data
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being edited.
``x`` Positive integer ``0`` The left edge (in pixels) of the destination rectangle
``y`` Positive integer ``0`` The top edge (in pixels) of the destination rectangle

View File

@@ -17,9 +17,10 @@ Then hold down :kbd:`ctrl+shift` and click the name of the file.
|kitty| will ask you what you want to do with the remote file. You can choose
to *Edit* it in which case kitty will download it and open it locally in your
``EDITOR``. As you make changes to the file, they are automatically transferred
to the remote computer. Note that this happens without needing to install *any*
special software on the server, beyond ``ls`` that supports hyperlinks.
:envvar:`EDITOR`. As you make changes to the file, they are automatically
transferred to the remote computer. Note that this happens without needing
to install *any* special software on the server, beyond ``ls`` that supports
hyperlinks.
.. versionadded:: 0.19.0

View File

@@ -41,8 +41,22 @@
// Get the name of the specified display, or NULL
//
static char* getDisplayName(CGDirectDisplayID displayID)
static char*
getDisplayName(CGDirectDisplayID displayID, NSScreen* screen)
{
// IOKit doesn't work on Apple Silicon anymore
// Luckily, 10.15 introduced -[NSScreen localizedName].
// Use it if available, and fall back to IOKit otherwise.
if (screen)
{
if ([screen respondsToSelector:@selector(localizedName)])
{
NSString* name = [screen valueForKey:@"localizedName"];
if (name) {
return _glfw_strdup([name UTF8String]);
}
}
}
io_iterator_t it;
io_service_t service;
CFDictionaryRef info;
@@ -89,7 +103,7 @@ static char* getDisplayName(CGDirectDisplayID displayID)
if (!service)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Cocoa: Failed to find service port for display, cannot get its name, using Unknown");
"Cocoa: Failed to find service port for display");
return NULL;
}
@@ -314,11 +328,9 @@ void _glfwClearDisplayLinks() {
if (_glfw.ns.displayLinks.entries[i].displayLink) {
CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink);
CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink);
_glfw.ns.displayLinks.entries[i].displayLink = nil;
_glfw.ns.displayLinks.entries[i].lastRenderFrameRequestedAt = 0;
_glfw.ns.displayLinks.entries[i].first_unserviced_render_frame_request_at = 0;
}
}
memset(_glfw.ns.displayLinks.entries, 0, sizeof(_GLFWDisplayLinkNS) * _glfw.ns.displayLinks.count);
_glfw.ns.displayLinks.count = 0;
}
@@ -340,16 +352,21 @@ _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) {
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID);
}
static void
createDisplayLink(CGDirectDisplayID displayID) {
if (_glfw.ns.displayLinks.count >= sizeof(_glfw.ns.displayLinks.entries)/sizeof(_glfw.ns.displayLinks.entries[0]) - 1) return;
_GLFWDisplayLinkNS*
_glfw_create_display_link(CGDirectDisplayID displayID) {
if (_glfw.ns.displayLinks.count >= arraysz(_glfw.ns.displayLinks.entries) - 1) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many monitors cannot create display link");
return NULL;
}
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return;
// already created in this run
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return _glfw.ns.displayLinks.entries + i;
}
_GLFWDisplayLinkNS *entry = &_glfw.ns.displayLinks.entries[_glfw.ns.displayLinks.count++];
memset(entry, 0, sizeof(_GLFWDisplayLinkNS));
entry->displayID = displayID;
_glfw_create_cv_display_link(entry);
return entry;
}
// Poll for changes in the set of connected monitors
@@ -357,11 +374,13 @@ createDisplayLink(CGDirectDisplayID displayID) {
void _glfwPollMonitorsNS(void)
{
uint32_t displayCount;
CGGetOnlineDisplayList(0, NULL, &displayCount);
CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID));
CGGetOnlineDisplayList(displayCount, displays, &displayCount);
_glfwClearDisplayLinks();
if (_glfw.hints.init.debugRendering) {
fprintf(stderr, "Polling for monitors: %u found\n", displayCount);
}
for (int i = 0; i < _glfw.monitorCount; i++)
_glfw.monitors[i]->ns.screen = nil;
@@ -378,32 +397,57 @@ void _glfwPollMonitorsNS(void)
for (uint32_t i = 0; i < displayCount; i++)
{
if (CGDisplayIsAsleep(displays[i]))
if (CGDisplayIsAsleep(displays[i])) {
if (_glfw.hints.init.debugRendering) fprintf(stderr, "Ignoring sleeping display: %u", displays[i]);
continue;
}
const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
NSScreen* screen = nil;
for (screen in [NSScreen screens])
{
NSNumber* screenNumber = [screen deviceDescription][@"NSScreenNumber"];
// HACK: Compare unit numbers instead of display IDs to work around
// display replacement on machines with automatic graphics
// switching
if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber)
break;
}
// HACK: Compare unit numbers instead of display IDs to work around
// display replacement on machines with automatic graphics
// switching
const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
for (uint32_t j = 0; j < disconnectedCount; j++)
uint32_t j;
for (j = 0; j < disconnectedCount; j++)
{
if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
{
disconnected[j]->ns.displayID = displays[i];
disconnected[j]->ns.screen = screen;
_glfw_create_display_link(displays[i]);
disconnected[j] = NULL;
break;
}
}
if (j < disconnectedCount)
continue;
const CGSize size = CGDisplayScreenSize(displays[i]);
char* name = getDisplayName(displays[i]);
if (!name)
name = _glfw_strdup("Unknown");
char* name = getDisplayName(displays[i], screen);
if (!name) {
_glfwInputError(GLFW_PLATFORM_ERROR,
"Failed to get name for display, using generic name");
name = _glfw_strdup("Display with no name");
}
_GLFWmonitor* monitor = _glfwAllocMonitor(name, (int)size.width, (int)size.height);
monitor->ns.displayID = displays[i];
monitor->ns.unitNumber = unitNumber;
createDisplayLink(monitor->ns.displayID);
monitor->ns.screen = screen;
_glfw_create_display_link(monitor->ns.displayID);
free(name);
@@ -554,15 +598,16 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
const GLFWvidmode mode =
vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
CFIndex j;
for (CFIndex j = 0; j < *count; j++)
for (j = 0; j < *count; j++)
{
if (_glfwCompareVideoModes(result + j, &mode) == 0)
break;
}
// Skip duplicate modes
if (i < *count)
if (j < *count)
continue;
(*count)++;

View File

@@ -250,3 +250,4 @@ void _glfwDispatchRenderFrame(CGDirectDisplayID);
void _glfwShutdownCVDisplayLink(unsigned long long, void*);
void _glfwCocoaPostEmptyEvent(void);
void _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry);
_GLFWDisplayLinkNS* _glfw_create_display_link(CGDirectDisplayID);

View File

@@ -337,9 +337,12 @@ requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL);
}
monotonic_t now = glfwGetTime();
bool found_display_link = false;
_GLFWDisplayLinkNS *dl = NULL;
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
dl = &_glfw.ns.displayLinks.entries[i];
if (dl->displayID == displayID) {
found_display_link = true;
dl->lastRenderFrameRequestedAt = now;
if (!dl->first_unserviced_render_frame_request_at) dl->first_unserviced_render_frame_request_at = now;
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
@@ -359,6 +362,14 @@ requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
dl->first_unserviced_render_frame_request_at = 0;
}
}
if (!found_display_link) {
dl = _glfw_create_display_link(displayID);
if (dl) {
dl->lastRenderFrameRequestedAt = now;
dl->first_unserviced_render_frame_request_at = now;
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
}
}
}
void

View File

@@ -222,6 +222,7 @@ def generate_wrappers(glfw_header: str) -> None:
unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, \
const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *data)
void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)
int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
'''.splitlines():
if line:
functions.append(Function(line.strip(), check_fail=False))

1
glfw/wl_init.c vendored
View File

@@ -29,7 +29,6 @@
#define _GNU_SOURCE
#include "internal.h"
#include "backend_utils.h"
#include "wl_client_side_decorations.h"
#include "linux_desktop_settings.h"
#include "../kitty/monotonic.h"

42
glfw/wl_window.c vendored
View File

@@ -254,15 +254,39 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height
_glfwInputWindowDamage(window);
}
static void
inform_compositor_of_window_geometry(_GLFWwindow *window, const char *event) {
#define geometry window->wl.decorations.geometry
debug("Setting window geometry in %s event: x=%d y=%d %dx%d\n", event, geometry.x, geometry.y, geometry.width, geometry.height);
xdg_surface_set_window_geometry(window->wl.xdg.surface, geometry.x, geometry.y, geometry.width, geometry.height);
#undef geometry
}
static void xdgDecorationHandleConfigure(void* data,
static void
xdgDecorationHandleConfigure(void* data,
struct zxdg_toplevel_decoration_v1* decoration UNUSED,
uint32_t mode)
{
_GLFWwindow* window = data;
window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
bool has_server_side_decorations = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
debug("XDG decoration configure event received: has_server_side_decorations: %d\n", has_server_side_decorations);
if (has_server_side_decorations == window->wl.decorations.serverSide) return;
window->wl.decorations.serverSide = has_server_side_decorations;
int width = window->wl.width, height = window->wl.height;
if (window->wl.decorations.serverSide) {
free_csd_surfaces(window);
height += window->wl.decorations.metrics.visible_titlebar_height;
} else {
ensure_csd_resources(window);
}
set_csd_window_geometry(window, &width, &height);
dispatchChangesAfterConfigure(window, width, height);
ensure_csd_resources(window);
wl_surface_commit(window->wl.surface);
debug("final window content size: %dx%d\n", window->wl.width, window->wl.height);
inform_compositor_of_window_geometry(window, "configure-decorations");
}
static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = {
@@ -396,14 +420,6 @@ _glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {
return !already_fullscreen;
}
static void
inform_compositor_of_window_geometry(_GLFWwindow *window, const char *event) {
#define geometry window->wl.decorations.geometry
debug("Setting window geometry in %s event: x=%d y=%d %dx%d\n", event, geometry.x, geometry.y, geometry.width, geometry.height);
xdg_surface_set_window_geometry(window->wl.xdg.surface, geometry.x, geometry.y, geometry.width, geometry.height);
#undef geometry
}
static void
xdgToplevelHandleConfigure(void* data,
struct xdg_toplevel* toplevel UNUSED,
@@ -488,7 +504,8 @@ static const struct xdg_surface_listener xdgSurfaceListener = {
xdgSurfaceHandleConfigure
};
static void setXdgDecorations(_GLFWwindow* window)
static void
setXdgDecorations(_GLFWwindow* window)
{
if (_glfw.wl.decorationManager)
{
@@ -510,7 +527,8 @@ static void setXdgDecorations(_GLFWwindow* window)
}
}
static bool createXdgSurface(_GLFWwindow* window)
static bool
createXdgSurface(_GLFWwindow* window)
{
window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,
window->wl.surface);

7
glfw/x11_window.c vendored
View File

@@ -3122,3 +3122,10 @@ GLFWAPI unsigned long long glfwDBusUserNotify(const char *app_name, const char*
GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {
glfw_dbus_set_user_notification_activated_handler(handler);
}
GLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
{
_GLFW_REQUIRE_INIT_OR_RETURN(0);
_GLFWwindow* window = (_GLFWwindow*) handle;
return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc);
}

View File

@@ -1 +1 @@
See https://sw.kovidgoyal.net/kitty/kittens/diff.html
See https://sw.kovidgoyal.net/kitty/kittens/diff/

View File

@@ -16,6 +16,7 @@ from typing import (
from kitty.cli import parse_args
from kitty.cli_stub import HintsCLIOptions
from kitty.constants import website_url
from kitty.fast_data_types import set_clipboard_string
from kitty.key_encoding import KeyEvent
from kitty.typing import BossType, KittyCommonOpts
@@ -640,7 +641,7 @@ The foreground color for text pointed to by the hints
--customize-processing
Name of a python file in the kitty config directory which will be imported to provide
custom implementations for pattern finding and performing actions
on selected matches. See https://sw.kovidgoyal.net/kitty/kittens/hints.html
on selected matches. See {hints_url}
for details. You can also specify absolute paths to load the script from elsewhere.
@@ -649,7 +650,8 @@ The window title for the hints window, default title is selected based on
the type of text being hinted.
'''.format(
default_regex=DEFAULT_REGEX,
line='{{line}}', path='{{path}}'
line='{{line}}', path='{{path}}',
hints_url=website_url('kittens/hints'),
).format
help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
usage = ''

View File

@@ -11,14 +11,15 @@ from contextlib import suppress
from enum import IntEnum
from itertools import count
from typing import (
Any, Callable, DefaultDict, Deque, Dict, Iterator, List, Optional,
Sequence, Tuple, Union
Any, Callable, ClassVar, DefaultDict, Deque, Dict, Generic, Iterator, List,
Optional, Sequence, Tuple, Type, TypeVar, Union, cast
)
from kitty.conf.utils import positive_float, positive_int
from kitty.fast_data_types import create_canvas
from kitty.typing import (
CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t, HandlerType
GRT_C, CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t,
HandlerType
)
from kitty.utils import ScreenSize, find_exe, fit_image
@@ -298,49 +299,75 @@ def can_display_images() -> bool:
ImageKey = Tuple[str, int, int]
SentImageKey = Tuple[int, int, int]
T = TypeVar('T')
class Alias(Generic[T]):
currently_processing: ClassVar[str] = ''
def __init__(self, defval: T) -> None:
self.name = ''
self.defval = defval
def __get__(self, instance: Optional['GraphicsCommand'], cls: Optional[Type['GraphicsCommand']] = None) -> T:
if instance is None:
return self.defval
return cast(T, instance._actual_values.get(self.name, self.defval))
def __set__(self, instance: 'GraphicsCommand', val: T) -> None:
if val == self.defval:
instance._actual_values.pop(self.name, None)
else:
instance._actual_values[self.name] = val
def __set_name__(self, owner: Type['GraphicsCommand'], name: str) -> None:
if len(name) == 1:
Alias.currently_processing = name
self.name = Alias.currently_processing
class GraphicsCommand:
a: GRT_a = 't' # action
q: int = 0 # suppress responses
f: GRT_f = 32 # image data format
t: GRT_t = 'd' # transmission medium
s: int = 0 # sent image width
v: int = 0 # sent image height
S: int = 0 # size of data to read from file
O: int = 0 # offset of data to read from file
i: int = 0 # image id
I: int = 0 # image number
p: int = 0 # placement id
o: Optional[GRT_o] = None # type of compression
m: GRT_m = 0 # 0 or 1 whether there is more chunked data
x: int = 0 # left edge of image area to display
y: int = 0 # top edge of image area to display
w: int = 0 # image width to display
h: int = 0 # image height to display
X: int = 0 # X-offset within cell
Y: int = 0 # Y-offset within cell
c: int = 0 # number of cols to display image over
r: int = 0 # number of rows to display image over
z: int = 0 # z-index
d: GRT_d = 'a' # what to delete
a = action = Alias(cast(GRT_a, 't'))
q = quiet = Alias(0)
f = format = Alias(32)
t = transmission_type = Alias(cast(GRT_t, 'd'))
s = data_width = animation_state = Alias(0)
v = data_height = loop_count = Alias(0)
S = data_size = Alias(0)
O = data_offset = Alias(0) # noqa
i = image_id = Alias(0)
I = image_number = Alias(0) # noqa
p = placement_id = Alias(0)
o = compression = Alias(cast(Optional[GRT_o], None))
m = more = Alias(cast(GRT_m, 0))
x = left_edge = Alias(0)
y = top_edge = Alias(0)
w = width = Alias(0)
h = height = Alias(0)
X = cell_x_offset = blend_mode = Alias(0)
Y = cell_y_offset = bgcolor = Alias(0)
c = columns = other_frame_number = dest_frame = Alias(0)
r = rows = frame_number = source_frame = Alias(0)
z = z_index = gap = Alias(0)
C = cursor_movement = compose_mode = Alias(cast(GRT_C, 0))
d = delete_action = Alias(cast(GRT_d, 'a'))
def __init__(self) -> None:
self._actual_values: Dict[str, Any] = {}
def __repr__(self) -> str:
return self.serialize().decode('ascii').replace('\033', '^]')
def clone(self) -> 'GraphicsCommand':
ans = GraphicsCommand()
for k in GraphicsCommand.__annotations__:
setattr(ans, k, getattr(self, k))
ans._actual_values = self._actual_values.copy()
return ans
def serialize(self, payload: Union[bytes, str] = b'') -> bytes:
items = []
for k in GraphicsCommand.__annotations__:
val: Union[str, None, int] = getattr(self, k)
defval: Union[str, None, int] = getattr(GraphicsCommand, k)
if val != defval and val is not None:
items.append('{}={}'.format(k, val))
for k, val in self._actual_values.items():
items.append('{}={}'.format(k, val))
ans: List[bytes] = []
w = ans.append
@@ -355,9 +382,27 @@ class GraphicsCommand:
return b''.join(ans)
def clear(self) -> None:
for k in GraphicsCommand.__annotations__:
defval: Union[str, None, int] = getattr(GraphicsCommand, k)
setattr(self, k, defval)
self._actual_values = {}
def iter_transmission_chunks(self, data: Optional[bytes] = None, level: int = -1, compression_threshold: int = 1024) -> Iterator[bytes]:
if data is None:
yield self.serialize()
return
gc = self.clone()
gc.S = len(data)
if level and len(data) >= compression_threshold:
import zlib
compressed = zlib.compress(data, level)
if len(compressed) < len(data):
gc.o = 'z'
data = compressed
gc.S = len(data)
data = standard_b64encode(data)
while data:
chunk, data = data[:4096], data[4096:]
gc.m = 1 if data else 0
yield gc.serialize(chunk)
gc.clear()
class Placement:

View File

@@ -24,7 +24,7 @@ from .conf.utils import BadLine, KeyAction, to_cmdline
from .config import common_opts_as_dict, prepare_config_file_for_editing
from .constants import (
appname, config_dir, is_macos, is_wayland, kitty_exe,
supports_primary_selection
supports_primary_selection, website_url
)
from .fast_data_types import (
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
@@ -1141,7 +1141,7 @@ class Boss:
self._run_kitten('ask', [
'--name=create-marker', '--message',
_('Create marker, for example:\ntext 1 ERROR\nSee https://sw.kovidgoyal.net/kitty/marks.html\n')
_('Create marker, for example:\ntext 1 ERROR\nSee {}\n').format(website_url('marks'))
],
custom_callback=done, action_on_removal=done2)
@@ -1691,14 +1691,17 @@ class Boss:
target_tab = tm.new_tab(empty_tab=True)
else:
target_os_window_id = target_os_window_id or current_os_window()
if target_tab_id == 'new':
if isinstance(target_tab_id, str):
if not isinstance(target_os_window_id, int):
q = self.active_tab_manager
assert q is not None
tm = q
else:
tm = self.os_window_map[target_os_window_id]
target_tab = tm.new_tab(empty_tab=True)
if target_tab_id == 'new':
target_tab = tm.new_tab(empty_tab=True)
else:
target_tab = tm.tab_at_location(target_tab_id) or tm.new_tab(empty_tab=True)
else:
for tab in self.all_tabs:
if tab.id == target_tab_id:
@@ -1766,8 +1769,9 @@ class Boss:
'''
if not args or args[0] == 'new':
return self._move_window_to(target_os_window_id='new')
if args[0] == 'new-tab':
return self._move_window_to(target_tab_id='new')
if args[0] in ('new-tab', 'tab-prev', 'tab-left', 'tab-right'):
where = 'new' if args[0] == 'new-tab' else args[0][4:]
return self._move_window_to(target_tab_id=where)
title = 'Choose a tab to move the window to'
lines = [title, '']
fmt = ': {1}'

View File

@@ -12,7 +12,7 @@ from typing import (
from .cli_stub import CLIOptions
from .conf.utils import resolve_config
from .constants import appname, defconf, is_macos, str_version
from .constants import appname, defconf, is_macos, str_version, website_url
from .options.types import Options as KittyOpts
from .typing import BadLineType, TypedDict
@@ -57,42 +57,60 @@ def surround(x: str, start: int, end: int) -> str:
return x
role_map: Dict[str, Callable[[str], str]] = {}
def role(func: Callable[[str], str]) -> Callable[[str], str]:
role_map[func.__name__] = func
return func
@role
def emph(x: str) -> str:
return surround(x, 91, 39)
@role
def cyan(x: str) -> str:
return surround(x, 96, 39)
@role
def green(x: str) -> str:
return surround(x, 32, 39)
@role
def blue(x: str) -> str:
return surround(x, 34, 39)
@role
def yellow(x: str) -> str:
return surround(x, 93, 39)
@role
def italic(x: str) -> str:
return surround(x, 3, 23)
@role
def bold(x: str) -> str:
return surround(x, 1, 22)
@role
def title(x: str) -> str:
return blue(bold(x))
@role
def opt(text: str) -> str:
return text
@role
def option(x: str) -> str:
idx = x.rfind('--')
if idx < 0:
@@ -103,24 +121,32 @@ def option(x: str) -> str:
return ' '.join(parts)
@role
def code(x: str) -> str:
return x
@role
def kbd(x: str) -> str:
return x
@role
def env(x: str) -> str:
return italic(x)
role_map['envvar'] = role_map['env']
@role
def file(x: str) -> str:
return italic(x)
@role
def doc(x: str) -> str:
return f'https://sw.kovidgoyal.net/kitty/{x}.html'
return website_url(x)
OptionSpecSeq = List[Union[str, OptionDict]]
@@ -197,11 +223,13 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option
def prettify(text: str) -> str:
role_map = globals()
def identity(x: str) -> str:
return x
def sub(m: Match) -> str:
role, text = m.group(1, 2)
return str(role_map[role](text))
return role_map.get(role, identity)(text)
text = re.sub(r':([a-z]+):`([^`]+)`', sub, text)
return text
@@ -265,7 +293,8 @@ Run the :italic:`{appname}` terminal emulator. You can also specify the :italic:
to run inside :italic:`{appname}` as normal arguments following the :italic:`options`.
For example: {appname} sh -c "echo hello, world. Press ENTER to quit; read"
For comprehensive documentation for kitty, please see: https://sw.kovidgoyal.net/kitty/''').format(appname=appname)
For comprehensive documentation for kitty, please see: {url}''').format(
appname=appname, url=website_url())
class PrintHelpForSeq:

View File

@@ -182,6 +182,8 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]:
a(' config_overrides: typing.Tuple[str, ...] = ()')
a('')
a(' def __init__(self, options_dict: typing.Optional[typing.Dict[str, typing.Any]] = None) -> None:')
if defn.has_color_table:
a(' self.color_table = array(self.color_table.typecode, self.color_table)')
a(' if options_dict is not None:')
a(' for key in option_names:')
a(' setattr(self, key, options_dict[key])')

View File

@@ -12,6 +12,7 @@ from typing import (
)
import kitty.conf.utils as generic_parsers
from kitty.constants import website_url
if typing.TYPE_CHECKING:
Only = typing.Literal['macos', 'linux', '']
@@ -41,15 +42,15 @@ def expand_opt_references(conf_name: str, text: str) -> str:
def remove_markup(text: str) -> str:
base = 'https://sw.kovidgoyal.net/kitty'
ref_map = {
'layouts': f'{website_url("overview")}#layouts',
'sessions': f'{website_url("overview")}#layouts',
'functional': f'{website_url("keyboard-protocol")}#functional-key-definitions',
}
def sub(m: Match) -> str:
if m.group(1) == 'ref':
return {
'layouts': f'{base}/overview/#layouts',
'sessions': f'{base}/overview/#sessions',
'functional': f'{base}/keyboard-protocol/#functional-key-definitions',
}[m.group(2)]
return ref_map[m.group(2)]
return str(m.group(2))
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)

View File

@@ -23,7 +23,7 @@ class Version(NamedTuple):
appname: str = 'kitty'
kitty_face = '🐱'
version: Version = Version(0, 22, 0)
version: Version = Version(0, 22, 2)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat
@@ -186,3 +186,11 @@ def read_kitty_resource(name: str) -> bytes:
except ImportError:
from importlib_resources import read_binary # type: ignore
return read_binary('kitty', name)
def website_url(doc_name: str = '') -> str:
if doc_name:
doc_name = doc_name.rstrip('/')
if doc_name:
doc_name += '/'
return f'https://sw.kovidgoyal.net/kitty/{doc_name}'

View File

@@ -7,12 +7,14 @@ from functools import partial
from pprint import pformat
from typing import Callable, Dict, Generator, Iterable, Set, Tuple
from kittens.tui.operations import colored, styled
from .cli import version
from .conf.utils import KeyAction
from .constants import is_macos, is_wayland
from kittens.tui.operations import colored
from .options.types import Options as KittyOpts, defaults
from .options.utils import MouseMap
from .rgb import Color, color_as_sharp
from .types import MouseEvent, SingleKey
from .typing import SequenceMap
@@ -113,6 +115,7 @@ def compare_opts(opts: KittyOpts, print: Callable) -> None:
]
field_len = max(map(len, changed_opts)) if changed_opts else 20
fmt = '{{:{:d}s}}'.format(field_len)
colors = []
for f in changed_opts:
val = getattr(opts, f)
if isinstance(val, dict):
@@ -123,7 +126,11 @@ def compare_opts(opts: KittyOpts, print: Callable) -> None:
else:
print(pformat(val))
else:
print(title(fmt.format(f)), str(getattr(opts, f)))
val = getattr(opts, f)
if isinstance(val, Color):
colors.append(fmt.format(f) + ' ' + color_as_sharp(val) + ' ' + styled(' ', bg=val))
else:
print(fmt.format(f), str(getattr(opts, f)))
compare_mousemaps(opts.mousemap, default_opts.mousemap, print)
final_, initial_ = opts.keymap, default_opts.keymap
@@ -133,6 +140,9 @@ def compare_opts(opts: KittyOpts, print: Callable) -> None:
final.update(final_s)
initial.update(initial_s)
compare_keymaps(final, initial, print)
if colors:
print(f'{title("Colors")}:', end='\n\t')
print('\n\t'.join(sorted(colors)))
def debug_config(opts: KittyOpts) -> str:

3
kitty/glfw-wrapper.c generated
View File

@@ -441,6 +441,9 @@ load_glfw(const char* path) {
*(void **) (&glfwDBusSetUserNotificationHandler_impl) = dlsym(handle, "glfwDBusSetUserNotificationHandler");
if (glfwDBusSetUserNotificationHandler_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwSetX11LaunchCommand_impl) = dlsym(handle, "glfwSetX11LaunchCommand");
if (glfwSetX11LaunchCommand_impl == NULL) dlerror(); // clear error indicator
return NULL;
}

4
kitty/glfw-wrapper.h generated
View File

@@ -2168,4 +2168,8 @@ typedef void (*glfwDBusSetUserNotificationHandler_func)(GLFWDBusnotificationacti
GFW_EXTERN glfwDBusSetUserNotificationHandler_func glfwDBusSetUserNotificationHandler_impl;
#define glfwDBusSetUserNotificationHandler glfwDBusSetUserNotificationHandler_impl
typedef int (*glfwSetX11LaunchCommand_func)(GLFWwindow*, char**, int);
GFW_EXTERN glfwSetX11LaunchCommand_func glfwSetX11LaunchCommand_impl;
#define glfwSetX11LaunchCommand glfwSetX11LaunchCommand_impl
const char* load_glfw(const char* path);

View File

@@ -622,11 +622,13 @@ native_window_handle(GLFWwindow *w) {
}
static PyObject*
create_os_window(PyObject UNUSED *self, PyObject *args) {
create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
int x = -1, y = -1;
char *title, *wm_class_class, *wm_class_name;
PyObject *load_programs = NULL, *get_window_size, *pre_show_callback;
if (!PyArg_ParseTuple(args, "OOsss|Oii", &get_window_size, &pre_show_callback, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
static const char* kwlist[] = {"get_window_size", "pre_show_callback", "title", "wm_class_name", "wm_class_class", "load_programs", "x", "y", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "OOsss|Oii", (char**)kwlist,
&get_window_size, &pre_show_callback, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
static bool is_first_window = true;
if (is_first_window) {
@@ -1431,7 +1433,7 @@ stop_main_loop(void) {
static PyMethodDef module_methods[] = {
METHODB(set_custom_cursor, METH_VARARGS),
METHODB(create_os_window, METH_VARARGS),
{"create_os_window", (PyCFunction)(void (*) (void))(create_os_window), METH_VARARGS | METH_KEYWORDS, NULL},
METHODB(set_default_window_icon, METH_VARARGS),
METHODB(get_clipboard_string, METH_NOARGS),
METHODB(get_content_scale_for_window, METH_NOARGS),

View File

@@ -1362,6 +1362,7 @@ handle_compose_command(GraphicsManager *self, bool *is_dirty, const GraphicsComm
dest_frame->x = 0; dest_frame->y = 0; dest_frame->width = img->width; dest_frame->height = img->height;
dest_frame->base_frame_id = 0; dest_frame->bgcolor = 0;
*is_dirty = (g->other_frame_number - 1) == img->current_frame_index;
if (*is_dirty) update_current_frame(self, img, &dest_data);
}
// }}}

View File

@@ -2410,19 +2410,23 @@ opt('shell', '.',
long_text='''
The shell program to execute. The default value of . means to use whatever shell
is set as the default shell for the current user. Note that on macOS if you
change this, you might need to add :code:`--login` to ensure that the shell
starts in interactive mode and reads its startup rc files.
change this, you might need to add :code:`--login` and :code:`--interactive`
to ensure that the shell starts in interactive mode and reads its startup rc files.
'''
)
opt('editor', '.',
long_text='''
The console editor to use when editing the kitty config file or similar tasks. A
value of . means to use the environment variables VISUAL and EDITOR in that
order. Note that this environment variable has to be set not just in your shell
startup scripts but system-wide, otherwise kitty will not see it.
'''
)
The terminal editor (such as ``vim`` or ``nano``) to use when editing the kitty
config file or similar tasks.
The default value of . means to use the environment variables :envvar:`VISUAL`
and :envvar:`EDITOR` in that order. If these variables aren't set, kitty will
run your :opt:`shell` (``$SHELL -l -i -c env``) to see if your shell config
files set :envvar:`VISUAL` or :envvar:`EDITOR`. If that doesn't work, kitty
will cycle through various known editors (``vim``, ``emacs``, etc) and take the
first one that exists on your system.
''')
opt('close_on_child_death', 'no',
option_type='to_bool', ctype='bool',

View File

@@ -606,6 +606,7 @@ class Options:
config_overrides: typing.Tuple[str, ...] = ()
def __init__(self, options_dict: typing.Optional[typing.Dict[str, typing.Any]] = None) -> None:
self.color_table = array(self.color_table.typecode, self.color_table)
if options_dict is not None:
for key in option_names:
setattr(self, key, options_dict[key])

View File

@@ -102,7 +102,7 @@ def goto_tab_parse(func: str, rest: str) -> FuncArgsType:
@func_with_args('detach_window')
def detach_window_parse(func: str, rest: str) -> FuncArgsType:
if rest not in ('new', 'new-tab', 'ask'):
if rest not in ('new', 'new-tab', 'ask', 'tab-prev', 'tab-left', 'tab-right'):
log_error('Ignoring invalid detach_window argument: {}'.format(rest))
rest = 'new'
return func, (rest,)

View File

@@ -4,6 +4,7 @@
from typing import TYPE_CHECKING, Optional
from kitty.constants import website_url
from kitty.options.utils import parse_marker_spec
from .base import (
@@ -26,8 +27,8 @@ class CreateMarker(RemoteCommand):
short_desc = 'Create a marker that highlights specified text'
desc = (
'Create a marker which can highlight text in the specified window. For example: '
'create_marker text 1 ERROR. For full details see: https://sw.kovidgoyal.net/kitty/marks.html'
)
'create_marker text 1 ERROR. For full details see: {}'
).format(website_url('marks'))
options_spec = MATCH_WINDOW_OPTION + '''\n
--self
type=bool-set

View File

@@ -5,16 +5,15 @@
import base64
import os
import sys
from typing import TYPE_CHECKING, Dict, Generator, List, Optional
from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Union
from kitty.options.utils import parse_send_text_bytes
from kitty.key_encoding import decode_key_event_as_window_system_key
from kitty.fast_data_types import KeyEvent as WindowSystemKeyEvent
from kitty.key_encoding import decode_key_event_as_window_system_key
from kitty.options.utils import parse_send_text_bytes
from .base import (
MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError,
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType,
Window
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window
)
if TYPE_CHECKING:
@@ -139,14 +138,18 @@ Do not send text to the active window, even if it is one of the matched windows.
raise MatchError(payload_get('match_tab'), 'tabs')
for tab in tabs:
windows += tuple(tab)
encoding, _, q = payload_get('data').partition(':')
pdata: str = payload_get('data')
encoding, _, q = pdata.partition(':')
if encoding == 'text':
data = q.encode('utf-8')
data: Union[bytes, WindowSystemKeyEvent] = q.encode('utf-8')
elif encoding == 'base64':
data = base64.standard_b64decode(q)
elif encoding == 'kitty-key':
data = base64.standard_b64decode(q)
data = decode_key_event_as_window_system_key(data)
bdata = base64.standard_b64decode(q)
candidate = decode_key_event_as_window_system_key(bdata.decode('ascii'))
if candidate is None:
raise ValueError(f'Could not decode window system key: {q}')
data = candidate
else:
raise TypeError(f'Invalid encoding for send-text data: {encoding}')
exclude_active = payload_get('exclude_active')

11
kitty/rgb.py generated
View File

@@ -8,9 +8,9 @@ from typing import Optional, NamedTuple
class Color(NamedTuple):
red: int
green: int
blue: int
red: int = 0
green: int = 0
blue: int = 0
def luminance(self) -> float:
return 0.299 * self.red + 0.587 * self.green + 0.114 * self.blue
@@ -25,6 +25,11 @@ class Color(NamedTuple):
def __int__(self) -> int:
return self.red << 16 | self.green << 8 | self.blue
def as_bytearray(self, alpha: Optional[int] = None) -> bytearray:
if alpha is None:
return bytearray((self.red, self.green, self.blue))
return bytearray((self.red, self.green, self.blue, alpha))
def alpha_blend_channel(top_color: int, bottom_color: int, alpha: float) -> int:
return int(alpha * top_color + (1 - alpha) * bottom_color)

View File

@@ -293,7 +293,8 @@ class Tab: # {{{
w = self.active_window
if w is not None and self.resize_window_by(
w.id, increment, is_horizontal) is not None:
ring_bell()
if get_options().enable_audio_bell:
ring_bell()
def reset_window_sizes(self) -> None:
'@ac:win:Reset window sizes undoing any dynamic resizing of windows'
@@ -625,6 +626,9 @@ class TabManager: # {{{
@active_tab_idx.setter
def active_tab_idx(self, val: int) -> None:
new_active_tab_idx = max(0, min(val, len(self.tabs) - 1))
if new_active_tab_idx == self._active_tab_idx:
return
try:
old_active_tab: Optional[Tab] = self.tabs[self._active_tab_idx]
except Exception:
@@ -632,7 +636,7 @@ class TabManager: # {{{
else:
assert old_active_tab is not None
add_active_id_to_history(self.active_tab_history, old_active_tab.id)
self._active_tab_idx = max(0, min(val, len(self.tabs) - 1))
self._active_tab_idx = new_active_tab_idx
try:
new_active_tab: Optional[Tab] = self.tabs[self._active_tab_idx]
except Exception:
@@ -716,6 +720,18 @@ class TabManager: # {{{
if len(self.tabs) > 1:
self.set_active_tab_idx((self.active_tab_idx + len(self.tabs) + delta) % len(self.tabs))
def tab_at_location(self, loc: str) -> Optional[Tab]:
if loc == 'prev':
if self.active_tab_history:
old_active_tab_id = self.active_tab_history[-1]
for idx, tab in enumerate(self.tabs):
if tab.id == old_active_tab_id:
return tab
elif loc in ('left', 'right'):
delta = -1 if loc == 'left' else 1
idx = (len(self.tabs) + self.active_tab_idx + delta) % len(self.tabs)
return self.tabs[idx]
def goto_tab(self, tab_num: int) -> None:
if tab_num >= len(self.tabs):
tab_num = max(0, len(self.tabs) - 1)

View File

@@ -11,7 +11,7 @@ AddressFamily = PopenType = Socket = StartupCtx = None
SessionTab = SessionType = LayoutType = SpecialWindowInstance = None
MarkType = RemoteCommandType = CoreTextFont = FontConfigPattern = None
KeyEventType = ImageManagerType = KittyCommonOpts = HandlerType = None
GRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = None
GRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = GRT_C = None
ScreenSize = KittensKeyActionType = MouseEvent = AbstractEventLoop = None
TermManagerType = LoopType = Debug = GraphicsCommandType = None

View File

@@ -37,11 +37,12 @@ from .window import Window as WindowType
EdgeLiteral = Literal['left', 'top', 'right', 'bottom']
MatchType = Literal['mime', 'ext', 'protocol', 'file', 'path', 'url', 'fragment_matches']
PowerlineStyle = Literal['angled', 'slanted', 'round']
GRT_a = Literal['t', 'T', 'q', 'p', 'd', 'f', 'a']
GRT_a = Literal['t', 'T', 'q', 'p', 'd', 'f', 'a', 'c', 'q']
GRT_f = Literal[24, 32, 100]
GRT_t = Literal['d', 'f', 't', 's']
GRT_o = Literal['z']
GRT_m = Literal[0, 1]
GRT_C = Literal[0, 1]
GRT_d = Literal['a', 'A', 'c', 'C', 'i', 'I', 'p', 'P', 'q', 'Q', 'x', 'X', 'y', 'Y', 'z', 'Z', 'f', 'F']

View File

@@ -10,13 +10,13 @@ from typing import Dict, NamedTuple, Optional
from urllib.request import urlopen
from .config import atomic_save
from .constants import Version, cache_dir, kitty_exe, version
from .constants import Version, cache_dir, kitty_exe, version, website_url
from .fast_data_types import add_timer, get_boss, monitor_pid
from .notify import notify
from .utils import log_error, open_url
CHANGELOG_URL = 'https://sw.kovidgoyal.net/kitty/changelog.html'
RELEASED_VERSION_URL = 'https://sw.kovidgoyal.net/kitty/current-version.txt'
CHANGELOG_URL = website_url('changelog')
RELEASED_VERSION_URL = website_url() + 'current-version.txt'
CHECK_INTERVAL = 24 * 60 * 60.
@@ -104,8 +104,8 @@ def process_current_release(raw: str) -> None:
def run_worker() -> None:
import time
import random
import time
time.sleep(random.randint(1000, 4000) / 1000)
with suppress(BrokenPipeError): # happens if parent process is killed before us
print(get_released_version())

View File

@@ -623,8 +623,12 @@ def read_shell_environment(opts: Optional[Options] = None) -> Dict[str, str]:
shell = resolved_shell(opts)
master, slave = openpty()
remove_blocking(master)
if '-l' not in shell and '--login' not in shell:
shell += ['-l']
if '-i' not in shell and '--interactive' not in shell:
shell += ['-i']
try:
p = subprocess.Popen(shell + ['-l', '-c', 'env'], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True)
p = subprocess.Popen(shell + ['-c', 'env'], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True)
except FileNotFoundError:
log_error('Could not find shell to read environment')
return ans

View File

@@ -328,7 +328,7 @@ class GitHub(Base): # {{{
'target_commitish': 'master',
'name': 'version %s' % self.version,
'body': f'Release version {self.version}.'
' For changelog, see https://sw.kovidgoyal.net/kitty/changelog.html'
' For changelog, see https://sw.kovidgoyal.net/kitty/changelog/'
' GPG key used for signing tarballs is: https://calibre-ebook.com/signatures/kovid.gpg',
'draft': False,
'prerelease': False

View File

@@ -48,7 +48,7 @@ mkShell rec {
shellHook = if stdenv.isDarwin then ''
export KITTY_NO_LTO=
'' else ''
export KITTY_EGL_LIBRARY='${stdenv.lib.getLib libGL}/lib/libEGL.so.1'
export KITTY_EGL_LIBRARY='${lib.getLib libGL}/lib/libEGL.so.1'
export KITTY_STARTUP_NOTIFICATION_LIBRARY='${libstartup_notification}/lib/libstartup-notification-1.so'
export KITTY_CANBERRA_LIBRARY='${libcanberra}/lib/libcanberra.so'
'';