Compare commits

..

48 Commits

Author SHA1 Message Date
Kovid Goyal
55b53bcac6 version 0.26.4 2022-10-17 08:18:51 +05:30
Kovid Goyal
57d044861f Merge branch 'patch-1' of https://github.com/ms-test/kitty 2022-10-16 13:28:06 +05:30
ms-test
aab149366d Check correct pair member
I haven't looked deeply, but this looked to be a simple typo unless it's checking one both times because of a type invariant. If that is the case, and the check is correct, then a comment and/or code refactoring to call attention to this would likely be a good idea.
2022-10-16 03:53:16 -04:00
Kovid Goyal
896f93822d kitty shell: Also output active tab id
Fixes #5590
2022-10-16 08:52:35 +05:30
Kovid Goyal
6890e265b6 GNOME Wayland: Fix a memory leak in gnome-shell when using client side decorations
destroy CSD buffers that were never attached explicitly, as mutter does
not send release events for these even when the pool is destroyed.
2022-10-12 17:52:47 +05:30
Kovid Goyal
48a4edc199 Merge branch 'action_cwd_doc' of https://github.com/cbosdo/kitty 2022-10-10 14:04:29 +05:30
Cédric Bosdonnat
d63be90a7f Recommend using --cwd=current in open-actions.conf
Editors like Vim are basing path-related commands on the current working
directory. Launching the action with the current CWD in hyperlink opens
makes life easier and would work with every editor.
2022-10-10 10:12:20 +02:00
Kovid Goyal
6af020e4ef Merge branch 'marksisson/simple-docs-change' of https://github.com/marksisson/kitty 2022-10-09 12:18:25 +05:30
Mark Sisson
ca24e9ca67 change linux_package build instructions to use make target 2022-10-09 00:02:38 -05:00
Kovid Goyal
fbfaefd72a Wayland: Fix an abort when changing background colors with wayland_titlebar_color set to background
Fixes #5562
2022-10-07 14:13:12 +05:30
Kovid Goyal
94b3776066 Merge branch 'master' of https://github.com/Diff-fusion/kitty 2022-10-05 09:09:23 +05:30
Felix
15e4be9051 Fix clipboard target properties and memory leak 2022-10-04 17:22:15 +02:00
Kovid Goyal
ae8f3de070 Allow centering the background image
Fixes #5525
2022-10-04 08:26:28 +05:30
Kovid Goyal
c8d18ffe26 Add a note about yet another gnome bug 2022-10-03 22:06:00 +05:30
Kovid Goyal
4d80427908 Wayland: Fix background image scaling using tiled mode on high DPI screens 2022-10-03 21:49:52 +05:30
Kovid Goyal
0e69324469 Fix resize draw strategy == static causing background image/borders to not be drawn 2022-10-03 21:40:00 +05:30
Kovid Goyal
cf22729dfa Fix background_tint not applying to window margins and padding
Fixes #3933
2022-10-03 21:39:57 +05:30
Kovid Goyal
1402c5bbfa Forgot to change check for space for next tab to use its max tab length rather than the current tabs max tab length, since they can now be different 2022-10-03 16:21:50 +05:30
Kovid Goyal
f0f0c8f4fe max_title_length -> max_tab_length 2022-10-03 16:15:24 +05:30
Kovid Goyal
96faac95db DRYer 2022-10-03 15:32:42 +05:30
Kovid Goyal
08041415d1 Document the purpose of for_layout 2022-10-03 13:26:55 +05:30
Kovid Goyal
647b18d345 Tab bar: Improve empty space management when some tabs have short titles, allocate the saved space to the active tab
Fixes #5548
2022-10-03 13:24:21 +05:30
Kovid Goyal
a116e3cadd Merge branch 'patch-1' of https://github.com/sorrycc/kitty 2022-09-30 07:46:32 +05:30
chencheng (云谦)
52c9b5f5b8 Update custom.css 2022-09-30 08:44:09 +08:00
Kovid Goyal
bbc9f588f1 Merge branch 'unicode-15' of https://github.com/page-down/kitty 2022-09-29 09:17:02 +05:30
pagedown
13a3c6b5b2 Update to Unicode 15.0 2022-09-29 10:13:21 +08:00
pagedown
fc15b20f24 Update to Nerd Fonts 2.2.2 2022-09-29 10:13:10 +08:00
Kovid Goyal
cd8f2e5cc6 another mypy update another round of breakage 2022-09-27 07:19:03 +05:30
Kovid Goyal
267798a277 Update the docs of open_url_with to indicate that open-actions.conf is read by the "default" handler 2022-09-26 20:21:44 +05:30
Kovid Goyal
921ac12e90 Clear both actions caches when config file is reloaded 2022-09-26 20:15:08 +05:30
Kovid Goyal
901e00cab1 Cleanup previous PR
1) Dont use deprecated code
2) Always set the dock icon on startup as the dock icon doesnt change
   till the dock is restarted
3) Update the app icon automatically if the mtime on the custom icon in
   the config dir is newer than the mtime of the sentinel file apple
   puts inside the application bundle to indicate it has a custom icon
2022-09-25 18:11:17 +05:30
Kovid Goyal
d0769f3979 ... 2022-09-25 17:36:32 +05:30
Kovid Goyal
7e4dd8cf1e Merge branch 'macos-set-icon' of https://github.com/page-down/kitty 2022-09-25 14:40:36 +05:30
Kovid Goyal
5ea0519f62 Pass through python used for type check to the test code from before the env is sanitized 2022-09-25 14:36:12 +05:30
pagedown
a1029418f8 macOS: Allow to set custom app icon automatically 2022-09-25 15:25:43 +08:00
Kovid Goyal
98eacb2067 panel kitten: Do not depend on xprop 2022-09-24 15:44:54 +05:30
Kovid Goyal
2cef9abbd3 Get rid of one call to xprop in the panel kitten 2022-09-24 15:17:11 +05:30
Kovid Goyal
0fa158c809 Merge branch 'docs' of https://github.com/page-down/kitty 2022-09-24 14:38:42 +05:30
pagedown
c54a6b398b Add some missing no_response for rc commands 2022-09-24 12:43:13 +08:00
pagedown
27ea367123 Docs: Remove the extra backslashes from the install commands 2022-09-24 12:39:05 +08:00
Kovid Goyal
42178a4570 X11: Fix a regression in the previous release that caused pasting from GTK based applications to have extra newlines
Fixes #5528

And let me just re-iterate, GNOME/GTK is developed by morons.
2022-09-24 08:56:50 +05:30
Kovid Goyal
e98ecc1c4f Another auto-indent fix 2022-09-24 08:34:07 +05:30
Kovid Goyal
0d64246209 fix vim autoindent 2022-09-24 08:31:14 +05:30
Kovid Goyal
a0495219ab ... 2022-09-24 08:30:40 +05:30
Kovid Goyal
9d98aa3159 Merge branch 'patch-2' of https://github.com/sashashura/kitty 2022-09-24 07:29:42 +05:30
Alex
5c82f4beca build: harden ci.yml permissions
Signed-off-by: Alex <aleksandrosansan@gmail.com>
2022-09-23 22:14:57 +02:00
Alex
13539bd8c6 build: harden codeql-analysis.yml permissions
Signed-off-by: Alex <aleksandrosansan@gmail.com>
2022-09-23 22:14:16 +02:00
Kovid Goyal
26b8ab9adf Use a regex for bracketed paste sanitization 2022-09-23 22:18:03 +05:30
55 changed files with 33934 additions and 32020 deletions

View File

@@ -6,6 +6,9 @@ env:
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
linux:
name: Linux (python=${{ matrix.pyver }} cc=${{ matrix.cc }} sanitize=${{ matrix.sanitize }})

View File

@@ -9,9 +9,16 @@ on:
schedule:
- cron: '0 22 * * 5'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
CodeQL-Build:
permissions:
contents: read # to fetch code (actions/checkout)
security-events: write # to upload SARIF results (github/codeql-action/analyze)
runs-on: ubuntu-latest
steps:

View File

