Compare commits

..

43 Commits

Author SHA1 Message Date
Kovid Goyal
a5fea33757 version 0.33.1 2024-03-21 08:34:07 +05:30
Kovid Goyal
8cc360e344 Make preferential usage of NERD font for manual fallback more efficient 2024-03-20 20:33:50 +05:30
Kovid Goyal
a285d09459 ... 2024-03-20 20:07:03 +05:30
Kovid Goyal
e646596c5b macOS: When CoreText fails to find a fallback font for a character in the first Private Use Unicode Area, preferentially use the NERD font, if available, for it
Fixes #6043
2024-03-20 20:01:17 +05:30
Kovid Goyal
f3d9ad3244 ... 2024-03-20 18:59:52 +05:30
Kovid Goyal
752fcb6424 macOS: Fix text rendered with fallback fonts not respecting bold/italic styling 2024-03-20 18:23:09 +05:30
Kovid Goyal
0042288e92 remove unused headers 2024-03-20 17:28:14 +05:30
Kovid Goyal
7ea0af6f6d Fix debug-font-fallback to report re-used faces correctly 2024-03-20 17:26:18 +05:30
Kovid Goyal
69c0eaaf74 Dont request sRGB surfaces on Wayland
Apparently mesa just completely broke it. Besides it being already
broken on NVIDIA. Sigh, more of my life wasted on Wayland.

See https://github.com/kovidgoyal/kitty/issues/7174#issuecomment-2000033873
2024-03-19 08:57:48 +05:30
Kovid Goyal
a0424bf1bd Dont omit frame pointer in debug and profile builds 2024-03-18 10:56:50 +05:30
Kovid Goyal
481eccfe3c Merge branch 'dependabot/go_modules/all-go-deps-75b28919f3' of https://github.com/kovidgoyal/kitty 2024-03-18 09:28:52 +05:30
dependabot[bot]
c5c3f98595 Bump the all-go-deps group with 1 update
Bumps the all-go-deps group with 1 update: [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma).


Updates `github.com/alecthomas/chroma/v2` from 2.12.0 to 2.13.0
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v2.12.0...v2.13.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-go-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 03:35:28 +00:00
Kovid Goyal
3c19b6f734 Merge branch 'docs/fine-tuning' of https://github.com/Uaitt/kitty 2024-03-17 09:05:18 +05:30
Lorenzo Zabot
2d8deb86bb docs: minor adjustements 2024-03-16 14:43:23 +01:00
Kovid Goyal
5e0185d3eb Ensure KITTY_NO_SIMD is defined for all files on arches without SIMD support 2024-03-15 12:19:47 +05:30
Kovid Goyal
aee84c32e5 Merge branch 'fix-typo' of https://github.com/KaranveerB/kitty 2024-03-15 11:53:19 +05:30
KaranveerB
19a9594143 Fix typo in mapping.rst 2024-03-14 23:01:27 -07:00
Kovid Goyal
32f0da2e77 Ensure no frame is created for assembly functions 2024-03-15 07:58:09 +05:30
Kovid Goyal
d329cb3fff Update FAQ 2024-03-14 21:40:16 +05:30
Kovid Goyal
3bb9e36fc8 ... 2024-03-14 21:00:57 +05:30
Kovid Goyal
76ae5f5b9b DRYer: Use the SIMD detection in setup.py to avoid calling __builtin_cpu_supports 2024-03-14 20:57:09 +05:30
Kovid Goyal
393169f79d Fix #7225 2024-03-14 20:55:05 +05:30
Kovid Goyal
f5570c38dd Turn off sanitizers in CI as they are segfaulting
Trying to debug this in CI is too much work. Hopefully whatever
update in the CI env that is causing these will eventually be fixed.
2024-03-14 18:37:19 +05:30
Kovid Goyal
0153c9bb85 Use -g3 for profiling rather than -g 2024-03-14 17:07:38 +05:30
Kovid Goyal
1712a317d5 ... 2024-03-14 16:38:19 +05:30
Kovid Goyal
10cff577d6 Also get a backtrace when generating go code segfaults on CI 2024-03-14 16:25:52 +05:30
Kovid Goyal
86c59016c6 Try outputting core dump when multiprocessing spawn segfaults 2024-03-14 16:19:33 +05:30
Kovid Goyal
7821ae39ab Also need gdb to get coredumps in CI 2024-03-14 16:09:51 +05:30
Kovid Goyal
039d144c84 Splits layout: Allow resizing until one of the halves in a split is minimally sized
Fixes #7220
2024-03-14 15:59:23 +05:30
Kovid Goyal
af0d570725 Install systemd-coredump on CI so we can see coredumps 2024-03-14 15:18:33 +05:30
Kovid Goyal
288fa0128b Fix test suite running under sanitizers 2024-03-14 15:01:55 +05:30
Kovid Goyal
77125798a4 Redirect to NULL instead of pipe since we dont use the output 2024-03-14 12:15:15 +05:30
Kovid Goyal
c26954c69e ... 2024-03-14 12:05:57 +05:30
Kovid Goyal
9667da307c Print detected compiler type in verbose mode 2024-03-14 12:04:33 +05:30
Kovid Goyal
baa3ec0a62 Explicitly detect compiler types gcc vs clang 2024-03-14 12:02:01 +05:30
Kovid Goyal
478fc766b6 ... 2024-03-14 11:53:44 +05:30
Kovid Goyal
6c49066cde Fix undefined function pointer usage found by clang sanitizer 2024-03-14 11:47:56 +05:30
Kovid Goyal
a839af04dc Fix #7219 2024-03-14 11:13:54 +05:30
Kovid Goyal
3950632517 Switch to detecting clang rather than gcc
gcc makes it impossible to detect that it is gcc via --version
so instead detect clang and assume gcc if not clang.

