Compare commits

...

68 Commits

Author SHA1 Message Date
Kovid Goyal
e06ef732d8 version 0.19.2 2020-11-13 09:32:53 +05:30
Kovid Goyal
792a3e7db8 Dont mix the raw/non-raw malloc/free for pagerhist 2020-11-13 08:12:26 +05:30
Kovid Goyal
9b32842f66 Fix #3096 2020-11-13 07:47:40 +05:30
Kovid Goyal
9816979169 Don't restrict the DCH control code to only the current scroll region
Matches behavior of other terminal emulators.  Fixes #3090
2020-11-12 11:44:09 +05:30
Kovid Goyal
a74679dd95 Fix last character of URL not being detected if it is the only character on a new line
Fixes #3088
2020-11-11 14:00:33 +05:30
Kovid Goyal
a40059729e Fix the send_text action not working in URL handlers
send_text keeps its args as byte string, so variable expansion was not
working. Fixes #3081
2020-11-11 07:51:43 +05:30
Kovid Goyal
6bab967586 Merge branch 'memory-leak' of https://github.com/Luflosi/kitty 2020-11-05 18:15:44 +05:30
Luflosi
bb88b33469 Fix memory leak
Found with the Clang Static Analyzer.
2020-11-05 13:15:19 +01:00
Kovid Goyal
c440470276 Entry point to edit config file 2020-11-04 16:30:29 +05:30
Kovid Goyal
ef6164eecb Merge branch 'incorrect-reference-count-decrement' of https://github.com/Luflosi/kitty 2020-11-04 07:44:00 +05:30
Luflosi
7ad360d872 Fix incorrect decrement of the reference counts of objects
Found with the Clang Static Analyzer.
The function `schedule_notification()` for the new notification API also does not `release` similar objects.
2020-11-03 18:17:58 +01:00
Kovid Goyal
0870a33956 Merge branch 'memory-leak' of https://github.com/Luflosi/kitty 2020-11-01 18:47:31 +05:30
Luflosi
3ccc2f538d Fix memory leak in unlikely failure condition
Found with the Clang Static Analyzer.
2020-11-01 13:41:52 +01:00
Kovid Goyal
17375f0be2 Add a link to nix-shell 2020-10-31 09:10:50 +05:30
Kovid Goyal
5a987a162e Merge branch 'add-shell.nix' of https://github.com/Luflosi/kitty 2020-10-31 07:31:19 +05:30
Luflosi
5058fb9919 Add shell.nix
This makes it possible to execute `nix-shell` to create the correct environment to build kitty. Use `nix-shell --pure` to eliminate most of the influence of the outside system, e.g. globally installed packages.
This works on NixOS and any Linux or macOS system with the Nix package manager installed.
The build inputs are split into `buildInputs`, `nativeBuildInputs`, `propagatedBuildInputs` and `checkInputs` so it closely resembles 2bb3a9da24/pkgs/applications/terminal-emulators/kitty/default.nix. This makes it easy to port changes between the two files.
2020-10-31 01:44:33 +01:00
Luflosi
3335f8602b Allow reading some build system flags as environment variables 2020-10-29 12:20:11 +01:00
Kovid Goyal
87ab1bbe54 Add a changelog entry for previous merge 2020-10-29 07:20:45 +05:30
Kovid Goyal
f8d52b33f4 Merge branch 'wl_fix_destroy_csds' of https://github.com/LeonardKoenig/kitty 2020-10-29 07:19:57 +05:30
Leonard König
bc2dcdf9db wl: Fix destroying CSDs in correct order (#3051)
On Wayland, some compositors refuse to implement SSDs in which case we draw
primitive CSDs.  However, the destructors were called in the wrong order,
leading to a dereference of an already freed object.
2020-10-28 18:57:37 +01:00
Kovid Goyal
93d1aacfb6 macOS: Fix kitty not being added to PATH automatically
Fixes #3063. Apparently in newer Python, Py_Initialize() nukes
sys._xoptions when the isolate flag is set.
2020-10-27 10:38:33 +05:30
Kovid Goyal
3ddaa4aff9 Make it clear that remote control mappings dont need remote control enabled 2020-10-26 16:51:53 +05:30
Kovid Goyal
75488d568a Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-10-26 16:34:30 +05:30
Luflosi
3df80545bd Reduce the difference of GLFW to upstream 2020-10-26 11:02:55 +01:00
Kovid Goyal
c605fd9783 Merge branch 'remove-extra-space' of https://github.com/Luflosi/kitty 2020-10-26 15:03:25 +05:30
Luflosi
60719e7bff Remove extra space 2020-10-26 10:22:14 +01:00
Kovid Goyal
e9faa3277a Merge branch 'fix-memory-leak' of https://github.com/Luflosi/kitty 2020-10-26 07:33:06 +05:30
Luflosi
35f7388725 Fix memory leaks in coretext_all_fonts()
Found with the Clang Static Analyzer.
2020-10-25 17:40:51 +01:00
Kovid Goyal
ef1486a779 Add .js and .json to known extensions 2020-10-25 20:25:31 +05:30
Kovid Goyal
95c44165e9 Merge branch 'fix-clang-analyze-error' of https://github.com/Luflosi/kitty 2020-10-25 20:24:28 +05:30
Kovid Goyal
7b816bb96f Font independent rendering for braille characters, which ensures they are properly aligned at all font sizes. 2020-10-25 20:20:22 +05:30
Luflosi
d2ac51aa4a Fix Clang Static Analyzer complaining about Apple coding conventions
The error message was: "Method accepting NSError** should have a non-void return value to indicate whether or not an error occurred".
2020-10-25 15:47:43 +01:00
Kovid Goyal
26974b1f3a Dont ignore --title when using an empty session
Fixes #3055
2020-10-25 18:53:57 +05:30
Kovid Goyal
3960c6678d Override MIME for some types that are actually text 2020-10-25 15:11:44 +05:30
Kovid Goyal
30b8991a73 DRYer 2020-10-25 13:47:12 +05:30
Kovid Goyal
75a94bcd96 Improving MIME type detection for some common file types when they are missing from the system MIME database
Also allow the user to specify their own database via mime.types in the
kitty config directory. See #3056
2020-10-25 13:42:11 +05:30
Kovid Goyal
e160cbf32b ssh kitten: Fix parsing of combined single letter options to more closely match ssh. Fixes #3053 2020-10-23 16:55:53 +05:30
Kovid Goyal
2da2e63245 Add another integration 2020-10-23 09:36:35 +05:30
Kovid Goyal
f160ea6012 Fix #3048 2020-10-21 21:57:35 +05:30
Kovid Goyal
8057bc639d Add diff context parser defs 2020-10-20 11:37:48 +05:30
Kovid Goyal
6f5ec5aa03 Merge branch 'fix-mistake-in-previous-pr' of https://github.com/Luflosi/kitty into master 2020-10-19 16:41:01 +05:30
Luflosi
e343c9445c Creating a temporary directory is not actually required
While developing the previous PR (https://github.com/kovidgoyal/kitty/pull/3043), I was using more compiler command line arguments, one of which was `-MMD`. This caused the .d file to be created. Without this argument, there is no need to create a temporary directory.
2020-10-19 13:08:32 +02:00
Kovid Goyal
c73e374f1a Remove unneeded space 2020-10-19 16:02:49 +05:30
Kovid Goyal
d9b292f808 Merge branch 'fix-macos-notification-api-detection' of https://github.com/Luflosi/kitty into master 2020-10-19 16:00:38 +05:30
Luflosi
46ef2f313b macOS: Fix detection for notification API
For some reason it can happen that the `UserNotifications` framework exists but the `UserNotifications/UserNotifications.h` header doesn't.
Closes https://github.com/kovidgoyal/kitty/issues/3042.
2020-10-19 12:19:51 +02:00
Luflosi
92a705c79e Add language parameter to first_successful_compile() and test_compile()
This allows compiling Objective-C files.
The temporary directory is needed because clang would try to write to `/dev/null.d`, which would obviously fail.
2020-10-18 23:08:19 +02:00
Kovid Goyal
12ebdd2574 Add a changelog entry for the last PR 2020-10-18 08:48:07 +05:30
Kovid Goyal
ed2e951032 Merge branch 'pointer_shape_when_dragging' of https://github.com/jaeheum/kitty into master 2020-10-18 08:46:18 +05:30
Kovid Goyal
01a34a8846 Add links to viu that supports the graphics protocol 2020-10-18 08:42:59 +05:30
Jay Han
b13174cb9c pointer_shape_when_dragging option + set_mouse_cursor_when_dragging() 2020-10-17 09:52:44 -07:00
Jay Han
03ac6cab99 default_pointer_shape option 2020-10-17 09:28:04 -07:00
Kovid Goyal
532eebaefb Merge branch 'fix-garbage-pointer-dereference' of https://github.com/Luflosi/kitty into master 2020-10-16 07:35:31 +05:30
Luflosi
f825345b14 Fix potential garbage pointer dereference
`create_os_window()` calls `get_window_content_scale()` without initializing the `xscale` and `yscale` variables. Instead of initializing them everywhere, where this function is called, just do it once in the function itself.
Found with the Clang Static Analyzer.
2020-10-15 20:41:16 +02:00
Kovid Goyal
1409477ed7 Merge branch 'double-free' of https://github.com/Luflosi/kitty into master 2020-10-15 19:11:31 +05:30
Luflosi
d8e4294432 Fix double free and memory leak
Found with the Clang Static Analyzer.
This is a copy & paste error. This bug was introduced in 72e2307c16 in Jul 2018.
2020-10-15 15:29:44 +02:00
Kovid Goyal
5d13908776 github actions CI updated to python 3.9
Pillow wheel currently not available for python 3.9. So tries to install
from source and fails on missing zlib. So install zlib in brew and make
it available via PKG_CONFIG_PATH. Apparently, this wont be necessary
after Pillow 8, but who knows.
2020-10-14 19:13:46 +05:30
Luflosi
2832a73285 GLFW: Wayland: Assume 96 DPI if physical size is <= 0
From upstream: 0ef149c8f2.
2020-10-14 14:43:40 +02:00
Kovid Goyal
4152ed5809 Update changelog 2020-10-14 08:51:07 +05:30
Kovid Goyal
8a90ed70ee Merge branch 'fix-upwards-selection-clearing' of https://github.com/Luflosi/kitty into master 2020-10-14 05:31:08 +05:30
Luflosi
e2646c08a5 Fix upwards selection not being cleared when lines change
When selecting text by dragging upwards, the "end" of the selection is above the "start". This causes the `selection_has_screen_line()` to never return `true` in this case.
In practice this bug causes the selection to not be cleared when the screen contents under the selection change if it was made by dragging upwards.
2020-10-13 21:40:03 +02:00
Kovid Goyal
7122ba820f Fix eight bar unicode chars not being rendered at very small font sizes
Fixes #3025
2020-10-13 20:00:25 +05:30
Kovid Goyal
efca2658ea Fix typing issues pointed out by updated mypy 2020-10-12 23:03:07 +05:30
Kovid Goyal
91a714947c Fix encoding error when reporting capabilities for number of colors 2020-10-12 22:39:05 +05:30
Kovid Goyal
5d3ac649ba Fix #3017 2020-10-08 17:39:44 +05:30
Kovid Goyal
6443e726a6 Add a note about how the query_terminal kitten works 2020-10-08 07:42:47 +05:30
Kovid Goyal
6a755bda45 A new kitten to easily query kitty for config values 2020-10-07 13:35:16 +05:30
Kovid Goyal
dab555ea3b kittens: Dont discard pending input on startup
Fixes #3012
2020-10-06 16:39:00 +05:30
Kovid Goyal
a3bbad0060 Allow specifying the optional actions for tcsetattr 2020-10-06 16:30:23 +05:30
57 changed files with 660 additions and 154 deletions

3
.gitattributes vendored
View File

@@ -11,3 +11,6 @@ kitty/parse-graphics-command.h linguist-generated=true
glfw/*.c linguist-vendored=true
glfw/*.h linguist-vendored=true
kittens/unicode_input/names.h linguist-generated=true
*.py text diff=python
*.m text diff=objc

View File

@@ -40,6 +40,9 @@ def install_deps():
if is_bundle:
install_bundle()
else:
if is_macos:
# needed for zlib for pillow, should not be needed after pillow 8.0
os.environ['PKG_CONFIG_PATH'] = '/usr/local/opt/zlib/lib/pkgconfig'
run('pip3 install Pillow pygments')

View File

@@ -1,4 +1,5 @@
pkg-config
zlib
python
imagemagick
harfbuzz

View File

@@ -65,6 +65,11 @@ def run_kitten(args: List[str]) -> None:
rk(kitten)
def edit_config_file(args: List[str]) -> None:
from kitty.utils import edit_config_file as f
f()
def namespaced(args: List[str]) -> None:
func = namespaced_entry_points[args[1]]
func(args[1:])
@@ -77,6 +82,7 @@ entry_points = {
'runpy': runpy,
'launch': launch,
'kitten': run_kitten,
'edit-config': edit_config_file,
'@': remote_control,
'+': namespaced,

View File

@@ -78,6 +78,19 @@ you might have to rebuild the app.
a self signed certificate, see for example, `here
<https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`_.
Build and run from source with Nix
-------------------------------------------
On NixOS or any other Linux or macOS system with the Nix package manager
installed, execute `nix-shell
<https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`_ to create
the correct environment to build kitty or use ``nix-shell --pure`` instead to
eliminate most of the influence of the outside system, e.g. globally installed
packages. ``nix-shell`` will automatically fetch all required dependencies and
make them available in the newly spawned shell.
Then proceed with ``make`` or ``make app`` according to the platform specific instructions above.
Note for Linux/macOS packagers
----------------------------------

View File

@@ -4,6 +4,49 @@ Changelog
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
To update |kitty|, :doc:`follow the instructions <binary>`.
0.19.2 [2020-11-13]
-------------------
- A new :doc:`kittens/query_terminal` kitten to easily query the running kitty
via escape codes to detect its version, and the values of
configuration options that enable or disable terminal features.
- Options to control mouse pointer shape, :opt:`default_pointer_shape`, and
:opt:`pointer_shape_when_dragging` (:pull:`3041`)
- Font independent rendering for braille characters, which ensures they are properly
aligned at all font sizes.
- Fix a regression in 0.19.0 that caused borders not to be drawn when setting
:opt:`window_margin_width` and keeping :opt:`draw_minimal_borders` on
(:iss:`3017`)
- Fix a regression in 0.19.0 that broke rendering of one-eight bar unicode
characters at very small font sizes (:iss:`3025`)
- Wayland: Fix a crash under GNOME when using multiple OS windows
(:pull:`3066`)
- Fix selections created by dragging upwards not being auto-cleared when
screen contents change (:pull:`3028`)
- macOS: Fix kitty not being added to PATH automatically when using pre-built
binaries (:iss:`3063`)
- Allow adding MIME definitions to kitty by placing a ``mime.types`` file in
the kitty config directory (:iss:`3056`)
- Dont ignore :option:`--title` when using a session file that defines no
windows (:iss:`3055`)
- Fix the send_text action not working in URL handlers (:iss:`3081`)
- Fix last character of URL not being detected if it is the only character on a
new line (:iss:`3088`)
- Don't restrict the ICH,DCH,REP control codes to only the current scroll region (:iss:`3090`, :iss:`3096`)
0.19.1 [2020-10-06]
-------------------

View File

@@ -165,9 +165,12 @@ kitty is not able to use my favorite font?
|kitty| achieves its stellar performance by caching alpha masks of each rendered
character on the GPU, so that every character needs to be rendered only once.
This means it is a strictly character cell based display. As such it can use
only monospace fonts, since every cell in the grid has to be the same size. If
your font is not listed in ``kitty list-fonts`` it means that it is not
monospace. On Linux you can list all monospace fonts with::
only monospace fonts, since every cell in the grid has to be the same size.
Furthermore, it needs fonts to be freely resizable, so it does not support
bitmapped fonts.
If your font is not listed in ``kitty list-fonts`` it means that it is not
monospace or is a bitmapped font. On Linux you can list all monospace fonts with::
fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True

View File

@@ -37,6 +37,7 @@ Some programs that use the kitty graphics protocol:
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
information tool
* `viu <https://github.com/atanunq/viu>`_ - a terminal image viewer
.. contents::

View File

@@ -30,6 +30,10 @@ images and other types of documents directly in your terminal, even over SSH.
`koneko <https://github.com/twenty5151/koneko>`_
browse images from the pixiv artist community directly in kitty
`viu <https://github.com/atanunq/viu>`_
view images in the terminal, similar to kitty's icat, however also supports
displaying animated GIFs
System and data visualisation tools
---------------------------------------
@@ -63,6 +67,9 @@ System and data visualisation tools
iplot 'sin(x*3)*exp(x*.2)'
`onefetch <https://github.com/o2sh/onefetch>`_
a tool to fetch information about your git repositories
Editor integration
-----------------------

View File

@@ -0,0 +1,21 @@
Query terminal
=================
Used to query kitty from terminal programs about version, values of various
runtime options controlling its features, etc.
The querying is done using the (*semi*) standard XTGETTCAP escape sequence
pioneered by XTerm, so it works over SSH as well. The downside is that it
is slow, since it requires a roundtrip to the terminal emulator and back.
If you want to do some of the same querying in your terminal program without
depending on the kitten, you can do so, by processing the same escape codes.
Search `this page <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html>`_
for *XTGETTCAP* to see the syntax for the escape code and read the source
of this kitten to find the values of the keys for the various queries.
Command Line Interface
-------------------------
.. include:: ../generated/cli-kitten-query_terminal.rst

View File

@@ -81,7 +81,11 @@ lines. The various available criteria are:
``mime``
A comma separated list of MIME types, for example: ``text/*, image/*,
application/pdf``
application/pdf``. You can add MIME types to kitty by creating the
:file:`mime.types` in the kitty configuration directory. Useful if your
system MIME database does not have definitions you need. This file is
in the standard format of one definition per line, like: ``text/plain rst
md``.
``ext``
A comma separated list of file extensions, for example: ``jpeg, tar.gz``

View File

@@ -144,7 +144,8 @@ you can map it in :file:`kitty.conf`. For example::
Then pressing the :kbd:`F1` key will set the active window margins to 30.
The syntax for what follows :code:`remote_control` is exactly the same
as the syntax for what follows :code:`kitty @` above.
as the syntax for what follows :code:`kitty @` above. You do not need
to enable remote control to use these mappings.
Documentation for the remote control protocol

View File

@@ -1994,8 +1994,10 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
bitmapFormat:NSBitmapFormatAlphaNonpremultiplied
bytesPerRow:src->width * 4
bitsPerPixel:32];
if (rep == nil)
if (rep == nil) {
[native release];
return false;
}
memcpy([rep bitmapData], src->pixels, src->width * src->height * 4);
[native addRepresentation:rep];
@@ -2446,7 +2448,7 @@ START_ALLOW_CASE_RANGE
END_ALLOW_CASE_RANGE
}
if (utf_16_key != 0) {
strncpy(cocoa_key, [[NSString stringWithCharacters:&utf_16_key length:1] UTF8String], key_sz - 1);
strncpy(cocoa_key, [[NSString stringWithCharacters:&utf_16_key length:1] UTF8String], key_sz - 1);
} else {
unsigned str_pos = 0;
for (unsigned i = 0; i < 4 && str_pos < key_sz - 1; i++) {

3
glfw/memfd.h vendored
View File

@@ -39,8 +39,7 @@ static inline int memfd_create(const char *name, unsigned int flags) {
#include <unistd.h>
#include <fcntl.h>
static inline int
createTmpfileCloexec(char* tmpname)
static inline int createTmpfileCloexec(char* tmpname)
{
int fd;

14
glfw/wl_init.c vendored
View File

@@ -717,6 +717,9 @@ glfwWaylandCheckForServerSideDecorations(void) {
int _glfwPlatformInit(void)
{
int i;
_GLFWmonitor* monitor;
_glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0");
if (!_glfw.wl.cursor.handle)
{
@@ -768,6 +771,17 @@ int _glfwPlatformInit(void)
// Sync so we got all initial output events
wl_display_roundtrip(_glfw.wl.display);
for (i = 0; i < _glfw.monitorCount; ++i)
{
monitor = _glfw.monitors[i];
if (monitor->widthMM <= 0 || monitor->heightMM <= 0)
{
// If Wayland does not provide a physical size, assume the default 96 DPI
monitor->widthMM = (int) (monitor->modes[monitor->wl.currentMode].width * 25.4f / 96.f);
monitor->heightMM = (int) (monitor->modes[monitor->wl.currentMode].height * 25.4f / 96.f);
}
}
if (!_glfw.wl.wmBase)
{
_glfwInputError(GLFW_PLATFORM_ERROR,

8
glfw/wl_platform.h vendored
View File

@@ -204,14 +204,14 @@ typedef struct _GLFWlibraryWayland
struct wl_seat* seat;
struct wl_pointer* pointer;
struct wl_keyboard* keyboard;
struct wl_data_device_manager* dataDeviceManager;
struct wl_data_device* dataDevice;
struct xdg_wm_base* wmBase;
struct zxdg_decoration_manager_v1* decorationManager;
struct wp_viewporter* viewporter;
struct zwp_relative_pointer_manager_v1* relativePointerManager;
struct zwp_pointer_constraints_v1* pointerConstraints;
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
struct wl_data_device_manager* dataDeviceManager;
struct wl_data_device* dataDevice;
struct wl_data_source* dataSourceForClipboard;
struct zwp_primary_selection_device_manager_v1* primarySelectionDeviceManager;
struct zwp_primary_selection_device_v1* primarySelectionDevice;
@@ -226,6 +226,7 @@ typedef struct _GLFWlibraryWayland
int32_t keyboardRepeatRate;
monotonic_t keyboardRepeatDelay;
struct {
uint32_t key;
id_type keyRepeatTimer;
@@ -275,7 +276,8 @@ typedef struct _GLFWmonitorWayland
int x;
int y;
int scale;
} _GLFWmonitorWayland;
} _GLFWmonitorWayland;
// Wayland-specific per-cursor data
//

23
glfw/wl_window.c vendored
View File

@@ -42,8 +42,7 @@
#include <sys/mman.h>
static void
setCursorImage(_GLFWwindow* window)
static void setCursorImage(_GLFWwindow* window)
{
_GLFWcursorWayland defaultCursor = {.shape = GLFW_ARROW_CURSOR};
_GLFWcursorWayland* cursorWayland = window->cursor ? &window->cursor->wl : &defaultCursor;
@@ -247,8 +246,7 @@ static void dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, in
* is set to ENOSPC. If posix_fallocate() is not supported, program may
* receive SIGBUS on accessing mmap()'ed file contents instead.
*/
static int
createAnonymousFile(off_t size)
static int createAnonymousFile(off_t size)
{
int ret, fd = -1, shm_anon = 0;
#ifdef HAS_MEMFD_CREATE
@@ -415,10 +413,10 @@ static void createDecorations(_GLFWwindow* window)
static void destroyDecoration(_GLFWdecorationWayland* decoration)
{
if (decoration->surface)
wl_surface_destroy(decoration->surface);
if (decoration->subsurface)
wl_subsurface_destroy(decoration->subsurface);
if (decoration->surface)
wl_surface_destroy(decoration->surface);
if (decoration->viewport)
wp_viewport_destroy(decoration->viewport);
decoration->surface = NULL;
@@ -439,7 +437,9 @@ static void xdgDecorationHandleConfigure(void* data,
uint32_t mode)
{
_GLFWwindow* window = data;
window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
if (!window->wl.decorations.serverSide)
createDecorations(window);
}
@@ -547,8 +547,7 @@ static bool createSurface(_GLFWwindow* window,
return true;
}
static void
setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on)
static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on)
{
if (window->wl.xdg.toplevel)
{
@@ -739,8 +738,7 @@ static bool createXdgSurface(_GLFWwindow* window)
return true;
}
static void
incrementCursorImage(_GLFWwindow* window)
static void incrementCursorImage(_GLFWwindow* window)
{
if (window && window->wl.decorations.focus == mainWindow && window->cursorMode != GLFW_CURSOR_HIDDEN) {
_GLFWcursor* cursor = window->wl.currentCursor;
@@ -788,8 +786,7 @@ wayland_read_events(int poll_result, int events, void *data UNUSED) {
else wl_display_cancel_read(_glfw.wl.display);
}
static void
handleEvents(monotonic_t timeout)
static void handleEvents(monotonic_t timeout)
{
struct wl_display* display = _glfw.wl.display;
errno = 0;
@@ -958,7 +955,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
window->context.destroy(window);
destroyDecorations(window);
if (window->wl.xdg.decoration)
zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
@@ -1362,6 +1358,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
cursor->wl.buffer = createShmBuffer(image);
if (!cursor->wl.buffer)
return false;
cursor->wl.width = image->width;
cursor->wl.height = image->height;
cursor->wl.xhot = xhot;

6
glfw/x11_window.c vendored
View File

@@ -423,14 +423,16 @@ static char* convertLatin1toUTF8(const char* source)
const char* sp;
if (source) {
for (sp = source; *sp; sp++) size += (*sp & 0x80) ? 2 : 1;
for (sp = source; *sp; sp++)
size += (*sp & 0x80) ? 2 : 1;
}
char* target = calloc(size, 1);
char* tp = target;
if (source) {
for (sp = source; *sp; sp++) tp += encodeUTF8(tp, *sp);
for (sp = source; *sp; sp++)
tp += encodeUTF8(tp, *sp);
}
return target;

View File

@@ -7,7 +7,7 @@ import re
from contextlib import suppress
from functools import lru_cache
from hashlib import md5
from mimetypes import guess_type
from kitty.guess_mime_type import guess_type
from typing import TYPE_CHECKING, Dict, List, Set, Optional, Iterator, Tuple, Union
if TYPE_CHECKING:
@@ -68,7 +68,10 @@ class Collection:
self.removed_count += len(lines_for_path(left_path))
def finalize(self) -> None:
self.all_paths.sort(key=path_name_map.get)
def key(x: str) -> str:
return path_name_map.get(x, '')
self.all_paths.sort(key=key)
def __iter__(self) -> Iterator[Tuple[str, str, Optional[str]]]:
for path in self.all_paths:
@@ -133,7 +136,7 @@ def sanitize(text: str) -> str:
@lru_cache(maxsize=1024)
def mime_type_for_path(path: str) -> str:
return guess_type(path)[0] or 'application/octet-stream'
return guess_type(path) or 'application/octet-stream'
@lru_cache(maxsize=1024)

View File

@@ -13,7 +13,9 @@ from collections import defaultdict
from contextlib import suppress
from functools import partial
from gettext import gettext as _
from typing import DefaultDict, Dict, Iterable, List, Optional, Tuple, Union
from typing import (
Any, DefaultDict, Dict, Iterable, List, Optional, Tuple, Union
)
from kitty.cli import CONFIG_HELP, parse_args
from kitty.cli_stub import DiffCLIOptions
@@ -24,6 +26,11 @@ from kitty.key_encoding import RELEASE, KeyEvent, enter_key, key_defs as K
from kitty.options_stub import DiffOptions
from kitty.utils import ScreenSize
from ..tui.handler import Handler
from ..tui.images import ImageManager, Placement
from ..tui.line_edit import LineEdit
from ..tui.loop import Loop
from ..tui.operations import styled
from . import global_data
from .collect import (
Collection, create_collection, data_for_path, lines_for_path, sanitize,
@@ -35,14 +42,11 @@ from .render import (
ImagePlacement, ImageSupportWarning, Line, LineRef, Reference, render_diff
)
from .search import BadRegex, Search
from ..tui.handler import Handler
from ..tui.images import ImageManager, Placement
from ..tui.line_edit import LineEdit
from ..tui.loop import Loop
from ..tui.operations import styled
try:
from .highlight import initialize_highlighter, highlight_collection, DiffHighlight
from .highlight import (
DiffHighlight, highlight_collection, initialize_highlighter
)
has_highlighter = True
DiffHighlight
except ImportError:
@@ -566,8 +570,8 @@ class ShowWarning:
def __init__(self) -> None:
self.warnings: List[str] = []
def __call__(self, message: str, category: object, filename: str, lineno: int, file: object = None, line: object = None) -> None:
if category is ImageSupportWarning:
def __call__(self, message: Any, category: Any, filename: str, lineno: int, file: object = None, line: object = None) -> None:
if category is ImageSupportWarning and isinstance(message, str):
showwarning.warnings.append(message)

View File

@@ -3,7 +3,6 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import contextlib
import mimetypes
import os
import re
import socket
@@ -18,6 +17,7 @@ from typing import (
Dict, Generator, List, NamedTuple, Optional, Pattern, Tuple, Union
)
from kitty.guess_mime_type import guess_type
from kitty.cli import parse_args
from kitty.cli_stub import IcatCLIOptions
from kitty.constants import appname
@@ -273,7 +273,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
def scan(d: str) -> Generator[Tuple[str, str], None, None]:
for dirpath, dirnames, filenames in os.walk(d):
for f in filenames:
mt = mimetypes.guess_type(f)[0]
mt = guess_type(f)
if mt and mt.startswith('image/'):
yield os.path.join(dirpath, f), mt

View File

View File

@@ -0,0 +1,162 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import re
import sys
from binascii import unhexlify, hexlify
from contextlib import suppress
from typing import Dict, Iterable, List, Type
from kitty.cli import parse_args
from kitty.cli_stub import QueryTerminalCLIOptions
from kitty.constants import appname
from kitty.utils import TTYIO
from kitty.terminfo import names
class Query:
name: str = ''
ans: str = ''
query_name: str = ''
help_text: str = ''
def __init__(self) -> None:
self.encoded_query_name = hexlify(self.query_name.encode('utf-8')).decode('ascii')
self.pat = re.compile('\x1bP([01])\\+r{}(.*?)\x1b\\\\'.format(self.encoded_query_name).encode('ascii'))
def query_code(self) -> str:
return "\x1bP+q{}\x1b\\".format(self.encoded_query_name)
def decode_response(self, res: bytes) -> str:
return unhexlify(res).decode('utf-8')
def more_needed(self, buffer: bytes) -> bool:
m = self.pat.search(buffer)
if m is None:
return True
if m.group(1) == b'1':
q = m.group(2)
if q.startswith(b'='):
with suppress(Exception):
self.ans = self.decode_response(memoryview(q)[1:])
return False
def output_line(self) -> str:
return self.ans
all_queries: Dict[str, Type[Query]] = {}
def query(cls: Type[Query]) -> Type[Query]:
all_queries[cls.name] = cls
return cls
@query
class TerminalName(Query):
name: str = 'name'
query_name: str = 'TN'
help_text: str = f'Terminal name ({names[0]})'
@query
class TerminalVersion(Query):
name: str = 'version'
query_name: str = 'kitty-query-version'
help_text: str = 'Terminal version, for e.g.: 0.19.2'
@query
class AllowHyperlinks(Query):
name: str = 'allow_hyperlinks'
query_name: str = 'kitty-query-allow_hyperlinks'
help_text: str = 'yes, no or ask'
def do_queries(queries: Iterable, cli_opts: QueryTerminalCLIOptions) -> Dict[str, str]:
actions = tuple(all_queries[x]() for x in queries)
qstring = ''.join(a.query_code() for a in actions)
received = b''
def more_needed(data: bytes) -> bool:
nonlocal received
received += data
for a in actions:
if a.more_needed(received):
return True
return False
with TTYIO() as ttyio:
ttyio.send(qstring)
ttyio.recv(more_needed, timeout=cli_opts.wait_for)
return {a.name: a.output_line() for a in actions}
def options_spec() -> str:
return '''\
--wait-for
type=float
default=10
The amount of time (in seconds) to wait for a response from the terminal, after
querying it.
'''
help_text = '''\
Query the terminal this kitten is run in for various
capabilities. This sends escape codes to the terminal
and based on its response prints out data about supported
capabilities. Note that this is a blocking operation, since
it has to wait for a response from the terminal. You can control
the maximum wait time via the ``--wait-for`` option.
The output is lines of the form::
query: data
If a particular query is unsupported by the running kitty version,
the data will be blank.
Note that when calling this from another program, be very
careful not to perform any I/O on the terminal device
until the kitten exits.
Available queries are::
{}
'''.format(' ' + '\n '.join(
f'{name}: {c.help_text}' for name, c in all_queries.items()))
usage = '[query1 query2 ...]'
def main(args: List[str] = sys.argv) -> None:
cli_opts, items_ = parse_args(
args[1:],
options_spec,
usage,
help_text,
'{} +kitten query_terminal'.format(appname),
result_class=QueryTerminalCLIOptions
)
queries: List[str] = list(items_)
if 'all' in queries or not queries:
queries = sorted(all_queries)
else:
extra = frozenset(queries) - frozenset(all_queries)
if extra:
raise SystemExit(f'Unknown queries: {", ".join(extra)}')
for key, val in do_queries(queries, cli_opts).items():
print(key + ':', val)
if __name__ == '__main__':
main()
elif __name__ == '__doc__':
cd = sys.cli_docs # type: ignore
cd['usage'] = usage
cd['options'] = options_spec
cd['help_text'] = help_text

View File

@@ -118,15 +118,14 @@ def parse_ssh_args(args: List[str]) -> Tuple[List[str], List[str], bool]:
if arg in boolean_ssh_args:
ssh_args.append(arg)
continue
if arg.startswith('-p') and arg[2:].isdigit():
ssh_args.append(arg)
continue
if arg in other_ssh_args:
if i != len(all_args) - 1:
raise SystemExit('Option {} cannot occur in the middle'.format(arg))
ssh_args.append(arg)
expecting_option_val = True
continue
rest = all_args[i+1:]
if rest:
ssh_args.append(rest)
else:
expecting_option_val = True
break
raise SystemExit('Unknown option: {}'.format(arg))
continue
if expecting_option_val:

View File

@@ -10,6 +10,7 @@ import re
import selectors
import signal
import sys
import termios
from contextlib import contextmanager
from functools import partial
from typing import Any, Callable, Dict, Generator, List, NamedTuple, Optional
@@ -61,8 +62,9 @@ debug = Debug()
class TermManager:
def __init__(self) -> None:
def __init__(self, optional_actions: int = termios.TCSANOW) -> None:
self.extra_finalize: Optional[str] = None
self.optional_actions = optional_actions
def set_state_for_loop(self, set_raw: bool = True) -> None:
if set_raw:
@@ -82,7 +84,7 @@ class TermManager:
self.set_state_for_loop()
def __enter__(self) -> 'TermManager':
self.tty_fd, self.original_termios = open_tty()
self.tty_fd, self.original_termios = open_tty(False, self.optional_actions)
self.set_state_for_loop(set_raw=False)
return self
@@ -183,7 +185,8 @@ class Loop:
def __init__(
self,
sanitize_bracketed_paste: str = '[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'
sanitize_bracketed_paste: str = '[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]',
optional_actions: int = termios.TCSADRAIN
):
if is_macos:
# On macOS PTY devices are not supported by the KqueueSelector and
@@ -193,6 +196,7 @@ class Loop:
else:
self.asycio_loop = asyncio.get_event_loop()
self.return_code = 0
self.optional_actions = optional_actions
self.read_buf = ''
self.decoder = codecs.getincrementaldecoder('utf-8')('ignore')
try:
@@ -388,7 +392,7 @@ class Loop:
handler.on_resize(handler.screen_size)
signal_manager = SignalManager(self.asycio_loop, _on_sigwinch, handler.on_interrupt, handler.on_term)
with TermManager() as term_manager, signal_manager:
with TermManager(self.optional_actions) as term_manager, signal_manager:
self._get_screen_size: ScreenSizeGetter = screen_size_function(term_manager.tty_fd)
image_manager = None
if handler.image_manager_class is not None:

View File

@@ -69,7 +69,7 @@ class Borders:
self.os_window_id = os_window_id
self.tab_id = tab_id
self.draw_active_borders = opts.active_border_color is not None
self.draw_minimal_borders = opts.draw_minimal_borders
self.draw_minimal_borders = opts.draw_minimal_borders and max(opts.window_margin_width) < 1
def __call__(
self,

View File

@@ -13,6 +13,7 @@ class CLIOptions:
LaunchCLIOptions = AskCLIOptions = ClipboardCLIOptions = DiffCLIOptions = CLIOptions
HintsCLIOptions = IcatCLIOptions = PanelCLIOptions = ResizeCLIOptions = CLIOptions
ErrorCLIOptions = UnicodeCLIOptions = RCOptions = RemoteFileCLIOptions = CLIOptions
QueryTerminalCLIOptions = CLIOptions
def generate_stub() -> None:
@@ -50,6 +51,9 @@ def generate_stub() -> None:
from kittens.icat.main import options_spec
do(options_spec(), 'IcatCLIOptions')
from kittens.query_terminal.main import options_spec
do(options_spec(), 'QueryTerminalCLIOptions')
from kittens.panel.main import OPTIONS
do(OPTIONS(), 'PanelCLIOptions')

View File

@@ -182,14 +182,9 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
if (!center) {PyErr_SetString(PyExc_RuntimeError, "Failed to get the user notification center"); return NULL; }
if (!center.delegate) center.delegate = [[NotificationDelegate alloc] init];
NSUserNotification *n = [NSUserNotification new];
#define SET(x) { \
if (x) { \
NSString *t = @(x); \
n.x = t; \
[t release]; \
}}
SET(title); SET(subtitle); SET(informativeText);
#undef SET
if (title) n.title = @(title);
if (subtitle) n.subtitle = @(subtitle);
if (informativeText) n.informativeText = @(informativeText);
if (identifier) {
n.userInfo = @{@"user_id": @(identifier)};
}
@@ -307,17 +302,17 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
@implementation ServiceProvider
- (void)openTab:(NSPasteboard*)pasteboard
- (BOOL)openTab:(NSPasteboard*)pasteboard
userData:(NSString *) UNUSED userData error:(NSError **) UNUSED error {
[self openFilesFromPasteboard:pasteboard type:NEW_TAB_WITH_WD];
return [self openFilesFromPasteboard:pasteboard type:NEW_TAB_WITH_WD];
}
- (void)openOSWindow:(NSPasteboard*)pasteboard
- (BOOL)openOSWindow:(NSPasteboard*)pasteboard
userData:(NSString *) UNUSED userData error:(NSError **) UNUSED error {
[self openFilesFromPasteboard:pasteboard type:NEW_OS_WINDOW_WITH_WD];
return [self openFilesFromPasteboard:pasteboard type:NEW_OS_WINDOW_WITH_WD];
}
- (void)openFilesFromPasteboard:(NSPasteboard *)pasteboard type:(int)type {
- (BOOL)openFilesFromPasteboard:(NSPasteboard *)pasteboard type:(int)type {
NSDictionary *options = @{ NSPasteboardURLReadingFileURLsOnlyKey: @YES };
NSArray *filePathArray = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:options];
for (NSURL *url in filePathArray) {
@@ -330,6 +325,7 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
set_cocoa_pending_action(type, [path UTF8String]);
}
}
return YES;
}
@end

View File

@@ -394,10 +394,10 @@ def complete_files_and_dirs(
def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
from mimetypes import guess_type
from .guess_mime_type import guess_type
def icat_file_predicate(filename: str) -> bool:
mt = guess_type(filename)[0]
mt = guess_type(filename)
if mt and mt.startswith('image/'):
return True
return False

View File

@@ -576,6 +576,16 @@ The shape of the mouse pointer when the program running in the terminal grabs th
Valid values are: :code:`arrow`, :code:`beam` and :code:`hand`
'''))
o('default_pointer_shape', 'beam', option_type=choices('arrow', 'beam', 'hand'), long_text=('''
The default shape of the mouse pointer.
Valid values are: :code:`arrow`, :code:`beam` and :code:`hand`
'''))
o('pointer_shape_when_dragging', 'beam', option_type=choices('arrow', 'beam', 'hand'), long_text=('''
The default shape of the mouse pointer when dragging across text.
Valid values are: :code:`arrow`, :code:`beam` and :code:`hand`
'''))
# }}}
g('performance') # {{{

View File

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

View File

@@ -161,12 +161,13 @@ coretext_all_fonts(PyObject UNUSED *_self) {
CFArrayRef matches = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection());
const CFIndex count = CFArrayGetCount(matches);
PyObject *ans = PyTuple_New(count), *temp;
if (ans == NULL) return PyErr_NoMemory();
if (ans == NULL) { CFRelease(matches); return PyErr_NoMemory(); }
for (CFIndex i = 0; i < count; i++) {
temp = font_descriptor_to_python((CTFontDescriptorRef) CFArrayGetValueAtIndex(matches, i));
if (temp == NULL) { Py_DECREF(ans); return NULL; }
if (temp == NULL) { CFRelease(matches); Py_DECREF(ans); return NULL; }
PyTuple_SET_ITEM(ans, i, temp); temp = NULL;
}
CFRelease(matches);
return ans;
}
@@ -212,6 +213,7 @@ manually_search_fallback_fonts(CTFontRef current_font, CPUCell *cell) {
CFRelease(new_font);
}
}
CFRelease(fonts);
return ans;
}

View File

@@ -91,7 +91,7 @@ stop_profiler(PyObject UNUSED *self, PyObject *args UNUSED) {
#endif
static inline bool
put_tty_in_raw_mode(int fd, const struct termios* termios_p, bool read_with_timeout) {
put_tty_in_raw_mode(int fd, const struct termios* termios_p, bool read_with_timeout, int optional_actions) {
struct termios raw_termios = *termios_p;
cfmakeraw(&raw_termios);
if (read_with_timeout) {
@@ -99,14 +99,14 @@ put_tty_in_raw_mode(int fd, const struct termios* termios_p, bool read_with_time
} else {
raw_termios.c_cc[VMIN] = 1; raw_termios.c_cc[VTIME] = 0;
}
if (tcsetattr(fd, TCSAFLUSH, &raw_termios) != 0) { PyErr_SetFromErrno(PyExc_OSError); return false; }
if (tcsetattr(fd, optional_actions, &raw_termios) != 0) { PyErr_SetFromErrno(PyExc_OSError); return false; }
return true;
}
static PyObject*
open_tty(PyObject *self UNUSED, PyObject *args) {
int read_with_timeout = 0;
if (!PyArg_ParseTuple(args, "|p", &read_with_timeout)) return NULL;
int read_with_timeout = 0, optional_actions = TCSAFLUSH;
if (!PyArg_ParseTuple(args, "|pi", &read_with_timeout, &optional_actions)) return NULL;
int flags = O_RDWR | O_CLOEXEC | O_NOCTTY;
if (!read_with_timeout) flags |= O_NONBLOCK;
static char ctty[L_ctermid+1];
@@ -115,26 +115,26 @@ open_tty(PyObject *self UNUSED, PyObject *args) {
struct termios *termios_p = calloc(1, sizeof(struct termios));
if (!termios_p) return PyErr_NoMemory();
if (tcgetattr(fd, termios_p) != 0) { free(termios_p); PyErr_SetFromErrno(PyExc_OSError); return NULL; }
if (!put_tty_in_raw_mode(fd, termios_p, read_with_timeout != 0)) { free(termios_p); return NULL; }
if (!put_tty_in_raw_mode(fd, termios_p, read_with_timeout != 0, optional_actions)) { free(termios_p); return NULL; }
return Py_BuildValue("iN", fd, PyLong_FromVoidPtr(termios_p));
}
#define TTY_ARGS \
PyObject *tp; int fd; \
if (!PyArg_ParseTuple(args, "iO!", &fd, &PyLong_Type, &tp)) return NULL; \
PyObject *tp; int fd; int optional_actions = TCSAFLUSH; \
if (!PyArg_ParseTuple(args, "iO!|i", &fd, &PyLong_Type, &tp, &optional_actions)) return NULL; \
struct termios *termios_p = PyLong_AsVoidPtr(tp);
static PyObject*
normal_tty(PyObject *self UNUSED, PyObject *args) {
TTY_ARGS
if (tcsetattr(fd, TCSAFLUSH, termios_p) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
if (tcsetattr(fd, optional_actions, termios_p) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
Py_RETURN_NONE;
}
static PyObject*
raw_tty(PyObject *self UNUSED, PyObject *args) {
TTY_ARGS
if (!put_tty_in_raw_mode(fd, termios_p, false)) return NULL;
if (!put_tty_in_raw_mode(fd, termios_p, false, optional_actions)) return NULL;
Py_RETURN_NONE;
}
@@ -142,7 +142,7 @@ raw_tty(PyObject *self UNUSED, PyObject *args) {
static PyObject*
close_tty(PyObject *self UNUSED, PyObject *args) {
TTY_ARGS
tcsetattr(fd, TCSAFLUSH, termios_p); // deliberately ignore failure
tcsetattr(fd, optional_actions, termios_p); // deliberately ignore failure
free(termios_p);
safe_close(fd, __FILE__, __LINE__);
Py_RETURN_NONE;

View File

@@ -4,6 +4,7 @@ from typing import (
Union
)
import termios
from kitty.boss import Boss
from kitty.fonts.render import FontObject
from kitty.options_stub import Options
@@ -865,19 +866,19 @@ def viewport_for_window(
TermiosPtr = NewType('TermiosPtr', int)
def raw_tty(fd: int, termios_ptr: TermiosPtr) -> None:
def raw_tty(fd: int, termios_ptr: TermiosPtr, optional_actions: int = termios.TCSAFLUSH) -> None:
pass
def close_tty(fd: int, termios_ptr: TermiosPtr) -> None:
def close_tty(fd: int, termios_ptr: TermiosPtr, optional_actions: int = termios.TCSAFLUSH) -> None:
pass
def normal_tty(fd: int, termios_ptr: TermiosPtr) -> None:
def normal_tty(fd: int, termios_ptr: TermiosPtr, optional_actions: int = termios.TCSAFLUSH) -> None:
pass
def open_tty(read_with_timeout: bool = False) -> Tuple[int, TermiosPtr]:
def open_tty(read_with_timeout: bool = False, optional_actions: int = termios.TCSAFLUSH) -> Tuple[int, TermiosPtr]:
pass

View File

@@ -596,6 +596,7 @@ START_ALLOW_CASE_RANGE
case 0x2500 ... 0x2573:
case 0x2574 ... 0x259f:
case 0xe0b0 ... 0xe0b4:
case 0x2800 ... 0x28ff:
case 0xe0b6:
case 0xe0b8: // 
case 0xe0ba: // 
@@ -642,8 +643,10 @@ START_ALLOW_CASE_RANGE
return 0xa0 + ch - 0xe0b0; // IDs from 0xa0 to 0xc4
case 0x1fb00 ... 0x1fb8b:
return 0xc5 + ch - 0x1fb00; // IDs from 0xc5 to 0x150
case 0x1fba0 ... 0x1fbae:
case 0x1fba0 ... 0x1fbae: // IDs from 0x151 to 0x15f
return 0x151 + ch - 0x1fba0;
case 0x2800 ... 0x28ff:
return 0x160 + ch - 0x2800;
default:
return 0xffff;
}

View File

@@ -666,12 +666,12 @@ def smooth_mosaic(
def eight_bar(buf: BufType, width: int, height: int, level: int = 1, which: int = 0, horizontal: bool = False) -> None:
if horizontal:
x_range = range(0, width)
thickness = height // 8
thickness = max(1, height // 8)
y_start = min(which * thickness, height - 2)
y_range = range(y_start, height if which == 7 else min(y_start + thickness, height))
else:
y_range = range(0, height)
thickness = width // 8
thickness = max(1, width // 8)
x_start = min(which * thickness, width - 2)
x_range = range(x_start, width if which == 7 else min(x_start + thickness, width))
for y in y_range:
@@ -685,6 +685,32 @@ def eight_block(buf: BufType, width: int, height: int, level: int = 1, which: Tu
eight_bar(buf, width, height, level, x, horizontal)
def braille_dot(buf: BufType, width: int, height: int, col: int, row: int) -> None:
dot_height = max(1, height // 8)
dot_width = max(1, width // 4)
top_margin = (height - 7 * dot_height) // 2
left_margin = (width - 3 * dot_width) // 2
x_start = left_margin + (col * 2 * dot_width)
y_start = top_margin + (row * 2 * dot_height)
if y_start < height:
for y in range(y_start, min(height, y_start + dot_height)):
if x_start < width:
offset = y * width
for x in range(x_start, min(width, x_start + dot_width)):
buf[offset + x] = 255
def braille(buf: BufType, width: int, height: int, which: int = 0) -> None:
if not which:
return
for i, x in enumerate(reversed(bin(which)[2:])):
if x == '1':
q = i + 1
col = 0 if q in (1, 2, 3, 7) else 1
row = 0 if q in (1, 4) else 1 if q in (2, 5) else 2 if q in (3, 6) else 3
braille_dot(buf, width, height, col, row)
box_chars: Dict[str, List[Callable]] = {
'': [hline],
'': [p(hline, level=3)],
@@ -908,6 +934,9 @@ for chars, func_ in (('╒╕╘╛', dvcorner), ('╓╖╙╜', dhcorner), ('
for ch in chars:
box_chars[ch] = [p(cast(Callable, func_), which=ch)]
for i in range(256):
box_chars[chr(0x2800 + i)] = [p(braille, which=i)]
c = 0x1fb00
for i in range(1, 63):

View File

@@ -421,6 +421,7 @@ make_os_window_context_current(OSWindow *w) {
static inline void
get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi) {
*xscale = 1; *yscale = 1;
if (w) glfwGetWindowContentScale(w, xscale, yscale);
else {
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
@@ -440,7 +441,7 @@ get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xd
static inline void
get_window_dpi(GLFWwindow *w, double *x, double *y) {
float xscale = 1, yscale = 1;
float xscale, yscale;
get_window_content_scale(w, &xscale, &yscale, x, y);
}

57
kitty/guess_mime_type.py Normal file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import os
from contextlib import suppress
from typing import Optional
known_extensions = {
'asciidoc': 'text/asciidoctor',
'conf': 'text/config',
'md': 'text/markdown',
'pyj': 'text/rapydscript-ng',
'recipe': 'text/python',
'rst': 'text/restructured-text',
'toml': 'text/toml',
'vim': 'text/vim',
'yaml': 'text/yaml',
'js': 'text/javascript',
'json': 'text/json',
}
text_mimes = ('application/javascript', 'application/x-sh', 'application/json')
def is_rc_file(path: str) -> bool:
name = os.path.basename(path)
return '.' not in name and name.endswith('rc')
def initialize_mime_database() -> None:
if hasattr(initialize_mime_database, 'inited'):
return
setattr(initialize_mime_database, 'inited', True)
from mimetypes import init
init(None)
from kitty.constants import config_dir
local_defs = os.path.join(config_dir, 'mime.types')
if os.path.exists(local_defs):
init((local_defs,))
def guess_type(path: str) -> Optional[str]:
from mimetypes import guess_type as stdlib_guess_type
initialize_mime_database()
mt = None
with suppress(Exception):
mt = stdlib_guess_type(path)[0]
if not mt:
ext = path.rpartition('.')[-1].lower()
mt = known_extensions.get(ext)
if mt in text_mimes:
mt = 'text/' + mt.split('/', 1)[-1]
if not mt and is_rc_file(path):
mt = 'text/plain'
return mt

View File

@@ -63,7 +63,7 @@ alloc_pagerhist(size_t pagerhist_sz) {
if (!ph) return NULL;
ph->max_sz = pagerhist_sz;
ph->buffer_size = MIN(1024u*1024u, ph->max_sz);
ph->buffer = PyMem_RawMalloc(ph->buffer_size);
ph->buffer = PyMem_Malloc(ph->buffer_size);
if (!ph->buffer) { PyMem_Free(ph); return NULL; }
return ph;
}

2
kitty/key_encoding.py generated
View File

@@ -424,7 +424,7 @@ def update_encoding() -> None:
ans = ENCODING
key_map = {}
i = len(ans)
for k in sorted(keys, key=lambda k: getattr(defines, k)):
for k in sorted(keys, key=lambda k: int(getattr(defines, k))):
if k in ('GLFW_KEY_LAST', 'GLFW_KEY_LAST_PRINTABLE'):
continue
val = getattr(defines, k)

View File

@@ -245,10 +245,8 @@ empty:
error:
Py_XDECREF(py_ret);
if (procargs != NULL)
free(procargs);
if (procenv != NULL)
free(procargs);
free(procargs);
free(procenv);
return NULL;
}

View File

@@ -195,6 +195,14 @@ cell_for_pos(Window *w, unsigned int *x, unsigned int *y, bool *in_left_half_of_
#define HANDLER(name) static inline void name(Window UNUSED *w, int UNUSED button, int UNUSED modifiers, unsigned int UNUSED window_idx)
static inline void
set_mouse_cursor_when_dragging(void) {
if (mouse_cursor_shape != OPT(pointer_shape_when_dragging)) {
mouse_cursor_shape = OPT(pointer_shape_when_dragging);
set_mouse_cursor(mouse_cursor_shape);
}
}
static inline void
update_drag(bool from_button, Window *w, bool is_release, int modifiers) {
Screen *screen = w->render_data.screen;
@@ -213,6 +221,7 @@ update_drag(bool from_button, Window *w, bool is_release, int modifiers) {
} else if (screen->selections.in_progress) {
screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, false, false);
}
set_mouse_cursor_when_dragging();
}
static inline bool
@@ -250,6 +259,7 @@ extend_selection(Window *w, bool ended) {
if (screen_has_selection(screen)) {
screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, ended, false);
}
set_mouse_cursor_when_dragging();
}
static inline void
@@ -265,7 +275,7 @@ extend_url(Screen *screen, Line *line, index_type *x, index_type *y, char_type s
// we deliberately allow non-continued lines as some programs, like
// mutt split URLs with newlines at line boundaries
index_type new_x = line_url_end_at(line, 0, false, sentinel, next_line_starts_with_url_chars);
if (!new_x) break;
if (!new_x && !line_startswith_url_chars(line)) break;
*y += 1; *x = new_x;
}
}
@@ -295,7 +305,7 @@ get_url_sentinel(Line *line, index_type url_start) {
static inline void
set_mouse_cursor_for_screen(Screen *screen) {
mouse_cursor_shape = screen->modes.mouse_tracking_mode == NO_TRACKING ? BEAM : OPT(pointer_shape_when_grabbed);
mouse_cursor_shape = screen->modes.mouse_tracking_mode == NO_TRACKING ? OPT(default_pointer_shape): OPT(pointer_shape_when_grabbed);
}
static inline void
@@ -397,6 +407,7 @@ multi_click(Window *w, unsigned int count) {
screen_start_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, false, mode);
screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, false, true);
}
set_mouse_cursor_when_dragging();
}
static inline double

View File

@@ -17,6 +17,7 @@ from .config import KeyAction, parse_key_action
from .constants import config_dir
from .typing import MatchType
from .utils import expandvars, log_error
from .guess_mime_type import guess_type
class MatchCriteria(NamedTuple):
@@ -75,12 +76,8 @@ def url_matches_criterion(purl: 'ParseResult', url: str, unquoted_path: str, mc:
if mc.type == 'mime':
import fnmatch
from mimetypes import guess_type
try:
mt = guess_type(unquoted_path)[0]
except Exception:
return False
if mt is None:
mt = guess_type(unquoted_path)
if not mt:
return False
mt = mt.lower()
for mpat in mc.value.split(','):
@@ -161,8 +158,14 @@ def actions_for_url_from_list(url: str, actions: Iterable[OpenAction]) -> Genera
}
def expand(x: Any) -> Any:
as_bytes = isinstance(x, bytes)
if as_bytes:
x = x.decode('utf-8')
if isinstance(x, str):
return expandvars(x, env, fallback_to_os_env=False)
ans = expandvars(x, env, fallback_to_os_env=False)
if as_bytes:
return ans.encode('utf-8')
return ans
return x
for action in actions:

View File

@@ -373,8 +373,10 @@ selection_has_screen_line(const Selections *selections, const int y) {
for (size_t i = 0; i < selections->count; i++) {
const Selection *s = selections->items + i;
if (!is_selection_empty(s)) {
int top = (int)s->start.y - s->start_scrolled_by;
int bottom = (int)s->end.y - s->end_scrolled_by;
int start = (int)s->start.y - s->start_scrolled_by;
int end = (int)s->end.y - s->end_scrolled_by;
int top = MIN(start, end);
int bottom = MAX(start, end);
if (top <= y && y <= bottom) return true;
}
}
@@ -1311,7 +1313,7 @@ screen_delete_lines(Screen *self, unsigned int count) {
void
screen_insert_characters(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
const unsigned int top = 0, bottom = self->lines ? self->lines - 1 : 0;
if (count == 0) count = 1;
if (top <= self->cursor->y && self->cursor->y <= bottom) {
unsigned int x = self->cursor->x;
@@ -1327,7 +1329,7 @@ screen_insert_characters(Screen *self, unsigned int count) {
void
screen_repeat_character(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
const unsigned int top = 0, bottom = self->lines ? self->lines - 1 : 0;
unsigned int x = self->cursor->x;
if (count == 0) count = 1;
if (x > self->columns) return;
@@ -1349,7 +1351,7 @@ screen_repeat_character(Screen *self, unsigned int count) {
void
screen_delete_characters(Screen *self, unsigned int count) {
// Delete characters, later characters are moved left
unsigned int top = self->margin_top, bottom = self->margin_bottom;
const unsigned int top = 0, bottom = self->lines ? self->lines - 1 : 0;
if (count == 0) count = 1;
if (top <= self->cursor->y && self->cursor->y <= bottom) {
unsigned int x = self->cursor->x;

View File

@@ -115,7 +115,7 @@ def parse_session(raw: str, opts: Options, default_title: Optional[str] = None)
w: Optional[Watchers] = None
if t.watchers.has_watchers:
w = t.watchers.copy()
t.windows.append(SpecialWindow(cmd=resolved_shell(opts), watchers=w))
t.windows.append(SpecialWindow(cmd=resolved_shell(opts), watchers=w, override_title=default_title))
return ans
ans = Session(default_title)

View File

@@ -703,6 +703,8 @@ PYWRAP1(set_options) {
S(resize_in_steps, PyObject_IsTrue);
S(allow_hyperlinks, PyObject_IsTrue);
S(pointer_shape_when_grabbed, pointer_shape);
S(default_pointer_shape, pointer_shape);
S(pointer_shape_when_dragging, pointer_shape);
GA(tab_bar_style);
global_state.tab_bar_hidden = PyUnicode_CompareWithASCIIString(ret, "hidden") == 0 ? true: false;

View File

@@ -66,6 +66,8 @@ typedef struct {
bool allow_hyperlinks;
monotonic_t resize_debounce_time;
MouseShape pointer_shape_when_grabbed;
MouseShape default_pointer_shape;
MouseShape pointer_shape_when_dragging;
struct {
UrlPrefix *values;
size_t num, max_prefix_len;

View File

@@ -4,7 +4,10 @@
import re
from binascii import hexlify, unhexlify
from typing import cast, Dict
from typing import TYPE_CHECKING, Dict, Generator, Optional, cast
if TYPE_CHECKING:
from .options_stub import Options
def modify_key_bytes(keybytes: bytes, amt: int) -> bytes:
@@ -449,28 +452,42 @@ def key_as_bytes(name: str) -> bytes:
return ans.encode('ascii')
def get_capabilities(query_string: str) -> str:
def get_capabilities(query_string: str, opts: 'Options') -> Generator[str, None, None]:
from .fast_data_types import ERROR_PREFIX
ans = []
try:
for q in query_string.split(';'):
name = qname = unhexlify(q).decode('utf-8')
if name in ('TN', 'name'):
val = names[0]
def result(encoded_query_name: str, x: Optional[str] = None) -> str:
if x is None:
return '0+r' + encoded_query_name
return '1+r' + encoded_query_name + '=' + hexlify(str(x).encode('utf-8')).decode('ascii')
for encoded_query_name in query_string.split(';'):
name = qname = unhexlify(encoded_query_name).decode('utf-8')
if name in ('TN', 'name'):
yield result(encoded_query_name, names[0])
elif name.startswith('kitty-query-'):
name = name[len('kitty-query-'):]
if name == 'version':
from .constants import str_version
yield result(encoded_query_name, str_version)
elif name == 'allow_hyperlinks':
yield result(encoded_query_name,
'ask' if opts.allow_hyperlinks == 0b11 else ('yes' if opts.allow_hyperlinks else 'no'))
else:
from .utils import log_error
log_error('Unknown kitty terminfo query:', name)
yield result(encoded_query_name)
else:
try:
val = queryable_capabilities[name]
except KeyError:
try:
val = queryable_capabilities[name]
except KeyError:
try:
qname = termcap_aliases[name]
val = queryable_capabilities[qname]
except Exception:
from .utils import log_error
log_error(ERROR_PREFIX, 'Unknown terminfo property:', name)
raise
if qname in string_capabilities and '%' not in val:
val = key_as_bytes(qname).decode('ascii')
ans.append(q + '=' + hexlify(str(val).encode('utf-8')).decode('ascii'))
return '1+r' + ';'.join(ans)
except Exception:
return '0+r' + query_string
qname = termcap_aliases[name]
val = queryable_capabilities[qname]
except Exception:
from .utils import log_error
log_error(ERROR_PREFIX, 'Unknown terminfo property:', name)
yield result(encoded_query_name)
continue
if qname in string_capabilities and '%' not in val:
val = key_as_bytes(qname).decode('ascii')
yield result(encoded_query_name, val)

View File

@@ -580,6 +580,13 @@ def parse_uri_list(text: str) -> Generator[str, None, None]:
yield unquote(purl.path)
def edit_config_file() -> None:
from kitty.config import prepare_config_file_for_editing
p = prepare_config_file_for_editing()
editor = get_editor()
os.execvp(editor[0], editor + [p])
class SSHConnectionData(NamedTuple):
binary: str
hostname: str

View File

@@ -704,7 +704,8 @@ class Window:
self.refresh()
def request_capabilities(self, q: str) -> None:
self.screen.send_escape_code_to_child(DCS, get_capabilities(q))
for result in get_capabilities(q, self.opts):
self.screen.send_escape_code_to_child(DCS, result)
def handle_remote_cmd(self, cmd: str) -> None:
get_boss().handle_remote_cmd(cmd, self)

View File

@@ -31,8 +31,8 @@ class Callbacks:
def request_capabilities(self, q):
from kitty.terminfo import get_capabilities
c = get_capabilities(q)
self.write(c.encode('ascii'))
for c in get_capabilities(q, None):
self.write(c.encode('ascii'))
def use_utf8(self, on):
self.iutf8 = on

View File

@@ -142,6 +142,13 @@ class TestScreen(BaseTest):
self.ae(str(s.line(0)), 'ade')
self.assertTrue(s.line(0).cursor_from(4).bold)
self.assertFalse(s.line(0).cursor_from(2).bold)
s = self.create_screen()
s.set_margins(1, 2)
s.cursor.y = 3
s.draw('abcde')
s.cursor.x = 0
s.delete_characters(2)
self.ae('cde', str(s.line(s.cursor.y)))
init()
s.erase_characters(2)

View File

@@ -42,10 +42,11 @@ safe_realpath(const char* src, char *buf, size_t buf_sz) {
#endif
static inline void
set_xoptions(const wchar_t *exe_dir, const char *lc_ctype) {
set_xoptions(const wchar_t *exe_dir, const char *lc_ctype, bool from_source) {
wchar_t buf[PATH_MAX+1] = {0};
swprintf(buf, PATH_MAX, L"bundle_exe_dir=%ls", exe_dir);
PySys_AddXOption(buf);
if (from_source) PySys_AddXOption(L"kitty_from_source=1");
if (lc_ctype) {
swprintf(buf, PATH_MAX, L"lc_ctype_before_python=%s", lc_ctype);
PySys_AddXOption(buf);
@@ -66,7 +67,6 @@ static int run_embedded(const char* exe_dir_, const char *libpath, int argc, wch
int ret = 1;
wchar_t *exe_dir = Py_DecodeLocale(exe_dir_, NULL);
if (exe_dir == NULL) { fprintf(stderr, "Fatal error: cannot decode exe_dir\n"); return 1; }
set_xoptions(exe_dir, lc_ctype);
wchar_t stdlib[PATH_MAX+1] = {0};
#ifdef __APPLE__
const char *python_relpath = "../Resources/Python/lib";
@@ -80,9 +80,9 @@ static int run_embedded(const char* exe_dir_, const char *libpath, int argc, wch
);
if (num < 0 || num >= PATH_MAX) { fprintf(stderr, "Failed to create path to python stdlib\n"); return 1; }
Py_SetPath(stdlib);
PyMem_RawFree(exe_dir);
if (num < 0 || num >= PATH_MAX) { fprintf(stderr, "Failed to create path to kitty lib\n"); return 1; }
Py_Initialize();
set_xoptions(exe_dir, lc_ctype, false);
PyMem_RawFree(exe_dir);
PySys_SetArgvEx(argc - 1, argv + 1, 0);
PySys_SetObject("frozen", Py_True);
PyObject *kitty = PyUnicode_FromString(libpath);
@@ -104,10 +104,11 @@ static int run_embedded(const char* exe_dir_, const char *libpath, int argc, wch
(void)libpath;
wchar_t *exe_dir = Py_DecodeLocale(exe_dir_, NULL);
if (exe_dir == NULL) { fprintf(stderr, "Fatal error: cannot decode exe_dir: %s\n", exe_dir_); return 1; }
set_xoptions(exe_dir, lc_ctype);
bool from_source = false;
#ifdef FROM_SOURCE
PySys_AddXOption(L"kitty_from_source=1");
from_source = true;
#endif
set_xoptions(exe_dir, lc_ctype, from_source);
PyMem_RawFree(exe_dir);
return Py_Main(argc, argv);
}

View File

@@ -137,7 +137,7 @@ class ReadFileWithProgressReporting(io.FileIO): # {{{
def __len__(self) -> int:
return self._total
def read(self, size: int = -1) -> Optional[bytes]:
def read(self, size: int = -1) -> bytes:
data = io.FileIO.read(self, size)
if data:
self.report_progress(len(data))

View File

@@ -63,11 +63,11 @@ class Options(argparse.Namespace):
for_freeze: bool = False
libdir_name: str = 'lib'
extra_logging: List[str] = []
link_time_optimization: bool = True
link_time_optimization: bool = 'KITTY_NO_LTO' not in os.environ
update_check_interval: float = 24
egl_library: Optional[str] = None
startup_notification_library: Optional[str] = None
canberra_library: Optional[str] = None
egl_library: Optional[str] = os.getenv('KITTY_EGL_LIBRARY')
startup_notification_library: Optional[str] = os.getenv('KITTY_STARTUP_NOTIFICATION_LIBRARY')
canberra_library: Optional[str] = os.getenv('KITTY_CANBERRA_LIBRARY')
class CompileKey(NamedTuple):
@@ -211,11 +211,11 @@ def get_sanitize_args(cc: str, ccver: Tuple[int, int]) -> List[str]:
return sanitize_args
def test_compile(cc: str, *cflags: str, src: Optional[str] = None) -> bool:
def test_compile(cc: str, *cflags: str, src: Optional[str] = None, lang: str = 'c') -> bool:
src = src or 'int main(void) { return 0; }'
p = subprocess.Popen(
[cc] + list(cflags) + ['-x', 'c', '-o', os.devnull, '-'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.PIPE
[cc] + list(cflags) + ['-x', lang, '-o', os.devnull, '-'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.PIPE,
)
stdin = p.stdin
assert stdin is not None
@@ -227,9 +227,9 @@ def test_compile(cc: str, *cflags: str, src: Optional[str] = None) -> bool:
return p.wait() == 0
def first_successful_compile(cc: str, *cflags: str, src: Optional[str] = None) -> str:
def first_successful_compile(cc: str, *cflags: str, src: Optional[str] = None, lang: str = 'c') -> str:
for x in cflags:
if test_compile(cc, *shlex.split(x), src=src):
if test_compile(cc, *shlex.split(x), src=src, lang=lang):
return x
return ''
@@ -340,7 +340,10 @@ def kitty_env() -> Env:
platform_libs = [
'-framework', 'CoreText', '-framework', 'CoreGraphics',
]
user_notifications_framework = first_successful_compile(ans.cc, '-framework UserNotifications')
test_program_src = '''#include <UserNotifications/UserNotifications.h>
int main(void) { return 0; }\n'''
user_notifications_framework = first_successful_compile(
ans.cc, '-framework UserNotifications', src=test_program_src, lang='objective-c')
if user_notifications_framework:
platform_libs.extend(shlex.split(user_notifications_framework))
else:

55
shell.nix Normal file
View File

@@ -0,0 +1,55 @@
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
let
inherit (lib) optional optionals;
inherit (xorg) libX11 libXrandr libXinerama libXcursor libXi libXext;
inherit (darwin.apple_sdk.frameworks) Cocoa CoreGraphics Foundation IOKit Kernel OpenGL;
harfbuzzWithCoreText = harfbuzz.override { withCoreText = stdenv.isDarwin; };
in
mkShell rec {
buildInputs = [
harfbuzzWithCoreText
ncurses
lcms2
] ++ optionals stdenv.isDarwin [
Cocoa
CoreGraphics
Foundation
IOKit
Kernel
OpenGL
libpng
python3
zlib
] ++ optionals stdenv.isLinux [
fontconfig libunistring libcanberra libX11
libXrandr libXinerama libXcursor libxkbcommon libXi libXext
wayland-protocols wayland dbus
] ++ checkInputs;
nativeBuildInputs = [
pkgconfig python3Packages.sphinx ncurses
] ++ optionals stdenv.isDarwin [
imagemagick
libicns # For the png2icns tool.
installShellFiles
];
propagatedBuildInputs = optional stdenv.isLinux libGL;
checkInputs = [
python3Packages.pillow
];
# Causes build failure due to warning when using Clang
hardeningDisable = [ "strictoverflow" ];
shellHook = if stdenv.isDarwin then ''
export KITTY_NO_LTO=
'' else ''
export KITTY_EGL_LIBRARY='${stdenv.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'
'';
}

View File

@@ -97,7 +97,7 @@ def run_tests() -> None:
def run_cli(suite: unittest.TestSuite, verbosity: int = 4) -> None:
r = unittest.TextTestRunner
r.resultclass = unittest.TextTestResult # type: ignore
r.resultclass = unittest.TextTestResult
init_env()
runner = r(verbosity=verbosity)
runner.tb_locals = True # type: ignore