@@ -10,7 +10,7 @@
}
.sidebar-logo {
max-height: 128px;
height: 128px;
}
.major-features li {

View File

@@ -85,7 +85,7 @@ Customizing the installation
.. code-block:: sh
_kitty_install_cmd \\
_kitty_install_cmd \
installer=nightly
If you want to install it in parallel to the released kitty specify a
@@ -93,14 +93,14 @@ Customizing the installation
.. code-block:: sh
_kitty_install_cmd \\
_kitty_install_cmd \
installer=nightly dest=/some/other/location
* You can specify a different install location, with ``dest``:
.. code-block:: sh
_kitty_install_cmd \\
_kitty_install_cmd \
dest=/some/other/location
* You can tell the installer not to launch |kitty| after installing it with
@@ -108,14 +108,14 @@ Customizing the installation
.. code-block:: sh
_kitty_install_cmd \\
_kitty_install_cmd \
launch=n
* You can use a previously downloaded dmg/tarball, with ``installer``:
.. code-block:: sh
_kitty_install_cmd \\
_kitty_install_cmd \
installer=/path/to/dmg or tarball

View File

@@ -160,7 +160,7 @@ While |kitty| does use Python, it is not a traditional Python package, so please
do not install it in site-packages.
Instead run::
python3 setup.py linux-package
make linux-package
This will install |kitty| into the directory :file:`linux-package`. You can run
|kitty| with :file:`linux-package/bin/kitty`. All the files needed to run kitty

View File

@@ -35,6 +35,28 @@ mouse anywhere in the current command to move the cursor there. See
Detailed list of changes
-------------------------------------
0.26.4 [2022-10-17]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- macOS: Allow changing the kitty icon by placing a custom icon in the kitty config folder (:pull:`5464`)
- Allow centering the :opt:`background_image` (:iss:`5525`)
- X11: Fix a regression in the previous release that caused pasting from GTK based applications to have extra newlines (:iss:`5528`)
- Tab bar: Improve empty space management when some tabs have short titles, allocate the saved space to the active tab (:iss:`5548`)
- Fix :opt:`background_tint` not applying to window margins and padding (:iss:`3933`)
- Wayland: Fix background image scaling using tiled mode on high DPI screens
- Wayland: Fix an abort when changing background colors with :opt:`wayland_titlebar_color` set to ``background`` (:iss:`5562`)
- Update to Unicode 15.0 (:pull:`5542`)
- GNOME Wayland: Fix a memory leak in gnome-shell when using client side decorations
0.26.3 [2022-09-22]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -205,13 +205,15 @@ fonts to be freely resizable, so it does not support bitmapped fonts.
.. note::
If you are trying to use a font patched with `Nerd Fonts
<https://nerdfonts.com/>`__ symbols, don't do that as patching destroys
fonts. There is no need, simply install the standalone ``Symbols Nerd Font``
fonts. There is no need, simply install the standalone ``Symbols Nerd Font Mono``
(the file :file:`NerdFontsSymbolsOnly.zip` from the `Nerd Fonts releases page
<https://github.com/ryanoasis/nerd-fonts/releases>`__). kitty should pick up
symbols from it automatically, and you can tell it to do so explicitly in
case it doesn't with the :opt:`symbol_map` directive::
symbol_map U+23FB-U+23FE,U+2665,U+26A1,U+2B58,U+E000-U+E00A,U+E0A0-U+E0A3,U+E0B0-U+E0C8,U+E0CA,U+E0CC-U+E0D2,U+E0D4,U+E200-U+E2A9,U+E300-U+E3E3,U+E5FA-U+E62F,U+E700-U+E7C5,U+F000-U+F2E0,U+F300-U+F31C,U+F400-U+F4A9,U+F500-U+F8FF Symbols Nerd Font
# Nerd Fonts v2.2.2
symbol_map U+23FB-U+23FE,U+2665,U+26A1,U+2B58,U+E000-U+E00A,U+E0A0-U+E0A3,U+E0B0-U+E0C8,U+E0CA,U+E0CC-U+E0D2,U+E0D4,U+E200-U+E2A9,U+E300-U+E3E3,U+E5FA-U+E634,U+E700-U+E7C5,U+EA60-U+EBEB,U+F000-U+F2E0,U+F300-U+F32F,U+F400-U+F4A9,U+F500-U+F8FF Symbols Nerd Font Mono
Those Unicode symbols beyond the ``E000-F8FF`` Unicode private use area are
not included.
@@ -287,13 +289,36 @@ homepage:
:target: https://github.com/samholmes/whiskers
:width: 256
On macOS you can change the icon by following the steps:
On macOS you can put :file:`kitty.app.icns` or :file:`kitty.app.png` in the
:ref:`kitty configuration directory <confloc>`, and this icon will be applied
automatically at startup. Unfortunately, Apple's Dock does not change its
cached icon so the custom icon will revert when kitty is quit. Run the
following to force the Dock to update its cached icons:
.. code-block:: sh
rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock
If you prefer not to keep a custom icon in the kitty config folder, you can
also set it with the following command:
.. code-block:: sh
# Set kitty.icns as the icon for currently running kitty
kitty +runpy 'from kitty.fast_data_types import cocoa_set_app_icon; import sys; cocoa_set_app_icon(*sys.argv[1:]); print("OK")' kitty.icns
# Set the icon for app bundle specified by the path
kitty +runpy 'from kitty.fast_data_types import cocoa_set_app_icon; import sys; cocoa_set_app_icon(*sys.argv[1:]); print("OK")' /path/to/icon.png /Applications/kitty.app
You can also change the icon manually by following the steps:
#. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+I`
#. Drag :file:`kitty.icns` onto the application icon in the kitty info pane
#. Delete the icon cache and restart Dock::
#. Delete the icon cache and restart Dock:
$ rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock
.. code-block:: sh
rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock
How do I map key presses in kitty to different keys in the terminal program?

View File

@@ -17,12 +17,12 @@ following contents:
# by the hyperlink_grep kitten and nothing else so far.
protocol file
fragment_matches [0-9]+
action launch --type=overlay vim +${FRAGMENT} ${FILE_PATH}
action launch --type=overlay --cwd=current vim +${FRAGMENT} ${FILE_PATH}
# Open text files without fragments in the editor
protocol file
mime text/*
action launch --type=overlay ${EDITOR} ${FILE_PATH}
action launch --type=overlay --cwd=current ${EDITOR} ${FILE_PATH}
Now, run a search with::

View File

@@ -172,6 +172,8 @@ class Arg:
while self.name.startswith('*'):
self.name = self.name[1:]
self.type = self.type + '*'
if '[' in self.name:
self.type += '[' + self.name.partition('[')[-1]
def __repr__(self) -> str:
return f'Arg({self.type}, {self.name})'
@@ -252,6 +254,8 @@ def generate_wrappers(glfw_header: str) -> None:
const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *data)
void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)
int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
void glfwSetX11WindowAsDock(int32_t x11_window_id)
void glfwSetX11WindowStrut(int32_t x11_window_id, uint32_t dimensions[12])
'''.splitlines():
if line:
functions.append(Function(line.strip(), check_fail=False))

View File

@@ -104,9 +104,10 @@ init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, unsi
static bool
window_has_buffer(_GLFWwindow *window, struct wl_buffer *q) {
#define Q(which) decs.which.buffer.a == q || decs.which.buffer.b == q
return Q(left) || Q(top) || Q(right) || Q(bottom);
#define Q(which) if (decs.which.buffer.a == q) { decs.which.buffer.a_needs_to_be_destroyed = false; return true; } if (decs.which.buffer.b == q) { decs.which.buffer.b_needs_to_be_destroyed = false; return true; }
Q(left); Q(top); Q(right); Q(bottom);
#undef Q
return false;
}
static void
@@ -122,10 +123,12 @@ static void
alloc_buffer_pair(uintptr_t window_id, _GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {
pair->data.a = data + *offset;
pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
pair->a_needs_to_be_destroyed = true;
wl_buffer_add_listener(pair->a, &handle_buffer_events, (void*)window_id);
*offset += pair->size_in_bytes;
pair->data.b = data + *offset;
pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
pair->b_needs_to_be_destroyed = true;
wl_buffer_add_listener(pair->b, &handle_buffer_events, (void*)window_id);
*offset += pair->size_in_bytes;
pair->front = pair->a; pair->back = pair->b;
@@ -331,6 +334,8 @@ free_csd_surfaces(_GLFWwindow *window) {
static void
free_csd_buffers(_GLFWwindow *window) {
#define d(which) { \
if (decs.which.buffer.a_needs_to_be_destroyed && decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \
if (decs.which.buffer.b_needs_to_be_destroyed && decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \
memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \
}
d(left); d(top); d(right); d(bottom);
@@ -353,9 +358,10 @@ create_csd_surfaces(_GLFWwindow *window, _GLFWWaylandCSDEdge *s) {
}
#define damage_csd(which, xbuffer) \
wl_surface_attach(decs.which.surface, xbuffer, 0, 0); \
wl_surface_attach(decs.which.surface, (xbuffer), 0, 0); \
wl_surface_damage(decs.which.surface, 0, 0, decs.which.buffer.width, decs.which.buffer.height); \
wl_surface_commit(decs.which.surface)
wl_surface_commit(decs.which.surface); \
if (decs.which.buffer.a == (xbuffer)) { decs.which.buffer.a_needs_to_be_destroyed = false; } else { decs.which.buffer.b_needs_to_be_destroyed = false; }
bool
ensure_csd_resources(_GLFWwindow *window) {
@@ -450,8 +456,5 @@ set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color) {
decs.use_custom_titlebar_color = use_custom_color;
decs.titlebar_color = color;
}
if (window->decorated && decs.top.surface) {
update_title_bar(window);
damage_csd(top, decs.top.buffer.front);
}
change_csd_title(window);
}

1
glfw/wl_platform.h vendored
View File

@@ -104,6 +104,7 @@ typedef struct _GLFWWaylandBufferPair {
struct { uint8_t *a, *b, *front, *back; } data;
bool has_pending_update;
size_t size_in_bytes, width, height, stride;
bool a_needs_to_be_destroyed, b_needs_to_be_destroyed;
} _GLFWWaylandBufferPair;
typedef struct _GLFWWaylandCSDEdge {

4
glfw/x11_init.c vendored
View File

@@ -137,6 +137,8 @@ static void detectEWMH(void)
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE");
_glfw.x11.NET_WM_WINDOW_TYPE_NORMAL =
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL");
_glfw.x11.NET_WM_WINDOW_TYPE_DOCK =
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_DOCK");
_glfw.x11.NET_WORKAREA =
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WORKAREA");
_glfw.x11.NET_CURRENT_DESKTOP =
@@ -147,6 +149,8 @@ static void detectEWMH(void)
getAtomIfSupported(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS");
_glfw.x11.NET_REQUEST_FRAME_EXTENTS =
getAtomIfSupported(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS");
_glfw.x11.NET_WM_STRUT_PARTIAL =
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STRUT_PARTIAL");
XFree(supportedAtoms);
}

2
glfw/x11_platform.h vendored
View File

@@ -253,6 +253,7 @@ typedef struct _GLFWlibraryX11
Atom NET_WM_PING;
Atom NET_WM_WINDOW_TYPE;
Atom NET_WM_WINDOW_TYPE_NORMAL;
Atom NET_WM_WINDOW_TYPE_DOCK;
Atom NET_WM_STATE;
Atom NET_WM_STATE_ABOVE;
Atom NET_WM_STATE_FULLSCREEN;
@@ -268,6 +269,7 @@ typedef struct _GLFWlibraryX11
Atom NET_ACTIVE_WINDOW;
Atom NET_FRAME_EXTENTS;
Atom NET_REQUEST_FRAME_EXTENTS;
Atom NET_WM_STRUT_PARTIAL;
Atom MOTIF_WM_HINTS;
// Xdnd (drag and drop) atoms

27
glfw/x11_window.c vendored
View File

@@ -793,7 +793,7 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
32,
PropModeReplace,
(unsigned char*) targets,
sizeof(targets[0]) * (aa->sz + 2));
aa->sz + 2);
free(targets);
return request->property;
}
@@ -2881,7 +2881,7 @@ void _glfwPlatformSetClipboard(GLFWClipboardType t) {
}
if (aa->capacity < cd->num_mime_types + 32) {
aa->capacity = cd->num_mime_types + 32;
aa->array = malloc(sizeof(aa->array[0]) * aa->capacity);
aa->array = reallocarray(aa->array, aa->capacity, sizeof(aa->array[0]));
}
aa->sz = 0;
for (size_t i = 0; i < cd->num_mime_types; i++) {
@@ -2945,10 +2945,16 @@ _glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_typ
return;
}
size_t count = 0;
atoms[count++] = atom_for_mime(mime_type).atom;
if (strcmp(mime_type, "text/plain") == 0) {
// we need to do this because GTK/GNOME is developed by morons
// they convert text/plain to DOS line endings
// https://gitlab.gnome.org/GNOME/gtk/-/issues/2307
atoms[count++] = atom_for_mime("text/plain;charset=utf-8").atom;
atoms[count++] = _glfw.x11.UTF8_STRING;
atoms[count++] = atom_for_mime("text/plain").atom;
atoms[count++] = XA_STRING;
} else {
atoms[count++] = atom_for_mime(mime_type).atom;
}
getSelectionString(which, atoms, count, write_data, object, true);
}
@@ -3191,3 +3197,18 @@ GLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
_GLFWwindow* window = (_GLFWwindow*) handle;
return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc);
}
GLFWAPI void glfwSetX11WindowAsDock(int32_t x11_window_id) {
_GLFW_REQUIRE_INIT();
Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_DOCK;
XChangeProperty(_glfw.x11.display, x11_window_id,
_glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
PropModeReplace, (unsigned char*) &type, 1);
}
GLFWAPI void glfwSetX11WindowStrut(int32_t x11_window_id, uint32_t dimensions[12]) {
_GLFW_REQUIRE_INIT();
XChangeProperty(_glfw.x11.display, x11_window_id,
_glfw.x11.NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32,
PropModeReplace, (unsigned char*) dimensions, 12);
}

View File

@@ -1,3 +1,3 @@
# generated by gen-wcwidth.py, do not edit
url_delimiters = '\x00-\x09\x0b-\x0c\x0e-\x20\x7f-\xa0\xad\u0600-\u0605\u061c\u06dd\u070f\u0890-\u0891\u08e2\u1680\u180e\u2000-\u200f\u2028-\u202f\u205f-\u2064\u2066-\u206f\u3000\ud800-\uf8ff\ufeff\ufff9-\ufffb\U000110bd\U000110cd\U00013430-\U00013438\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f\U000f0000-\U000ffffd\U00100000-\U0010fffd' # noqa
url_delimiters = '\x00-\x09\x0b-\x0c\x0e-\x20\x7f-\xa0\xad\u0600-\u0605\u061c\u06dd\u070f\u0890-\u0891\u08e2\u1680\u180e\u2000-\u200f\u2028-\u202f\u205f-\u2064\u2066-\u206f\u3000\ud800-\uf8ff\ufeff\ufff9-\ufffb\U000110bd\U000110cd\U00013430-\U0001343f\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f\U000f0000-\U000ffffd\U00100000-\U0010fffd' # noqa

View File

@@ -2,8 +2,6 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import os
import shutil
import subprocess
import sys
from typing import Any, Callable, Dict, List, Tuple
@@ -11,6 +9,7 @@ from kitty.cli import parse_args
from kitty.cli_stub import PanelCLIOptions
from kitty.constants import appname, is_macos
from kitty.os_window_size import WindowSizeData
from kitty.fast_data_types import make_x11_window_a_dock_window
OPTIONS = r'''
--lines
@@ -65,14 +64,7 @@ def parse_panel_args(args: List[str]) -> Tuple[PanelCLIOptions, List[str]]:
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten panel', result_class=PanelCLIOptions)
def call_xprop(*cmd: str, silent: bool = False) -> None:
cmd_ = ['xprop'] + list(cmd)
try:
cp = subprocess.run(cmd_, stdout=subprocess.DEVNULL if silent else None)
except FileNotFoundError:
raise SystemExit('You must have the xprop program installed')
if cp.returncode != 0:
raise SystemExit(cp.returncode)
Strut = Tuple[int, int, int, int, int, int, int, int, int, int, int, int]
def create_strut(
@@ -80,43 +72,33 @@ def create_strut(
left: int = 0, right: int = 0, top: int = 0, bottom: int = 0, left_start_y: int = 0, left_end_y: int = 0,
right_start_y: int = 0, right_end_y: int = 0, top_start_x: int = 0, top_end_x: int = 0,
bottom_start_x: int = 0, bottom_end_x: int = 0
) -> None:
call_xprop(
'-id',
str(int(win_id)), '-format', '_NET_WM_STRUT_PARTIAL', '32cccccccccccc',
'-set', '_NET_WM_STRUT_PARTIAL',
f'{left},{right},{top},{bottom},'
f'{left_start_y},{left_end_y},{right_start_y},{right_end_y},'
f'{top_start_x},{top_end_x},{bottom_start_x},{bottom_end_x}'
)
) -> Strut:
return left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x
def create_top_strut(win_id: int, width: int, height: int) -> None:
create_strut(win_id, top=height, top_end_x=width)
def create_top_strut(win_id: int, width: int, height: int) -> Strut:
return create_strut(win_id, top=height, top_end_x=width)
def create_bottom_strut(win_id: int, width: int, height: int) -> None:
create_strut(win_id, bottom=height, bottom_end_x=width)
def create_bottom_strut(win_id: int, width: int, height: int) -> Strut:
return create_strut(win_id, bottom=height, bottom_end_x=width)
def create_left_strut(win_id: int, width: int, height: int) -> None:
create_strut(win_id, left=width, left_end_y=height)
def create_left_strut(win_id: int, width: int, height: int) -> Strut:
return create_strut(win_id, left=width, left_end_y=height)
def create_right_strut(win_id: int, width: int, height: int) -> None:
create_strut(win_id, right=width, right_end_y=height)
def create_right_strut(win_id: int, width: int, height: int) -> Strut:
return create_strut(win_id, right=width, right_end_y=height)
window_width = window_height = 0
def setup_x11_window(win_id: int) -> None:
call_xprop(
'-id', str(win_id), '-format', '_NET_WM_WINDOW_TYPE', '32a',
'-set', '_NET_WM_WINDOW_TYPE', '_NET_WM_WINDOW_TYPE_DOCK'
)
func = globals()[f'create_{args.edge}_strut']
func(win_id, window_width, window_height)
strut = func(win_id, window_width, window_height)
make_x11_window_a_dock_window(win_id, strut)
def initial_window_size_func(opts: WindowSizeData, cached_values: Dict[str, Any]) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
@@ -140,8 +122,6 @@ def main(sys_args: List[str]) -> None:
global args
if is_macos or not os.environ.get('DISPLAY'):
raise SystemExit('Currently the panel kitten is supported only on X11 desktops')
if not shutil.which('xprop'):
raise SystemExit('The xprop program is required for the panel kitten')
args, items = parse_panel_args(sys_args[1:])
if not items:
raise SystemExit('You must specify the program to run')

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
#version GLSL_VERSION
#define left -1.0f
#define top 1.0f
#define right 1.0f
#define bottom -1.0f
#define left 0
#define top 1
#define right 2
#define bottom 3
#define tex_left 0
#define tex_top 0
#define tex_right 1
@@ -14,15 +14,10 @@
uniform float tiled;
uniform vec4 sizes; // [ window_width, window_height, image_width, image_height ]
uniform vec4 positions; // [ left, top, right, bottom ]
out vec2 texcoord;
const vec2 pos_map[] = vec2[4](
vec2(left, top),
vec2(left, bottom),
vec2(right, bottom),
vec2(right, top)
);
const vec2 tex_map[] = vec2[4](
vec2(tex_left, tex_top),
vec2(tex_left, tex_bottom),
@@ -39,6 +34,12 @@ float tiling_factor(int i) {
}
void main() {
vec2 pos_map[] = vec2[4](
vec2(positions[left], positions[top]),
vec2(positions[left], positions[bottom]),
vec2(positions[right], positions[bottom]),
vec2(positions[right], positions[top])
);
vec2 tex_coords = tex_map[gl_VertexID];
texcoord = vec2(
tex_coords[x_axis] * tiling_factor(x_axis),

View File

@@ -1,8 +1,7 @@
#version GLSL_VERSION
uniform float background_opacity;
in vec3 color;
in vec4 color;
out vec4 final_color;
void main() {
final_color = vec4(color * background_opacity, background_opacity);
final_color = color;
}

View File

@@ -6,9 +6,11 @@ uniform vec3 inactive_border_color;
uniform vec3 bell_border_color;
uniform vec3 tab_bar_bg;
uniform vec3 tab_bar_margin_color;
uniform float background_opacity;
uniform float tint_opacity, tint_premult;
in vec4 rect; // left, top, right, bottom
in uint rect_color;
out vec3 color;
out vec4 color;
// indices into the rect vector
const int LEFT = 0;
@@ -35,5 +37,9 @@ void main() {
gl_Position = vec4(rect[pos.x], rect[pos.y], 0, 1);
int rc = int(rect_color);
vec3 window_bg = vec3(to_color(rect_color >> 24), to_color(rect_color >> 16), to_color(rect_color >> 8));
color = W(0, default_bg) + W(1, active_border_color) + W(2, inactive_border_color) + W(3, window_bg) + W(4, bell_border_color) + W(5, tab_bar_bg) + W(6, tab_bar_margin_color);
vec3 color3 = W(0, default_bg) + W(1, active_border_color) + W(2, inactive_border_color) + W(3, window_bg) + W(4, bell_border_color) + W(5, tab_bar_bg) + W(6, tab_bar_margin_color);
float is_default_bg = float(rc & 1);
float final_opacity = is_default_bg * tint_opacity + (1 - is_default_bg) * background_opacity;
float final_premult_opacity = is_default_bg * tint_premult + (1 - is_default_bg) * background_opacity;
color = vec4(color3 * final_premult_opacity, final_opacity);
}

View File

@@ -86,7 +86,7 @@ class Borders:
draw_minimal_borders = opts.draw_minimal_borders and max(opts.window_margin_width) < 1
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
has_background_image = os_window_has_background_image(self.os_window_id)
if not has_background_image:
if not has_background_image or opts.background_tint > 0.0:
for br in current_layout.blank_rects:
add_borders_rect(self.os_window_id, self.tab_id, *br, BorderColor.default_bg)
for tbr in tab_bar_rects:

View File

@@ -1700,7 +1700,11 @@ class Boss:
cmd = [kitty_exe(), '@']
aw = self.active_window
if aw is not None:
kw['env'] = {'KITTY_SHELL_ACTIVE_WINDOW_ID': str(aw.id)}
env = {'KITTY_SHELL_ACTIVE_WINDOW_ID': str(aw.id)}
at = self.active_tab
if at is not None:
env['KITTY_SHELL_ACTIVE_TAB_ID'] = str(at.id)
kw['env'] = env
if window_type == 'tab':
tab = self._new_tab(SpecialWindow(cmd, **kw))
if tab is not None:
@@ -2212,8 +2216,8 @@ class Boss:
if bad_lines:
self.show_bad_config_lines(bad_lines)
self.apply_new_options(opts)
from .open_actions import load_open_actions
load_open_actions.clear_cached()
from .open_actions import clear_caches
clear_caches()
def safe_delete_temp_file(self, path: str) -> None:
if is_path_in_temp_dir(path):

View File

@@ -687,16 +687,9 @@ render_os_window(OSWindow *os_window, unsigned int active_window_id, color_type
if (os_window->clear_count++ < 3) blank_os_window(os_window);
Tab *tab = os_window->tabs + os_window->active_tab;
BorderRects *br = &tab->border_rects;
bool static_live_resize_in_progress = os_window->live_resize.in_progress && OPT(resize_draw_strategy) == RESIZE_DRAW_STATIC;
float x_ratio = 1, y_ratio = 1;
if (static_live_resize_in_progress) {
x_ratio = (float) os_window->viewport_width / (float) os_window->live_resize.width;
y_ratio = (float) os_window->viewport_height / (float) os_window->live_resize.height;
}
if (!static_live_resize_in_progress) {
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);
br->is_dirty = false;
}
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);
br->is_dirty = false;
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, 0, &TD, x_ratio, y_ratio, os_window, true, false, NULL);
for (unsigned int i = 0; i < tab->num_windows; i++) {
Window *w = tab->windows + i;

View File

@@ -862,6 +862,65 @@ cocoa_set_url_handler(PyObject UNUSED *self, PyObject *args) {
} // autoreleasepool
}
static PyObject*
cocoa_set_app_icon(PyObject UNUSED *self, PyObject *args) {
@autoreleasepool {
const char *icon_path = NULL, *app_path = NULL;
if (!PyArg_ParseTuple(args, "s|z", &icon_path, &app_path)) return NULL;
if (!icon_path || icon_path[0] == '\0') {
PyErr_SetString(PyExc_TypeError, "Empty icon file path");
return NULL;
}
NSString *custom_icon_path = [NSString stringWithUTF8String:icon_path];
NSString *bundle_path = @"";
if (!app_path) {
bundle_path = [[NSBundle mainBundle] bundlePath];
if (!bundle_path || bundle_path.length == 0) bundle_path = @"/Applications/kitty.app";
} else if (app_path[0] != '\0') {
bundle_path = [NSString stringWithUTF8String:app_path];
}
if (![[NSFileManager defaultManager] fileExistsAtPath:custom_icon_path]) {
PyErr_Format(PyExc_FileNotFoundError, "Icon file not found: %s", [custom_icon_path UTF8String]);
return NULL;
}
if (![[NSFileManager defaultManager] fileExistsAtPath:bundle_path]) {
PyErr_Format(PyExc_FileNotFoundError, "Application bundle not found: %s", [bundle_path UTF8String]);
return NULL;
}
NSImage *icon_image = [[NSImage alloc] initWithContentsOfFile:custom_icon_path];
BOOL result = [[NSWorkspace sharedWorkspace] setIcon:icon_image forFile:bundle_path options:NSExcludeQuickDrawElementsIconCreationOption];
[icon_image release];
if (result) Py_RETURN_NONE;
PyErr_Format(PyExc_OSError, "Failed to set custom icon %s for %s", [custom_icon_path UTF8String], [bundle_path UTF8String]);
return NULL;
} // autoreleasepool
}
static PyObject*
cocoa_set_dock_icon(PyObject UNUSED *self, PyObject *args) {
@autoreleasepool {
const char *icon_path = NULL;
if (!PyArg_ParseTuple(args, "s", &icon_path)) return NULL;
if (!icon_path || icon_path[0] == '\0') {
PyErr_SetString(PyExc_TypeError, "Empty icon file path");
return NULL;
}
NSString *custom_icon_path = [NSString stringWithUTF8String:icon_path];
if ([[NSFileManager defaultManager] fileExistsAtPath:custom_icon_path]) {
NSImage *icon_image = [[[NSImage alloc] initWithContentsOfFile:custom_icon_path] autorelease];
[NSApplication sharedApplication].applicationIconImage = icon_image;
Py_RETURN_NONE;
}
return NULL;
} // autoreleasepool
}
static NSSound *beep_sound = nil;
static void
@@ -923,6 +982,8 @@ static PyMethodDef module_methods[] = {
{"cocoa_send_notification", (PyCFunction)cocoa_send_notification, METH_VARARGS, ""},
{"cocoa_set_notification_activated_callback", (PyCFunction)set_notification_activated_callback, METH_O, ""},
{"cocoa_set_url_handler", (PyCFunction)cocoa_set_url_handler, METH_VARARGS, ""},
{"cocoa_set_app_icon", (PyCFunction)cocoa_set_app_icon, METH_VARARGS, ""},
{"cocoa_set_dock_icon", (PyCFunction)cocoa_set_dock_icon, METH_VARARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};

View File

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

View File

@@ -65,7 +65,7 @@ typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MOD
typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL, SGR_PIXEL_PROTOCOL} MouseTrackingProtocol;
typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn;
typedef enum { TILING, SCALED, MIRRORED, CLAMPED } BackgroundImageLayout;
typedef enum { TILING, SCALED, MIRRORED, CLAMPED, CENTER_CLAMPED } BackgroundImageLayout;
typedef struct ImageAnchorPosition {
float canvas_x, canvas_y, image_x, image_y;
} ImageAnchorPosition;

26
kitty/emoji.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2022-01-05
// unicode data, built from the unicode standard on: 2022-09-29
// see gen-wcwidth.py
#pragma once
#include "data-types.h"
@@ -739,13 +739,13 @@ is_symbol(char_type code) {
return true;
case 0x1f300 ... 0x1f6d7:
return true;
case 0x1f6dd ... 0x1f6ec:
case 0x1f6dc ... 0x1f6ec:
return true;
case 0x1f6f0 ... 0x1f6fc:
return true;
case 0x1f700 ... 0x1f773:
case 0x1f700 ... 0x1f776:
return true;
case 0x1f780 ... 0x1f7d8:
case 0x1f77b ... 0x1f7d9:
return true;
case 0x1f7e0 ... 0x1f7eb:
return true;
@@ -767,23 +767,19 @@ is_symbol(char_type code) {
return true;
case 0x1fa60 ... 0x1fa6d:
return true;
case 0x1fa70 ... 0x1fa74:
case 0x1fa70 ... 0x1fa7c:
return true;
case 0x1fa78 ... 0x1fa7c:
case 0x1fa80 ... 0x1fa88:
return true;
case 0x1fa80 ... 0x1fa86:
case 0x1fa90 ... 0x1fabd:
return true;
case 0x1fa90 ... 0x1faac:
case 0x1fabf ... 0x1fac5:
return true;
case 0x1fab0 ... 0x1faba:
case 0x1face ... 0x1fadb:
return true;
case 0x1fac0 ... 0x1fac5:
case 0x1fae0 ... 0x1fae8:
return true;
case 0x1fad0 ... 0x1fad9:
return true;
case 0x1fae0 ... 0x1fae7:
return true;
case 0x1faf0 ... 0x1faf6:
case 0x1faf0 ... 0x1faf8:
return true;
case 0x1fb00 ... 0x1fb92:
return true;

View File

@@ -730,7 +730,15 @@ def cocoa_get_lang() -> Optional[str]:
pass
def cocoa_set_url_handler(url_scheme: str, bundle_id: Optional[str]) -> None:
def cocoa_set_url_handler(url_scheme: str, bundle_id: Optional[str] = None) -> None:
pass
def cocoa_set_app_icon(icon_path: str, app_path: Optional[str] = None) -> None:
pass
def cocoa_set_dock_icon(icon_path: str) -> None:
pass
@@ -1475,3 +1483,4 @@ def clearenv() -> None: ...
def set_clipboard_data_types(ct: int, mime_types: Tuple[str, ...]) -> None: ...
def get_clipboard_mime(ct: int, mime: Optional[str], callback: Callable[[bytes], None]) -> None: ...
def run_with_activation_token(func: Callable[[str], None]) -> None: ...
def make_x11_window_a_dock_window(x11_window_id: int, strut: Tuple[int, int, int, int, int, int, int, int, int, int, int, int]) -> None: ...

6
kitty/glfw-wrapper.c generated
View File

@@ -467,6 +467,12 @@ load_glfw(const char* path) {
*(void **) (&glfwSetX11LaunchCommand_impl) = dlsym(handle, "glfwSetX11LaunchCommand");
if (glfwSetX11LaunchCommand_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwSetX11WindowAsDock_impl) = dlsym(handle, "glfwSetX11WindowAsDock");
if (glfwSetX11WindowAsDock_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwSetX11WindowStrut_impl) = dlsym(handle, "glfwSetX11WindowStrut");
if (glfwSetX11WindowStrut_impl == NULL) dlerror(); // clear error indicator
return NULL;
}

8
kitty/glfw-wrapper.h generated
View File

@@ -2230,4 +2230,12 @@ typedef int (*glfwSetX11LaunchCommand_func)(GLFWwindow*, char**, int);
GFW_EXTERN glfwSetX11LaunchCommand_func glfwSetX11LaunchCommand_impl;
#define glfwSetX11LaunchCommand glfwSetX11LaunchCommand_impl
typedef void (*glfwSetX11WindowAsDock_func)(int32_t);
GFW_EXTERN glfwSetX11WindowAsDock_func glfwSetX11WindowAsDock_impl;
#define glfwSetX11WindowAsDock glfwSetX11WindowAsDock_impl
typedef void (*glfwSetX11WindowStrut_func)(int32_t, uint32_t[12]);
GFW_EXTERN glfwSetX11WindowStrut_func glfwSetX11WindowStrut_impl;
#define glfwSetX11WindowStrut glfwSetX11WindowStrut_impl
const char* load_glfw(const char* path);

View File

@@ -1686,6 +1686,22 @@ get_clipboard_mime(PyObject *self UNUSED, PyObject *args) {
if (PyErr_Occurred()) return NULL;
Py_RETURN_NONE;
}
static PyObject*
make_x11_window_a_dock_window(PyObject *self UNUSED, PyObject *args UNUSED) {
int x11_window_id;
PyObject *dims;
if (!PyArg_ParseTuple(args, "iO!", &x11_window_id, &PyTuple_Type, &dims)) return NULL;
if (PyTuple_GET_SIZE(dims) != 12 ) { PyErr_SetString(PyExc_TypeError, "dimesions must be a tuple of length 12"); return NULL; }
if (!glfwSetX11WindowAsDock) { PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwGetX11Window"); return NULL; }
uint32_t dimensions[12];
for (Py_ssize_t i = 0; i < 12; i++) dimensions[i] = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(dims, i));
if (PyErr_Occurred()) return NULL;
glfwSetX11WindowAsDock(x11_window_id);
glfwSetX11WindowStrut(x11_window_id, dimensions);
Py_RETURN_NONE;
}
// Boilerplate {{{
static PyMethodDef module_methods[] = {
@@ -1704,6 +1720,7 @@ static PyMethodDef module_methods[] = {
METHODB(x11_display, METH_NOARGS),
METHODB(get_click_interval, METH_NOARGS),
METHODB(x11_window_id, METH_O),
METHODB(make_x11_window_a_dock_window, METH_VARARGS),
METHODB(strip_csi, METH_O),
#ifndef __APPLE__
METHODB(dbus_send_notification, METH_VARARGS),

View File

@@ -566,7 +566,7 @@ class Splits(Layout):
ans['one'] = p.one
if isinstance(p.two, Pair):
ans['two'] = add_pair(p.two)
elif p.one is not None:
elif p.two is not None:
ans['two'] = p.two
return ans

View File

@@ -16,14 +16,13 @@ from .cli_stub import CLIOptions
from .conf.utils import BadLine
from .config import cached_values_for
from .constants import (
appname, beam_cursor_data_file, clear_handled_signals, config_dir,
glfw_path, is_macos, is_wayland, kitty_exe, logo_png_file,
running_in_kitty, website_url
appname, beam_cursor_data_file, clear_handled_signals, config_dir, glfw_path,
is_macos, is_wayland, kitty_exe, logo_png_file, running_in_kitty, website_url,
)
from .fast_data_types import (
GLFW_IBEAM_CURSOR, GLFW_MOD_ALT, GLFW_MOD_SHIFT, SingleKey,
create_os_window, free_font_data, glfw_init, glfw_terminate, load_png_data,
set_custom_cursor, set_default_window_icon, set_options
GLFW_IBEAM_CURSOR, GLFW_MOD_ALT, GLFW_MOD_SHIFT, SingleKey, create_os_window,
free_font_data, glfw_init, glfw_terminate, load_png_data, set_custom_cursor,
set_default_window_icon, set_options,
)
from .fonts.box_drawing import set_scale
from .fonts.render import set_font_family
@@ -33,8 +32,8 @@ from .os_window_size import initial_window_size_func
from .prewarm import PrewarmProcess, fork_prewarm_process
from .session import create_sessions, get_os_window_sizing_data
from .utils import (
cleanup_ssh_control_masters, detach, expandvars, log_error,
single_instance, startup_notification_handler, unix_socket_paths
cleanup_ssh_control_masters, detach, expandvars, log_error, single_instance,
startup_notification_handler, unix_socket_paths,
)
from .window import load_shader_programs
@@ -135,6 +134,32 @@ def get_macos_shortcut_for(
return ans
def safe_mtime(path: str) -> Optional[float]:
with suppress(OSError):
return os.path.getmtime(path)
return None
def set_macos_app_custom_icon() -> None:
for name in ('kitty.app.icns', 'kitty.app.png'):
icon_path = os.path.join(config_dir, name)
custom_icon_mtime = safe_mtime(icon_path)
if custom_icon_mtime is not None:
from .fast_data_types import cocoa_set_app_icon, cocoa_set_dock_icon
krd = getattr(sys, 'kitty_run_data')
bundle_path = os.path.dirname(os.path.dirname(krd.get('bundle_exe_dir')))
icon_sentinel = os.path.join(bundle_path, 'Icon\r')
sentinel_mtime = safe_mtime(icon_sentinel)
if sentinel_mtime is None or sentinel_mtime < custom_icon_mtime:
cocoa_set_app_icon(icon_path)
# macOS Dock does not reload icons until it is restarted, so we set
# the application icon here. This will revert when kitty quits, but
# cant be helped since there appears to be no way to get the dock
# to reload short of killing it.
cocoa_set_dock_icon(icon_path)
break
def set_x11_window_icon() -> None:
# max icon size on X11 64bits is 128x128
path, ext = os.path.splitext(logo_png_file)
@@ -167,8 +192,11 @@ def _run_app(opts: Options, args: CLIOptions, prewarm: PrewarmProcess, bad_lines
val = get_macos_shortcut_for(func_map, f'open_url {website_url()}', lookup_name='open_kitty_website')
if val is not None:
global_shortcuts['open_kitty_website'] = val
if is_macos and opts.macos_custom_beam_cursor:
set_custom_ibeam_cursor()
if opts.macos_custom_beam_cursor:
set_custom_ibeam_cursor()
set_macos_app_custom_icon()
if not is_wayland() and not is_macos: # no window icons on wayland
set_x11_window_icon()
with cached_values_for(run_app.cached_values_name) as cached_values:
@@ -208,7 +236,7 @@ class AppRunner:
free_font_data() # must free font data before glfw/freetype/fontconfig/opengl etc are finalized
if is_macos:
from kitty.fast_data_types import (
cocoa_set_notification_activated_callback
cocoa_set_notification_activated_callback,
)
cocoa_set_notification_activated_callback(None)

View File

@@ -5,27 +5,44 @@
# in kitty, when using the spawn launcher.
import os
from concurrent.futures import ProcessPoolExecutor
from multiprocessing import util # type: ignore
from multiprocessing import util
from multiprocessing import context, get_all_start_methods, get_context, spawn
from typing import Any, Callable, List, Optional, Tuple, Union
from typing import Any, Callable, Sequence, Optional, Tuple, Union, TYPE_CHECKING
from .constants import kitty_exe
orig_spawn_passfds = util.spawnv_passfds
orig_executable = spawn.get_executable()
if TYPE_CHECKING:
from pickle import PickleBuffer
from array import array
from mmap import mmap
from ctypes import _CData
from typing import SupportsInt, SupportsIndex, Protocol
def spawnv_passfds(path: str, args: List[str], passfds: List[int]) -> Any:
class SupportsTrunc(Protocol):
def __trunc__(self) -> int: ...
ArgsType = Sequence[
Union[str, Union[bytes, Union[bytearray, memoryview, array[Any], mmap, _CData, PickleBuffer]], SupportsInt, SupportsIndex, SupportsTrunc]
]
else:
ArgsType = Sequence[str]
def spawnv_passfds(path: bytes, args: ArgsType, passfds: Sequence[int]) -> int:
if '-c' in args:
idx = args.index('-c')
patched_args = [spawn.get_executable(), '+runpy'] + args[idx + 1:]
patched_args = [spawn.get_executable(), '+runpy'] + list(args)[idx + 1:]
else:
idx = args.index('--multiprocessing-fork')
prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
prog %= ', '.join(item for item in args[idx+1:])
prog %= ', '.join(str(item) for item in args[idx+1:])
patched_args = [spawn.get_executable(), '+runpy', prog]
return orig_spawn_passfds(kitty_exe(), patched_args, passfds)
return orig_spawn_passfds(os.fsencode(kitty_exe()), patched_args, passfds)
def monkey_patch_multiprocessing() -> None:

View File

@@ -221,6 +221,11 @@ def load_launch_actions() -> Tuple[OpenAction, ...]:
return tuple(parse(f))
def clear_caches() -> None:
load_open_actions.clear_cached()
load_launch_actions.clear_cached()
@run_once
def default_open_actions() -> Tuple[OpenAction, ...]:
return tuple(parse('''\

View File

@@ -402,9 +402,9 @@ opt('url_style', 'curly',
opt('open_url_with', 'default',
option_type='to_cmdline',
long_text='''
The program to open clicked URLs. The special value :code:`default` means to
use the operating system's default URL handler (:program:`open` on macOS and
:program:`xdg-open` on Linux).
The program to open clicked URLs. The special value :code:`default` with first look for any URL handlers defined via
the :doc:`open_actions` facility and if non are found, it will use the Operating System's
default URL handler (:program:`open` on macOS and :program:`xdg-open` on Linux).
'''
)
@@ -1285,10 +1285,10 @@ opt('background_image', 'none',
)
opt('background_image_layout', 'tiled',
choices=('mirror-tiled', 'scaled', 'tiled', 'clamped'), ctype='bglayout',
choices=('mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered'), ctype='bglayout',
long_text='''
Whether to tile, scale or clamp the background image. The value can be one of
:code:`tiled`, :code:`mirror-tiled`, :code:`scaled`, :code:`clamped`.
:code:`tiled`, :code:`mirror-tiled`, :code:`scaled`, :code:`clamped` or :code:`centered`.
'''
)
@@ -1310,11 +1310,10 @@ this option by reloading the config is not supported.
opt('background_tint', '0.0',
option_type='unit_float', ctype='float',
long_text='''
How much to tint the background image by the background color. The tint is
applied only under the text area, not margin/borders. This option makes it
easier to read the text. Tinting is done using the current background color for
each window. This option applies only if :opt:`background_opacity` is set and
transparent windows are supported or :opt:`background_image` is set.
How much to tint the background image by the background color. This option
makes it easier to read the text. Tinting is done using the current background
color for each window. This option applies only if :opt:`background_opacity` is
set and transparent windows are supported or :opt:`background_image` is set.
'''
)

View File

@@ -73,7 +73,7 @@ class Parser:
raise ValueError(f"The value {val} is not a valid choice for background_image_layout")
ans["background_image_layout"] = val
choices_for_background_image_layout = frozenset(('mirror-tiled', 'scaled', 'tiled', 'clamped'))
choices_for_background_image_layout = frozenset(('mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered'))
def background_image_linear(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['background_image_linear'] = to_bool(val)

View File

@@ -64,7 +64,9 @@ bglayout(PyObject *layout_name) {
case 't': return TILING;
case 'm': return MIRRORED;
case 's': return SCALED;
case 'c': return CLAMPED;
case 'c': {
return name[1] == 'l' ? CLAMPED : CENTER_CLAMPED;
}
default: break;
}
return TILING;

View File

@@ -15,7 +15,7 @@ import kitty.types
if typing.TYPE_CHECKING:
choices_for_allow_cloning = typing.Literal['yes', 'y', 'true', 'no', 'n', 'false', 'ask']
choices_for_allow_remote_control = typing.Literal['password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true']
choices_for_background_image_layout = typing.Literal['mirror-tiled', 'scaled', 'tiled', 'clamped']
choices_for_background_image_layout = typing.Literal['mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered']
choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'hand']
choices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11']
choices_for_macos_colorspace = typing.Literal['srgb', 'default', 'displayp3']

View File

@@ -17,6 +17,7 @@ class FocusTab(RemoteCommand):
'''
match/str: The tab to focus
no_response/bool: Boolean indicating whether to wait for a response
'''
short_desc = 'Focus the specified tab'
@@ -32,7 +33,7 @@ using this option means that you will not be notified of failures.
argspec = ''
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
return {'match': opts.match}
return {'match': opts.match, 'no_response': opts.no_response}
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
for tab in self.tabs_for_match_payload(boss, window, payload_get):

View File

@@ -18,6 +18,7 @@ if TYPE_CHECKING:
class FocusWindow(RemoteCommand):
'''
match/str: The window to focus
no_response/bool: Boolean indicating whether to wait for a response
'''
short_desc = 'Focus the specified window'

View File

@@ -17,6 +17,7 @@ class LastUsedLayout(RemoteCommand):
'''
match/str: Which tab to change the layout of
all/bool: Boolean to match all tabs
no_response/bool: Boolean indicating whether to wait for a response
'''
short_desc = 'Switch to the last used layout'
@@ -37,7 +38,7 @@ the command will exit with a success code.
''' + '\n\n\n' + MATCH_TAB_OPTION
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
return {'match': opts.match, 'all': opts.all}
return {'match': opts.match, 'all': opts.all, 'no_response': opts.no_response}
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
for tab in self.tabs_for_match_payload(boss, window, payload_get):

View File

@@ -184,7 +184,7 @@ typedef struct {
static CellProgramLayout cell_program_layouts[NUM_PROGRAMS];
static ssize_t blit_vertex_array;
typedef struct {
GLint image_location, tiled_location, sizes_location, opacity_location, premult_location;
GLint image_location, tiled_location, sizes_location, positions_location, opacity_location, premult_location;
} BGImageProgramLayout;
static BGImageProgramLayout bgimage_program_layout = {0};
typedef struct {
@@ -212,6 +212,7 @@ init_cell_program(void) {
bgimage_program_layout.image_location = get_uniform_location(BGIMAGE_PROGRAM, "image");
bgimage_program_layout.opacity_location = get_uniform_location(BGIMAGE_PROGRAM, "opacity");
bgimage_program_layout.sizes_location = get_uniform_location(BGIMAGE_PROGRAM, "sizes");
bgimage_program_layout.positions_location = get_uniform_location(BGIMAGE_PROGRAM, "positions");
bgimage_program_layout.tiled_location = get_uniform_location(BGIMAGE_PROGRAM, "tiled");
bgimage_program_layout.premult_location = get_uniform_location(BGIMAGE_PROGRAM, "premult");
tint_program_layout.tint_color_location = get_uniform_location(TINT_PROGRAM, "tint_color");
@@ -425,16 +426,29 @@ draw_bg(OSWindow *w) {
glUniform1i(bgimage_program_layout.image_location, BGIMAGE_UNIT);
glUniform1f(bgimage_program_layout.opacity_location, OPT(background_opacity));
glUniform4f(bgimage_program_layout.sizes_location,
(GLfloat)w->window_width, (GLfloat)w->window_height, (GLfloat)w->bgimage->width, (GLfloat)w->bgimage->height);
(GLfloat)w->viewport_width, (GLfloat)w->viewport_height, (GLfloat)w->bgimage->width, (GLfloat)w->bgimage->height);
glUniform1f(bgimage_program_layout.premult_location, w->is_semi_transparent ? 1.f : 0.f);
GLfloat tiled = 0.f;;
GLfloat left = -1.0, top = 1.0, right = 1.0, bottom = -1.0;
switch (OPT(background_image_layout)) {
case TILING: case MIRRORED: case CLAMPED:
tiled = 1.f; break;
case SCALED:
tiled = 0.f; break;
case CENTER_CLAMPED:
tiled = 1.f;
if (w->viewport_width > (int)w->bgimage->width) {
GLfloat frac = (w->viewport_width - w->bgimage->width) / (GLfloat)w->viewport_width;
left += frac; right += frac;
}
if (w->viewport_height > (int)w->bgimage->height) {
GLfloat frac = (w->viewport_height - w->bgimage->height) / (GLfloat)w->viewport_height;
top -= frac; bottom -= frac;
}
break;
}
glUniform1f(bgimage_program_layout.tiled_location, tiled);
glUniform4f(bgimage_program_layout.positions_location, left, top, right, bottom);
glActiveTexture(GL_TEXTURE0 + BGIMAGE_UNIT);
glBindTexture(GL_TEXTURE_2D, w->bgimage->texture_id);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -517,12 +531,12 @@ has_bgimage(OSWindow *w) {
static void
draw_tint(bool premult, Screen *screen, const CellRenderData *crd) {
// On GNOME+Wayland this causes ghosting while rendering. Yet another GNOME bug, does not occur under KDE, Hyprland or Sway.
bind_program(TINT_PROGRAM);
color_type window_bg = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.default_bg, screen->color_profile->configured.default_bg).rgb;
#define C(shift) ((((GLfloat)((window_bg >> shift) & 0xFF)) / 255.0f))
float alpha = OPT(background_tint);
if (premult) glUniform4f(tint_program_layout.tint_color_location, C(16) * alpha, C(8) * alpha, C(0) * alpha, alpha);
else glUniform4f(tint_program_layout.tint_color_location, C(16), C(8), C(0), alpha);
#define C(shift) ((((GLfloat)((window_bg >> shift) & 0xFF)) / 255.0f)) * premult_factor
GLfloat premult_factor = premult ? OPT(background_tint) : 1.0f;
glUniform4f(tint_program_layout.tint_color_location, C(16), C(8), C(0), OPT(background_tint));
#undef C
glUniform4f(tint_program_layout.edges_location, crd->gl.xstart, crd->gl.ystart - crd->gl.height, crd->gl.xstart + crd->gl.width, crd->gl.ystart);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -707,7 +721,7 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
} else if (OPT(background_tint) > 0) {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
BLEND_ONTO_OPAQUE;
draw_tint(false, screen, crd);
BLEND_ONTO_OPAQUE;
}
@@ -924,7 +938,7 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, float
// }}}
// Borders {{{
enum BorderUniforms { BORDER_viewport, BORDER_background_opacity, BORDER_default_bg, BORDER_active_border_color, BORDER_inactive_border_color, BORDER_bell_border_color, BORDER_tab_bar_bg, BORDER_tab_bar_margin_color, NUM_BORDER_UNIFORMS };
enum BorderUniforms { BORDER_viewport, BORDER_background_opacity, BORDER_tint_opacity, BORDER_tint_premult, BORDER_default_bg, BORDER_active_border_color, BORDER_inactive_border_color, BORDER_bell_border_color, BORDER_tab_bar_bg, BORDER_tab_bar_margin_color, NUM_BORDER_UNIFORMS };
static GLint border_uniform_locations[NUM_BORDER_UNIFORMS] = {0};
static void
@@ -932,6 +946,8 @@ init_borders_program(void) {
#define SET_LOC(which) border_uniform_locations[BORDER_##which] = get_uniform_location(BORDERS_PROGRAM, #which);
SET_LOC(viewport)
SET_LOC(background_opacity)
SET_LOC(tint_opacity)
SET_LOC(tint_premult)
SET_LOC(default_bg)
SET_LOC(active_border_color)
SET_LOC(inactive_border_color)
@@ -956,12 +972,17 @@ create_border_vao(void) {
void
draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg, OSWindow *w) {
float background_opacity = w->is_semi_transparent ? w->background_opacity: 1.0f;
float tint_opacity = background_opacity;
float tint_premult = background_opacity;
if (has_bgimage(w)) {
glEnable(GL_BLEND);
BLEND_ONTO_OPAQUE;
draw_bg(w);
BLEND_ONTO_OPAQUE;
background_opacity = 1.0f;
tint_opacity = OPT(background_tint);
tint_premult = 1.0f;
}
if (num_border_rects) {
@@ -974,7 +995,9 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
unmap_vao_buffer(vao_idx, 0);
}
#define CV3(x) (((float)((x >> 16) & 0xff))/255.f), (((float)((x >> 8) & 0xff))/255.f), (((float)(x & 0xff))/255.f)
glUniform1f(border_uniform_locations[BORDER_background_opacity], w->is_semi_transparent ? w->background_opacity: 1.0f);
glUniform1f(border_uniform_locations[BORDER_background_opacity], background_opacity);
glUniform1f(border_uniform_locations[BORDER_tint_opacity], tint_opacity);
glUniform1f(border_uniform_locations[BORDER_tint_premult], tint_premult);
glUniform3f(border_uniform_locations[BORDER_active_border_color], CV3(OPT(active_border_color)));
glUniform3f(border_uniform_locations[BORDER_inactive_border_color], CV3(OPT(inactive_border_color)));
glUniform3f(border_uniform_locations[BORDER_bell_border_color], CV3(OPT(bell_border_color)));

View File

@@ -185,7 +185,11 @@ def real_main(global_opts: RCOptions, encrypter: CommandEncrypter = NoEncryption
print('Use {} for assistance or {} to quit'.format(green('help'), green('exit')))
awid = os.environ.pop('KITTY_SHELL_ACTIVE_WINDOW_ID', None)
if awid is not None:
print(f'The ID of the previously active window is: {awid}')
atid = os.environ.pop('KITTY_SHELL_ACTIVE_TAB_ID', None)
am = f'Previously active window id: {awid}'
if atid is not None:
am += f' and tab id: {atid}'
print(am)
pre_prompt = set_window_title('The kitty shell') + set_cursor_shape('bar')
pre_prompt += f'\x1b]133;A;redraw={0 if is_libedit else 1}\x1b\\'

View File

@@ -134,15 +134,14 @@ window_for_window_id(id_type kitty_window_id) {
static void
send_bgimage_to_gpu(BackgroundImageLayout layout, BackgroundImage *bgimage) {
RepeatStrategy r;
RepeatStrategy r = REPEAT_DEFAULT;
switch (layout) {
case SCALED:
case CLAMPED:
case CLAMPED: case CENTER_CLAMPED:
r = REPEAT_CLAMP; break;
case MIRRORED:
r = REPEAT_MIRROR; break;
case TILING:
default:
r = REPEAT_DEFAULT; break;
}
bgimage->texture_id = 0;

View File

@@ -5,7 +5,7 @@ import os
from functools import lru_cache, partial, wraps
from string import Formatter as StringFormatter
from typing import (
Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union
Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union,
)
from .borders import Border, BorderColor
@@ -13,7 +13,7 @@ from .config import build_ansi_color_table
from .constants import config_dir
from .fast_data_types import (
DECAWM, Color, Region, Screen, cell_size_for_window, get_boss, get_options,
pt_to_px, set_tab_bar_render_data, viewport_for_window
pt_to_px, set_tab_bar_render_data, viewport_for_window,
)
from .rgb import alpha_blend, color_as_sgr, color_from_int, to_color
from .types import WindowGeometry, run_once
@@ -151,6 +151,10 @@ class SupSub:
class ExtraData:
prev_tab: Optional[TabBarData] = None
next_tab: Optional[TabBarData] = None
# true if the draw_tab function is called just for layout. In such cases,
# if drawing is expensive the draw_tab function should avoid drawing and
# just move the cursor to its final position, as if drawing was performed.
for_layout: bool = False
def draw_attributed_string(title: str, screen: Screen) -> None:
@@ -238,7 +242,7 @@ DrawTabFunc = Callable[[DrawData, Screen, TabBarData, int, int, int, bool, Extra
def draw_tab_with_slant(
draw_data: DrawData, screen: Screen, tab: TabBarData,
before: int, max_title_length: int, index: int, is_last: bool,
before: int, max_tab_length: int, index: int, is_last: bool,
extra_data: ExtraData
) -> int:
orig_fg = screen.cursor.fg
@@ -253,20 +257,20 @@ def draw_tab_with_slant(
screen.cursor.bg = tab_bg
screen.cursor.fg = orig_fg
max_title_length += 1
if max_title_length <= 1:
max_tab_length += 1
if max_tab_length <= 1:
screen.draw('')
elif max_title_length == 2:
elif max_tab_length == 2:
screen.draw('…|')
elif max_title_length < 6:
elif max_tab_length < 6:
draw_sep(left_sep)
screen.draw((' ' if max_title_length == 5 else '') + '' + (' ' if max_title_length >= 4 else ''))
screen.draw((' ' if max_tab_length == 5 else '') + '' + (' ' if max_tab_length >= 4 else ''))
draw_sep(right_sep)
else:
draw_sep(left_sep)
screen.draw(' ')
draw_title(draw_data, screen, tab, index, max_title_length)
extra = screen.cursor.x - before - max_title_length
draw_title(draw_data, screen, tab, index, max_tab_length)
extra = screen.cursor.x - before - max_tab_length
if extra >= 0:
screen.cursor.x -= extra + 3
screen.draw('')
@@ -281,15 +285,15 @@ def draw_tab_with_slant(
def draw_tab_with_separator(
draw_data: DrawData, screen: Screen, tab: TabBarData,
before: int, max_title_length: int, index: int, is_last: bool,
before: int, max_tab_length: int, index: int, is_last: bool,
extra_data: ExtraData
) -> int:
if draw_data.leading_spaces:
screen.draw(' ' * draw_data.leading_spaces)
draw_title(draw_data, screen, tab, index, max_title_length)
trailing_spaces = min(max_title_length - 1, draw_data.trailing_spaces)
max_title_length -= trailing_spaces
extra = screen.cursor.x - before - max_title_length
draw_title(draw_data, screen, tab, index, max_tab_length)
trailing_spaces = min(max_tab_length - 1, draw_data.trailing_spaces)
max_tab_length -= trailing_spaces
extra = screen.cursor.x - before - max_tab_length
if extra > 0:
screen.cursor.x -= extra + 1
screen.draw('')
@@ -307,7 +311,7 @@ def draw_tab_with_separator(
def draw_tab_with_fade(
draw_data: DrawData, screen: Screen, tab: TabBarData,
before: int, max_title_length: int, index: int, is_last: bool,
before: int, max_tab_length: int, index: int, is_last: bool,
extra_data: ExtraData
) -> int:
orig_bg = screen.cursor.bg
@@ -317,12 +321,12 @@ def draw_tab_with_fade(
screen.cursor.bg = bg
screen.draw(' ')
screen.cursor.bg = orig_bg
draw_title(draw_data, screen, tab, index, max(0, max_title_length - 8))
extra = screen.cursor.x - before - max_title_length
draw_title(draw_data, screen, tab, index, max(0, max_tab_length - 8))
extra = screen.cursor.x - before - max_tab_length
if extra > 0:
screen.cursor.x = before
draw_title(draw_data, screen, tab, index, max(0, max_title_length - 4))
extra = screen.cursor.x - before - max_title_length
draw_title(draw_data, screen, tab, index, max(0, max_tab_length - 4))
extra = screen.cursor.x - before - max_tab_length
if extra > 0:
screen.cursor.x -= extra + 1
screen.draw('')
@@ -346,7 +350,7 @@ powerline_symbols: Dict[PowerlineStyle, Tuple[str, str]] = {
def draw_tab_with_powerline(
draw_data: DrawData, screen: Screen, tab: TabBarData,
before: int, max_title_length: int, index: int, is_last: bool,
before: int, max_tab_length: int, index: int, is_last: bool,
extra_data: ExtraData
) -> int:
tab_bg = screen.cursor.bg
@@ -369,11 +373,11 @@ def draw_tab_with_powerline(
start_draw = 1
screen.cursor.bg = tab_bg
if min_title_length >= max_title_length:
if min_title_length >= max_tab_length:
screen.draw('')
else:
draw_title(draw_data, screen, tab, index, max_title_length)
extra = screen.cursor.x + start_draw - before - max_title_length
draw_title(draw_data, screen, tab, index, max_tab_length)
extra = screen.cursor.x + start_draw - before - max_tab_length
if extra > 0 and extra + 1 < screen.cursor.x:
screen.cursor.x -= extra + 1
screen.draw('')
@@ -416,14 +420,14 @@ def load_custom_draw_tab() -> DrawTabFunc:
@wraps(func)
def draw_tab(
draw_data: DrawData, screen: Screen, tab: TabBarData,
before: int, max_title_length: int, index: int, is_last: bool,
before: int, max_tab_length: int, index: int, is_last: bool,
extra_data: ExtraData
) -> int:
try:
return func(draw_data, screen, tab, before, max_title_length, index, is_last, extra_data)
return func(draw_data, screen, tab, before, max_tab_length, index, is_last, extra_data)
except Exception as e:
log_error(f'Custom draw tab function failed with error: {e}')
return draw_tab_with_fade(draw_data, screen, tab, before, max_title_length, index, is_last, extra_data)
return draw_tab_with_fade(draw_data, screen, tab, before, max_tab_length, index, is_last, extra_data)
return draw_tab
@@ -435,6 +439,7 @@ class TabBar:
self.num_tabs = 1
self.data_buffer_size = 0
self.blank_rects: Tuple[Border, ...] = ()
self.cell_ranges: List[Tuple[int, int]] = []
self.laid_out_once = False
self.apply_options()
@@ -571,31 +576,67 @@ class TabBar:
if not self.laid_out_once:
return
s = self.screen
s.cursor.x = 0
s.erase_in_line(2, False)
max_title_length = max(1, (self.screen.columns // max(1, len(data))) - 1)
cr = []
last_tab = data[-1] if data else None
ed = ExtraData()
for i, t in enumerate(data):
def draw_tab(i: int, tab: TabBarData, cell_ranges: List[Tuple[int, int]], max_tab_length: int) -> None:
ed.prev_tab = data[i - 1] if i > 0 else None
ed.next_tab = data[i + 1] if i + 1 < len(data) else None
s.cursor.bg = as_rgb(self.draw_data.tab_bg(t))
s.cursor.fg = as_rgb(self.draw_data.tab_fg(t))
s.cursor.bold, s.cursor.italic = self.active_font_style if t.is_active else self.inactive_font_style
before = s.cursor.x
end = self.draw_func(self.draw_data, s, t, before, max_title_length, i + 1, t is last_tab, ed)
end = self.draw_func(self.draw_data, s, t, before, max_tab_length, i + 1, t is last_tab, ed)
s.cursor.bg = s.cursor.fg = 0
cr.append((before, end))
if s.cursor.x > s.columns - max_title_length and t is not last_tab:
cell_ranges.append((before, end))
if not ed.for_layout and t is not last_tab and s.cursor.x > s.columns - max_tab_lengths[i+1]:
# Stop if there is no space for next tab
s.cursor.x = s.columns - 2
s.cursor.bg = as_rgb(color_as_int(self.draw_data.default_bg))
s.cursor.fg = as_rgb(0xff0000)
s.draw('')
raise StopIteration()
unconstrained_tab_length = max(1, s.columns - 2)
ideal_tab_lengths = [i for i in range(len(data))]
default_max_tab_length = max(1, (s.columns // max(1, len(data))) - 1)
max_tab_lengths = [default_max_tab_length for _ in range(len(data))]
active_idx = 0
extra = 0
ed.for_layout = True
for i, t in enumerate(data):
s.cursor.x = 0
draw_tab(i, t, [], unconstrained_tab_length)
ideal_tab_lengths[i] = tl = max(1, s.cursor.x)
if t.is_active:
active_idx = i
if tl < default_max_tab_length:
max_tab_lengths[i] = tl
extra += default_max_tab_length - tl
if extra > 0:
if ideal_tab_lengths[active_idx] > max_tab_lengths[active_idx]:
d = min(extra, ideal_tab_lengths[active_idx] - max_tab_lengths[active_idx])
max_tab_lengths[active_idx] += d
extra -= d
if extra > 0:
over_achievers = tuple(i for i in range(len(data)) if ideal_tab_lengths[i] > max_tab_lengths[i])
if over_achievers:
amt_per_over_achiever = extra // len(over_achievers)
if amt_per_over_achiever > 0:
for i in over_achievers:
max_tab_lengths[i] += amt_per_over_achiever
s.cursor.x = 0
s.erase_in_line(2, False)
cr: List[Tuple[int, int]] = []
ed.for_layout = False
for i, t in enumerate(data):
try:
draw_tab(i, t, cr, max_tab_lengths[i])
except StopIteration:
break
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
self.cell_ranges = cr
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
self.align()
def align_with_factor(self, factor: int = 1) -> None:

550
kitty/unicode-data.c generated

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
#include "data-types.h"
#include "state.h"
// START_KNOWN_MARKS
static const combining_type VS15 = 1362, VS16 = 1363;
static const combining_type VS15 = 1364, VS16 = 1365;
// END_KNOWN_MARKS
bool is_combining_char(char_type ch);

View File

@@ -1086,3 +1086,13 @@ def docs_url(which: str = '', local_docs_root: Optional[str] = '') -> str:
if frag:
url += '#' + frag
return url
def sanitize_for_bracketed_paste(text: bytes) -> bytes:
pat = re.compile(b'(?:(?:\033\\\x5b)|(?:\x9b))201~')
while True:
new_text = pat.sub(b'', text)
if new_text == text:
break
text = new_text
return text

128
kitty/wcwidth-std.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2022-01-05
// unicode data, built from the unicode standard on: 2022-09-29
// see gen-wcwidth.py
#pragma once
#include "data-types.h"
@@ -14,7 +14,7 @@ wcwidth_std(int32_t code) {
return 2;
// }}}
// Marks (6350 codepoints) {{{
// Marks (6399 codepoints) {{{
case 0x0:
return 0;
case 0xad:
@@ -185,6 +185,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0xce2 ... 0xce3:
return 0;
case 0xcf3:
return 0;
case 0xd00 ... 0xd03:
return 0;
case 0xd3b ... 0xd3c:
@@ -221,7 +223,7 @@ wcwidth_std(int32_t code) {
return 0;
case 0xeb4 ... 0xebc:
return 0;
case 0xec8 ... 0xecd:
case 0xec8 ... 0xece:
return 0;
case 0xf18 ... 0xf19:
return 0;
@@ -439,6 +441,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0x10eab ... 0x10eac:
return 0;
case 0x10efd ... 0x10eff:
return 0;
case 0x10f46 ... 0x10f50:
return 0;
case 0x10f82 ... 0x10f85:
@@ -481,6 +485,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0x1123e:
return 0;
case 0x11241:
return 0;
case 0x112df ... 0x112ea:
return 0;
case 0x11300 ... 0x11303:
@@ -575,7 +581,17 @@ wcwidth_std(int32_t code) {
return 0;
case 0x11ef3 ... 0x11ef6:
return 0;
case 0x13430 ... 0x13438:
case 0x11f00 ... 0x11f01:
return 0;
case 0x11f03:
return 0;
case 0x11f34 ... 0x11f3a:
return 0;
case 0x11f3e ... 0x11f42:
return 0;
case 0x13430 ... 0x13440:
return 0;
case 0x13447 ... 0x13455:
return 0;
case 0x16af0 ... 0x16af4:
return 0;
@@ -631,12 +647,16 @@ wcwidth_std(int32_t code) {
return 0;
case 0x1e026 ... 0x1e02a:
return 0;
case 0x1e08f:
return 0;
case 0x1e130 ... 0x1e136:
return 0;
case 0x1e2ae:
return 0;
case 0x1e2ec ... 0x1e2ef:
return 0;
case 0x1e4ec ... 0x1e4ef:
return 0;
case 0x1e8d0 ... 0x1e8d6:
return 0;
case 0x1e944 ... 0x1e94a:
@@ -1257,7 +1277,7 @@ wcwidth_std(int32_t code) {
return -2;
// }}}
// East Asian double width (182470 codepoints) {{{
// East Asian double width (182492 codepoints) {{{
case 0x1100 ... 0x115e:
return 2;
case 0x231a ... 0x231b:
@@ -1402,8 +1422,12 @@ wcwidth_std(int32_t code) {
return 2;
case 0x1b000 ... 0x1b122:
return 2;
case 0x1b132:
return 2;
case 0x1b150 ... 0x1b152:
return 2;
case 0x1b155:
return 2;
case 0x1b164 ... 0x1b167:
return 2;
case 0x1b170 ... 0x1b2fb:
@@ -1474,7 +1498,7 @@ wcwidth_std(int32_t code) {
return 2;
case 0x1f6d5 ... 0x1f6d7:
return 2;
case 0x1f6dd ... 0x1f6df:
case 0x1f6dc ... 0x1f6df:
return 2;
case 0x1f6eb ... 0x1f6ec:
return 2;
@@ -1490,23 +1514,19 @@ wcwidth_std(int32_t code) {
return 2;
case 0x1f947 ... 0x1f9ff:
return 2;
case 0x1fa70 ... 0x1fa74:
case 0x1fa70 ... 0x1fa7c:
return 2;
case 0x1fa78 ... 0x1fa7c:
case 0x1fa80 ... 0x1fa88:
return 2;
case 0x1fa80 ... 0x1fa86:
case 0x1fa90 ... 0x1fabd:
return 2;
case 0x1fa90 ... 0x1faac:
case 0x1fabf ... 0x1fac5:
return 2;
case 0x1fab0 ... 0x1faba:
case 0x1face ... 0x1fadb:
return 2;
case 0x1fac0 ... 0x1fac5:
case 0x1fae0 ... 0x1fae8:
return 2;
case 0x1fad0 ... 0x1fad9:
return 2;
case 0x1fae0 ... 0x1fae7:
return 2;
case 0x1faf0 ... 0x1faf6:
case 0x1faf0 ... 0x1faf8:
return 2;
case 0x20000 ... 0x2fffd:
return 2;
@@ -1517,7 +1537,7 @@ wcwidth_std(int32_t code) {
// Emoji Presentation (0 codepoints) {{{
// }}}
// Not assigned in the unicode character database (760767 codepoints) {{{
// Not assigned in the unicode character database (760471 codepoints) {{{
case 0x378 ... 0x379:
return -4;
case 0x380 ... 0x383:
@@ -1760,7 +1780,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0xcf0:
return -4;
case 0xcf3 ... 0xcff:
case 0xcf4 ... 0xcff:
return -4;
case 0xd0d:
return -4;
@@ -1820,7 +1840,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0xec7:
return -4;
case 0xece ... 0xecf:
case 0xecf:
return -4;
case 0xeda ... 0xedb:
return -4;
@@ -2354,7 +2374,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x10eae ... 0x10eaf:
return -4;
case 0x10eb2 ... 0x10eff:
case 0x10eb2 ... 0x10efc:
return -4;
case 0x10f28 ... 0x10f2f:
return -4;
@@ -2390,7 +2410,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x11212:
return -4;
case 0x1123f ... 0x1127f:
case 0x11242 ... 0x1127f:
return -4;
case 0x11287:
return -4;
@@ -2494,7 +2514,9 @@ wcwidth_std(int32_t code) {
return -4;
case 0x11aa3 ... 0x11aaf:
return -4;
case 0x11af9 ... 0x11bff:
case 0x11af9 ... 0x11aff:
return -4;
case 0x11b0a ... 0x11bff:
return -4;
case 0x11c09:
return -4;
@@ -2536,7 +2558,13 @@ wcwidth_std(int32_t code) {
return -4;
case 0x11daa ... 0x11edf:
return -4;
case 0x11ef9 ... 0x11faf:
case 0x11ef9 ... 0x11eff:
return -4;
case 0x11f11:
return -4;
case 0x11f3b ... 0x11f3d:
return -4;
case 0x11f5a ... 0x11faf:
return -4;
case 0x11fb1 ... 0x11fbf:
return -4;
@@ -2552,9 +2580,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x12ff3 ... 0x12fff:
return -4;
case 0x1342f:
return -4;
case 0x13439 ... 0x143ff:
case 0x13456 ... 0x143ff:
return -4;
case 0x14647 ... 0x167ff:
return -4;
@@ -2606,9 +2632,13 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1afff:
return -4;
case 0x1b123 ... 0x1b14f:
case 0x1b123 ... 0x1b131:
return -4;
case 0x1b153 ... 0x1b163:
case 0x1b133 ... 0x1b14f:
return -4;
case 0x1b153 ... 0x1b154:
return -4;
case 0x1b156 ... 0x1b163:
return -4;
case 0x1b168 ... 0x1b16f:
return -4;
@@ -2636,7 +2666,9 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1d1eb ... 0x1d1ff:
return -4;
case 0x1d246 ... 0x1d2df:
case 0x1d246 ... 0x1d2bf:
return -4;
case 0x1d2d4 ... 0x1d2df:
return -4;
case 0x1d2f4 ... 0x1d2ff:
return -4;
@@ -2690,7 +2722,9 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1dab0 ... 0x1deff:
return -4;
case 0x1df1f ... 0x1dfff:
case 0x1df1f ... 0x1df24:
return -4;
case 0x1df2b ... 0x1dfff:
return -4;
case 0x1e007:
return -4;
@@ -2700,7 +2734,11 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1e025:
return -4;
case 0x1e02b ... 0x1e0ff:
case 0x1e02b ... 0x1e02f:
return -4;
case 0x1e06e ... 0x1e08e:
return -4;
case 0x1e090 ... 0x1e0ff:
return -4;
case 0x1e12d ... 0x1e12f:
return -4;
@@ -2714,7 +2752,9 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1e2fa ... 0x1e2fe:
return -4;
case 0x1e300 ... 0x1e7df:
case 0x1e300 ... 0x1e4cf:
return -4;
case 0x1e4fa ... 0x1e7df:
return -4;
case 0x1e7e7:
return -4;
@@ -2830,15 +2870,15 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1f266 ... 0x1f2ff:
return -4;
case 0x1f6d8 ... 0x1f6dc:
case 0x1f6d8 ... 0x1f6db:
return -4;
case 0x1f6ed ... 0x1f6ef:
return -4;
case 0x1f6fd ... 0x1f6ff:
return -4;
case 0x1f774 ... 0x1f77f:
case 0x1f777 ... 0x1f77a:
return -4;
case 0x1f7d9 ... 0x1f7df:
case 0x1f7da ... 0x1f7df:
return -4;
case 0x1f7ec ... 0x1f7ef:
return -4;
@@ -2860,23 +2900,19 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1fa6e ... 0x1fa6f:
return -4;
case 0x1fa75 ... 0x1fa77:
return -4;
case 0x1fa7d ... 0x1fa7f:
return -4;
case 0x1fa87 ... 0x1fa8f:
case 0x1fa89 ... 0x1fa8f:
return -4;
case 0x1faad ... 0x1faaf:
case 0x1fabe:
return -4;
case 0x1fabb ... 0x1fabf:
case 0x1fac6 ... 0x1facd:
return -4;
case 0x1fac6 ... 0x1facf:
case 0x1fadc ... 0x1fadf:
return -4;
case 0x1fada ... 0x1fadf:
case 0x1fae9 ... 0x1faef:
return -4;
case 0x1fae8 ... 0x1faef:
return -4;
case 0x1faf7 ... 0x1faff:
case 0x1faf9 ... 0x1faff:
return -4;
case 0x1fb93:
return -4;

View File

@@ -14,39 +14,37 @@ from gettext import gettext as _
from itertools import chain
from time import monotonic
from typing import (
TYPE_CHECKING, Any, Callable, Deque, Dict, Iterable, List, NamedTuple,
Optional, Pattern, Sequence, Tuple, Union
TYPE_CHECKING, Any, Callable, Deque, Dict, Iterable, List, NamedTuple, Optional,
Pattern, Sequence, Tuple, Union,
)
from .child import ProcessDesc
from .cli_stub import CLIOptions
from .clipboard import (
get_clipboard_string, get_primary_selection, set_clipboard_string,
set_primary_selection
set_primary_selection,
)
from .config import build_ansi_color_table
from .constants import (
appname, clear_handled_signals, config_dir, is_macos, wakeup_io_loop
appname, clear_handled_signals, config_dir, is_macos, wakeup_io_loop,
)
from .fast_data_types import (
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK,
CURSOR_UNDERLINE, DCS, DECORATION, DECORATION_MASK, DIM, GLFW_MOD_CONTROL,
GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM,
MARK, MARK_MASK, NO_CURSOR_SHAPE, NUM_UNDERLINE_STYLES, OSC, REVERSE,
SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, STRIKETHROUGH, TINT_PROGRAM, Color,
KeyEvent, Screen, add_timer, add_window, cell_size_for_window,
click_mouse_cmd_output, click_mouse_url, compile_program,
current_os_window, encode_key_for_tty, get_boss, get_click_interval,
get_options, init_cell_program, mark_os_window_dirty, mouse_selection,
move_cursor_to_mouse_if_in_prompt, pt_to_px, set_titlebar_color,
set_window_logo, set_window_padding, set_window_render_data,
update_ime_position_for_window, update_window_title,
update_window_visibility, wakeup_main_loop
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM,
CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DCS, DECORATION,
DECORATION_MASK, DIM, GLFW_MOD_CONTROL, GRAPHICS_ALPHA_MASK_PROGRAM,
GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM, MARK, MARK_MASK, NO_CURSOR_SHAPE,
NUM_UNDERLINE_STYLES, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
STRIKETHROUGH, TINT_PROGRAM, Color, KeyEvent, Screen, add_timer, add_window,
cell_size_for_window, click_mouse_cmd_output, click_mouse_url, compile_program,
current_os_window, encode_key_for_tty, get_boss, get_click_interval, get_options,
init_cell_program, mark_os_window_dirty, mouse_selection,
move_cursor_to_mouse_if_in_prompt, pt_to_px, set_titlebar_color, set_window_logo,
set_window_padding, set_window_render_data, update_ime_position_for_window,
update_window_title, update_window_visibility, wakeup_main_loop,
)
from .keys import keyboard_mode_name, mod_mask
from .notify import (
NotificationCommand, handle_notification_cmd, sanitize_identifier_pat
NotificationCommand, handle_notification_cmd, sanitize_identifier_pat,
)
from .options.types import Options
from .rgb import to_color
@@ -54,9 +52,9 @@ from .terminfo import get_capabilities
from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
from .utils import (
docs_url, kitty_ansi_sanitizer_pat, load_shaders, log_error, open_cmd,
open_url, parse_color_set, path_from_osc7_url, resolve_custom_file,
resolved_shell, sanitize_title
docs_url, kitty_ansi_sanitizer_pat, load_shaders, log_error, open_cmd, open_url,
parse_color_set, path_from_osc7_url, resolve_custom_file, resolved_shell,
sanitize_for_bracketed_paste, sanitize_title,
)
MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]]
@@ -1463,11 +1461,7 @@ class Window:
if isinstance(text, str):
text = text.encode('utf-8')
if self.screen.in_bracketed_paste_mode:
while True:
new_text = text.replace(b'\033[201~', b'').replace(b'\x9b201~', b'')
if len(text) == len(new_text):
break
text = new_text
text = sanitize_for_bracketed_paste(text)
else:
# Workaround for broken editors like nano that cannot handle
# newlines in pasted text see https://github.com/kovidgoyal/kitty/issues/994

View File

@@ -559,3 +559,11 @@ class TestDataTypes(BaseTest):
def test_notify_identifier_sanitization(self):
from kitty.notify import sanitize_identifier_pat
self.ae(sanitize_identifier_pat().sub('', '\x1b\nabc\n[*'), 'abc')
def test_bracketed_paste_sanitizer(self):
from kitty.utils import sanitize_for_bracketed_paste
for x in ('\x1b[201~ab\x9b201~cd', '\x1b[201\x1b[201~~ab'): # ]]]
q = sanitize_for_bracketed_paste(x.encode('utf-8'))
self.assertNotIn(b'\x1b[201~', q)
self.assertNotIn('\x9b201~'.encode('utf-8'), q)
self.assertIn(b'ab', q)

View File

@@ -66,7 +66,7 @@ def type_check() -> NoReturn:
generate_stub()
from kittens.tui.operations_stub import generate_stub # type: ignore
generate_stub()
py = shutil.which('python') or shutil.which('python3')
py = os.environ.get('PYTHON_FOR_TYPE_CHECK') or shutil.which('python') or shutil.which('python3')
os.execlp(py, py, '-m', 'mypy', '--pretty')

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
import importlib
import os
import shutil
import warnings
from contextlib import contextmanager
from tempfile import TemporaryDirectory
@@ -30,13 +31,14 @@ def main() -> None:
path = os.pathsep.join(x for x in paths if not x.startswith(current_home))
launcher_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty', 'launcher')
path = f'{launcher_dir}{os.pathsep}{path}'
PYTHON_FOR_TYPE_CHECK = shutil.which('python') or shutil.which('python3') or ''
with TemporaryDirectory() as tdir, env_vars(
PYTHONWARNINGS='error', HOME=tdir, USERPROFILE=tdir, PATH=path,
XDG_CONFIG_HOME=os.path.join(tdir, '.config'),
XDG_CONFIG_DIRS=os.path.join(tdir, '.config'),
XDG_DATA_DIRS=os.path.join(tdir, '.local', 'xdg'),
XDG_CACHE_HOME=os.path.join(tdir, '.cache'),
KITTY_PREWARM_SOCKET='',
PYTHON_FOR_TYPE_CHECK=PYTHON_FOR_TYPE_CHECK,
):
m = importlib.import_module('kitty_tests.main')
getattr(m, 'run_tests')()