Fixes #7218
2024-03-14 10:48:27 +05:30
Kovid Goyal
297ac9c3fe ... 2024-03-14 08:54:39 +05:30
Kovid Goyal
60d4ed3a1c ... 2024-03-13 14:12:49 +05:30
Kovid Goyal
a1b40cc8f5 Handle exception raised by Cocoa runtime when trying to get user notification center from a non-bundled executable 2024-03-13 11:43:38 +05:30
Kovid Goyal
5a9cf82564 Fix requesting data from clipboard via OSC 52 getting it from primary selection instead
Fixes #7213
2024-03-13 09:43:28 +05:30
29 changed files with 246 additions and 102 deletions

View File

@@ -75,7 +75,7 @@ def install_deps():
run('sudo apt-get install -y libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev ca-certificates'
' libxcursor-dev libxcb-xkb-dev libdbus-1-dev libxkbcommon-dev libharfbuzz-dev libx11-xcb-dev zsh'
' libpng-dev liblcms2-dev libfontconfig-dev libxkbcommon-x11-dev libcanberra-dev libxxhash-dev uuid-dev'
' libsimde-dev zsh bash dash')
' libsimde-dev zsh bash dash systemd-coredump gdb')
# for some reason these directories are world writable which causes zsh
# compinit to break
run('sudo chmod -R og-w /usr/share/zsh')

View File

@@ -2,7 +2,7 @@ name: CI
on: [push, pull_request]
env:
CI: 'true'
ASAN_OPTIONS: leak_check_at_exit=0
ASAN_OPTIONS: detect_leaks=0
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
@@ -28,11 +28,11 @@ jobs:
- python: b
pyver: "3.11"
sanitize: 1
sanitize: 0 # disabled because causes segfaults
- python: c
pyver: "3.9"
sanitize: 1
sanitize: 0 # disabled because causes segfaults
exclude:

View File

@@ -47,6 +47,18 @@ rsync algorithm to speed up repeated transfers of large files.
Detailed list of changes
-------------------------------------
0.33.1 [2024-03-21]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Fix a regression in the previous release that caused requesting data from the clipboard via OSC 52 to instead return data from the primary selection (:iss:`7213`)
- Splits layout: Allow resizing until one of the halves in a split is minimally sized (:iss:`7220`)
- macOS: Fix text rendered with fallback fonts not respecting bold/italic styling (:disc:`7241`)
- macOS: When CoreText fails to find a fallback font for a character in the first Private Use Unicode Area, preferentially use the NERD font, if available, for it (:iss:`6043`)
0.33.0 [2024-03-12]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -449,9 +449,6 @@ do not use them, if at all possible. kitty contains features that do all of what
tmux does, but better, with the exception of remote persistence (:iss:`391`).
If you still want to use tmux, read on.
Image display will not work, see `tmux issue
<https://github.com/tmux/tmux/issues/1391>`__.
Using ancient versions of tmux such as 1.8 will cause gibberish on screen when
pressing keys (:iss:`3541`).
@@ -460,6 +457,10 @@ and then switch to another and these terminals have different :envvar:`TERM`
variables, tmux will break. You will need to restart it as tmux does not support
multiple terminfo definitions.
Displaying images with inside programs such nvim or ranger may not work
depending on whether those programs have adopted support for the unicode
placeholders workaround that kitty created for tmux refusing to support images.
If you use any of the advanced features that kitty has innovated, such as
:doc:`styled underlines </underlines>`, :doc:`desktop notifications
</desktop-notifications>`, :doc:`extended keyboard support

View File

@@ -141,7 +141,7 @@ The Splits Layout
--------------------
This is the most flexible layout. You can create any arrangement of windows
by splitting exiting windows repeatedly. To best use this layout you should
by splitting existing windows repeatedly. To best use this layout you should
define a few extra key bindings in :file:`kitty.conf`::
# Create a new window splitting the space used by the existing one so that

View File

@@ -81,7 +81,7 @@ control scripts. To run a kitten on a key press::
map f1 kitten mykitten.py
Many of kitty;s features are themselves implemented as kittens, for example,
Many of kitty's features are themselves implemented as kittens, for example,
:doc:`/kittens/unicode_input`, :doc:`/kittens/hints` and
:doc:`/kittens/themes`. To learn about writing your own kittens, see
:doc:`/kittens/custom`.

View File

@@ -46,6 +46,11 @@ class BinaryArch(NamedTuple):
isa: ISA = ISA.AMD64
class CompilerType(Enum):
gcc = 'gcc'
clang = 'clang'
unknown = 'unknown'
class Env:
@@ -81,6 +86,7 @@ class Env:
self.binary_arch = binary_arch
self.native_optimizations = native_optimizations
self._cc_version_string = ''
self._compiler_type: Optional[CompilerType] = None
@property
def cc_version_string(self) -> str:
@@ -89,8 +95,16 @@ class Env:
return self._cc_version_string
@property
def is_gcc(self) -> bool:
return 'gcc' in self.cc_version_string.split(maxsplit=1)[0].lower()
def compiler_type(self) -> CompilerType:
if self._compiler_type is None:
raw = self.cc_version_string
if 'Free Software Foundation' in raw:
self._compiler_type = CompilerType.gcc
elif 'clang' in raw.lower().split():
self._compiler_type = CompilerType.clang
else:
self._compiler_type = CompilerType.unknown
return self._compiler_type
def copy(self) -> 'Env':
ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver)
@@ -286,7 +300,7 @@ def generate_wrappers(glfw_header: str) -> None:
int glfwCocoaSetBackgroundBlur(GLFWwindow *w, int blur_radius)
bool glfwSetX11WindowBlurred(GLFWwindow *w, bool enable_blur)
void* glfwGetX11Display(void)
int32_t glfwGetX11Window(GLFWwindow* window)
unsigned long glfwGetX11Window(GLFWwindow* window)
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
void glfwCocoaSetWindowChrome(GLFWwindow* window, unsigned int color, bool use_system_color, unsigned int system_color,\
int background_blur, unsigned int hide_window_decorations, bool show_text_in_titlebar, int color_space, float background_opacity, bool resizable)

3
glfw/x11_window.c vendored
View File

@@ -41,7 +41,6 @@
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
// Action for EWMH client messages
#define _NET_WM_STATE_REMOVE 0
@@ -3227,7 +3226,7 @@ GLFWAPI Display* glfwGetX11Display(void)
return _glfw.x11.display;
}
GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
GLFWAPI unsigned long glfwGetX11Window(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(None);

2
go.mod
View File

@@ -4,7 +4,7 @@ go 1.22
require (
github.com/ALTree/bigfloat v0.2.0
github.com/alecthomas/chroma/v2 v2.12.0
github.com/alecthomas/chroma/v2 v2.13.0
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/disintegration/imaging v1.6.2
github.com/dlclark/regexp2 v1.11.0

12
go.sum
View File

@@ -1,11 +1,11 @@
github.com/ALTree/bigfloat v0.2.0 h1:AwNzawrpFuw55/YDVlcPw0F0cmmXrmngBHhVrvdXPvM=
github.com/ALTree/bigfloat v0.2.0/go.mod h1:+NaH2gLeY6RPBPPQf4aRotPPStg+eXc8f9ZaE4vRfD4=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

View File

@@ -406,7 +406,7 @@ class ClipboardRequestManager:
def parse_osc_52(self, data: memoryview, is_partial: bool = False) -> None:
idx = find_in_memoryview(data, ord(b';'))
if idx > -1:
where = str(data[idx:], "utf-8", 'replace')
where = str(data[:idx], "utf-8", 'replace')
data = data[idx+1:]
else:
where = str(data, "utf-8", 'replace')

View File

@@ -441,7 +441,13 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
static void
schedule_notification(const char *identifier, const char *title, const char *body, const char *subtitle) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNUserNotificationCenter *center = nil;
@try {
center = [UNUserNotificationCenter currentNotificationCenter];
} @catch (NSException *e) {
log_error("Failed to get current UNUserNotificationCenter object with error: %s (%s)",
[[e name] UTF8String], [[e reason] UTF8String]);
}
if (!center) return;
// Configure the notification's payload.
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
@@ -503,7 +509,13 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
char *identifier = NULL, *title = NULL, *body = NULL, *subtitle = NULL;
if (!PyArg_ParseTuple(args, "zsz|z", &identifier, &title, &body, &subtitle)) return NULL;
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNUserNotificationCenter *center = nil;
@try {
center = [UNUserNotificationCenter currentNotificationCenter];
} @catch (NSException *e) {
log_error("Failed to get current UNUserNotificationCenter object with error: %s (%s)",
[[e name] UTF8String], [[e reason] UTF8String]);
}
if (!center) Py_RETURN_NONE;
if (!center.delegate) center.delegate = [[NotificationDelegate alloc] init];
queue_notification(identifier, title, body, subtitle);

View File

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

View File

@@ -184,38 +184,79 @@ glyph_id_for_codepoint_ctfont(CTFontRef ct_font, char_type ch) {
return glyphs[0];
}
static bool
cf_string_equals(CFStringRef a, CFStringRef b) { return CFStringCompare(a, b, 0) == kCFCompareEqualTo; }
#define LAST_RESORT_FONT_NAME "LastResort"
static bool
is_last_resort_font(CTFontRef new_font) {
CFStringRef name = CTFontCopyPostScriptName(new_font);
CFComparisonResult cr = CFStringCompare(name, CFSTR("LastResort"), 0);
bool ans = cf_string_equals(name, CFSTR(LAST_RESORT_FONT_NAME));
CFRelease(name);
return cr == kCFCompareEqualTo;
return ans;
}
static CTFontDescriptorRef _nerd_font_descriptor = NULL;
static CTFontRef nerd_font(CGFloat sz) {
static bool searched = false;
if (!searched) {
searched = true;
CFArrayRef fonts = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection());
const CFIndex count = CFArrayGetCount(fonts);
for (CFIndex i = 0; i < count; i++) {
CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);
CFStringRef name = CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute);
bool is_nerd_font = cf_string_equals(name, CFSTR("SymbolsNFM"));
CFRelease(name);
if (is_nerd_font) {
_nerd_font_descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, CTFontDescriptorCopyAttributes(descriptor));
break;
}
}
CFRelease(fonts);
}
return _nerd_font_descriptor ? CTFontCreateWithFontDescriptor(_nerd_font_descriptor, sz, NULL) : NULL;
}
static bool
font_can_render_cell(CTFontRef font, CPUCell *cell) {
char_type ch = cell->ch ? cell->ch : ' ';
bool found = true;
if (!glyph_id_for_codepoint_ctfont(font, ch)) found = false;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i] && found; i++) {
char_type cch = codepoint_for_mark(cell->cc_idx[i]);
if (!glyph_id_for_codepoint_ctfont(font, cch)) found = false;
}
return found;
}
static CTFontRef
manually_search_fallback_fonts(CTFontRef current_font, CPUCell *cell) {
char_type ch = cell->ch ? cell->ch : ' ';
const bool in_first_pua = 0xe000 <= ch && ch <= 0xf8ff;
// preferentially load from NERD fonts
if (in_first_pua) {
CTFontRef nf = nerd_font(CTFontGetSize(current_font));
if (nf) {
if (font_can_render_cell(nf, cell)) return nf;
CFRelease(nf);
}
}
CFArrayRef fonts = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection());
CTFontRef ans = NULL;
const CFIndex count = CFArrayGetCount(fonts);
for (CFIndex i = 0; i < count; i++) {
CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);
CTFontRef new_font = CTFontCreateWithFontDescriptor(descriptor, CTFontGetSize(current_font), NULL);
if (new_font) {
if (!is_last_resort_font(new_font)) {
char_type ch = cell->ch ? cell->ch : ' ';
bool found = true;
if (!glyph_id_for_codepoint_ctfont(new_font, ch)) found = false;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i] && found; i++) {
ch = codepoint_for_mark(cell->cc_idx[i]);
if (!glyph_id_for_codepoint_ctfont(new_font, ch)) found = false;
}
if (found) {
ans = new_font;
break;
}
if (!is_last_resort_font(new_font)) {
if (font_can_render_cell(new_font, cell)) {
ans = new_font;
break;
}
CFRelease(new_font);
}
CFRelease(new_font);
}
CFRelease(fonts);
return ans;
@@ -231,16 +272,15 @@ find_substitute_face(CFStringRef str, CTFontRef old_font, CPUCell *cpu_cell) {
CTFontRef new_font = CTFontCreateForString(old_font, str, CFRangeMake(start, amt));
if (amt == len && len != 1) amt = 1;
else start++;
if (new_font == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to find fallback CTFont"); return NULL; }
if (new_font == old_font) { CFRelease(new_font); continue; }
if (is_last_resort_font(new_font)) {
CFRelease(new_font);
if (!new_font || is_last_resort_font(new_font)) {
if (new_font) CFRelease(new_font);
if (is_private_use(cpu_cell->ch)) {
// CoreTexts fallback font mechanism does not work for private use characters
new_font = manually_search_fallback_fonts(old_font, cpu_cell);
if (new_font) return new_font;
}
PyErr_Format(PyExc_ValueError, "Failed to find fallback CTFont other than the LastResort font for: %s", [(NSString *)str UTF8String]);
PyErr_Format(PyExc_ValueError, "Failed to find fallback CTFont other than the %s font for: %s", LAST_RESORT_FONT_NAME, [(NSString *)str UTF8String]);
return NULL;
}
return new_font;
@@ -249,8 +289,26 @@ find_substitute_face(CFStringRef str, CTFontRef old_font, CPUCell *cpu_cell) {
return NULL;
}
static CTFontRef
apply_styles_to_fallback_font(CTFontRef original_fallback_font, bool bold, bool italic) {
if (!original_fallback_font || is_last_resort_font(original_fallback_font)) return original_fallback_font;
CTFontRef ans = nil;
CTFontDescriptorRef original_descriptor = CTFontCopyFontDescriptor(original_fallback_font);
CTFontSymbolicTraits traits = kCTFontTraitMonoSpace;
if (bold) traits |= kCTFontTraitBold;
if (italic) traits |= kCTFontTraitItalic;
CTFontDescriptorRef descriptor = CTFontDescriptorCreateCopyWithSymbolicTraits(original_descriptor, traits, traits);
CFRelease(original_descriptor);
if (descriptor) {
ans = CTFontCreateWithFontDescriptor(descriptor, CTFontGetSize(original_fallback_font), NULL);
CFRelease(descriptor);
}
if (ans) { CFRelease(original_fallback_font); return ans; }
return original_fallback_font;
}
PyObject*
create_fallback_face(PyObject *base_face, CPUCell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {
create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {
CTFace *self = (CTFace*)base_face;
CTFontRef new_font;
#define search_for_fallback() \
@@ -268,21 +326,19 @@ create_fallback_face(PyObject *base_face, CPUCell* cell, bool UNUSED bold, bool
search_for_fallback();
}
}
else { search_for_fallback(); }
else { search_for_fallback(); new_font = apply_styles_to_fallback_font(new_font, bold, italic); }
if (new_font == NULL) return NULL;
NSURL *url = (NSURL*)CTFontCopyAttribute(new_font, kCTFontURLAttribute);
const char *font_path = [[url path] UTF8String];
PyObject *postscript_name = Py_BuildValue("s", convert_cfstring(CTFontCopyPostScriptName(new_font), true));
ssize_t idx = -1;
PyObject *q, *ans = NULL;
while ((q = iter_fallback_faces(fg, &idx))) {
CTFace *qf = (CTFace*)q;
const char *qpath;
if (qf->path && (qpath = PyUnicode_AsUTF8(qf->path)) && strcmp(qpath, font_path) == 0) {
if (PyObject_RichCompareBool(postscript_name, qf->postscript_name, Py_EQ) == 1) {
ans = PyLong_FromSsize_t(idx);
break;
}
}
[url release];
Py_CLEAR(postscript_name);
if (ans == NULL) return (PyObject*)ct_face(new_font, fg);
CFRelease(new_font);
return ans;
@@ -449,6 +505,7 @@ finalize(void) {
if (all_fonts_collection_data) CFRelease(all_fonts_collection_data);
if (window_title_font) CFRelease(window_title_font);
window_title_font = nil;
if (_nerd_font_descriptor) CFRelease(_nerd_font_descriptor);
}

View File

@@ -194,7 +194,7 @@ queue_canberra_sound(const char *which_sound, const char *event_id, bool is_path
current_sound.event_id = strdup(event_id);
current_sound.media_role = strdup(media_role);
current_sound.is_path = is_path;
current_sound.theme_name = strdup(theme_name);
current_sound.theme_name = theme_name ? strdup(theme_name) : NULL;
pthread_mutex_unlock(&canberra_lock);
while (true) {
ssize_t ret = write(canberra_pipe_w, "w", 1);

View File

@@ -5,13 +5,11 @@
* Distributed under terms of the GPL3 license.
*/
#include "state.h"
#include "cleanup.h"
#include "lineops.h"
#include "fonts.h"
#include <fontconfig/fontconfig.h>
#include <dlfcn.h>
#include "emoji.h"
#include "freetype_render_ui_text.h"
#ifndef FC_COLOR
#define FC_COLOR "color"

View File

@@ -15,9 +15,8 @@
#define MISSING_GLYPH (NUM_UNDERLINE_STYLES + 2)
#define MAX_NUM_EXTRA_GLYPHS_PUA 4u
typedef void (*send_sprite_to_gpu_func)(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*);
send_sprite_to_gpu_func current_send_sprite_to_gpu = NULL;
static PyObject *python_send_to_gpu_impl = NULL;
#define current_send_sprite_to_gpu(...) (python_send_to_gpu_impl ? python_send_to_gpu(__VA_ARGS__) : send_sprite_to_gpu(__VA_ARGS__))
extern PyTypeObject Line_Type;
enum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};
@@ -453,7 +452,7 @@ has_cell_text(Font *self, CPUCell *cell) {
}
static void
output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face) {
printf("U+%x ", cell->ch);
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
@@ -461,8 +460,8 @@ output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_pres
if (bold) printf("bold ");
if (italic) printf("italic ");
if (emoji_presentation) printf("emoji_presentation ");
if (PyLong_Check(face)) printf("using previous fallback font at index: ");
PyObject_Print(face, stdout, 0);
if (new_face) printf(" (new face)");
printf("\n");
}
@@ -488,7 +487,7 @@ load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool em
PyObject *face = create_fallback_face(fg->fonts[f].face, cell, bold, italic, emoji_presentation, (FONTS_DATA_HANDLE)fg);
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face, true);
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face);
if (PyLong_Check(face)) { ssize_t ans = fg->first_fallback_font_idx + PyLong_AsSsize_t(face); Py_DECREF(face); return ans; }
set_size_for_face(face, fg->cell_height, true, (FONTS_DATA_HANDLE)fg);
@@ -1580,7 +1579,6 @@ set_send_sprite_to_gpu(PyObject UNUSED *self, PyObject *func) {
python_send_to_gpu_impl = func;
Py_INCREF(python_send_to_gpu_impl);
}
current_send_sprite_to_gpu = python_send_to_gpu_impl ? python_send_to_gpu : send_sprite_to_gpu;
Py_RETURN_NONE;
}
@@ -1738,6 +1736,5 @@ init_fonts(PyObject *module) {
create_feature("-calt", CALT_FEATURE);
#undef create_feature
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
current_send_sprite_to_gpu = send_sprite_to_gpu;
return true;
}

View File

@@ -38,10 +38,6 @@ check_for_gl_error(void UNUSED *ret, const char *name, GLADapiproc UNUSED funcpt
}
}
static bool is_nvidia = false;
bool is_nvidia_gpu_driver(void) { return is_nvidia; }
void
gl_init(void) {
static bool glad_loaded = false;
@@ -64,7 +60,6 @@ gl_init(void) {
int gl_major = GLAD_VERSION_MAJOR(gl_version);
int gl_minor = GLAD_VERSION_MINOR(gl_version);
const char *gvs = (const char*)glGetString(GL_VERSION);
if (strstr(gvs, "NVIDIA")) is_nvidia = true;
if (global_state.debug_rendering) printf("GL version string: '%s' Detected version: %d.%d\n", gvs, gl_major, gl_minor);
if (gl_major < OPENGL_REQUIRED_VERSION_MAJOR || (gl_major == OPENGL_REQUIRED_VERSION_MAJOR && gl_minor < OPENGL_REQUIRED_VERSION_MINOR)) {
fatal("OpenGL version is %d.%d, version >= 3.3 required for kitty", gl_major, gl_minor);

View File

@@ -56,4 +56,3 @@ void bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index)
void unbind_vertex_array(void);
void unbind_program(void);
GLuint compile_shaders(GLenum shader_type, GLsizei count, const GLchar * const * string);
bool is_nvidia_gpu_driver(void);

2
kitty/glfw-wrapper.h generated
View File

@@ -2258,7 +2258,7 @@ typedef void* (*glfwGetX11Display_func)(void);
GFW_EXTERN glfwGetX11Display_func glfwGetX11Display_impl;
#define glfwGetX11Display glfwGetX11Display_impl
typedef int32_t (*glfwGetX11Window_func)(GLFWwindow*);
typedef unsigned long (*glfwGetX11Window_func)(GLFWwindow*);
GFW_EXTERN glfwGetX11Window_func glfwGetX11Window_impl;
#define glfwGetX11Window glfwGetX11Window_impl

View File

@@ -1044,7 +1044,7 @@ native_window_handle(GLFWwindow *w) {
void *ans = glfwGetCocoaWindow(w);
return PyLong_FromVoidPtr(ans);
#endif
if (glfwGetX11Window) return PyLong_FromLong((long)glfwGetX11Window(w));
if (glfwGetX11Window) return PyLong_FromUnsignedLong(glfwGetX11Window(w));
return Py_None;
}
@@ -1077,7 +1077,10 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
// Request SRGB output buffer
// Prevents kitty from starting on Wayland + NVIDIA, sigh: https://github.com/kovidgoyal/kitty/issues/7021
// Remove after https://github.com/NVIDIA/egl-wayland/issues/85 is fixed.
if (!global_state.is_wayland || !is_nvidia_gpu_driver()) glfwWindowHint(GLFW_SRGB_CAPABLE, true);
// Also apparently mesa has introduced a bug with sRGB surfaces and Wayland.
// Sigh. Wayland is such a pile of steaming crap.
// See https://github.com/kovidgoyal/kitty/issues/7174#issuecomment-2000033873
if (!global_state.is_wayland) glfwWindowHint(GLFW_SRGB_CAPABLE, true);
#ifdef __APPLE__
cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
@@ -1768,7 +1771,7 @@ x11_window_id(PyObject UNUSED *self, PyObject *os_wid) {
OSWindow *w = os_window_for_id(PyLong_AsUnsignedLongLong(os_wid));
if (!w) { PyErr_SetString(PyExc_ValueError, "No OSWindow with the specified id found"); return NULL; }
if (!glfwGetX11Window) { PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwGetX11Window"); return NULL; }
return Py_BuildValue("l", (long)glfwGetX11Window(w->handle));
return PyLong_FromUnsignedLong(glfwGetX11Window(w->handle));
}
static PyObject*

View File

@@ -165,6 +165,36 @@ class Pair:
return id_window_map[wid].effective_border()
return 0
def minimum_width(self, id_window_map: Dict[int, WindowGroup]) -> int:
if self.one is None or self.two is None or not self.horizontal:
return lgd.cell_width
bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0
ans = 2 * bw
if isinstance(self.one, Pair):
ans += self.one.minimum_width(id_window_map)
else:
ans += lgd.cell_width
if isinstance(self.two, Pair):
ans += self.two.minimum_width(id_window_map)
else:
ans += lgd.cell_width
return ans
def minimum_height(self, id_window_map: Dict[int, WindowGroup]) -> int:
if self.one is None or self.two is None or self.horizontal:
return lgd.cell_height
bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0
ans = 2 * bw
if isinstance(self.one, Pair):
ans += self.one.minimum_height(id_window_map)
else:
ans += lgd.cell_height
if isinstance(self.two, Pair):
ans += self.two.minimum_height(id_window_map)
else:
ans += lgd.cell_height
return ans
def layout_pair(
self,
left: int, top: int, width: int, height: int,
@@ -191,8 +221,13 @@ class Pair:
self.apply_window_geometry(q, geom, id_window_map, layout_object)
return
if self.horizontal:
w1 = max(2*lgd.cell_width + 1, int(self.bias * width) - bw)
w2 = max(2*lgd.cell_width + 1, width - w1 - bw2)
min_w1 = self.one.minimum_width(id_window_map) if isinstance(self.one, Pair) else lgd.cell_width
min_w2 = self.two.minimum_width(id_window_map) if isinstance(self.two, Pair) else lgd.cell_width
w1 = max(min_w1, int(self.bias * width) - bw)
w2 = width - w1 - bw2
if w2 < min_w2 and w1 >= min_w1 + bw2:
w2 = min_w2
w1 = width - w2
self.first_extent = Extent(max(0, left - bw), left + w1 + bw)
self.second_extent = Extent(left + w1 + bw, left + width + bw)
if isinstance(self.one, Pair):
@@ -217,8 +252,13 @@ class Pair:
geom = window_geometry_from_layouts(xl, yl)
self.apply_window_geometry(self.two, geom, id_window_map, layout_object)
else:
h1 = max(2*lgd.cell_height + 1, int(self.bias * height) - bw)
h2 = max(2*lgd.cell_height + 1, height - h1 - bw2)
min_h1 = self.one.minimum_height(id_window_map) if isinstance(self.one, Pair) else lgd.cell_height
min_h2 = self.two.minimum_height(id_window_map) if isinstance(self.two, Pair) else lgd.cell_height
h1 = max(min_h1, int(self.bias * height) - bw)
h2 = height - h1 - bw2
if h2 < min_h2 and h1 >= min_h1 + bw2:
h2 = min_h2
h1 = height - h2
self.first_extent = Extent(max(0, top - bw), top + h1 + bw)
self.second_extent = Extent(top + h1 + bw, top + height + bw)
if isinstance(self.one, Pair):
@@ -247,7 +287,7 @@ class Pair:
if is_horizontal == self.horizontal and not self.is_redundant:
if which == 2:
increment *= -1
new_bias = max(0.1, min(self.bias + increment, 0.9))
new_bias = max(0, min(self.bias + increment, 1))
if new_bias != self.bias:
self.bias = new_bias
return True

View File

@@ -67,13 +67,25 @@ def get_process_pool_executor(
def test_spawn() -> None:
monkey_patch_multiprocessing()
import shutil
import subprocess
from queue import Empty
try:
from multiprocessing import get_context
ctx = get_context('spawn')
q = ctx.Queue()
p = ctx.Process(target=q.put, args=('hello',))
p.start()
x = q.get(timeout=8)
try:
x = q.get(timeout=8)
except Empty:
p.join()
rc = p.exitcode
if rc == 0:
raise TimeoutError('Timed out waiting for response from spawned process')
if shutil.which('coredumpctl'):
subprocess.run(['sh', '-c', 'echo bt | coredumpctl debug'])
raise SystemExit(f'Spawned process exited with return code: {rc}')
assert x == 'hello'
p.join()
finally:

View File

@@ -58,7 +58,7 @@ opt('bold_italic_font', 'auto')
opt('font_size', '11.0',
option_type='to_font_size', ctype='double',
long_text='Font size (in pts)'
long_text='Font size (in pts).'
)
opt('force_ltr', 'no',
@@ -311,7 +311,7 @@ opt('cursor_text_color', '#111111',
long_text='''
The color of text under the cursor. If you want it rendered with the
background color of the cell underneath instead, use the special keyword:
background. Note that if :opt:`cursor` is set to :code:`none` then this option
`background`. Note that if :opt:`cursor` is set to :code:`none` then this option
is ignored. Note that some themes set this value, so if you want to override it,
place your value after the lines where the theme file is included.
'''
@@ -791,7 +791,7 @@ mma('Select line from point',
'select_line_from_point ctrl+alt+left triplepress ungrabbed mouse_selection line_from_point',
long_text='Select from the clicked point to the end of the line.'
' If you would like to select the word at the point and then extend to the rest of the line,'
' change line_from_point to word_and_line_from_point.'
' change `line_from_point` to `word_and_line_from_point`.'
)
mma('Extend the current selection',
@@ -830,7 +830,7 @@ mma('Select line from point even when grabbed',
'select_line_from_point_grabbed ctrl+shift+alt+left triplepress ungrabbed,grabbed mouse_selection line_from_point',
long_text='Select from the clicked point to the end of the line even when grabbed.'
' If you would like to select the word at the point and then extend to the rest of the line,'
' change line_from_point to word_and_line_from_point.'
' change `line_from_point` to `word_and_line_from_point`.'
)
mma('Extend the current selection even when grabbed',
@@ -915,7 +915,7 @@ opt('window_alert_on_bell', 'yes',
option_type='to_bool', ctype='bool',
long_text='''
Request window attention on bell. Makes the dock icon bounce on macOS or the
taskbar flash on linux.
taskbar flash on Linux.
'''
)
@@ -948,7 +948,7 @@ opt('bell_path', 'none',
Path to a sound file to play as the bell sound. If set to :code:`none`, the
system default bell sound is used. Must be in a format supported by the
operating systems sound API, such as WAV or OGA on Linux (libcanberra) or AIFF,
MP3 or WAV on macOS (NSSound)
MP3 or WAV on macOS (NSSound).
'''
)

View File

@@ -20,7 +20,7 @@
#define NOSIMD { fatal("No SIMD implementations for this CPU"); }
bool FUNC(utf8_decode_to_esc)(UTF8Decoder *d UNUSED, const uint8_t *src UNUSED, size_t src_sz UNUSED) NOSIMD
const uint8_t* FUNC(find_either_of_two_bytes)(const uint8_t *haystack UNUSED, const size_t sz UNUSED, const uint8_t a UNUSED, const uint8_t b UNUSED) NOSIMD
void FUNC(xor_data64)(const uint8_t key[64], uint8_t* data, const size_t data_sz);
void FUNC(xor_data64)(const uint8_t key[64] UNUSED, uint8_t* data UNUSED, const size_t data_sz UNUSED) NOSIMD
#undef NOSIMD
#else

View File

@@ -200,7 +200,7 @@ init_simd(void *x) {
#ifdef __APPLE__
#ifdef __arm64__
// simde takes care of NEON on Apple Silicon
// ARM has only 128 bit registers buy using the avx2 code is still slightly faster
// ARM has only 128 bit registers but using the avx2 code is still slightly faster
has_sse4_2 = true; has_avx2 = true;
#else
do_check();
@@ -215,9 +215,9 @@ init_simd(void *x) {
#ifdef __aarch64__
// no idea how to probe ARM cpu for NEON support. This file uses pretty
// basic AVX2 and SSE4.2 intrinsics, so hopefully they work on ARM
// ARM has only 128 bit registers buy using the avx2 code is still slightly faster
// ARM has only 128 bit registers but using the avx2 code is still slightly faster
has_sse4_2 = true; has_avx2 = true;
#else
#elif !defined(KITTY_NO_SIMD)
do_check();
#endif
#endif

View File

@@ -265,6 +265,7 @@ def run_tests(report_env: bool = False) -> None:
if args.name and args.name[0] in ('type-check', 'type_check', 'mypy'):
type_check()
go_pkgs = reduce_go_pkgs(args.module, args.name)
os.environ['ASAN_OPTIONS'] = 'detect_leaks=0' # ensure subprocesses dont fail because of leak detection
if go_pkgs:
go_proc: 'Optional[GoProc]' = run_go(go_pkgs, args.name)
else:

View File

@@ -23,7 +23,7 @@ from pathlib import Path
from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, Sequence, Set, Tuple, Union, cast
from glfw import glfw
from glfw.glfw import ISA, BinaryArch, Command, CompileKey
from glfw.glfw import ISA, BinaryArch, Command, CompileKey, CompilerType
if sys.version_info[:2] < (3, 8):
raise SystemExit('kitty requires python >= 3.8')
@@ -507,6 +507,9 @@ def init_env(
cflags.append('-g3')
ldflags.append('-lprofiler')
if debug or profile:
cflags.append('-fno-omit-frame-pointer')
library_paths: Dict[str, List[str]] = {}
def add_lpath(which: str, name: str, val: Optional[str]) -> None:
@@ -536,6 +539,8 @@ def init_env(
set_arches(ldflags, building_arch)
ba = test_compile(cc, *(cppflags + cflags), ldflags=ldflags, get_output_arch=True)
assert isinstance(ba, BinaryArch)
if ba.isa not in (ISA.AMD64, ISA.X86, ISA.ARM64):
cppflags.append('-DKITTY_NO_SIMD')
control_flow_protection = ''
if ba.isa == ISA.AMD64:
@@ -557,7 +562,8 @@ def init_env(
ccver=ccver, ldpaths=ldpaths, vcs_rev=vcs_rev,
)
if verbose:
print(ans.cc_version_string)
print(ans.cc_version_string.strip())
print('Detected:', ans.compiler_type)
return ans
@@ -701,13 +707,11 @@ def get_source_specific_cflags(env: Env, src: str) -> List[str]:
ans.append('-msse4.2' if '128' in src else '-mavx2')
if '256' in src:
# We have manual vzeroupper so prevent compiler from emitting it causing duplicates
if env.is_gcc:
ans.append('-mno-vzeroupper')
else:
if env.compiler_type is CompilerType.clang:
ans.append('-mllvm')
ans.append('-x86-use-vzeroupper=0')
elif env.binary_arch.isa != ISA.ARM64:
ans.append('-DKITTY_NO_SIMD')
else:
ans.append('-mno-vzeroupper')
elif src.startswith('3rdparty/base64/lib/arch/'):
if env.binary_arch.isa in (ISA.AMD64, ISA.X86):
q = src.split(os.path.sep)
@@ -1066,9 +1070,11 @@ def update_go_generated_files(args: Options, kitty_exe: str) -> None:
env = os.environ.copy()
env['ASAN_OPTIONS'] = 'detect_leaks=0'
cp = subprocess.run([kitty_exe, '+launch', os.path.join(src_base, 'gen/go_code.py')], stdout=subprocess.PIPE, env=env)
cp = subprocess.run([kitty_exe, '+launch', os.path.join(src_base, 'gen/go_code.py')], stdout=subprocess.DEVNULL, env=env)
if cp.returncode != 0:
raise SystemExit(cp.returncode)
if os.environ.get('CI') == 'true' and cp.returncode < 0 and shutil.which('coredumpctl'):
subprocess.run(['sh', '-c', 'echo bt | coredumpctl debug'])
raise SystemExit(f'Generating go code failed with exit code: {cp.returncode}')
def parse_go_version(x: str) -> Tuple[int, int, int]:
@@ -1165,14 +1171,12 @@ def build_launcher(args: Options, launcher_dir: str = '.', bundle_type: str = 's
libs: List[str] = []
ldflags = shlex.split(os.environ.get('LDFLAGS', ''))
if args.profile or args.sanitize:
cflags.append('-g3')
if args.sanitize:
cflags.append('-g3')
sanitize_args = get_sanitize_args(env.cc, env.ccver)
cflags.extend(sanitize_args)
ldflags.extend(sanitize_args)
libs += ['-lasan'] if not is_macos and env.is_gcc else []
else:
cflags.append('-g')
libs += ['-lasan'] if not is_macos and env.compiler_type is not CompilerType.clang else []
if args.profile:
libs.append('-lprofiler')
else:

View File

@@ -1151,7 +1151,7 @@ func (s *Function) OutputASM(w io.Writer) {
}
fmt.Fprint(w, "// ")
s.print_signature(w)
fmt.Fprintf(w, "\nTEXT ·%s(SB), NOSPLIT, $0-%d\n", s.Name, s.Size)
fmt.Fprintf(w, "\nTEXT ·%s(SB), NOSPLIT|TOPFRAME|NOFRAME, $0-%d\n", s.Name, s.Size)
has_trailing_return := false
for _, i := range s.Instructions {