Compare commits

...

104 Commits

Author SHA1 Message Date
Kovid Goyal
cc29640604 version 0.17.3 2020-04-23 08:39:07 +05:30
Kovid Goyal
d0329460b8 Fix #2573 2020-04-21 22:04:34 +05:30
Kovid Goyal
717c0c96de Allow mapping arbitrary remote control commands to key presses in kitty.conf 2020-04-19 20:14:23 +05:30
Kovid Goyal
b3f161efce ... 2020-04-19 19:39:56 +05:30
Kovid Goyal
0286940163 Allow changing margings/padding via remote control
Fixes #2546
2020-04-19 19:29:36 +05:30
Kovid Goyal
d4a4ee386f Fix minimal borders in grid layout 2020-04-19 17:58:35 +05:30
Kovid Goyal
33498f9133 ... 2020-04-19 11:01:22 +05:30
Kovid Goyal
da3d4d1bef ... 2020-04-19 09:51:06 +05:30
Kovid Goyal
c69b8870d2 Allow individually setting margins and padding for each edge (left, right, top, bottom) 2020-04-19 09:36:26 +05:30
Kovid Goyal
85b55b31b6 Add a new :opt:listen_on option in kitty.conf
Also allow using environment variables in this option
Fixes #2569
2020-04-19 09:23:52 +05:30
Kovid Goyal
948919e42b Forgot to apply --title on creation of subsequent OS windows
Also update changelog
2020-04-19 08:42:30 +05:30
Kovid Goyal
85c40090ed Fix #2570 2020-04-19 08:36:29 +05:30
Kovid Goyal
98b50ec145 ... 2020-04-19 08:28:40 +05:30
Kovid Goyal
1ab4f7d2d0 Fix a regression in 0.17 that broke displaying graphics over SSH
The new types GraphicsCommand can generate a command with only a payload
and no control keys, as the last command during chunked transfer.
Fix the parser to handle this. Fixes #2568
2020-04-19 08:13:33 +05:30
Kovid Goyal
98801b6fa6 Update changelog 2020-04-19 07:22:02 +05:30
Kovid Goyal
bdd68284b5 Merge branch 'terminfo' of https://github.com/wladh/kitty 2020-04-19 07:21:10 +05:30
Vlad Hanciuta
a61d70ea9a Add strikethrough capabilities to terminfo
The termcap aliases are not standard, but they are used by Vim, it
seems. As far as I can tell, there are no standard termcap attribute for
strikethrough.
2020-04-18 16:37:09 +01:00
Vlad Hanciuta
e271719dd2 Add strikeout capability to terminfo 2020-04-18 16:05:30 +01:00
Kovid Goyal
a08efa1fc5 Fix failing test 2020-04-17 19:57:37 +05:30
Kovid Goyal
ffac3600e0 Layouts: Apply per window padding and margin values
Also make the calculation of blank rects more robust and with less code,
which should hopefully fix #1762
2020-04-17 19:47:34 +05:30
Kovid Goyal
6499f8ac24 Allow window objects to store custom margin/padding 2020-04-17 09:02:08 +05:30
Kovid Goyal
63307cb1d8 Code to serialize state 2020-04-17 08:42:45 +05:30
Kovid Goyal
0a6fb362f6 kitty @ kitten: Allow no_ui kittens to return data to the remote client
Fixes #2552 since the OP can now write a kitten to query the window
manager and return position data even over SSH.
2020-04-15 08:29:55 +05:30
Kovid Goyal
457fcbfb90 Merge branch 'master' of https://github.com/akobel/kitty 2020-04-15 07:44:27 +05:30
Alexander Kobel
3b6277b9e2 Hide "No render frame received" without --debug-rendering 2020-04-14 19:36:58 +02:00
Alexander Kobel
e6e61028b1 Rename --debug-gl to --debug-rendering 2020-04-14 19:35:40 +02:00
Kovid Goyal
e9be66126b Merge branch 'hints-support-empty-word-characters' of https://github.com/trygveaa/kitty 2020-04-14 08:17:13 +05:30
Trygve Aaberge
681fe7f2f2 Don't set word_characters to select_by_word_characters when empty string
This allows you to pass in --word-characters "" which will make hints
only match \w.
2020-04-13 23:02:44 +02:00
Kovid Goyal
0196920533 icat kitten: Fix display of 16-bit depth images
Fixes #2542
2020-04-13 23:05:10 +05:30
Kovid Goyal
5f5bb3ace0 Update Changelog 2020-04-12 20:10:29 +05:30
Kovid Goyal
3c970e0122 Merge branch 'kitten-send-mouse-events' of https://github.com/trygveaa/kitty 2020-04-12 20:09:45 +05:30
Trygve Aaberge
43af6e3b8a Add support for sending mouse events from a kitten
This allows you to a control a program running in kitty from a kitten
using mouse events. If the program is not receiving mouse events of that
type, it is not sent.
2020-04-12 16:19:50 +02:00
Kovid Goyal
ac8cfc12fb Merge branch 'docs-fix-kitten-custom-indentation' of https://github.com/trygveaa/kitty 2020-04-12 19:49:18 +05:30
Trygve Aaberge
b19472e0f5 Fix indentation in kittens/custom.rst to 4 spaces
This file used 3 spaces some places and 4 spaces other places. Other
files like diff.rst and hints.rst use 4 spaces.
2020-04-12 16:04:32 +02:00
Kovid Goyal
b4d08044a0 ... 2020-04-12 17:01:39 +05:30
Kovid Goyal
b2fe02e63f Merge branch 'patch-1' of https://github.com/LesnyRumcajs/kitty 2020-04-12 16:55:10 +05:30
Kovid Goyal
d1a5d30233 Add another test for marking 2020-04-12 16:54:38 +05:30
LesnyRumcajs
bba30138e1 Update Fedora link
The link was pointing to a deprecated `copr`. Let's point it to official Fedora repos! (as copr page suggests anyway)
2020-04-12 13:13:02 +02:00
Kovid Goyal
082546a1e7 Marks: Fix marks not handling wide characters and tab characters correctly
Fixes #2534
2020-04-12 13:28:21 +05:30
Kovid Goyal
41ffd58e26 A bit of cleanup 2020-04-11 21:22:17 +05:30
Kovid Goyal
b4f24e4e1e Merge branch 'must_draw_borders_flag' of https://github.com/franco/kitty 2020-04-11 21:19:52 +05:30
Franco Sebregondi
75c8ffd715 Add flag to Layout to enforce drawing a border
Layout.must_draw_borders = True enforces drawing of a border even
though only a single window is visible. See #2531.
2020-04-11 17:01:44 +02:00
Kovid Goyal
688606ff7c Merge branch 'ignore-keyboard-layout-switch-keys' of https://github.com/trygveaa/kitty 2020-04-10 21:46:07 +05:30
Trygve Aaberge
2e3f9dffa7 Ignore keys for switching keyboard layout
XKB has various options for using keys to switch the keyboard layout.
When these are set, XKB will return keysyms which are unknown to GLFW,
so kitty will fallback to using a keymap without the options set, which
causes the keys to be interpreted as the original keysyms.

However, when these options are set, kitty shouldn't interpret the keys.
Therefore, check for some specific keysyms and return before translating
to GLFW keysyms.

There may be more keysyms which should be ignored, but as far as I can
see from https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/xkeyboard-config-2.29/symbols/group
these are the ones which can be triggered by XKB options.

Fixes #2519
2020-04-10 17:21:53 +02:00
Kovid Goyal
859033415b macOS: Fix incorrect horizontal positioning of some full-width East Asian characters
Fixes #1457
Also render multi-cell PUA characters centered, matching behavior on other platforms
2020-04-09 13:25:19 +05:30
Kovid Goyal
1f1b52b580 Fix specifying options for layouts in the startup session file not working
Fixes #2520
2020-04-09 12:40:09 +05:30
Kovid Goyal
4e7bf80447 diff kitten: New option to control the background color for filler lines in the margin
Fixes #2518
2020-04-08 20:42:49 +05:30
Kovid Goyal
e39df50884 Merge branch 'patch-1' of https://github.com/georgeabr/kitty 2020-04-08 18:56:15 +05:30
George Abraham
8d6a9d45c2 Changing match target to "font"
As suggested in #2516, changing match target to "font" in the fonts.conf file.
2020-04-08 14:02:54 +01:00
Kovid Goyal
0862e85577 Rewrite wcswidth as a state machine 2020-04-07 10:10:30 +05:30
Kovid Goyal
0b9a37139e Render any flag pair in two cells not just known ones 2020-04-06 23:00:34 +05:30
Kovid Goyal
6357f7b811 Update wcswidth() to match new country flag rendering 2020-04-06 22:56:34 +05:30
Kovid Goyal
24197dc422 Render known country flags designated by a pair of unicode codepoints in two cells instead of four. 2020-04-06 22:16:59 +05:30
Kovid Goyal
9bc2ab3245 Function to detect flag pairs 2020-04-06 21:16:14 +05:30
Kovid Goyal
bf4e8c490c Update to Unicode 13.0
Fixes #2513
2020-04-06 18:59:35 +05:30
Kovid Goyal
590a4d70ee Fix --stdin-add-formatting not working
Fixes #2512
2020-04-06 09:35:16 +05:30
Kovid Goyal
40341d2ddd ... 2020-04-04 09:17:38 +05:30
Kovid Goyal
0cd2c41ee3 Update changelog 2020-04-03 22:42:06 +05:30
Kovid Goyal
5d65cdb6a8 Fix reverse video not being rendered correctly when using transparency or a background image
Fixes #2419
2020-04-03 22:40:33 +05:30
Kovid Goyal
759a15ccbb X11: Handle invalid data being sent with drag events by some applications
Fixes #2505
2020-04-02 21:01:19 +05:30
Kovid Goyal
5aead5eebe Replace new-window with launch in the docs 2020-04-01 08:39:37 +05:30
Kovid Goyal
a325a5fdee Merge branch 'strikethrough-info-from-font' of https://github.com/trygveaa/kitty 2020-03-31 09:34:07 +05:30
Trygve Aaberge
931e91f1a7 Read strikethrough info from font when using FreeType
This reads the strikethrough position and thickness from the font so it
is rendered correctly.

This is only implemented for FreeType, not Core Text, because I didn't
find any way to get the info from Core Text, and I don't have a Mac to
test it on either. When using Core Text or when the font doesn't provide
the info, the same approximation as before is used.
2020-03-30 20:09:30 +02:00
Kovid Goyal
6e13509720 Allow entry points and external command combined completion on first word 2020-03-30 11:38:54 +05:30
Kovid Goyal
5b4844e71e oops
Broke normal completion in previous commit. Delegate should only be used
when actual unknown words are present
2020-03-30 11:12:00 +05:30
Kovid Goyal
66cbbea9bb Add completion for executables at kitty invocation in zsh
So now kitty vi<TAB> and kitty vim something<TAB> delegate
completion to zsh for the command that will be executed inside kitty.
2020-03-30 10:54:17 +05:30
Kovid Goyal
b65119b8a9 Color separators in the tab bar using inactive bg except for the last one 2020-03-29 09:20:17 +05:30
Kovid Goyal
86703da917 version 0.17.2 2020-03-29 09:09:16 +05:30
Kovid Goyal
610e9afdf0 Add completion of conf filenames to kitty @ set-colors 2020-03-29 08:41:27 +05:30
Kovid Goyal
e3af9f68d3 Linux: Fix selection of fonts with multiple width variants not preferring the normal width faces
Fixes #2491
2020-03-29 01:52:33 +05:30
Kovid Goyal
747ac85e7c Add an :option:launch --watcher option that allows defining callbacks that are called for various events in the window's life-cycle
Fixes #2440
2020-03-28 12:13:42 +05:30
Kovid Goyal
8c23f9e526 DRYer 2020-03-28 09:20:28 +05:30
Kovid Goyal
e6e339fcd3 ... 2020-03-28 08:44:48 +05:30
Kovid Goyal
f04680ac47 Fix a regression in 0.17 that broke the kitty @ launch remote command and also broke the --tab-title option when creating a new tab.
Fixes #2488
2020-03-28 08:43:32 +05:30
Kovid Goyal
90985cc846 Merge branch 'hints-copy-to-primary-selection' of https://github.com/trygveaa/kitty 2020-03-28 08:21:08 +05:30
Trygve Aaberge
63917944f2 Add an option to hints for copying to the primary selection 2020-03-27 20:23:02 +01:00
Kovid Goyal
793605f80f Update changelog 2020-03-26 22:06:13 +05:30
Kovid Goyal
93a281c7e3 Add a new mappable action `close_other_windows_in_tab` to close all but the active window
Fixes #2484
2020-03-26 22:05:00 +05:30
Kovid Goyal
8f1c6c4d74 Fix #2482 2020-03-26 00:53:19 +05:30
Kovid Goyal
1610dba7ab patch tab bar colors correctly 2020-03-26 00:46:30 +05:30
Kovid Goyal
d9d419991c Respect tab_bar_background again 2020-03-26 00:41:19 +05:30
Kovid Goyal
f9e86b19aa Fix #2480 2020-03-26 00:20:10 +05:30
Kovid Goyal
fffb976e43 Fix blank space at the start of tab bar in powerline style when first tab is inactive
Fix #2478
2020-03-25 19:20:35 +05:30
Kovid Goyal
0da566b49f Hints kitten: Adjust the default regex used to detect line numbers to handle line+column numbers
See #2268
2020-03-25 10:03:25 +05:30
Kovid Goyal
da55717d20 The splits layout needs its own draw_minimal_borders implementation 2020-03-25 09:54:06 +05:30
Kovid Goyal
254836902c Merge branch 'fix/window-border' of https://github.com/pyrho/kitty 2020-03-25 09:35:46 +05:30
Damien Rajon
d01f8d1865 fix(window): inactive window border fix
Fixes #2474.
2020-03-25 00:09:10 +01:00
Kovid Goyal
a46c3f7007 Replace use of cast() 2020-03-24 22:03:10 +05:30
Kovid Goyal
4e58062025 Improve rendering of :option: in cli docs 2020-03-24 22:03:00 +05:30
Kovid Goyal
aa3be0535f version 0.17.1 2020-03-24 19:45:19 +05:30
Kovid Goyal
8ebf5e007c Fix rc setting cursor_text_color to background not working 2020-03-24 19:43:31 +05:30
Kovid Goyal
c2957e7f12 Fix a regression in 0.17 that caused kitty @ set-colors to require setting cursor_text_color
Fixes #2470
2020-03-24 19:29:25 +05:30
Kovid Goyal
bf7c26d357 CoreText: Prefer less bold variants over heavier variants when auto-selecting the bold face 2020-03-24 19:27:14 +05:30
Kovid Goyal
0547102bdd Fix #2468 2020-03-24 18:33:32 +05:30
Kovid Goyal
a07369ce69 Update changelog 2020-03-24 18:14:14 +05:30
Kovid Goyal
21c5610431 ... 2020-03-24 18:02:44 +05:30
Kovid Goyal
6dbdf72f40 CoreText: When matching fonts prefer non-expanded/condensed variants 2020-03-24 17:55:38 +05:30
Kovid Goyal
76a6bba643 Fix #2466 2020-03-24 17:07:37 +05:30
Kovid Goyal
b0b8222a96 ... 2020-03-24 17:04:17 +05:30
Kovid Goyal
0d87b8fac5 Fix a regression in 0.17 that caused tab bar background to be rendered after the last tab as well
Fixes #2464
2020-03-24 16:05:02 +05:30
Kovid Goyal
5227b1f98b Fix :opt:cursor_underline_thickness not working
Fixes #2465
2020-03-24 15:53:13 +05:30
Kovid Goyal
c4f7c5c1cd More explanation for OSC 52 extension 2020-03-24 11:58:30 +05:30
Kovid Goyal
33a84a609c ... 2020-03-24 11:35:34 +05:30
Kovid Goyal
037111715a ... 2020-03-24 11:34:29 +05:30
72 changed files with 31874 additions and 28992 deletions

View File

@@ -4,10 +4,110 @@ Changelog
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
To update |kitty|, :doc:`follow the instructions <binary>`.
0.17.3 [2020-04-23]
--------------------
- Allow individually setting margins and padding for each edge (left, right,
top, bottom). Margins can also be controlled per window via remote control
(:iss:`2546`)
- Fix reverse video not being rendered correctly when using transparency or a
background image (:iss:`2419`)
- Allow mapping arbitrary remote control commands to key presses in
:file:`kitty.conf`
- X11: Fix crash when doing drag and drop from some applications (:iss:`2505`)
- Fix :option:`launch --stdin-add-formatting` not working (:iss:`2512`)
- Update to Unicode 13.0 (:iss:`2513`)
- Render country flags designated by a pair of unicode codepoints
in two cells instead of four.
- diff kitten: New option to control the background color for filler lines in
the margin (:iss:`2518`)
- Fix specifying options for layouts in the startup session file not working
(:iss:`2520`)
- macOS: Fix incorrect horizontal positioning of some full-width East Asian characters
(:iss:`1457`)
- macOS: Render multi-cell PUA characters centered, matching behavior on other
platforms
- Linux: Ignore keys if they are designated as layout/group/mode switch keys
(:iss:`2519`)
- Marks: Fix marks not handling wide characters and tab characters correctly
(:iss:`2534`)
- Add a new :opt:`listen_on` option in kitty.conf to set :option:`kitty --listen-on`
globally. Also allow using environment variables in this option (:iss:`2569`).
- Allow sending mouse events in kittens (:pull:`2538`)
- icat kitten: Fix display of 16-bit depth images (:iss:`2542`)
- Add ncurses specific terminfo definitions for strikethrough (:pull:`2567`)
- Fix a regression in 0.17 that broke displaying graphics over SSH
(:iss:`2568`)
- Fix :option:`--title` not being applied at window creation time (:iss:`2570`)
0.17.2 [2020-03-29]
--------------------
- Add a :option:`launch --watcher` option that allows defining callbacks
that are called for various events in the window's life-cycle (:iss:`2440`)
- Fix a regression in 0.17 that broke drawing of borders with non-minimal
borders (:iss:`2474`)
- Hints kitten: Allow copying to primary selection as well as clipboard
(:pull:`2487`)
- Add a new mappable action ``close_other_windows_in_tab`` to close all but the
active window (:iss:`2484`)
- Hints kitten: Adjust the default regex used to detect line numbers to handle
line+column numbers (:iss:`2268`)
- Fix blank space at the start of tab bar in the powerline style when first tab is
inactive (:iss:`2478`)
- Fix regression causing incorrect rendering of separators in tab bar when
defining a tab bar background color (:pull:`2480`)
- Fix a regression in 0.17 that broke the kitty @ launch remote command and
also broke the --tab-title option when creating a new tab. (:iss:`2488`)
- Linux: Fix selection of fonts with multiple width variants not preferring
the normal width faces (:iss:`2491`)
0.17.1 [2020-03-24]
--------------------
- Fix :opt:`cursor_underline_thickness` not working (:iss:`2465`)
- Fix a regression in 0.17 that caused tab bar background to be rendered after
the last tab as well (:iss:`2464`)
- macOS: Fix a regression in 0.17 that caused incorrect variants to be
automatically selected for some fonts (:iss:`2462`)
- Fix a regression in 0.17 that caused kitty @ set-colors to require setting
cursor_text_color (:iss:`2470`)
0.17.0 [2020-03-24]
--------------------
- A new :ref:`splits_layout` to arrange windows in arbitrary splits
- :ref:`splits_layout` to arrange windows in arbitrary splits
(:iss:`2308`)
- Add support for specifying a background image, see :opt:`background_image`

View File

@@ -179,7 +179,7 @@ following :file:`~/.config/fontconfig/fonts.conf`::
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<match target="scan">
<match target="font">
<test name="family">
<string>Your Font Family Name</string>
</test>

View File

@@ -65,7 +65,7 @@ You can also use your favorite package manager to install the |kitty| package.
`openSUSE <https://build.opensuse.org/package/show/X11:terminals/kitty>`_,
`Arch Linux <https://www.archlinux.org/packages/community/x86_64/kitty/>`_,
`Gentoo <https://packages.gentoo.org/packages/x11-terms/kitty>`_,
`Fedora <https://copr.fedorainfracloud.org/coprs/gagbo/kitty-latest/>`_,
`Fedora <https://src.fedoraproject.org/rpms/kitty>`_,
`Void Linux <https://github.com/void-linux/void-packages/blob/master/srcpkgs/kitty/template>`_,
and `Solus <https://dev.getsol.us/source/kitty/>`_.
@@ -192,6 +192,12 @@ Similarly, you can detach the current tab, with::
# asks which OS Window to move the tab into
map ctrl+f4 detach_tab ask
Finally, you can define a shortcut to close all windows in a tab other than
the currently active window::
map f9 close_other_windows_in_tab
Other keyboard shortcuts
----------------------------------

View File

@@ -20,24 +20,24 @@ your machine).
.. code-block:: python
def main(args: List[str]) -> str:
# this is the main entry point of the kitten, it will be executed in
# the overlay window when the kitten is launched
answer = input('Enter some text: ')
# whatever this function returns will be available in the
# handle_result() function
return answer
def main(args: List[str]) -> str:
# this is the main entry point of the kitten, it will be executed in
# the overlay window when the kitten is launched
answer = input('Enter some text: ')
# whatever this function returns will be available in the
# handle_result() function
return answer
def handle_result(args: List[str], answer: str, target_window_id: int, boss: kitty.boss.Boss) -> None:
# get the kitty window into which to paste answer
w = boss.window_id_map.get(target_window_id)
if w is not None:
w.paste(answer)
def handle_result(args: List[str], answer: str, target_window_id: int, boss: kitty.boss.Boss) -> None:
# get the kitty window into which to paste answer
w = boss.window_id_map.get(target_window_id)
if w is not None:
w.paste(answer)
Now in :file:`kitty.conf` add the lines::
map ctrl+k kitten mykitten.py
map ctrl+k kitten mykitten.py
Start kitty and press :kbd:`ctrl+k` and you should see the kitten running.
@@ -53,7 +53,7 @@ Passing arguments to kittens
You can pass arguments to kittens by defining them in the map directive in
:file:`kitty.conf`. For example::
map ctrl+k kitten mykitten.py arg1 arg2
map ctrl+k kitten mykitten.py arg1 arg2
These will be available as the ``args`` parameter in the ``main()`` and
``handle_result()`` functions. Note also that the current working directory
@@ -70,17 +70,17 @@ function, telling kitty what kind of input your kitten would like. For example:
.. code-block:: py
# in main, STDIN is for the kitten process and will contain
# the contents of the screen
def main(args):
return sys.stdin.read()
# in main, STDIN is for the kitten process and will contain
# the contents of the screen
def main(args):
return sys.stdin.read()
# in handle_result, STDIN is for the kitty process itself, rather
# than the kitten process and should not be read from.
from kittens.tui.handler import result_handler
@result_handler(type_of_input='text')
def handle_result(args, stdin_data, target_window_id, boss):
pass
# in handle_result, STDIN is for the kitty process itself, rather
# than the kitten process and should not be read from.
from kittens.tui.handler import result_handler
@result_handler(type_of_input='text')
def handle_result(args, stdin_data, target_window_id, boss):
pass
This will send the plain text of the active window to the kitten's
@@ -104,23 +104,23 @@ Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py`
.. code-block:: py
def main(args):
pass
def main(args):
pass
from kittens.tui.handler import result_handler
@result_handler(no_ui=True)
def handle_result(args, answer, target_window_id, boss):
tab = boss.active_tab
if tab is not None:
if tab.current_layout.name == 'stack':
tab.last_used_layout()
else:
tab.goto_layout('stack')
from kittens.tui.handler import result_handler
@result_handler(no_ui=True)
def handle_result(args, answer, target_window_id, boss):
tab = boss.active_tab
if tab is not None:
if tab.current_layout.name == 'stack':
tab.last_used_layout()
else:
tab.goto_layout('stack')
Now in kitty.conf add::
map f11 kitten zoom_toggle.py
map f11 kitten zoom_toggle.py
Pressing :kbd:`F11` will now act as a zoom toggle function. You can get even
more fancy, switching the kitty OS window to fullscreen as well as changing the
@@ -132,6 +132,32 @@ layout, by simply adding the line::
To the ``handle_result()`` function, above.
Sending mouse events
--------------------
If the program running in a window is receiving mouse events you can simulate
those using::
from kitty.fast_data_types import send_mouse_event
send_mouse_event(screen, x, y, button, action, mods)
``screen`` is the ``screen`` attribute of the window you want to send the event
to. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is
``GLFW_MOUSE_BUTTON_{button}`` where ``{button}`` is one of ``LEFT``,
``RIGHT``, ``MIDDLE`` or a digit from ``1`` to ``8``. ``action`` is one of
``PRESS``, ``RELEASE``, ``DRAG`` or ``MOVE``. ``mods`` is a bitmask of
``GLFW_MOD_{mod}`` where ``{mod}`` is one of ``SHIFT``, ``CONTROL`` or ``ALT``.
All the mentioned constants are imported from ``kitty.fast_data_types``.
For example, to send a left click at position x: 2, y: 3 to the active window::
from kitty.fast_data_types import send_mouse_event, GLFW_MOUSE_BUTTON_LEFT, PRESS
send_mouse_event(boss.active_window.screen, 2, 3, GLFW_MOUSE_BUTTON_LEFT, PRESS, 0)
The function will only send the event if the program is receiving events of
that type, and will return ``True`` if it sent the event, and ``False`` if not.
Debugging kittens
--------------------
@@ -157,11 +183,11 @@ Kittens created by kitty users
---------------------------------------------
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
Makes the kitty scroll bindings work in full screen applications
Makes the kitty scroll bindings work in full screen applications
`insert password <https://github.com/kovidgoyal/kitty/issues/1222>`_
Insert a password from a CLI password manager, taking care to only do it at
a password prompt.
Insert a password from a CLI password manager, taking care to only do it at
a password prompt.

View File

@@ -61,6 +61,35 @@ currently active kitty window. For example::
map f1 launch my-program @active-kitty-window-id
Watching launched windows
---------------------------
The :option:`launch --watcher` option allows you to specify python functions
that will be called at specific events, such as when the window is resized or
closed. Simply specify the path to a python module that specifies callback
functions for the events you are interested in, for example:
.. code-block:: python
def on_resize(boss, window, data):
# Here data will contain old_geometry and new_geometry
def on_close(boss, window, data):
# called when window is closed, typically when the program running in
# it exits.
Every callback is passed a reference to the global ``Boss`` object as well as
the ``Window`` object the action is occurring on. The ``data`` object is
mapping that contains event dependent data. Some useful methods and attributes
for the ``Window`` object are: ``as_text(as_ans=False, add_history=False,
add_wrap_markers=False, alternate_screen=False)`` with which you can get the
contents of the window and its scrollback buffer. Similarly,
``window.child.pid`` is the PID of the processes that was launched
in the window and ``window.id`` is the internal kitty ``id`` of the
window.
Syntax reference
------------------

View File

@@ -24,7 +24,7 @@ other in arbitrary arrangements, based on *Layouts*, see below for examples:
There are many different layouts available. They are all enabled by default,
you can switch layouts using :sc:`next_layout`. To control which layouts
are available use :opt:`enabled_layouts` the first listed layout becomes
are available use :opt:`enabled_layouts`, the first listed layout becomes
the default. Individual layouts and how to use them are described below.
.. contents::

View File

@@ -219,6 +219,11 @@ first, for example::
<ESC>]52;c;!<ESC>\
Here ``!`` is not valid base64 encoded text, so it clears the clipboard.
Further, since it is invalid, it should be ignored by terminal emulators
that do not support this extension, thereby making it safe to use, simply
always send it before starting a new OSC 52 paste, even if you aren't chunking
up large pastes, that way kitty wont concatenate your paste, and it will have
no ill-effects in other terminal emulators.
In case you're using software that can't be easily adapted to this
protocol extension, it can be disabled by specifying ``no-append`` to the

View File

@@ -21,7 +21,7 @@ In order for control to work, :opt:`allow_remote_control` must be enabled in
Now, in the new |kitty| window, enter the command::
kitty @ new-window --title Output --keep-focus cat
kitty @ launch --title Output --keep-focus cat
This will open a new window, running the ``cat`` program that will appear next
to the current window.
@@ -54,7 +54,7 @@ window. Type ``Ctrl+D`` when you are ready to stop.
Now, let's open a new tab::
kitty @ new-window --new-tab --tab-title "My Tab" --keep-focus bash
kitty @ launch --type=tab --tab-title "My Tab" --keep-focus bash
This will open a new tab running the bash shell with the title "My Tab".
We can change the title of the tab with::
@@ -134,6 +134,19 @@ still write to the pipes of any other program on the same computer and
therefore can control |kitty|. It can, however, be useful to block programs
running on other computers (for example, over ssh) or as other users.
Mapping key presses to remote control commands
--------------------------------------------------
If you wish to trigger a remote control command easily with just a keypress,
you can map it in :file:`kitty.conf`. For example::
map F1 remote_control set-spacing margin=30
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.
Documentation for the remote control protocol
-----------------------------------------------

View File

@@ -137,6 +137,7 @@ static inline void
{parr}
{keys_enum}
enum KEYS key = '{initial_key}';
if (screen->parser_buf[pos] == ';') state = AFTER_VALUE;
while (pos < screen->parser_buf_pos) {{
switch(state) {{

View File

@@ -51,7 +51,8 @@ all_symbols: Set[int] = set()
name_map: Dict[int, str] = {}
word_search_map: DefaultDict[str, Set[int]] = defaultdict(set)
zwj = 0x200d
marks = set(emoji_skin_tone_modifiers) | {zwj}
flag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26))
marks = set(emoji_skin_tone_modifiers) | {zwj} | flag_codepoints
not_assigned = set(range(0, sys.maxunicode))
@@ -103,36 +104,88 @@ def parse_ucd() -> None:
word_search_map['lamda'] |= word_search_map['lambda']
def split_two(line: str) -> Tuple[Set[int], str]:
spec, rest = line.split(';', 1)
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
def parse_range_spec(spec: str) -> Set[int]:
spec = spec.strip()
if '..' in spec:
chars_ = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))
chars = set(range(chars_[0], chars_[1] + 1))
else:
chars = {int(spec, 16)}
return chars, rest
return chars
def split_two(line: str) -> Tuple[Set[int], str]:
spec, rest = line.split(';', 1)
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
return parse_range_spec(spec), rest
all_emoji: Set[int] = set()
emoji_categories: Dict[str, Set[int]] = {}
emoji_presentation_bases: Set[int] = set()
narrow_emoji: Set[int] = set()
wide_emoji: Set[int] = set()
flags: Dict[int, List[int]] = {}
def parse_basic_emoji(spec: str) -> None:
parts = list(filter(None, spec.split()))
has_emoji_presentation = len(parts) < 2
chars = parse_range_spec(parts[0])
all_emoji.update(chars)
emoji_presentation_bases.update(chars)
(wide_emoji if has_emoji_presentation else narrow_emoji).update(chars)
def parse_keycap_sequence(spec: str) -> None:
base, fe0f, cc = list(filter(None, spec.split()))
chars = parse_range_spec(base)
all_emoji.update(chars)
emoji_presentation_bases.update(chars)
narrow_emoji.update(chars)
def parse_flag_emoji_sequence(spec: str) -> None:
a, b = list(filter(None, spec.split()))
left, right = int(a, 16), int(b, 16)
chars = {left, right}
all_emoji.update(chars)
wide_emoji.update(chars)
emoji_presentation_bases.update(chars)
flags.setdefault(left, []).append(right)
def parse_emoji_tag_sequence(spec: str) -> None:
a = int(spec.split()[0], 16)
all_emoji.add(a)
wide_emoji.add(a)
emoji_presentation_bases.add(a)
def parse_emoji_modifier_sequence(spec: str) -> None:
a, b = list(filter(None, spec.split()))
char, mod = int(a, 16), int(b, 16)
mod
all_emoji.add(char)
wide_emoji.add(char)
emoji_presentation_bases.add(char)
def parse_emoji() -> None:
for line in get_data('emoji-data.txt', 'emoji'):
chars, rest = split_two(line)
s = emoji_categories.setdefault(rest, set())
s.update(chars)
all_emoji.update(chars)
for line in get_data('emoji-variation-sequences.txt', 'emoji'):
parts = line.split()
base, var = parts[0], parts[1]
if base.startswith('#'):
for line in get_data('emoji-sequences.txt', 'emoji'):
parts = [x.strip() for x in line.split(';')]
if len(parts) < 2:
continue
if var.upper() == 'FE0F':
ibase = int(base, 16)
emoji_presentation_bases.add(ibase)
data, etype = parts[:2]
if etype == 'Basic_Emoji':
parse_basic_emoji(data)
elif etype == 'Emoji_Keycap_Sequence':
parse_keycap_sequence(data)
elif etype == 'RGI_Emoji_Flag_Sequence':
parse_flag_emoji_sequence(data)
elif etype == 'RGI_Emoji_Tag_Sequence':
parse_emoji_tag_sequence(data)
elif etype == 'RGI_Emoji_Modifier_Sequence':
parse_emoji_modifier_sequence(data)
doublewidth: Set[int] = set()
@@ -204,15 +257,6 @@ def gen_emoji() -> None:
p('\t}')
p('\treturn false;\n}')
p('static inline bool\nis_emoji_modifier(char_type code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(emoji_categories['Emoji_Modifier'])):
write_case(spec, p)
p('\t\t\treturn true;')
p('\t\tdefault: return false;')
p('\t}')
p('\treturn false;\n}')
p('static inline bool\nis_symbol(char_type code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(all_symbols)):
@@ -427,7 +471,7 @@ def gen_names() -> None:
def gen_wcwidth() -> None:
seen: Set[int] = set()
def add(p: Callable, comment: str, chars_: Set[int], ret: int) -> None:
def add(p: Callable, comment: str, chars_: Union[Set[int], FrozenSet[int]], ret: int) -> None:
chars = chars_ - seen
seen.update(chars)
p(f'\t\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
@@ -441,13 +485,14 @@ def gen_wcwidth() -> None:
p('\tswitch(code) {')
non_printing = class_maps['Cc'] | class_maps['Cf'] | class_maps['Cs']
add(p, 'Flags', flag_codepoints, 2)
add(p, 'Marks', marks | {0}, 0)
add(p, 'Non-printing characters', non_printing, -1)
add(p, 'Private use', class_maps['Co'], -3)
add(p, 'Text Presentation', emoji_categories['Emoji'] - emoji_categories['Emoji_Presentation'], 1)
add(p, 'Text Presentation', narrow_emoji, 1)
add(p, 'East Asian ambiguous width', ambiguous, -2)
add(p, 'East Asian double width', doublewidth, 2)
add(p, 'Emoji Presentation', emoji_categories['Emoji_Presentation'], 2)
add(p, 'Emoji Presentation', wide_emoji, 2)
add(p, 'Not assigned in the unicode character database', not_assigned, -4)

View File

@@ -165,7 +165,7 @@ class Function:
self.args = [Arg('void v')]
def declaration(self) -> str:
return 'typedef {restype} (*{name}_func)({args});\n{name}_func {name}_impl;\n#define {name} {name}_impl'.format(
return 'typedef {restype} (*{name}_func)({args});\nGFW_EXTERN {name}_func {name}_impl;\n#define {name} {name}_impl'.format(
restype=self.restype,
name=self.name,
args=', '.join(a.type for a in self.args)
@@ -233,6 +233,9 @@ const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callbac
#include <stdint.h>
#include "monotonic.h"
#ifndef GFW_EXTERN
#define GFW_EXTERN extern
#endif
{}
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
@@ -251,6 +254,7 @@ const char* load_glfw(const char* path);
f.write(header)
code = '''
#define GFW_EXTERN
#include "data-types.h"
#include "glfw-wrapper.h"
#include <dlfcn.h>

17
glfw/x11_window.c vendored
View File

@@ -1075,6 +1075,21 @@ static void onConfigChange(void)
}
}
static void
get_atom_names(Atom *atoms, int count, char **atom_names) {
_glfwGrabErrorHandlerX11();
XGetAtomNames(_glfw.x11.display, atoms, count, atom_names);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success) {
for (int i = 0; i < count; i++) {
_glfwGrabErrorHandlerX11();
atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;
}
}
}
// Process the specified X event
//
static void processEvent(XEvent *event)
@@ -1474,7 +1489,7 @@ static void processEvent(XEvent *event)
}
char **atom_names = calloc(count, sizeof(char**));
if (atom_names) {
XGetAtomNames(_glfw.x11.display, formats, count, atom_names);
get_atom_names(formats, count, atom_names);
for (i = 0; i < count; i++)
{

3
glfw/xkb_glfw.c vendored
View File

@@ -620,6 +620,9 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
}
if (key_text[0]) { debug("%s: %s ", text_type, key_text); }
}
if (xkb_sym == XKB_KEY_ISO_First_Group || xkb_sym == XKB_KEY_ISO_Last_Group || xkb_sym == XKB_KEY_ISO_Next_Group || xkb_sym == XKB_KEY_ISO_Prev_Group || xkb_sym == XKB_KEY_Mode_switch) {
return;
}
int glfw_sym = glfw_key_for_sym(xkb_sym);
bool is_fallback = false;
if (glfw_sym == GLFW_KEY_UNKNOWN && !key_text[0]) {

View File

@@ -19,7 +19,7 @@ from .config_data import all_options, type_convert
defaults: Optional[DiffOptions] = None
formats = {
formats: Dict[str, str] = {
'title': '',
'margin': '',
'text': '',
@@ -35,6 +35,7 @@ def set_formats(opts: DiffOptions) -> None:
formats['added'] = '48' + color_as_sgr(opts.added_bg)
formats['removed'] = '48' + color_as_sgr(opts.removed_bg)
formats['filler'] = '48' + color_as_sgr(opts.filler_bg)
formats['margin_filler'] = '48' + color_as_sgr(opts.margin_filler_bg or opts.filler_bg)
formats['hunk_margin'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.hunk_margin_bg)
formats['hunk'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.hunk_bg)
formats['removed_highlight'] = '48' + color_as_sgr(opts.highlight_removed_bg)

View File

@@ -9,7 +9,9 @@ from gettext import gettext as _
from typing import Any, Dict, Sequence, Union
from kitty.conf.definition import Option, Shortcut, option_func
from kitty.conf.utils import positive_int, python_string, to_color
from kitty.conf.utils import (
positive_int, python_string, to_color, to_color_or_none
)
# }}}
@@ -78,6 +80,8 @@ c('highlight_added_bg', '#acf2bd')
c('added_margin_bg', '#cdffd8')
c('filler_bg', '#fafbfc', long_text=_('Filler (empty) line background'))
c('margin_filler_bg', 'none', option_type=to_color_or_none, long_text=_(
'Filler (empty) line background in margins, defaults to the filler background'))
c('hunk_margin_bg', '#dbedff', long_text=_('Hunk header colors'))
c('hunk_bg', '#f1f8ff')

View File

@@ -48,6 +48,9 @@ try:
except ImportError:
has_highlighter = False
def highlight_collection(collection: 'Collection', aliases: Optional[Dict[str, str]] = None) -> Union[str, Dict[str, 'DiffHighlight']]:
return ''
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
ESCAPE = K['ESCAPE']

View File

@@ -141,6 +141,7 @@ removed_format = format_func('removed')
removed_margin_format = format_func('removed_margin')
added_margin_format = format_func('added_margin')
filler_format = format_func('filler')
margin_filler_format = format_func('margin_filler')
hunk_margin_format = format_func('hunk_margin')
hunk_format = format_func('hunk')
highlight_map = {'remove': ('removed_highlight', 'removed'), 'add': ('added_highlight', 'added')}
@@ -214,7 +215,7 @@ def split_with_highlights(line: str, width: int, highlights: List, bg_highlight:
return _split_with_highlights(line, truncate_pts, highlights, bg_highlight)
margin_bg_map = {'filler': filler_format, 'remove': removed_margin_format, 'add': added_margin_format, 'context': margin_format}
margin_bg_map = {'filler': margin_filler_format, 'remove': removed_margin_format, 'add': added_margin_format, 'context': margin_format}
text_bg_map = {'filler': filler_format, 'remove': removed_format, 'add': added_format, 'context': text_format}

View File

@@ -21,7 +21,7 @@ from kitty.key_encoding import (
KeyEvent, backspace_key, enter_key, key_defs as K
)
from kitty.typing import BossType, KittyCommonOpts
from kitty.utils import ScreenSize, screen_size_function
from kitty.utils import ScreenSize, screen_size_function, set_primary_selection
from ..tui.handler import Handler, result_handler
from ..tui.loop import Loop
@@ -315,7 +315,7 @@ def functions_for(args: HintsCLIOptions) -> Tuple[str, List[PostprocessorFunc]]:
pattern = '[0-9a-f]{7,128}'
elif args.type == 'word':
chars = args.word_characters
if not chars:
if chars is None:
chars = kitty_common_opts()['select_by_word_characters']
pattern = r'(?u)[{}\w]{{{},}}'.format(escape(chars), args.minimum_match_length)
post_processors.extend((brackets, quotes))
@@ -349,7 +349,7 @@ def parse_input(text: str) -> str:
def linenum_marks(text: str, args: HintsCLIOptions, Mark: Type[Mark], extra_cli_args: Sequence[str], *a: Any) -> Generator[Mark, None, None]:
regex = args.regex
if regex == DEFAULT_REGEX:
regex = r'(?P<path>(?:\S*/\S+)|(?:\S+[.][a-zA-Z0-9]{2,7})):(?P<line>\d+)'
regex = r'(?P<path>(?:\S*/\S+?)|(?:\S+[.][a-zA-Z0-9]{2,7})):(?P<line>\d+)'
yield from mark(regex, [brackets, quotes], text, args)
@@ -360,12 +360,8 @@ def load_custom_processor(customize_processing: str) -> Any:
return {k: getattr(m, k) for k in dir(m)}
if customize_processing == '::linenum::':
return {'mark': linenum_marks, 'handle_result': linenum_handle_result}
from kitty.constants import config_dir
customize_processing = os.path.expandvars(os.path.expanduser(customize_processing))
if os.path.isabs(customize_processing):
custom_path = customize_processing
else:
custom_path = os.path.join(config_dir, customize_processing)
from kitty.constants import resolve_custom_file
custom_path = resolve_custom_file(customize_processing)
import runpy
return runpy.run_path(custom_path, run_name='__main__')
@@ -413,9 +409,11 @@ OPTIONS = r'''
type=list
What program to use to open matched text. Defaults to the default open program
for the operating system. Use a value of :file:`-` to paste the match into the
terminal window instead. A value of :file:`@` will copy the match to the clipboard.
A value of :file:`default` will run the default open program. Can be specified
multiple times to run multiple programs.
terminal window instead. A value of :file:`@` will copy the match to the
clipboard. A value of :file:`*` will copy the match to the primary selection
(on systems that support primary selections). A value of :file:`default` will
run the default open program. Can be specified multiple times to run multiple
programs.
--type
@@ -629,6 +627,8 @@ def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int,
w.paste(joined_text())
elif program == '@':
set_clipboard_string(joined_text())
elif program == '*':
set_primary_selection(joined_text())
else:
cwd = None
w = boss.window_id_map.get(target_window_id)

View File

@@ -94,6 +94,7 @@ def convert(
if scaled or width > available_width or height > available_height:
width, height = fit_image(width, height, available_width, available_height)
cmd += ['-resize', '{}x{}!'.format(width, height)]
cmd += ['-depth', '8']
with NamedTemporaryFile(prefix='icat-', suffix='.' + m.mode, delete=False, dir=tdir) as outfile:
run_imagemagick(path, cmd + [outfile.name])
# ImageMagick sometimes generated rgba images smaller than the specified

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,6 @@ from enum import IntFlag
from itertools import chain
from typing import List, Optional, Sequence, Tuple
from .constants import WindowGeometry
from .fast_data_types import (
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
os_window_has_background_image
@@ -22,22 +21,38 @@ class BorderColor(IntFlag):
def vertical_edge(os_window_id: int, tab_id: int, color: int, width: int, top: int, bottom: int, left: int) -> None:
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
if width > 0:
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, left: int, right: int, top: int) -> None:
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
if height > 0:
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], width: int, geometry: 'WindowGeometry', base_width: int = 0) -> None:
left = geometry.left - (width + base_width)
top = geometry.top - (width + base_width)
right = geometry.right + (width + base_width)
bottom = geometry.bottom + (width + base_width)
horizontal_edge(os_window_id, tab_id, colors[1], width, left, right, top)
horizontal_edge(os_window_id, tab_id, colors[3], width, left, right, geometry.bottom + base_width)
vertical_edge(os_window_id, tab_id, colors[0], width, top, bottom, left)
vertical_edge(os_window_id, tab_id, colors[2], width, top, bottom, geometry.right + base_width)
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], window: WindowType, borders: bool = False) -> None:
geometry = window.geometry
pl, pt = window.effective_padding('left'), window.effective_padding('top')
pr, pb = window.effective_padding('right'), window.effective_padding('bottom')
left = geometry.left - pl
top = geometry.top - pt
lr = geometry.right
right = lr + pr
bt = geometry.bottom
bottom = bt + pb
if borders:
width = window.effective_border()
bt = bottom
lr = right
left -= width
top -= width
right += width
bottom += width
pl = pr = pb = pt = width
horizontal_edge(os_window_id, tab_id, colors[1], pt, left, right, top)
horizontal_edge(os_window_id, tab_id, colors[3], pb, left, right, bt)
vertical_edge(os_window_id, tab_id, colors[0], pl, top, bottom, left)
vertical_edge(os_window_id, tab_id, colors[2], pr, top, bottom, lr)
def load_borders_program() -> None:
@@ -58,8 +73,6 @@ class Borders:
active_window: Optional[WindowType],
current_layout: LayoutType,
extra_blank_rects: Sequence[Tuple[int, int, int, int]],
padding_width: int,
border_width: int,
draw_window_borders: bool = True,
) -> None:
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
@@ -68,15 +81,14 @@ class Borders:
for br in chain(current_layout.blank_rects, extra_blank_rects):
left, top, right, bottom = br
add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, BorderColor.default_bg)
bw, pw = border_width, padding_width
if bw + pw <= 0:
return
bw = 0
if windows:
bw = windows[0].effective_border()
draw_borders = bw > 0 and draw_window_borders
if draw_borders:
border_data = current_layout.resolve_borders(windows, active_window)
for i, w in enumerate(windows):
g = w.geometry
window_bg = w.screen.color_profile.default_bg
window_bg = (window_bg << 8) | BorderColor.window_bg
if draw_borders:
@@ -85,14 +97,15 @@ class Borders:
color = BorderColor.active
else:
color = BorderColor.bell if w.needs_attention else BorderColor.inactive
colors = tuple(color if needed else window_bg for needed in next(border_data))
draw_edges(
self.os_window_id, self.tab_id, colors, bw, g, base_width=pw)
if pw > 0 and not has_background_image:
try:
colors = tuple(color if needed else window_bg for needed in next(border_data))
draw_edges(self.os_window_id, self.tab_id, colors, w, borders=True)
except StopIteration:
pass
if not has_background_image:
# Draw the background rectangles over the padding region
colors = (window_bg, window_bg, window_bg, window_bg)
draw_edges(
self.os_window_id, self.tab_id, colors, pw, g)
colors = window_bg, window_bg, window_bg, window_bg
draw_edges(self.os_window_id, self.tab_id, colors, w)
color = BorderColor.inactive
for (left, top, right, bottom) in current_layout.window_independent_borders(windows, active_window):

View File

@@ -198,7 +198,7 @@ class Boss:
os_window_id = create_os_window(
initial_window_size_func(opts_for_size, self.cached_values),
pre_show_callback,
appname, wname or self.args.name or wclass, wclass)
self.args.title or appname, wname or self.args.name or wclass, wclass)
tm = TabManager(os_window_id, self.opts, self.args, startup_session)
self.os_window_map[os_window_id] = tm
return os_window_id
@@ -339,6 +339,21 @@ class Boss:
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control to your kitty.conf'}
return response
def remote_control(self, *args: str) -> None:
from .remote_control import parse_rc_args
from .rc.base import command_for_name, parse_subcommand_cli, PayloadGetter
try:
global_opts, items = parse_rc_args(['@'] + list(args))
if not items:
return
cmd = items[0]
c = command_for_name(cmd)
opts, items = parse_subcommand_cli(c, items)
payload = c.message_to_kitty(global_opts, opts, items)
c.response_from_kitty(self, self.active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))
except (Exception, SystemExit) as e:
self.show_error(_('remote_control mapping failed'), str(e))
def peer_message_received(self, msg_bytes: bytes) -> Optional[bytes]:
msg = msg_bytes.decode('utf-8')
cmd_prefix = '\x1bP@kitty-cmd'
@@ -534,7 +549,9 @@ class Boss:
sz = os_window_font_size(os_window_id)
if sz:
os_window_font_size(os_window_id, sz, True)
tm.update_dpi_based_sizes()
for tab in tm:
for window in tab:
window.on_dpi_change(sz)
tm.resize()
def _set_os_window_background_opacity(self, os_window_id: int, opacity: float) -> None:
@@ -755,7 +772,7 @@ class Boss:
window: Optional[Window] = None,
custom_callback: Optional[Callable] = None,
action_on_removal: Optional[Callable] = None
) -> Optional[Window]:
) -> Any:
orig_args, args = list(args), list(args)
from kittens.runner import create_kitten_handler
end_kitten = create_kitten_handler(kitten, orig_args)
@@ -766,8 +783,7 @@ class Boss:
w = window
tab = w.tabref() if w else None
if end_kitten.no_ui:
end_kitten(None, getattr(w, 'id', None), self)
return None
return end_kitten(None, getattr(w, 'id', None), self)
if w is not None and tab is not None and w.overlay_for is None:
args[0:0] = [config_dir, kitten]

View File

@@ -208,7 +208,7 @@ void main() {
// Background {{{
#ifdef NEEDS_BACKROUND
float cell_has_non_default_bg = step(1, float(abs(bg_as_uint - default_colors[bg_index])));
float cell_has_non_default_bg = step(1, float(abs(bg_as_uint - default_colors[1])));
draw_bg = 1;
#if defined(BACKGROUND)

View File

@@ -649,7 +649,7 @@ draw_resizing_text(OSWindow *w) {
static inline bool
no_render_frame_received_recently(OSWindow *w, monotonic_t now, monotonic_t max_wait) {
bool ans = now - w->last_render_frame_received_at > max_wait;
if (ans) log_error("No render frame received in %.2f seconds, re-requesting at: %f", monotonic_t_to_s_double(max_wait), monotonic_t_to_s_double(now));
if (ans && global_state.debug_rendering) log_error("No render frame received in %.2f seconds, re-requesting at: %f", monotonic_t_to_s_double(max_wait), monotonic_t_to_s_double(now));
return ans;
}

View File

@@ -96,7 +96,9 @@ def opt(text: str) -> str:
def option(x: str) -> str:
idx = x.find('-')
idx = x.rfind('--')
if idx < 0:
idx = x.find('-')
if idx > -1:
x = x[idx:]
parts = map(bold, x.split())
@@ -119,6 +121,10 @@ def file(x: str) -> str:
return italic(x)
def doc(x: str) -> str:
return f'https://sw.kovidgoyal.net/kitty/{x}.html'
OptionSpecSeq = List[Union[str, OptionDict]]
@@ -618,12 +624,15 @@ Tell kitty to listen on the specified address for control
messages. For example, :option:`{appname} --listen-on`=unix:/tmp/mykitty or
:option:`{appname} --listen-on`=tcp:localhost:12345. On Linux systems, you can
also use abstract UNIX sockets, not associated with a file, like this:
:option:`{appname} --listen-on`=unix:@mykitty. To control kitty, you can send
:option:`{appname} --listen-on`=unix:@mykitty. Environment variables
in the setting are expanded and relative paths are resolved with
respect to the temporary directory. To control kitty, you can send
it commands with :italic:`kitty @` using the :option:`kitty @ --to` option to
specify this address. This option will be ignored, unless you set
:opt:`allow_remote_control` to yes in :file:`kitty.conf`. Note that if you run
:italic:`kitty @` within a kitty window, there is no need to specify the :italic:`--to`
option as it is read automatically from the environment.
option as it is read automatically from the environment. For UNIX sockets, this
can also be specified in :file:`kitty.conf`.
--start-as
@@ -656,10 +665,11 @@ can open a new kitty window to replay the commands with::
Path to file in which to store the raw bytes received from the child process
--debug-gl
--debug-rendering --debug-gl
type=bool-set
Debug OpenGL commands. This will cause all OpenGL calls to check for errors
instead of ignoring them. Useful when debugging rendering problems
Debug rendering commands. This will cause all OpenGL calls to check for errors
instead of ignoring them. Also prints out miscellaneous debug information.
Useful when debugging rendering problems
--debug-keyboard

View File

@@ -29,7 +29,7 @@ your shell's rc file.
2) Add an input_parser function, this takes the input from
the shell for the text being completed and returns a list of words
alongwith a boolean indicating if we are on a new word or not. This
and a boolean indicating if we are on a new word or not. This
is passed to kitty's completion system.
3) An output_serializer function that is responsible for
@@ -47,12 +47,32 @@ def debug(*a: Any, **kw: Any) -> None:
print(*a, **kw)
class Delegate:
def __init__(self, words: Sequence[str] = (), pos: int = -1, new_word: bool = False):
self.words: Sequence[str] = words
self.pos = pos
self.num_of_unknown_args = len(words) - pos
self.new_word = new_word
def __bool__(self) -> bool:
return self.pos > -1 and self.num_of_unknown_args > 0
@property
def precommand(self) -> str:
try:
return self.words[self.pos]
except IndexError:
return ''
class Completions:
def __init__(self) -> None:
self.match_groups: Dict[str, MathGroup] = {}
self.no_space_groups: Set[str] = set()
self.files_groups: Set[str] = set()
self.delegate: Delegate = Delegate()
@lru_cache(maxsize=2)
@@ -153,6 +173,15 @@ def zsh_output_serializer(ans: Completions) -> str:
for word in matches:
cmd.append(shlex.quote(word))
lines.append(' '.join(cmd) + ';')
if ans.delegate:
if ans.delegate.num_of_unknown_args == 1 and not ans.delegate.new_word:
lines.append('_command_names -e')
elif ans.delegate.precommand:
for i in range(ans.delegate.pos + 1):
lines.append('shift words')
lines.append('(( CURRENT-- ))')
lines.append(f'_normal -p "{ans.delegate.precommand}"')
# debug('\n'.join(lines))
return '\n'.join(lines)
@@ -188,6 +217,8 @@ def completions_for_first_word(ans: Completions, prefix: str, entry_points: Iter
list(entry_points) + cmds + ['+' + k for k in namespaced_entry_points]
if not prefix or k.startswith(prefix)
}
if prefix:
ans.delegate = Delegate([prefix], 0)
def kitty_cli_opts(ans: Completions, prefix: Optional[str] = None) -> None:
@@ -201,9 +232,11 @@ def kitty_cli_opts(ans: Completions, prefix: Optional[str] = None) -> None:
ans.match_groups['Options'] = matches
def complete_kitty_cli_arg(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_kitty_cli_arg(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
prefix = prefix or ''
if not opt:
if unknown_args.num_of_unknown_args > 0:
ans.delegate = unknown_args
return
dest = opt['dest']
if dest == 'override':
@@ -236,24 +269,24 @@ def complete_kitty_cli_arg(ans: Completions, opt: Optional[OptionDict], prefix:
complete_files_and_dirs(ans, prefix[len('unix:'):], files_group_name='UNIX sockets', add_prefix='unix:')
CompleteArgsFunc = Callable[[Completions, Optional[OptionDict], str], None]
CompleteArgsFunc = Callable[[Completions, Optional[OptionDict], str, Delegate], None]
def complete_alias_map(
ans: Completions,
words: Sequence[str],
new_word: str,
new_word: bool,
option_map: Dict[str, OptionDict],
complete_args: Optional[CompleteArgsFunc] = None
) -> None:
expecting_arg = False
opt: Optional[OptionDict] = None
last_word = words[-1] if words else ''
for w in words:
for i, w in enumerate(words):
if expecting_arg:
if w is last_word and not new_word:
if opt is not None and complete_args is not None:
complete_args(ans, opt, w)
complete_args(ans, opt, w, Delegate())
return
expecting_arg = False
continue
@@ -263,27 +296,27 @@ def complete_alias_map(
ans.match_groups['Options'] = {k: opt['help'] for k, opt in option_map.items() if k.startswith(last_word)}
else:
if complete_args is not None:
complete_args(ans, None, last_word)
complete_args(ans, None, last_word, Delegate(words, i))
return
if opt is None:
if complete_args is not None:
complete_args(ans, None, '' if new_word else last_word)
complete_args(ans, None, '' if new_word else last_word, Delegate(words, i, new_word))
return # some non-option word encountered
expecting_arg = not opt.get('type', '').startswith('bool-')
if expecting_arg:
if opt is not None and complete_args is not None:
complete_args(ans, opt, '' if new_word else last_word)
complete_args(ans, opt, '' if new_word else last_word, Delegate())
else:
prefix = '' if new_word else last_word
if complete_args is not None:
complete_args(ans, None, prefix)
complete_args(ans, None, prefix, Delegate())
ans.match_groups['Options'] = {k: opt['help'] for k, opt in option_map.items() if k.startswith(prefix)}
def complete_cli(
ans: Completions,
words: Sequence[str],
new_word: str,
new_word: bool,
seq: OptionSpecSeq,
complete_args: Optional[CompleteArgsFunc] = None
) -> None:
@@ -295,7 +328,7 @@ def complete_cli(
complete_alias_map(ans, words, new_word, option_map, complete_args)
def complete_remote_command(ans: Completions, cmd_name: str, words: Sequence[str], new_word: str) -> None:
def complete_remote_command(ans: Completions, cmd_name: str, words: Sequence[str], new_word: bool) -> None:
aliases, alias_map = options_for_cmd(cmd_name)
if not alias_map:
return
@@ -358,7 +391,7 @@ def complete_files_and_dirs(
ans.files_groups.add(files_group_name)
def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
from mimetypes import guess_type
def icat_file_predicate(filename: str) -> bool:
@@ -374,7 +407,7 @@ def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str)
def remote_files_completer(spec: Tuple[str, Tuple[str, ...]]) -> CompleteArgsFunc:
name, matchers = spec
def complete_files_map(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_files_map(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
def predicate(filename: str) -> bool:
for m in matchers:
@@ -392,14 +425,14 @@ def config_file_predicate(filename: str) -> bool:
return filename.endswith('.conf')
def complete_diff_args(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_diff_args(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
if opt is None:
complete_files_and_dirs(ans, prefix, 'Files')
elif opt['dest'] == 'config':
complete_files_and_dirs(ans, prefix, 'Config Files', config_file_predicate)
def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_word: str) -> None:
def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_word: bool) -> None:
try:
cd = get_kitten_cli_docs(kitten)
except SystemExit:
@@ -419,7 +452,7 @@ def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_wor
}.get(kitten))
def find_completions(words: Sequence[str], new_word: str, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> Completions:
def find_completions(words: Sequence[str], new_word: bool, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> Completions:
ans = Completions()
if not words or words[0] != 'kitty':
return ans

View File

@@ -6,7 +6,7 @@ import re
from functools import partial
from typing import (
Any, Callable, Dict, Generator, Iterable, List, Match, Optional, Sequence,
Set, Tuple, Union, cast, get_type_hints
Set, Tuple, Union, get_type_hints
)
from .utils import to_bool
@@ -156,7 +156,7 @@ def remove_markup(text: str) -> str:
'layouts': 'https://sw.kovidgoyal.net/kitty/index.html#layouts',
'sessions': 'https://sw.kovidgoyal.net/kitty/index.html#sessions',
}[m.group(2)]
return cast(str, m.group(2))
return str(m.group(2))
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)

View File

@@ -23,7 +23,7 @@ from .config_data import all_options, parse_mods, type_convert
from .constants import cache_dir, defconf, is_macos
from .key_names import get_key_name_lookup, key_name_aliases
from .options_stub import Options as OptionsStub
from .typing import TypedDict
from .typing import EdgeLiteral, TypedDict
from .utils import log_error
KeySpec = Tuple[int, bool, int]
@@ -240,6 +240,15 @@ def set_colors(func: str, rest: str) -> FuncArgsType:
return func, r
@func_with_args('remote_control')
def remote_control(func: str, rest: str) -> FuncArgsType:
import shlex
r = shlex.split(rest)
if len(r) < 1:
log_error('Too few arguments to remote_control function')
return func, r
@func_with_args('nth_window')
def nth_window(func: str, rest: str) -> FuncArgsType:
try:
@@ -576,7 +585,7 @@ def handle_deprecated_macos_show_window_title_in_menubar_alias(key: str, val: st
ans['macos_show_window_title_in'] = macos_show_window_title_in
def expandvars(val: str, env: Dict[str, str]) -> str:
def expandvars(val: str, env: Dict[str, str] = {}) -> str:
def sub(m: Match) -> str:
key = m.group(1)
@@ -730,12 +739,23 @@ def initial_window_size_func(opts: OptionsStub, cached_values: Dict) -> Callable
# scaling is not needed on Wayland, but is needed on macOS. Not
# sure about X11.
xscale = yscale = 1
def effective_margin(which: EdgeLiteral) -> float:
ans: float = getattr(opts.single_window_margin_width, which)
if ans < 0:
ans = getattr(opts.window_margin_width, which)
return ans
if w_unit == 'cells':
width = cell_width * w / xscale + (dpi_x / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
spacing = effective_margin('left') + effective_margin('right')
spacing += opts.window_padding_width.left + opts.window_padding_width.right
width = cell_width * w / xscale + (dpi_x / 72) * spacing + 1
else:
width = w
if h_unit == 'cells':
height = cell_height * h / yscale + (dpi_y / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
spacing = effective_margin('top') + effective_margin('bottom')
spacing += opts.window_padding_width.top + opts.window_padding_width.bottom
height = cell_height * h / yscale + (dpi_y / 72) * spacing + 1
else:
height = h
return int(width), int(height)

View File

@@ -6,8 +6,8 @@
import os
from gettext import gettext as _
from typing import (
Any, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set, Tuple,
TypeVar, Union
Any, Callable, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set,
Tuple, TypeVar, Union
)
from . import fast_data_types as defines
@@ -16,7 +16,7 @@ from .conf.utils import (
choices, positive_float, positive_int, to_bool, to_cmdline, to_color,
to_color_or_none, unit_float
)
from .constants import config_dir, is_macos
from .constants import FloatEdges, config_dir, is_macos
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
@@ -675,15 +675,42 @@ that separate the inactive window from a neighbor. Note that setting
a non-zero window margin overrides this and causes all borders to be drawn.
'''))
o('window_margin_width', 0.0, option_type=positive_float, long_text=_('''
The window margin (in pts) (blank area outside the border)'''))
o('single_window_margin_width', -1000.0, option_type=float, long_text=_('''
def edge_width(x: str, converter: Callable[[str], float] = positive_float) -> FloatEdges:
parts = str(x).split()
num = len(parts)
if num == 1:
val = converter(parts[0])
return FloatEdges(val, val, val, val)
if num == 2:
v = converter(parts[0])
h = converter(parts[1])
return FloatEdges(h, v, h, v)
if num == 3:
top, h, bottom = map(converter, parts)
return FloatEdges(h, top, h, bottom)
top, right, bottom, left = map(converter, parts)
return FloatEdges(left, top, right, bottom)
def optional_edge_width(x: str) -> FloatEdges:
return edge_width(x, float)
edge_desc = _(
'A single value sets all four sides. Two values set the vertical and horizontal sides.'
' Three values set top, horizontal and bottom. Four values set top, right, bottom and left.')
o('window_margin_width', '0', option_type=edge_width, long_text=_('''
The window margin (in pts) (blank area outside the border). ''' + edge_desc))
o('single_window_margin_width', '-1', option_type=optional_edge_width, long_text=_('''
The window margin (in pts) to use when only a single window is visible.
Negative values will cause the value of :opt:`window_margin_width` to be used instead.'''))
Negative values will cause the value of :opt:`window_margin_width` to be used instead. ''' + edge_desc))
o('window_padding_width', 0.0, option_type=positive_float, long_text=_('''
The window padding (in pts) (blank area between the text and the window border)'''))
o('window_padding_width', '0', option_type=edge_width, long_text=_('''
The window padding (in pts) (blank area between the text and the window border). ''' + edge_desc))
o('placement_strategy', 'center', option_type=choices('center', 'top-left'), long_text=_('''
When the window size is not an exact multiple of the cell size, the cell area of the terminal
@@ -1012,6 +1039,19 @@ you want to prevent programs running on a remote computer over ssh from
controlling kitty.
'''))
o('listen_on', 'none', long_text=_('''
Tell kitty to listen to the specified unix/tcp socket for remote control
connections. Note that this will apply to all kitty instances. It can be
overridden by the :option:`kitty --listen-on` command line flag. This
option accepts only UNIX sockets, such as unix:${TEMP}/mykitty or (on Linux)
unix:@mykitty. Environment variables are expanded. If {kitty_pid} is present
then it is replaced by the PID of the kitty process, otherwise the PID of the kitty
process is appended to the value, with a hyphen. This option is ignored unless
you also set :opt:`allow_remote_control` to enable remote control. See the
help for :option:`kitty --listen-on` for more details.
'''))
o(
'+env', '',
add_to_default=False,

View File

@@ -20,13 +20,27 @@ class Version(NamedTuple):
appname: str = 'kitty'
version: Version = Version(0, 17, 0)
version: Version = Version(0, 17, 3)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat
base = os.path.dirname(os.path.abspath(__file__))
class Edges(NamedTuple):
left: int = 0
top: int = 0
right: int = 0
bottom: int = 0
class FloatEdges(NamedTuple):
left: float = 0
top: float = 0
right: float = 0
bottom: float = 0
class ScreenGeometry(NamedTuple):
xstart: float
ystart: float
@@ -43,6 +57,7 @@ class WindowGeometry(NamedTuple):
bottom: int
xnum: int
ynum: int
spaces: Edges = Edges()
@lru_cache(maxsize=2)
@@ -198,3 +213,10 @@ def running_in_kitty(set_val: Optional[bool] = None) -> bool:
if set_val is not None:
setattr(running_in_kitty, 'ans', set_val)
return bool(getattr(running_in_kitty, 'ans', False))
def resolve_custom_file(path: str) -> str:
path = os.path.expandvars(os.path.expanduser(path))
if not os.path.isabs(path):
path = os.path.join(config_dir, path)
return path

View File

@@ -92,10 +92,10 @@ font_descriptor_to_python(CTFontDescriptorRef descriptor) {
NSString *style = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute);
NSDictionary *traits = (NSDictionary *) CTFontDescriptorCopyAttribute(descriptor, kCTFontTraitsAttribute);
unsigned int straits = [traits[(id)kCTFontSymbolicTrait] unsignedIntValue];
NSNumber *weightVal = traits[(id)kCTFontWeightTrait];
NSNumber *widthVal = traits[(id)kCTFontWidthTrait];
float weightVal = [traits[(id)kCTFontWeightTrait] floatValue];
float widthVal = [traits[(id)kCTFontWidthTrait] floatValue];
PyObject *ans = Py_BuildValue("{ssssssss sOsOsO sfsfsI}",
PyObject *ans = Py_BuildValue("{ssssssss sOsOsOsOsOsO sfsfsI}",
"path", [[url path] UTF8String],
"postscript_name", [psName UTF8String],
"family", [family UTF8String],
@@ -104,9 +104,12 @@ font_descriptor_to_python(CTFontDescriptorRef descriptor) {
"bold", (straits & kCTFontBoldTrait) != 0 ? Py_True : Py_False,
"italic", (straits & kCTFontItalicTrait) != 0 ? Py_True : Py_False,
"monospace", (straits & kCTFontMonoSpaceTrait) != 0 ? Py_True : Py_False,
"expanded", (straits & kCTFontExpandedTrait) != 0 ? Py_True : Py_False,
"condensed", (straits & kCTFontCondensedTrait) != 0 ? Py_True : Py_False,
"color_glyphs", (straits & kCTFontColorGlyphsTrait) != 0 ? Py_True : Py_False,
"weight", [weightVal floatValue],
"width", [widthVal floatValue],
"weight", weightVal,
"width", widthVal,
"traits", straits
);
[url release];
@@ -309,7 +312,7 @@ harfbuzz_font_for_face(PyObject* s) {
}
void
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) {
// See https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html
CTFace *self = (CTFace*)s;
#define count (128 - 32)
@@ -329,6 +332,8 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
*underline_position = (unsigned int)floor(self->ascent - self->underline_position + 0.5);
*underline_thickness = (unsigned int)ceil(MAX(0.1, self->underline_thickness));
*baseline = (unsigned int)self->ascent;
*strikethrough_position = (unsigned int)floor(*baseline * 0.65);
*strikethrough_thickness = *underline_thickness;
// float line_height = MAX(1, floor(self->ascent + self->descent + MAX(0, self->leading) + 0.5));
// Let CoreText's layout engine calculate the line height. Slower, but hopefully more accurate.
#define W "AQWMH_gyl "
@@ -500,10 +505,10 @@ do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_g
Region src = {.bottom=cell_height, .right=canvas_width}, dest = {.bottom=cell_height, .right=canvas_width};
render_alpha_mask(render_buf, canvas, &src, &dest, canvas_width, canvas_width);
}
if (num_cells > 1) {
// center glyphs
CGFloat delta = canvas_width - br.size.width;
if (delta > 1) right_shift_canvas(canvas, canvas_width, cell_height, (unsigned)(delta / 2.f));
if (num_cells && (center_glyph || (num_cells == 2 && *was_colored))) {
// center glyphs (two cell emoji and PUA glyphs)
CGFloat delta = (((CGFloat)canvas_width - br.size.width) / 2.f) - br.origin.x;
if (delta >= 1.f) right_shift_canvas(canvas, canvas_width, cell_height, (unsigned)(delta));
}
return true;
}

259
kitty/emoji.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2019-10-01
// unicode data, built from the unicode standard on: 2020-04-06
// see gen-wcwidth.py
#pragma once
#include "data-types.h"
@@ -18,14 +18,10 @@ is_emoji(char_type code) {
return true;
case 0xae:
return true;
case 0x200d:
return true;
case 0x203c:
return true;
case 0x2049:
return true;
case 0x20e3:
return true;
case 0x2122:
return true;
case 0x2139:
@@ -38,8 +34,6 @@ is_emoji(char_type code) {
return true;
case 0x2328:
return true;
case 0x2388:
return true;
case 0x23cf:
return true;
case 0x23e9 ... 0x23f3:
@@ -56,15 +50,91 @@ is_emoji(char_type code) {
return true;
case 0x25fb ... 0x25fe:
return true;
case 0x2600 ... 0x2605:
case 0x2600 ... 0x2604:
return true;
case 0x2607 ... 0x2612:
case 0x260e:
return true;
case 0x2614 ... 0x2685:
case 0x2611:
return true;
case 0x2690 ... 0x2705:
case 0x2614 ... 0x2615:
return true;
case 0x2708 ... 0x2712:
case 0x2618:
return true;
case 0x261d:
return true;
case 0x2620:
return true;
case 0x2622 ... 0x2623:
return true;
case 0x2626:
return true;
case 0x262a:
return true;
case 0x262e ... 0x262f:
return true;
case 0x2638 ... 0x263a:
return true;
case 0x2640:
return true;
case 0x2642:
return true;
case 0x2648 ... 0x2653:
return true;
case 0x265f ... 0x2660:
return true;
case 0x2663:
return true;
case 0x2665 ... 0x2666:
return true;
case 0x2668:
return true;
case 0x267b:
return true;
case 0x267e ... 0x267f:
return true;
case 0x2692 ... 0x2697:
return true;
case 0x2699:
return true;
case 0x269b ... 0x269c:
return true;
case 0x26a0 ... 0x26a1:
return true;
case 0x26a7:
return true;
case 0x26aa ... 0x26ab:
return true;
case 0x26b0 ... 0x26b1:
return true;
case 0x26bd ... 0x26be:
return true;
case 0x26c4 ... 0x26c5:
return true;
case 0x26c8:
return true;
case 0x26ce ... 0x26cf:
return true;
case 0x26d1:
return true;
case 0x26d3 ... 0x26d4:
return true;
case 0x26e9 ... 0x26ea:
return true;
case 0x26f0 ... 0x26f5:
return true;
case 0x26f7 ... 0x26fa:
return true;
case 0x26fd:
return true;
case 0x2702:
return true;
case 0x2705:
return true;
case 0x2708 ... 0x270d:
return true;
case 0x270f:
return true;
case 0x2712:
return true;
case 0x2714:
return true;
@@ -90,7 +160,7 @@ is_emoji(char_type code) {
return true;
case 0x2757:
return true;
case 0x2763 ... 0x2767:
case 0x2763 ... 0x2764:
return true;
case 0x2795 ... 0x2797:
return true;
@@ -118,15 +188,11 @@ is_emoji(char_type code) {
return true;
case 0x3299:
return true;
case 0xfe0f:
case 0x1f004:
return true;
case 0x1f000 ... 0x1f0ff:
case 0x1f0cf:
return true;
case 0x1f10d ... 0x1f10f:
return true;
case 0x1f12f:
return true;
case 0x1f16c ... 0x1f171:
case 0x1f170 ... 0x1f171:
return true;
case 0x1f17e ... 0x1f17f:
return true;
@@ -134,9 +200,9 @@ is_emoji(char_type code) {
return true;
case 0x1f191 ... 0x1f19a:
return true;
case 0x1f1ad ... 0x1f1ff:
case 0x1f1e6 ... 0x1f1ff:
return true;
case 0x1f201 ... 0x1f20f:
case 0x1f201 ... 0x1f202:
return true;
case 0x1f21a:
return true;
@@ -144,44 +210,107 @@ is_emoji(char_type code) {
return true;
case 0x1f232 ... 0x1f23a:
return true;
case 0x1f23c ... 0x1f23f:
case 0x1f250 ... 0x1f251:
return true;
case 0x1f249 ... 0x1f53d:
case 0x1f300 ... 0x1f321:
return true;
case 0x1f546 ... 0x1f64f:
case 0x1f324 ... 0x1f393:
return true;
case 0x1f680 ... 0x1f6ff:
case 0x1f396 ... 0x1f397:
return true;
case 0x1f774 ... 0x1f77f:
case 0x1f399 ... 0x1f39b:
return true;
case 0x1f7d5 ... 0x1f7ff:
case 0x1f39e ... 0x1f3f0:
return true;
case 0x1f80c ... 0x1f80f:
case 0x1f3f3 ... 0x1f3f5:
return true;
case 0x1f848 ... 0x1f84f:
case 0x1f3f7 ... 0x1f4fd:
return true;
case 0x1f85a ... 0x1f85f:
case 0x1f4ff ... 0x1f53d:
return true;
case 0x1f888 ... 0x1f88f:
case 0x1f549 ... 0x1f54e:
return true;
case 0x1f8ae ... 0x1f8ff:
case 0x1f550 ... 0x1f567:
return true;
case 0x1f56f ... 0x1f570:
return true;
case 0x1f573 ... 0x1f57a:
return true;
case 0x1f587:
return true;
case 0x1f58a ... 0x1f58d:
return true;
case 0x1f590:
return true;
case 0x1f595 ... 0x1f596:
return true;
case 0x1f5a4 ... 0x1f5a5:
return true;
case 0x1f5a8:
return true;
case 0x1f5b1 ... 0x1f5b2:
return true;
case 0x1f5bc:
return true;
case 0x1f5c2 ... 0x1f5c4:
return true;
case 0x1f5d1 ... 0x1f5d3:
return true;
case 0x1f5dc ... 0x1f5de:
return true;
case 0x1f5e1:
return true;
case 0x1f5e3:
return true;
case 0x1f5e8:
return true;
case 0x1f5ef:
return true;
case 0x1f5f3:
return true;
case 0x1f5fa ... 0x1f64f:
return true;
case 0x1f680 ... 0x1f6c5:
return true;
case 0x1f6cb ... 0x1f6d2:
return true;
case 0x1f6d5 ... 0x1f6d7:
return true;
case 0x1f6e0 ... 0x1f6e5:
return true;
case 0x1f6e9:
return true;
case 0x1f6eb ... 0x1f6ec:
return true;
case 0x1f6f0:
return true;
case 0x1f6f3 ... 0x1f6fc:
return true;
case 0x1f7e0 ... 0x1f7eb:
return true;
case 0x1f90c ... 0x1f93a:
return true;
case 0x1f93c ... 0x1f945:
return true;
case 0x1f947 ... 0x1fffd:
case 0x1f947 ... 0x1f978:
return true;
case 0xe0020 ... 0xe007f:
case 0x1f97a ... 0x1f9cb:
return true;
default: return false;
}
return false;
}
static inline bool
is_emoji_modifier(char_type code) {
switch(code) {
case 0x1f3fb ... 0x1f3ff:
case 0x1f9cd ... 0x1f9ff:
return true;
case 0x1fa70 ... 0x1fa74:
return true;
case 0x1fa78 ... 0x1fa7a:
return true;
case 0x1fa80 ... 0x1fa86:
return true;
case 0x1fa90 ... 0x1faa8:
return true;
case 0x1fab0 ... 0x1fab6:
return true;
case 0x1fac0 ... 0x1fac2:
return true;
case 0x1fad0 ... 0x1fad6:
return true;
default: return false;
}
@@ -390,10 +519,12 @@ is_symbol(char_type code) {
return true;
case 0x2b76 ... 0x2b95:
return true;
case 0x2b98 ... 0x2bff:
case 0x2b97 ... 0x2bff:
return true;
case 0x2ce5 ... 0x2cea:
return true;
case 0x2e50 ... 0x2e51:
return true;
case 0x2e80 ... 0x2e99:
return true;
case 0x2e9b ... 0x2ef3:
@@ -450,6 +581,8 @@ is_symbol(char_type code) {
return true;
case 0xab5b:
return true;
case 0xab6a ... 0xab6b:
return true;
case 0xfb29:
return true;
case 0xfbb2 ... 0xfbc1:
@@ -488,7 +621,7 @@ is_symbol(char_type code) {
return true;
case 0x1018c ... 0x1018e:
return true;
case 0x10190 ... 0x1019b:
case 0x10190 ... 0x1019c:
return true;
case 0x101a0:
return true;
@@ -582,9 +715,7 @@ is_symbol(char_type code) {
return true;
case 0x1f0d1 ... 0x1f0f5:
return true;
case 0x1f110 ... 0x1f16c:
return true;
case 0x1f170 ... 0x1f1ac:
case 0x1f10d ... 0x1f1ad:
return true;
case 0x1f1e6 ... 0x1f202:
return true;
@@ -596,11 +727,11 @@ is_symbol(char_type code) {
return true;
case 0x1f260 ... 0x1f265:
return true;
case 0x1f300 ... 0x1f6d5:
case 0x1f300 ... 0x1f6d7:
return true;
case 0x1f6e0 ... 0x1f6ec:
return true;
case 0x1f6f0 ... 0x1f6fa:
case 0x1f6f0 ... 0x1f6fc:
return true;
case 0x1f700 ... 0x1f773:
return true;
@@ -618,29 +749,33 @@ is_symbol(char_type code) {
return true;
case 0x1f890 ... 0x1f8ad:
return true;
case 0x1f900 ... 0x1f90b:
case 0x1f8b0 ... 0x1f8b1:
return true;
case 0x1f90d ... 0x1f971:
case 0x1f900 ... 0x1f978:
return true;
case 0x1f973 ... 0x1f976:
return true;
case 0x1f97a ... 0x1f9a2:
return true;
case 0x1f9a5 ... 0x1f9aa:
return true;
case 0x1f9ae ... 0x1f9ca:
case 0x1f97a ... 0x1f9cb:
return true;
case 0x1f9cd ... 0x1fa53:
return true;
case 0x1fa60 ... 0x1fa6d:
return true;
case 0x1fa70 ... 0x1fa73:
case 0x1fa70 ... 0x1fa74:
return true;
case 0x1fa78 ... 0x1fa7a:
return true;
case 0x1fa80 ... 0x1fa82:
case 0x1fa80 ... 0x1fa86:
return true;
case 0x1fa90 ... 0x1fa95:
case 0x1fa90 ... 0x1faa8:
return true;
case 0x1fab0 ... 0x1fab6:
return true;
case 0x1fac0 ... 0x1fac2:
return true;
case 0x1fad0 ... 0x1fad6:
return true;
case 0x1fb00 ... 0x1fb92:
return true;
case 0x1fb94 ... 0x1fbca:
return true;
default: return false;
}

View File

@@ -323,9 +323,14 @@ FC_MONO: int = 100
FC_DUAL: int
FC_WEIGHT_REGULAR: int
FC_WEIGHT_BOLD: int
FC_WIDTH_NORMAL: int
FC_SLANT_ROMAN: int
FC_SLANT_ITALIC: int
BORDERS_PROGRAM: int
PRESS: int
RELEASE: int
DRAG: int
MOVE: int
# }}}
@@ -402,6 +407,7 @@ class FontConfigPattern(TypedDict):
style: str
spacing: str
weight: int
width: int
slant: int
hint_style: int
subpixel: int
@@ -438,6 +444,9 @@ class CoreTextFont(TypedDict):
style: str
bold: bool
italic: bool
expanded: bool
condensed: bool
color_glyphs: bool
monospace: bool
weight: float
width: float
@@ -544,7 +553,7 @@ def update_window_visibility(
def set_options(
opts: Options,
is_wayland: bool = False,
debug_gl: bool = False,
debug_rendering: bool = False,
debug_font_fallback: bool = False
) -> None:
pass
@@ -897,7 +906,7 @@ def set_font_data(
box_drawing_func: Callable[[int, int, int, float],
Tuple[int, Union[bytearray, bytes, Array]]],
prerender_func: Callable[
[int, int, int, int, int, float, float, float, float],
[int, int, int, int, int, int, int, float, float, float, float],
Tuple[Union[Array, int], ...]],
descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]],
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,
@@ -1123,3 +1132,7 @@ def spawn(
def key_to_bytes(glfw_key: int, smkx: bool, extended: bool, mods: int, action: int) -> bytes:
pass
def set_window_padding(os_window_id: int, tab_id: int, window_id: int, left: int, top: int, right: int, bottom: int) -> None:
pass

View File

@@ -47,6 +47,7 @@ pattern_as_dict(FcPattern *pat) {
S(FC_FULLNAME, full_name);
S(FC_POSTSCRIPT_NAME, postscript_name);
I(FC_WEIGHT, weight);
I(FC_WIDTH, width)
I(FC_SLANT, slant);
I(FC_HINT_STYLE, hint_style);
I(FC_INDEX, index);
@@ -247,5 +248,7 @@ init_fontconfig_library(PyObject *module) {
PyModule_AddIntMacro(module, FC_DUAL);
PyModule_AddIntMacro(module, FC_MONO);
PyModule_AddIntMacro(module, FC_CHARCELL);
PyModule_AddIntMacro(module, FC_WIDTH_NORMAL);
return true;
}

View File

@@ -85,7 +85,7 @@ typedef struct {
typedef struct {
FONTS_DATA_HEAD
id_type id;
unsigned int baseline, underline_position, underline_thickness;
unsigned int baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
size_t fonts_capacity, fonts_count, fallback_fonts_count;
ssize_t medium_font_idx, bold_font_idx, italic_font_idx, bi_font_idx, first_symbol_font_idx, first_fallback_font_idx;
Font *fonts;
@@ -422,8 +422,8 @@ python_send_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigne
static inline void
calc_cell_metrics(FontGroup *fg) {
unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness;
cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness);
if (!cell_width) fatal("Failed to calculate cell width for the specified font");
unsigned int before_cell_height = cell_height;
int cw = cell_width, ch = cell_height;
@@ -455,7 +455,7 @@ calc_cell_metrics(FontGroup *fg) {
}
sprite_tracker_set_layout(&fg->sprite_tracker, cell_width, cell_height);
fg->cell_width = cell_width; fg->cell_height = cell_height;
fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness;
fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness, fg->strikethrough_position = strikethrough_position, fg->strikethrough_thickness = strikethrough_thickness;
free(fg->canvas);
fg->canvas = calloc(CELLS_IN_CANVAS * fg->cell_width * fg->cell_height, sizeof(pixel));
if (!fg->canvas) fatal("Out of memory allocating canvas for font group");
@@ -1243,7 +1243,7 @@ send_prerendered_sprites(FontGroup *fg) {
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas);
do_increment(fg, &error);
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, OPT(cursor_beam_thickness), fg->logical_dpi_x, fg->logical_dpi_y);
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, fg->strikethrough_position, fg->strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y);
if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); }
for (ssize_t i = 0; i < PyTuple_GET_SIZE(args) - 1; i++) {
x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z;

View File

@@ -25,7 +25,7 @@ int get_glyph_width(PyObject *, glyph_index);
bool is_glyph_empty(PyObject *, glyph_index);
hb_font_t* harfbuzz_font_for_face(PyObject*);
bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE);
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE, bool center_glyph);
PyObject* create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg);
PyObject* specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE);

View File

@@ -54,12 +54,16 @@ def find_best_match(family: str, bold: bool = False, italic: bool = False) -> Co
q = re.sub(r'\s+', ' ', family.lower())
font_map = all_fonts_map()
def score(candidate: CoreTextFont) -> Tuple[int, int]:
def score(candidate: CoreTextFont) -> Tuple[int, int, int, float]:
style_match = 1 if candidate['bold'] == bold and candidate[
'italic'
] == italic else 0
monospace_match = 1 if candidate['monospace'] else 0
return style_match, monospace_match
is_regular_width = not candidate['expanded'] and not candidate['condensed']
# prefer demi-bold to bold to heavy, less bold means less chance of
# overflow
weight_distance_from_medium = abs(candidate['weight'])
return style_match, monospace_match, 1 if is_regular_width else 0, 1 - weight_distance_from_medium
# First look for an exact match
for selector in ('ps_map', 'full_map'):

View File

@@ -8,7 +8,7 @@ from typing import Dict, Generator, List, Optional, Tuple, cast
from kitty.fast_data_types import (
FC_DUAL, FC_MONO, FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD,
FC_WEIGHT_REGULAR, fc_list, fc_match as fc_match_impl
FC_WEIGHT_REGULAR, FC_WIDTH_NORMAL, fc_list, fc_match as fc_match_impl
)
from kitty.options_stub import Options
from kitty.typing import FontConfigPattern
@@ -73,11 +73,13 @@ def find_best_match(family: str, bold: bool = False, italic: bool = False, monos
q = family_name_to_key(family)
font_map = all_fonts_map(monospaced)
def score(candidate: FontConfigPattern) -> Tuple[int, int]:
def score(candidate: FontConfigPattern) -> Tuple[int, int, int]:
bold_score = abs((FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR) - candidate.get('weight', 0))
italic_score = abs((FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN) - candidate.get('slant', 0))
monospace_match = 0 if candidate.get('spacing') == 'MONO' else 1
return bold_score + italic_score, monospace_match
width_score = abs(candidate.get('width', FC_WIDTH_NORMAL) - FC_WIDTH_NORMAL)
return bold_score + italic_score, monospace_match, width_score
# First look for an exact match
for selector in ('ps_map', 'full_map', 'family_map'):

View File

@@ -191,7 +191,9 @@ def render_special(
cell_width: int = 0, cell_height: int = 0,
baseline: int = 0,
underline_position: int = 0,
underline_thickness: int = 0
underline_thickness: int = 0,
strikethrough_position: int = 0,
strikethrough_thickness: int = 0
) -> ctypes.Array:
underline_position = min(underline_position, cell_height - underline_thickness)
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
@@ -216,8 +218,7 @@ def render_special(
t = max(1, min(cell_height - underline_position - 1, t))
dl([add_line, add_line, add_dline, add_curl][underline], underline_position, t, cell_height)
if strikethrough:
pos = int(0.65 * baseline)
dl(add_line, pos, underline_thickness, cell_height)
dl(add_line, strikethrough_position, strikethrough_thickness, cell_height)
return ans
@@ -268,6 +269,8 @@ def prerender_function(
baseline: int,
underline_position: int,
underline_thickness: int,
strikethrough_position: int,
strikethrough_thickness: int,
cursor_beam_thickness: float,
cursor_underline_thickness: float,
dpi_x: float,
@@ -276,7 +279,8 @@ def prerender_function(
# Pre-render the special underline, strikethrough and missing and cursor cells
f = partial(
render_special, cell_width=cell_width, cell_height=cell_height, baseline=baseline,
underline_position=underline_position, underline_thickness=underline_thickness)
underline_position=underline_position, underline_thickness=underline_thickness,
strikethrough_position=strikethrough_position, strikethrough_thickness=strikethrough_thickness)
c = partial(
render_cursor, cursor_beam_thickness=cursor_beam_thickness,
cursor_underline_thickness=cursor_underline_thickness, cell_width=cell_width,

View File

@@ -22,12 +22,13 @@
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_TRUETYPE_TABLES_H
typedef struct {
PyObject_HEAD
FT_Face face;
unsigned int units_per_EM;
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness;
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
int hinting, hintstyle, index;
bool is_scalable, has_color;
float size_in_pts;
@@ -207,6 +208,12 @@ init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_
if (self->harfbuzz_font == NULL) { PyErr_NoMemory(); return false; }
hb_ft_font_set_load_flags(self->harfbuzz_font, get_load_flags(self->hinting, self->hintstyle, FT_LOAD_DEFAULT));
TT_OS2 *os2 = (TT_OS2*)FT_Get_Sfnt_Table(self->face, FT_SFNT_OS2);
if (os2 != NULL) {
self->strikethrough_position = os2->yStrikeoutPosition;
self->strikethrough_thickness = os2->yStrikeoutSize;
}
self->path = path;
Py_INCREF(self->path);
self->index = self->face->face_index & 0xFFFF;
@@ -263,11 +270,11 @@ static PyObject *
repr(Face *self) {
const char *ps_name = FT_Get_Postscript_Name(self->face);
return PyUnicode_FromFormat(
"Face(family=%s, style=%s, ps_name=%s, path=%S, index=%d, is_scalable=%S, has_color=%S, ascender=%i, descender=%i, height=%i, underline_position=%i, underline_thickness=%i)",
"Face(family=%s, style=%s, ps_name=%s, path=%S, index=%d, is_scalable=%S, has_color=%S, ascender=%i, descender=%i, height=%i, underline_position=%i, underline_thickness=%i, strikethrough_position=%i, strikethrough_thickness=%i)",
self->face->family_name ? self->face->family_name : "", self->face->style_name ? self->face->style_name : "",
ps_name ? ps_name: "",
self->path, self->index, self->is_scalable ? Py_True : Py_False, self->has_color ? Py_True : Py_False,
self->ascender, self->descender, self->height, self->underline_position, self->underline_thickness
self->ascender, self->descender, self->height, self->underline_position, self->underline_thickness, self->strikethrough_position, self->strikethrough_thickness
);
}
@@ -292,13 +299,24 @@ calc_cell_width(Face *self) {
}
void
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) {
Face *self = (Face*)s;
*cell_width = calc_cell_width(self);
*cell_height = calc_cell_height(self, true);
*baseline = font_units_to_pixels_y(self, self->ascender);
*underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position)));
*underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness));
if (self->strikethrough_position != 0) {
*strikethrough_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->strikethrough_position)));
} else {
*strikethrough_position = (unsigned int)floor(*baseline * 0.65);
}
if (self->strikethrough_thickness > 0) {
*strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->strikethrough_thickness));
} else {
*strikethrough_thickness = *underline_thickness;
}
}
unsigned int
@@ -659,6 +677,8 @@ static PyMemberDef members[] = {
MEM(max_advance_height, T_INT),
MEM(underline_position, T_INT),
MEM(underline_thickness, T_INT),
MEM(strikethrough_position, T_INT),
MEM(strikethrough_thickness, T_INT),
MEM(is_scalable, T_BOOL),
MEM(path, T_OBJECT_EX),
{NULL} /* Sentinel */

View File

@@ -46,7 +46,7 @@ gl_init() {
if (!gl_version) {
fatal("Loading the OpenGL library failed");
}
if (!global_state.debug_gl) {
if (!global_state.debug_rendering) {
gladUninstallGLDebug();
}
gladSetGLPostCallback(check_for_gl_error);
@@ -59,7 +59,7 @@ gl_init() {
glad_loaded = true;
int gl_major = GLAD_VERSION_MAJOR(gl_version);
int gl_minor = GLAD_VERSION_MINOR(gl_version);
if (global_state.debug_gl) printf("GL version string: '%s' Detected version: %d.%d\n", glGetString(GL_VERSION), gl_major, gl_minor);
if (global_state.debug_rendering) printf("GL version string: '%s' Detected version: %d.%d\n", glGetString(GL_VERSION), 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);
}

1
kitty/glfw-wrapper.c generated
View File

@@ -1,4 +1,5 @@
#define GFW_EXTERN
#include "data-types.h"
#include "glfw-wrapper.h"
#include <dlfcn.h>

275
kitty/glfw-wrapper.h generated
View File

@@ -9,6 +9,9 @@
#include <stdint.h>
#include "monotonic.h"
#ifndef GFW_EXTERN
#define GFW_EXTERN extern
#endif
/*! @name GLFW version macros
@@ -1587,547 +1590,547 @@ typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, const char*);
typedef int (*glfwInit_func)(monotonic_t);
glfwInit_func glfwInit_impl;
GFW_EXTERN glfwInit_func glfwInit_impl;
#define glfwInit glfwInit_impl
typedef void (*glfwRunMainLoop_func)(GLFWtickcallback, void*);
glfwRunMainLoop_func glfwRunMainLoop_impl;
GFW_EXTERN glfwRunMainLoop_func glfwRunMainLoop_impl;
#define glfwRunMainLoop glfwRunMainLoop_impl
typedef void (*glfwStopMainLoop_func)(void);
glfwStopMainLoop_func glfwStopMainLoop_impl;
GFW_EXTERN glfwStopMainLoop_func glfwStopMainLoop_impl;
#define glfwStopMainLoop glfwStopMainLoop_impl
typedef unsigned long long (*glfwAddTimer_func)(monotonic_t, bool, GLFWuserdatafun, void *, GLFWuserdatafun);
glfwAddTimer_func glfwAddTimer_impl;
GFW_EXTERN glfwAddTimer_func glfwAddTimer_impl;
#define glfwAddTimer glfwAddTimer_impl
typedef void (*glfwUpdateTimer_func)(unsigned long long, monotonic_t, bool);
glfwUpdateTimer_func glfwUpdateTimer_impl;
GFW_EXTERN glfwUpdateTimer_func glfwUpdateTimer_impl;
#define glfwUpdateTimer glfwUpdateTimer_impl
typedef void (*glfwRemoveTimer_func)(unsigned long);
glfwRemoveTimer_func glfwRemoveTimer_impl;
GFW_EXTERN glfwRemoveTimer_func glfwRemoveTimer_impl;
#define glfwRemoveTimer glfwRemoveTimer_impl
typedef void (*glfwTerminate_func)(void);
glfwTerminate_func glfwTerminate_impl;
GFW_EXTERN glfwTerminate_func glfwTerminate_impl;
#define glfwTerminate glfwTerminate_impl
typedef void (*glfwInitHint_func)(int, int);
glfwInitHint_func glfwInitHint_impl;
GFW_EXTERN glfwInitHint_func glfwInitHint_impl;
#define glfwInitHint glfwInitHint_impl
typedef void (*glfwGetVersion_func)(int*, int*, int*);
glfwGetVersion_func glfwGetVersion_impl;
GFW_EXTERN glfwGetVersion_func glfwGetVersion_impl;
#define glfwGetVersion glfwGetVersion_impl
typedef const char* (*glfwGetVersionString_func)(void);
glfwGetVersionString_func glfwGetVersionString_impl;
GFW_EXTERN glfwGetVersionString_func glfwGetVersionString_impl;
#define glfwGetVersionString glfwGetVersionString_impl
typedef int (*glfwGetError_func)(const char**);
glfwGetError_func glfwGetError_impl;
GFW_EXTERN glfwGetError_func glfwGetError_impl;
#define glfwGetError glfwGetError_impl
typedef GLFWerrorfun (*glfwSetErrorCallback_func)(GLFWerrorfun);
glfwSetErrorCallback_func glfwSetErrorCallback_impl;
GFW_EXTERN glfwSetErrorCallback_func glfwSetErrorCallback_impl;
#define glfwSetErrorCallback glfwSetErrorCallback_impl
typedef GLFWmonitor** (*glfwGetMonitors_func)(int*);
glfwGetMonitors_func glfwGetMonitors_impl;
GFW_EXTERN glfwGetMonitors_func glfwGetMonitors_impl;
#define glfwGetMonitors glfwGetMonitors_impl
typedef GLFWmonitor* (*glfwGetPrimaryMonitor_func)(void);
glfwGetPrimaryMonitor_func glfwGetPrimaryMonitor_impl;
GFW_EXTERN glfwGetPrimaryMonitor_func glfwGetPrimaryMonitor_impl;
#define glfwGetPrimaryMonitor glfwGetPrimaryMonitor_impl
typedef void (*glfwGetMonitorPos_func)(GLFWmonitor*, int*, int*);
glfwGetMonitorPos_func glfwGetMonitorPos_impl;
GFW_EXTERN glfwGetMonitorPos_func glfwGetMonitorPos_impl;
#define glfwGetMonitorPos glfwGetMonitorPos_impl
typedef void (*glfwGetMonitorWorkarea_func)(GLFWmonitor*, int*, int*, int*, int*);
glfwGetMonitorWorkarea_func glfwGetMonitorWorkarea_impl;
GFW_EXTERN glfwGetMonitorWorkarea_func glfwGetMonitorWorkarea_impl;
#define glfwGetMonitorWorkarea glfwGetMonitorWorkarea_impl
typedef void (*glfwGetMonitorPhysicalSize_func)(GLFWmonitor*, int*, int*);
glfwGetMonitorPhysicalSize_func glfwGetMonitorPhysicalSize_impl;
GFW_EXTERN glfwGetMonitorPhysicalSize_func glfwGetMonitorPhysicalSize_impl;
#define glfwGetMonitorPhysicalSize glfwGetMonitorPhysicalSize_impl
typedef void (*glfwGetMonitorContentScale_func)(GLFWmonitor*, float*, float*);
glfwGetMonitorContentScale_func glfwGetMonitorContentScale_impl;
GFW_EXTERN glfwGetMonitorContentScale_func glfwGetMonitorContentScale_impl;
#define glfwGetMonitorContentScale glfwGetMonitorContentScale_impl
typedef const char* (*glfwGetMonitorName_func)(GLFWmonitor*);
glfwGetMonitorName_func glfwGetMonitorName_impl;
GFW_EXTERN glfwGetMonitorName_func glfwGetMonitorName_impl;
#define glfwGetMonitorName glfwGetMonitorName_impl
typedef void (*glfwSetMonitorUserPointer_func)(GLFWmonitor*, void*);
glfwSetMonitorUserPointer_func glfwSetMonitorUserPointer_impl;
GFW_EXTERN glfwSetMonitorUserPointer_func glfwSetMonitorUserPointer_impl;
#define glfwSetMonitorUserPointer glfwSetMonitorUserPointer_impl
typedef void* (*glfwGetMonitorUserPointer_func)(GLFWmonitor*);
glfwGetMonitorUserPointer_func glfwGetMonitorUserPointer_impl;
GFW_EXTERN glfwGetMonitorUserPointer_func glfwGetMonitorUserPointer_impl;
#define glfwGetMonitorUserPointer glfwGetMonitorUserPointer_impl
typedef GLFWmonitorfun (*glfwSetMonitorCallback_func)(GLFWmonitorfun);
glfwSetMonitorCallback_func glfwSetMonitorCallback_impl;
GFW_EXTERN glfwSetMonitorCallback_func glfwSetMonitorCallback_impl;
#define glfwSetMonitorCallback glfwSetMonitorCallback_impl
typedef const GLFWvidmode* (*glfwGetVideoModes_func)(GLFWmonitor*, int*);
glfwGetVideoModes_func glfwGetVideoModes_impl;
GFW_EXTERN glfwGetVideoModes_func glfwGetVideoModes_impl;
#define glfwGetVideoModes glfwGetVideoModes_impl
typedef const GLFWvidmode* (*glfwGetVideoMode_func)(GLFWmonitor*);
glfwGetVideoMode_func glfwGetVideoMode_impl;
GFW_EXTERN glfwGetVideoMode_func glfwGetVideoMode_impl;
#define glfwGetVideoMode glfwGetVideoMode_impl
typedef void (*glfwSetGamma_func)(GLFWmonitor*, float);
glfwSetGamma_func glfwSetGamma_impl;
GFW_EXTERN glfwSetGamma_func glfwSetGamma_impl;
#define glfwSetGamma glfwSetGamma_impl
typedef const GLFWgammaramp* (*glfwGetGammaRamp_func)(GLFWmonitor*);
glfwGetGammaRamp_func glfwGetGammaRamp_impl;
GFW_EXTERN glfwGetGammaRamp_func glfwGetGammaRamp_impl;
#define glfwGetGammaRamp glfwGetGammaRamp_impl
typedef void (*glfwSetGammaRamp_func)(GLFWmonitor*, const GLFWgammaramp*);
glfwSetGammaRamp_func glfwSetGammaRamp_impl;
GFW_EXTERN glfwSetGammaRamp_func glfwSetGammaRamp_impl;
#define glfwSetGammaRamp glfwSetGammaRamp_impl
typedef void (*glfwDefaultWindowHints_func)(void);
glfwDefaultWindowHints_func glfwDefaultWindowHints_impl;
GFW_EXTERN glfwDefaultWindowHints_func glfwDefaultWindowHints_impl;
#define glfwDefaultWindowHints glfwDefaultWindowHints_impl
typedef void (*glfwWindowHint_func)(int, int);
glfwWindowHint_func glfwWindowHint_impl;
GFW_EXTERN glfwWindowHint_func glfwWindowHint_impl;
#define glfwWindowHint glfwWindowHint_impl
typedef void (*glfwWindowHintString_func)(int, const char*);
glfwWindowHintString_func glfwWindowHintString_impl;
GFW_EXTERN glfwWindowHintString_func glfwWindowHintString_impl;
#define glfwWindowHintString glfwWindowHintString_impl
typedef GLFWwindow* (*glfwCreateWindow_func)(int, int, const char*, GLFWmonitor*, GLFWwindow*);
glfwCreateWindow_func glfwCreateWindow_impl;
GFW_EXTERN glfwCreateWindow_func glfwCreateWindow_impl;
#define glfwCreateWindow glfwCreateWindow_impl
typedef bool (*glfwToggleFullscreen_func)(GLFWwindow*, unsigned int);
glfwToggleFullscreen_func glfwToggleFullscreen_impl;
GFW_EXTERN glfwToggleFullscreen_func glfwToggleFullscreen_impl;
#define glfwToggleFullscreen glfwToggleFullscreen_impl
typedef void (*glfwDestroyWindow_func)(GLFWwindow*);
glfwDestroyWindow_func glfwDestroyWindow_impl;
GFW_EXTERN glfwDestroyWindow_func glfwDestroyWindow_impl;
#define glfwDestroyWindow glfwDestroyWindow_impl
typedef int (*glfwWindowShouldClose_func)(GLFWwindow*);
glfwWindowShouldClose_func glfwWindowShouldClose_impl;
GFW_EXTERN glfwWindowShouldClose_func glfwWindowShouldClose_impl;
#define glfwWindowShouldClose glfwWindowShouldClose_impl
typedef void (*glfwSetWindowShouldClose_func)(GLFWwindow*, int);
glfwSetWindowShouldClose_func glfwSetWindowShouldClose_impl;
GFW_EXTERN glfwSetWindowShouldClose_func glfwSetWindowShouldClose_impl;
#define glfwSetWindowShouldClose glfwSetWindowShouldClose_impl
typedef void (*glfwSetWindowTitle_func)(GLFWwindow*, const char*);
glfwSetWindowTitle_func glfwSetWindowTitle_impl;
GFW_EXTERN glfwSetWindowTitle_func glfwSetWindowTitle_impl;
#define glfwSetWindowTitle glfwSetWindowTitle_impl
typedef void (*glfwSetWindowIcon_func)(GLFWwindow*, int, const GLFWimage*);
glfwSetWindowIcon_func glfwSetWindowIcon_impl;
GFW_EXTERN glfwSetWindowIcon_func glfwSetWindowIcon_impl;
#define glfwSetWindowIcon glfwSetWindowIcon_impl
typedef void (*glfwGetWindowPos_func)(GLFWwindow*, int*, int*);
glfwGetWindowPos_func glfwGetWindowPos_impl;
GFW_EXTERN glfwGetWindowPos_func glfwGetWindowPos_impl;
#define glfwGetWindowPos glfwGetWindowPos_impl
typedef void (*glfwSetWindowPos_func)(GLFWwindow*, int, int);
glfwSetWindowPos_func glfwSetWindowPos_impl;
GFW_EXTERN glfwSetWindowPos_func glfwSetWindowPos_impl;
#define glfwSetWindowPos glfwSetWindowPos_impl
typedef void (*glfwGetWindowSize_func)(GLFWwindow*, int*, int*);
glfwGetWindowSize_func glfwGetWindowSize_impl;
GFW_EXTERN glfwGetWindowSize_func glfwGetWindowSize_impl;
#define glfwGetWindowSize glfwGetWindowSize_impl
typedef void (*glfwSetWindowSizeLimits_func)(GLFWwindow*, int, int, int, int);
glfwSetWindowSizeLimits_func glfwSetWindowSizeLimits_impl;
GFW_EXTERN glfwSetWindowSizeLimits_func glfwSetWindowSizeLimits_impl;
#define glfwSetWindowSizeLimits glfwSetWindowSizeLimits_impl
typedef void (*glfwSetWindowSizeIncrements_func)(GLFWwindow*, int, int);
glfwSetWindowSizeIncrements_func glfwSetWindowSizeIncrements_impl;
GFW_EXTERN glfwSetWindowSizeIncrements_func glfwSetWindowSizeIncrements_impl;
#define glfwSetWindowSizeIncrements glfwSetWindowSizeIncrements_impl
typedef void (*glfwSetWindowAspectRatio_func)(GLFWwindow*, int, int);
glfwSetWindowAspectRatio_func glfwSetWindowAspectRatio_impl;
GFW_EXTERN glfwSetWindowAspectRatio_func glfwSetWindowAspectRatio_impl;
#define glfwSetWindowAspectRatio glfwSetWindowAspectRatio_impl
typedef void (*glfwSetWindowSize_func)(GLFWwindow*, int, int);
glfwSetWindowSize_func glfwSetWindowSize_impl;
GFW_EXTERN glfwSetWindowSize_func glfwSetWindowSize_impl;
#define glfwSetWindowSize glfwSetWindowSize_impl
typedef void (*glfwGetFramebufferSize_func)(GLFWwindow*, int*, int*);
glfwGetFramebufferSize_func glfwGetFramebufferSize_impl;
GFW_EXTERN glfwGetFramebufferSize_func glfwGetFramebufferSize_impl;
#define glfwGetFramebufferSize glfwGetFramebufferSize_impl
typedef void (*glfwGetWindowFrameSize_func)(GLFWwindow*, int*, int*, int*, int*);
glfwGetWindowFrameSize_func glfwGetWindowFrameSize_impl;
GFW_EXTERN glfwGetWindowFrameSize_func glfwGetWindowFrameSize_impl;
#define glfwGetWindowFrameSize glfwGetWindowFrameSize_impl
typedef void (*glfwGetWindowContentScale_func)(GLFWwindow*, float*, float*);
glfwGetWindowContentScale_func glfwGetWindowContentScale_impl;
GFW_EXTERN glfwGetWindowContentScale_func glfwGetWindowContentScale_impl;
#define glfwGetWindowContentScale glfwGetWindowContentScale_impl
typedef monotonic_t (*glfwGetDoubleClickInterval_func)(GLFWwindow*);
glfwGetDoubleClickInterval_func glfwGetDoubleClickInterval_impl;
GFW_EXTERN glfwGetDoubleClickInterval_func glfwGetDoubleClickInterval_impl;
#define glfwGetDoubleClickInterval glfwGetDoubleClickInterval_impl
typedef float (*glfwGetWindowOpacity_func)(GLFWwindow*);
glfwGetWindowOpacity_func glfwGetWindowOpacity_impl;
GFW_EXTERN glfwGetWindowOpacity_func glfwGetWindowOpacity_impl;
#define glfwGetWindowOpacity glfwGetWindowOpacity_impl
typedef void (*glfwSetWindowOpacity_func)(GLFWwindow*, float);
glfwSetWindowOpacity_func glfwSetWindowOpacity_impl;
GFW_EXTERN glfwSetWindowOpacity_func glfwSetWindowOpacity_impl;
#define glfwSetWindowOpacity glfwSetWindowOpacity_impl
typedef void (*glfwIconifyWindow_func)(GLFWwindow*);
glfwIconifyWindow_func glfwIconifyWindow_impl;
GFW_EXTERN glfwIconifyWindow_func glfwIconifyWindow_impl;
#define glfwIconifyWindow glfwIconifyWindow_impl
typedef void (*glfwRestoreWindow_func)(GLFWwindow*);
glfwRestoreWindow_func glfwRestoreWindow_impl;
GFW_EXTERN glfwRestoreWindow_func glfwRestoreWindow_impl;
#define glfwRestoreWindow glfwRestoreWindow_impl
typedef void (*glfwMaximizeWindow_func)(GLFWwindow*);
glfwMaximizeWindow_func glfwMaximizeWindow_impl;
GFW_EXTERN glfwMaximizeWindow_func glfwMaximizeWindow_impl;
#define glfwMaximizeWindow glfwMaximizeWindow_impl
typedef void (*glfwShowWindow_func)(GLFWwindow*);
glfwShowWindow_func glfwShowWindow_impl;
GFW_EXTERN glfwShowWindow_func glfwShowWindow_impl;
#define glfwShowWindow glfwShowWindow_impl
typedef void (*glfwHideWindow_func)(GLFWwindow*);
glfwHideWindow_func glfwHideWindow_impl;
GFW_EXTERN glfwHideWindow_func glfwHideWindow_impl;
#define glfwHideWindow glfwHideWindow_impl
typedef void (*glfwFocusWindow_func)(GLFWwindow*);
glfwFocusWindow_func glfwFocusWindow_impl;
GFW_EXTERN glfwFocusWindow_func glfwFocusWindow_impl;
#define glfwFocusWindow glfwFocusWindow_impl
typedef void (*glfwRequestWindowAttention_func)(GLFWwindow*);
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
GFW_EXTERN glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
typedef int (*glfwWindowBell_func)(GLFWwindow*);
glfwWindowBell_func glfwWindowBell_impl;
GFW_EXTERN glfwWindowBell_func glfwWindowBell_impl;
#define glfwWindowBell glfwWindowBell_impl
typedef GLFWmonitor* (*glfwGetWindowMonitor_func)(GLFWwindow*);
glfwGetWindowMonitor_func glfwGetWindowMonitor_impl;
GFW_EXTERN glfwGetWindowMonitor_func glfwGetWindowMonitor_impl;
#define glfwGetWindowMonitor glfwGetWindowMonitor_impl
typedef void (*glfwSetWindowMonitor_func)(GLFWwindow*, GLFWmonitor*, int, int, int, int, int);
glfwSetWindowMonitor_func glfwSetWindowMonitor_impl;
GFW_EXTERN glfwSetWindowMonitor_func glfwSetWindowMonitor_impl;
#define glfwSetWindowMonitor glfwSetWindowMonitor_impl
typedef int (*glfwGetWindowAttrib_func)(GLFWwindow*, int);
glfwGetWindowAttrib_func glfwGetWindowAttrib_impl;
GFW_EXTERN glfwGetWindowAttrib_func glfwGetWindowAttrib_impl;
#define glfwGetWindowAttrib glfwGetWindowAttrib_impl
typedef void (*glfwSetWindowAttrib_func)(GLFWwindow*, int, int);
glfwSetWindowAttrib_func glfwSetWindowAttrib_impl;
GFW_EXTERN glfwSetWindowAttrib_func glfwSetWindowAttrib_impl;
#define glfwSetWindowAttrib glfwSetWindowAttrib_impl
typedef void (*glfwSetWindowUserPointer_func)(GLFWwindow*, void*);
glfwSetWindowUserPointer_func glfwSetWindowUserPointer_impl;
GFW_EXTERN glfwSetWindowUserPointer_func glfwSetWindowUserPointer_impl;
#define glfwSetWindowUserPointer glfwSetWindowUserPointer_impl
typedef void* (*glfwGetWindowUserPointer_func)(GLFWwindow*);
glfwGetWindowUserPointer_func glfwGetWindowUserPointer_impl;
GFW_EXTERN glfwGetWindowUserPointer_func glfwGetWindowUserPointer_impl;
#define glfwGetWindowUserPointer glfwGetWindowUserPointer_impl
typedef GLFWwindowposfun (*glfwSetWindowPosCallback_func)(GLFWwindow*, GLFWwindowposfun);
glfwSetWindowPosCallback_func glfwSetWindowPosCallback_impl;
GFW_EXTERN glfwSetWindowPosCallback_func glfwSetWindowPosCallback_impl;
#define glfwSetWindowPosCallback glfwSetWindowPosCallback_impl
typedef GLFWwindowsizefun (*glfwSetWindowSizeCallback_func)(GLFWwindow*, GLFWwindowsizefun);
glfwSetWindowSizeCallback_func glfwSetWindowSizeCallback_impl;
GFW_EXTERN glfwSetWindowSizeCallback_func glfwSetWindowSizeCallback_impl;
#define glfwSetWindowSizeCallback glfwSetWindowSizeCallback_impl
typedef GLFWwindowclosefun (*glfwSetWindowCloseCallback_func)(GLFWwindow*, GLFWwindowclosefun);
glfwSetWindowCloseCallback_func glfwSetWindowCloseCallback_impl;
GFW_EXTERN glfwSetWindowCloseCallback_func glfwSetWindowCloseCallback_impl;
#define glfwSetWindowCloseCallback glfwSetWindowCloseCallback_impl
typedef GLFWwindowrefreshfun (*glfwSetWindowRefreshCallback_func)(GLFWwindow*, GLFWwindowrefreshfun);
glfwSetWindowRefreshCallback_func glfwSetWindowRefreshCallback_impl;
GFW_EXTERN glfwSetWindowRefreshCallback_func glfwSetWindowRefreshCallback_impl;
#define glfwSetWindowRefreshCallback glfwSetWindowRefreshCallback_impl
typedef GLFWwindowfocusfun (*glfwSetWindowFocusCallback_func)(GLFWwindow*, GLFWwindowfocusfun);
glfwSetWindowFocusCallback_func glfwSetWindowFocusCallback_impl;
GFW_EXTERN glfwSetWindowFocusCallback_func glfwSetWindowFocusCallback_impl;
#define glfwSetWindowFocusCallback glfwSetWindowFocusCallback_impl
typedef GLFWwindowocclusionfun (*glfwSetWindowOcclusionCallback_func)(GLFWwindow*, GLFWwindowocclusionfun);
glfwSetWindowOcclusionCallback_func glfwSetWindowOcclusionCallback_impl;
GFW_EXTERN glfwSetWindowOcclusionCallback_func glfwSetWindowOcclusionCallback_impl;
#define glfwSetWindowOcclusionCallback glfwSetWindowOcclusionCallback_impl
typedef GLFWwindowiconifyfun (*glfwSetWindowIconifyCallback_func)(GLFWwindow*, GLFWwindowiconifyfun);
glfwSetWindowIconifyCallback_func glfwSetWindowIconifyCallback_impl;
GFW_EXTERN glfwSetWindowIconifyCallback_func glfwSetWindowIconifyCallback_impl;
#define glfwSetWindowIconifyCallback glfwSetWindowIconifyCallback_impl
typedef GLFWwindowmaximizefun (*glfwSetWindowMaximizeCallback_func)(GLFWwindow*, GLFWwindowmaximizefun);
glfwSetWindowMaximizeCallback_func glfwSetWindowMaximizeCallback_impl;
GFW_EXTERN glfwSetWindowMaximizeCallback_func glfwSetWindowMaximizeCallback_impl;
#define glfwSetWindowMaximizeCallback glfwSetWindowMaximizeCallback_impl
typedef GLFWframebuffersizefun (*glfwSetFramebufferSizeCallback_func)(GLFWwindow*, GLFWframebuffersizefun);
glfwSetFramebufferSizeCallback_func glfwSetFramebufferSizeCallback_impl;
GFW_EXTERN glfwSetFramebufferSizeCallback_func glfwSetFramebufferSizeCallback_impl;
#define glfwSetFramebufferSizeCallback glfwSetFramebufferSizeCallback_impl
typedef GLFWwindowcontentscalefun (*glfwSetWindowContentScaleCallback_func)(GLFWwindow*, GLFWwindowcontentscalefun);
glfwSetWindowContentScaleCallback_func glfwSetWindowContentScaleCallback_impl;
GFW_EXTERN glfwSetWindowContentScaleCallback_func glfwSetWindowContentScaleCallback_impl;
#define glfwSetWindowContentScaleCallback glfwSetWindowContentScaleCallback_impl
typedef void (*glfwPostEmptyEvent_func)(void);
glfwPostEmptyEvent_func glfwPostEmptyEvent_impl;
GFW_EXTERN glfwPostEmptyEvent_func glfwPostEmptyEvent_impl;
#define glfwPostEmptyEvent glfwPostEmptyEvent_impl
typedef int (*glfwGetInputMode_func)(GLFWwindow*, int);
glfwGetInputMode_func glfwGetInputMode_impl;
GFW_EXTERN glfwGetInputMode_func glfwGetInputMode_impl;
#define glfwGetInputMode glfwGetInputMode_impl
typedef void (*glfwSetInputMode_func)(GLFWwindow*, int, int);
glfwSetInputMode_func glfwSetInputMode_impl;
GFW_EXTERN glfwSetInputMode_func glfwSetInputMode_impl;
#define glfwSetInputMode glfwSetInputMode_impl
typedef const char* (*glfwGetKeyName_func)(int, int);
glfwGetKeyName_func glfwGetKeyName_impl;
GFW_EXTERN glfwGetKeyName_func glfwGetKeyName_impl;
#define glfwGetKeyName glfwGetKeyName_impl
typedef int (*glfwGetNativeKeyForKey_func)(int);
glfwGetNativeKeyForKey_func glfwGetNativeKeyForKey_impl;
GFW_EXTERN glfwGetNativeKeyForKey_func glfwGetNativeKeyForKey_impl;
#define glfwGetNativeKeyForKey glfwGetNativeKeyForKey_impl
typedef int (*glfwGetKey_func)(GLFWwindow*, int);
glfwGetKey_func glfwGetKey_impl;
GFW_EXTERN glfwGetKey_func glfwGetKey_impl;
#define glfwGetKey glfwGetKey_impl
typedef int (*glfwGetMouseButton_func)(GLFWwindow*, int);
glfwGetMouseButton_func glfwGetMouseButton_impl;
GFW_EXTERN glfwGetMouseButton_func glfwGetMouseButton_impl;
#define glfwGetMouseButton glfwGetMouseButton_impl
typedef void (*glfwGetCursorPos_func)(GLFWwindow*, double*, double*);
glfwGetCursorPos_func glfwGetCursorPos_impl;
GFW_EXTERN glfwGetCursorPos_func glfwGetCursorPos_impl;
#define glfwGetCursorPos glfwGetCursorPos_impl
typedef void (*glfwSetCursorPos_func)(GLFWwindow*, double, double);
glfwSetCursorPos_func glfwSetCursorPos_impl;
GFW_EXTERN glfwSetCursorPos_func glfwSetCursorPos_impl;
#define glfwSetCursorPos glfwSetCursorPos_impl
typedef GLFWcursor* (*glfwCreateCursor_func)(const GLFWimage*, int, int, int);
glfwCreateCursor_func glfwCreateCursor_impl;
GFW_EXTERN glfwCreateCursor_func glfwCreateCursor_impl;
#define glfwCreateCursor glfwCreateCursor_impl
typedef GLFWcursor* (*glfwCreateStandardCursor_func)(GLFWCursorShape);
glfwCreateStandardCursor_func glfwCreateStandardCursor_impl;
GFW_EXTERN glfwCreateStandardCursor_func glfwCreateStandardCursor_impl;
#define glfwCreateStandardCursor glfwCreateStandardCursor_impl
typedef void (*glfwDestroyCursor_func)(GLFWcursor*);
glfwDestroyCursor_func glfwDestroyCursor_impl;
GFW_EXTERN glfwDestroyCursor_func glfwDestroyCursor_impl;
#define glfwDestroyCursor glfwDestroyCursor_impl
typedef void (*glfwSetCursor_func)(GLFWwindow*, GLFWcursor*);
glfwSetCursor_func glfwSetCursor_impl;
GFW_EXTERN glfwSetCursor_func glfwSetCursor_impl;
#define glfwSetCursor glfwSetCursor_impl
typedef GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboardfun);
glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl;
GFW_EXTERN glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl;
#define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl
typedef void (*glfwUpdateIMEState_func)(GLFWwindow*, int, int, int, int, int);
glfwUpdateIMEState_func glfwUpdateIMEState_impl;
GFW_EXTERN glfwUpdateIMEState_func glfwUpdateIMEState_impl;
#define glfwUpdateIMEState glfwUpdateIMEState_impl
typedef GLFWmousebuttonfun (*glfwSetMouseButtonCallback_func)(GLFWwindow*, GLFWmousebuttonfun);
glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;
GFW_EXTERN glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;
#define glfwSetMouseButtonCallback glfwSetMouseButtonCallback_impl
typedef GLFWcursorposfun (*glfwSetCursorPosCallback_func)(GLFWwindow*, GLFWcursorposfun);
glfwSetCursorPosCallback_func glfwSetCursorPosCallback_impl;
GFW_EXTERN glfwSetCursorPosCallback_func glfwSetCursorPosCallback_impl;
#define glfwSetCursorPosCallback glfwSetCursorPosCallback_impl
typedef GLFWcursorenterfun (*glfwSetCursorEnterCallback_func)(GLFWwindow*, GLFWcursorenterfun);
glfwSetCursorEnterCallback_func glfwSetCursorEnterCallback_impl;
GFW_EXTERN glfwSetCursorEnterCallback_func glfwSetCursorEnterCallback_impl;
#define glfwSetCursorEnterCallback glfwSetCursorEnterCallback_impl
typedef GLFWscrollfun (*glfwSetScrollCallback_func)(GLFWwindow*, GLFWscrollfun);
glfwSetScrollCallback_func glfwSetScrollCallback_impl;
GFW_EXTERN glfwSetScrollCallback_func glfwSetScrollCallback_impl;
#define glfwSetScrollCallback glfwSetScrollCallback_impl
typedef GLFWdropfun (*glfwSetDropCallback_func)(GLFWwindow*, GLFWdropfun);
glfwSetDropCallback_func glfwSetDropCallback_impl;
GFW_EXTERN glfwSetDropCallback_func glfwSetDropCallback_impl;
#define glfwSetDropCallback glfwSetDropCallback_impl
typedef GLFWliveresizefun (*glfwSetLiveResizeCallback_func)(GLFWwindow*, GLFWliveresizefun);
glfwSetLiveResizeCallback_func glfwSetLiveResizeCallback_impl;
GFW_EXTERN glfwSetLiveResizeCallback_func glfwSetLiveResizeCallback_impl;
#define glfwSetLiveResizeCallback glfwSetLiveResizeCallback_impl
typedef int (*glfwJoystickPresent_func)(int);
glfwJoystickPresent_func glfwJoystickPresent_impl;
GFW_EXTERN glfwJoystickPresent_func glfwJoystickPresent_impl;
#define glfwJoystickPresent glfwJoystickPresent_impl
typedef const float* (*glfwGetJoystickAxes_func)(int, int*);
glfwGetJoystickAxes_func glfwGetJoystickAxes_impl;
GFW_EXTERN glfwGetJoystickAxes_func glfwGetJoystickAxes_impl;
#define glfwGetJoystickAxes glfwGetJoystickAxes_impl
typedef const unsigned char* (*glfwGetJoystickButtons_func)(int, int*);
glfwGetJoystickButtons_func glfwGetJoystickButtons_impl;
GFW_EXTERN glfwGetJoystickButtons_func glfwGetJoystickButtons_impl;
#define glfwGetJoystickButtons glfwGetJoystickButtons_impl
typedef const unsigned char* (*glfwGetJoystickHats_func)(int, int*);
glfwGetJoystickHats_func glfwGetJoystickHats_impl;
GFW_EXTERN glfwGetJoystickHats_func glfwGetJoystickHats_impl;
#define glfwGetJoystickHats glfwGetJoystickHats_impl
typedef const char* (*glfwGetJoystickName_func)(int);
glfwGetJoystickName_func glfwGetJoystickName_impl;
GFW_EXTERN glfwGetJoystickName_func glfwGetJoystickName_impl;
#define glfwGetJoystickName glfwGetJoystickName_impl
typedef const char* (*glfwGetJoystickGUID_func)(int);
glfwGetJoystickGUID_func glfwGetJoystickGUID_impl;
GFW_EXTERN glfwGetJoystickGUID_func glfwGetJoystickGUID_impl;
#define glfwGetJoystickGUID glfwGetJoystickGUID_impl
typedef void (*glfwSetJoystickUserPointer_func)(int, void*);
glfwSetJoystickUserPointer_func glfwSetJoystickUserPointer_impl;
GFW_EXTERN glfwSetJoystickUserPointer_func glfwSetJoystickUserPointer_impl;
#define glfwSetJoystickUserPointer glfwSetJoystickUserPointer_impl
typedef void* (*glfwGetJoystickUserPointer_func)(int);
glfwGetJoystickUserPointer_func glfwGetJoystickUserPointer_impl;
GFW_EXTERN glfwGetJoystickUserPointer_func glfwGetJoystickUserPointer_impl;
#define glfwGetJoystickUserPointer glfwGetJoystickUserPointer_impl
typedef int (*glfwJoystickIsGamepad_func)(int);
glfwJoystickIsGamepad_func glfwJoystickIsGamepad_impl;
GFW_EXTERN glfwJoystickIsGamepad_func glfwJoystickIsGamepad_impl;
#define glfwJoystickIsGamepad glfwJoystickIsGamepad_impl
typedef GLFWjoystickfun (*glfwSetJoystickCallback_func)(GLFWjoystickfun);
glfwSetJoystickCallback_func glfwSetJoystickCallback_impl;
GFW_EXTERN glfwSetJoystickCallback_func glfwSetJoystickCallback_impl;
#define glfwSetJoystickCallback glfwSetJoystickCallback_impl
typedef int (*glfwUpdateGamepadMappings_func)(const char*);
glfwUpdateGamepadMappings_func glfwUpdateGamepadMappings_impl;
GFW_EXTERN glfwUpdateGamepadMappings_func glfwUpdateGamepadMappings_impl;
#define glfwUpdateGamepadMappings glfwUpdateGamepadMappings_impl
typedef const char* (*glfwGetGamepadName_func)(int);
glfwGetGamepadName_func glfwGetGamepadName_impl;
GFW_EXTERN glfwGetGamepadName_func glfwGetGamepadName_impl;
#define glfwGetGamepadName glfwGetGamepadName_impl
typedef int (*glfwGetGamepadState_func)(int, GLFWgamepadstate*);
glfwGetGamepadState_func glfwGetGamepadState_impl;
GFW_EXTERN glfwGetGamepadState_func glfwGetGamepadState_impl;
#define glfwGetGamepadState glfwGetGamepadState_impl
typedef void (*glfwSetClipboardString_func)(GLFWwindow*, const char*);
glfwSetClipboardString_func glfwSetClipboardString_impl;
GFW_EXTERN glfwSetClipboardString_func glfwSetClipboardString_impl;
#define glfwSetClipboardString glfwSetClipboardString_impl
typedef const char* (*glfwGetClipboardString_func)(GLFWwindow*);
glfwGetClipboardString_func glfwGetClipboardString_impl;
GFW_EXTERN glfwGetClipboardString_func glfwGetClipboardString_impl;
#define glfwGetClipboardString glfwGetClipboardString_impl
typedef monotonic_t (*glfwGetTime_func)(void);
glfwGetTime_func glfwGetTime_impl;
GFW_EXTERN glfwGetTime_func glfwGetTime_impl;
#define glfwGetTime glfwGetTime_impl
typedef void (*glfwMakeContextCurrent_func)(GLFWwindow*);
glfwMakeContextCurrent_func glfwMakeContextCurrent_impl;
GFW_EXTERN glfwMakeContextCurrent_func glfwMakeContextCurrent_impl;
#define glfwMakeContextCurrent glfwMakeContextCurrent_impl
typedef GLFWwindow* (*glfwGetCurrentContext_func)(void);
glfwGetCurrentContext_func glfwGetCurrentContext_impl;
GFW_EXTERN glfwGetCurrentContext_func glfwGetCurrentContext_impl;
#define glfwGetCurrentContext glfwGetCurrentContext_impl
typedef void (*glfwSwapBuffers_func)(GLFWwindow*);
glfwSwapBuffers_func glfwSwapBuffers_impl;
GFW_EXTERN glfwSwapBuffers_func glfwSwapBuffers_impl;
#define glfwSwapBuffers glfwSwapBuffers_impl
typedef void (*glfwSwapInterval_func)(int);
glfwSwapInterval_func glfwSwapInterval_impl;
GFW_EXTERN glfwSwapInterval_func glfwSwapInterval_impl;
#define glfwSwapInterval glfwSwapInterval_impl
typedef int (*glfwExtensionSupported_func)(const char*);
glfwExtensionSupported_func glfwExtensionSupported_impl;
GFW_EXTERN glfwExtensionSupported_func glfwExtensionSupported_impl;
#define glfwExtensionSupported glfwExtensionSupported_impl
typedef GLFWglproc (*glfwGetProcAddress_func)(const char*);
glfwGetProcAddress_func glfwGetProcAddress_impl;
GFW_EXTERN glfwGetProcAddress_func glfwGetProcAddress_impl;
#define glfwGetProcAddress glfwGetProcAddress_impl
typedef int (*glfwVulkanSupported_func)(void);
glfwVulkanSupported_func glfwVulkanSupported_impl;
GFW_EXTERN glfwVulkanSupported_func glfwVulkanSupported_impl;
#define glfwVulkanSupported glfwVulkanSupported_impl
typedef const char** (*glfwGetRequiredInstanceExtensions_func)(uint32_t*);
glfwGetRequiredInstanceExtensions_func glfwGetRequiredInstanceExtensions_impl;
GFW_EXTERN glfwGetRequiredInstanceExtensions_func glfwGetRequiredInstanceExtensions_impl;
#define glfwGetRequiredInstanceExtensions glfwGetRequiredInstanceExtensions_impl
typedef void* (*glfwGetCocoaWindow_func)(GLFWwindow*);
glfwGetCocoaWindow_func glfwGetCocoaWindow_impl;
GFW_EXTERN glfwGetCocoaWindow_func glfwGetCocoaWindow_impl;
#define glfwGetCocoaWindow glfwGetCocoaWindow_impl
typedef void* (*glfwGetNSGLContext_func)(GLFWwindow*);
glfwGetNSGLContext_func glfwGetNSGLContext_impl;
GFW_EXTERN glfwGetNSGLContext_func glfwGetNSGLContext_impl;
#define glfwGetNSGLContext glfwGetNSGLContext_impl
typedef uint32_t (*glfwGetCocoaMonitor_func)(GLFWmonitor*);
glfwGetCocoaMonitor_func glfwGetCocoaMonitor_impl;
GFW_EXTERN glfwGetCocoaMonitor_func glfwGetCocoaMonitor_impl;
#define glfwGetCocoaMonitor glfwGetCocoaMonitor_impl
typedef GLFWcocoatextinputfilterfun (*glfwSetCocoaTextInputFilter_func)(GLFWwindow*, GLFWcocoatextinputfilterfun);
glfwSetCocoaTextInputFilter_func glfwSetCocoaTextInputFilter_impl;
GFW_EXTERN glfwSetCocoaTextInputFilter_func glfwSetCocoaTextInputFilter_impl;
#define glfwSetCocoaTextInputFilter glfwSetCocoaTextInputFilter_impl
typedef GLFWcocoatogglefullscreenfun (*glfwSetCocoaToggleFullscreenIntercept_func)(GLFWwindow*, GLFWcocoatogglefullscreenfun);
glfwSetCocoaToggleFullscreenIntercept_func glfwSetCocoaToggleFullscreenIntercept_impl;
GFW_EXTERN glfwSetCocoaToggleFullscreenIntercept_func glfwSetCocoaToggleFullscreenIntercept_impl;
#define glfwSetCocoaToggleFullscreenIntercept glfwSetCocoaToggleFullscreenIntercept_impl
typedef GLFWapplicationshouldhandlereopenfun (*glfwSetApplicationShouldHandleReopen_func)(GLFWapplicationshouldhandlereopenfun);
glfwSetApplicationShouldHandleReopen_func glfwSetApplicationShouldHandleReopen_impl;
GFW_EXTERN glfwSetApplicationShouldHandleReopen_func glfwSetApplicationShouldHandleReopen_impl;
#define glfwSetApplicationShouldHandleReopen glfwSetApplicationShouldHandleReopen_impl
typedef GLFWapplicationwillfinishlaunchingfun (*glfwSetApplicationWillFinishLaunching_func)(GLFWapplicationwillfinishlaunchingfun);
glfwSetApplicationWillFinishLaunching_func glfwSetApplicationWillFinishLaunching_impl;
GFW_EXTERN glfwSetApplicationWillFinishLaunching_func glfwSetApplicationWillFinishLaunching_impl;
#define glfwSetApplicationWillFinishLaunching glfwSetApplicationWillFinishLaunching_impl
typedef void (*glfwGetCocoaKeyEquivalent_func)(int, int, char*, size_t, int*);
glfwGetCocoaKeyEquivalent_func glfwGetCocoaKeyEquivalent_impl;
GFW_EXTERN glfwGetCocoaKeyEquivalent_func glfwGetCocoaKeyEquivalent_impl;
#define glfwGetCocoaKeyEquivalent glfwGetCocoaKeyEquivalent_impl
typedef void (*glfwCocoaRequestRenderFrame_func)(GLFWwindow*, GLFWcocoarenderframefun);
glfwCocoaRequestRenderFrame_func glfwCocoaRequestRenderFrame_impl;
GFW_EXTERN glfwCocoaRequestRenderFrame_func glfwCocoaRequestRenderFrame_impl;
#define glfwCocoaRequestRenderFrame glfwCocoaRequestRenderFrame_impl
typedef void* (*glfwGetX11Display_func)(void);
glfwGetX11Display_func glfwGetX11Display_impl;
GFW_EXTERN glfwGetX11Display_func glfwGetX11Display_impl;
#define glfwGetX11Display glfwGetX11Display_impl
typedef int32_t (*glfwGetX11Window_func)(GLFWwindow*);
glfwGetX11Window_func glfwGetX11Window_impl;
GFW_EXTERN glfwGetX11Window_func glfwGetX11Window_impl;
#define glfwGetX11Window glfwGetX11Window_impl
typedef void (*glfwSetPrimarySelectionString_func)(GLFWwindow*, const char*);
glfwSetPrimarySelectionString_func glfwSetPrimarySelectionString_impl;
GFW_EXTERN glfwSetPrimarySelectionString_func glfwSetPrimarySelectionString_impl;
#define glfwSetPrimarySelectionString glfwSetPrimarySelectionString_impl
typedef const char* (*glfwGetPrimarySelectionString_func)(GLFWwindow*);
glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl;
GFW_EXTERN glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl;
#define glfwGetPrimarySelectionString glfwGetPrimarySelectionString_impl
typedef int (*glfwGetNativeKeyForName_func)(const char*, int);
glfwGetNativeKeyForName_func glfwGetNativeKeyForName_impl;
GFW_EXTERN glfwGetNativeKeyForName_func glfwGetNativeKeyForName_impl;
#define glfwGetNativeKeyForName glfwGetNativeKeyForName_impl
typedef void (*glfwRequestWaylandFrameEvent_func)(GLFWwindow*, unsigned long long, GLFWwaylandframecallbackfunc);
glfwRequestWaylandFrameEvent_func glfwRequestWaylandFrameEvent_impl;
GFW_EXTERN glfwRequestWaylandFrameEvent_func glfwRequestWaylandFrameEvent_impl;
#define glfwRequestWaylandFrameEvent glfwRequestWaylandFrameEvent_impl
typedef unsigned long long (*glfwDBusUserNotify_func)(const char*, const char*, const char*, const char*, const char*, int32_t, GLFWDBusnotificationcreatedfun, void*);
glfwDBusUserNotify_func glfwDBusUserNotify_impl;
GFW_EXTERN glfwDBusUserNotify_func glfwDBusUserNotify_impl;
#define glfwDBusUserNotify glfwDBusUserNotify_impl
typedef void (*glfwDBusSetUserNotificationHandler_func)(GLFWDBusnotificationactivatedfun);
glfwDBusSetUserNotificationHandler_func glfwDBusSetUserNotificationHandler_impl;
GFW_EXTERN glfwDBusSetUserNotificationHandler_func glfwDBusSetUserNotificationHandler_impl;
#define glfwDBusSetUserNotificationHandler glfwDBusSetUserNotificationHandler_impl
const char* load_glfw(const char* path);

View File

@@ -6,14 +6,15 @@
from functools import lru_cache
from typing import Any, Dict, List, Optional, Sequence, Tuple
from .boss import Boss
from .child import Child
from .cli import parse_args
from .cli_stub import LaunchCLIOptions
from .constants import resolve_custom_file
from .fast_data_types import set_clipboard_string
from .utils import set_primary_selection
from .window import Window
from .boss import Boss
from .tabs import Tab
from .utils import set_primary_selection
from .window import Watchers, Window
try:
from typing import TypedDict
@@ -148,6 +149,14 @@ defaults to :code:`kitty`.
--os-window-name
Set the WM_NAME property on X11 for the newly created OS Window when using
:option:`launch --type`=os-window. Defaults to :option:`launch --os-window-class`.
--watcher -w
type=list
Path to a python file. Appropriately named functions in this file will be called
for various events, such as when the window is resized or closed. See the section
on watchers in the launch command documentation :doc:`launch`. Relative paths are
resolved relative to the kitty config directory.
'''
@@ -176,7 +185,7 @@ def tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Optional[Tab]
tm = boss.active_tab_manager
if tm:
tab: Optional[Tab] = tm.new_tab(empty_tab=True, location=opts.location)
if opts.tab_title and tab:
if opts.tab_title and tab is not None:
tab.set_title(opts.tab_title)
else:
tab = None
@@ -184,7 +193,7 @@ def tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Optional[Tab]
oswid = boss.add_os_window(wclass=opts.os_window_class, wname=opts.os_window_name)
tm = boss.os_window_map[oswid]
tab = tm.new_tab(empty_tab=True)
if opts.tab_title:
if opts.tab_title and tab is not None:
tab.set_title(opts.tab_title)
else:
tab = target_tab or boss.active_tab
@@ -192,6 +201,23 @@ def tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Optional[Tab]
return tab
def load_watch_modules(opts: LaunchCLIOptions) -> Optional[Watchers]:
if not opts.watcher:
return None
import runpy
ans = Watchers()
for path in opts.watcher:
path = resolve_custom_file(path)
m = runpy.run_path(path, run_name='__kitty_watcher__')
w = m.get('on_close')
if callable(w):
ans.on_close.append(w)
w = m.get('on_resize')
if callable(w):
ans.on_resize.append(w)
return ans
class LaunchKwds(TypedDict):
allow_remote_control: bool
@@ -251,6 +277,9 @@ def launch(boss: Boss, opts: LaunchCLIOptions, args: List[str], target_tab: Opti
kw['overlay_for'] = active.id
if opts.stdin_source and opts.stdin_source != 'none':
q = opts.stdin_source
if opts.stdin_add_formatting:
if q in ('@screen', '@screen_scrollback', '@alternate', '@alternate_scrollback'):
q = '@ansi_' + q[1:]
if opts.stdin_add_line_wrap_markers:
q += '_wrap'
penv, stdin = boss.process_stdin_source(window=active, stdin=q)
@@ -274,7 +303,8 @@ def launch(boss: Boss, opts: LaunchCLIOptions, args: List[str], target_tab: Opti
else:
tab = tab_for_window(boss, opts, target_tab)
if tab is not None:
new_window: Window = tab.new_window(env=env or None, **kw)
watchers = load_watch_modules(opts)
new_window: Window = tab.new_window(env=env or None, watchers=watchers or None, **kw)
if opts.keep_focus and active:
boss.set_active_window(active, switch_os_window_if_needed=True)
return new_window

File diff suppressed because it is too large Load Diff

View File

@@ -684,6 +684,33 @@ report_marker_error(PyObject *marker) {
} else PyErr_Clear();
}
static inline void
apply_mark(Line *line, const attrs_type mark, index_type *cell_pos, unsigned int *match_pos) {
#define MARK { line->gpu_cells[x].attrs &= ATTRS_MASK_WITHOUT_MARK; line->gpu_cells[x].attrs |= mark; }
index_type x = *cell_pos;
MARK;
if (line->cpu_cells[x].ch) {
(*match_pos)++;
if (line->cpu_cells[x].ch == '\t') {
unsigned num_cells_to_skip_for_tab = line->cpu_cells[x].cc_idx[0];
while (num_cells_to_skip_for_tab && x + 1 < line->xnum && line->cpu_cells[x+1].ch == ' ') {
x++;
num_cells_to_skip_for_tab--;
MARK;
}
} else if ((line->gpu_cells[x].attrs & WIDTH_MASK) > 1 && x + 1 < line->xnum && !line->cpu_cells[x+1].ch) {
x++;
MARK;
} else {
for (index_type i = 0; i < arraysz(line->cpu_cells[x].cc_idx); i++) {
if (line->cpu_cells[x].cc_idx[i]) (*match_pos)++;
}
}
}
*cell_pos = x + 1;
#undef MARK
}
static inline void
apply_marker(PyObject *marker, Line *line, const PyObject *text) {
unsigned int l=0, r=0, col=0, match_pos=0;
@@ -695,33 +722,20 @@ apply_marker(PyObject *marker, Line *line, const PyObject *text) {
if (iter == NULL) { report_marker_error(marker); return; }
PyObject *match;
index_type x = 0;
#define INCREMENT_MATCH_POS { \
if (line->cpu_cells[x].ch) { \
match_pos++; \
for (index_type i = 0; i < arraysz(line->cpu_cells[x].cc_idx); i++) { \
if (line->cpu_cells[x].cc_idx[i]) match_pos++; \
}}}
while ((match = PyIter_Next(iter)) && x < line->xnum) {
Py_DECREF(match);
while (match_pos < l && x < line->xnum) {
line->gpu_cells[x].attrs &= ATTRS_MASK_WITHOUT_MARK;
INCREMENT_MATCH_POS;
x++;
apply_mark(line, 0, &x, &match_pos);
}
attrs_type am = (col & MARK_MASK) << MARK_SHIFT;
while(x < line->xnum && match_pos <= r) {
line->gpu_cells[x].attrs &= ATTRS_MASK_WITHOUT_MARK;
line->gpu_cells[x].attrs |= am;
INCREMENT_MATCH_POS;
x++;
apply_mark(line, am, &x, &match_pos);
}
}
while(x < line->xnum) line->gpu_cells[x++].attrs &= ATTRS_MASK_WITHOUT_MARK;
Py_DECREF(iter);
while(x < line->xnum) line->gpu_cells[x++].attrs &= ATTRS_MASK_WITHOUT_MARK;
if (PyErr_Occurred()) report_marker_error(marker);
#undef INCREMENT_MATCH_POS
}
void

View File

@@ -15,7 +15,7 @@ from .child import set_default_env
from .cli import create_opts, parse_args
from .cli_stub import CLIOptions
from .conf.utils import BadLine
from .config import cached_values_for, initial_window_size_func
from .config import cached_values_for, initial_window_size_func, expandvars
from .constants import (
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
is_wayland, kitty_exe, logo_data_file, running_in_kitty
@@ -131,7 +131,7 @@ def _run_app(opts: OptionsStub, args: CLIOptions, bad_lines: Sequence[BadLine] =
window_id = create_os_window(
run_app.initial_window_size_func(opts, cached_values),
pre_show_callback,
appname, args.name or args.cls or appname,
args.title or appname, args.name or args.cls or appname,
args.cls or appname, load_all_shaders)
boss = Boss(opts, args, cached_values, new_os_window_trigger)
boss.start(window_id)
@@ -152,7 +152,7 @@ class AppRunner:
def __call__(self, opts: OptionsStub, args: CLIOptions, bad_lines: Sequence[BadLine] = ()) -> None:
set_scale(opts.box_drawing_scale)
set_options(opts, is_wayland(), args.debug_gl, args.debug_font_fallback)
set_options(opts, is_wayland(), args.debug_rendering, args.debug_font_fallback)
set_font_family(opts, debug_font_matching=args.debug_font_fallback)
try:
_run_app(opts, args, bad_lines)
@@ -230,6 +230,22 @@ def get_editor_from_env(shell_env: Mapping[str, str]) -> Optional[str]:
return editor
def expand_listen_on(listen_on: str, from_config_file: bool) -> str:
listen_on = expandvars(listen_on)
if '{kitty_pid}' not in listen_on and from_config_file:
listen_on += '-{kitty_pid}'
listen_on = listen_on.replace('{kitty_pid}', str(os.getpid()))
if listen_on.startswith('unix:'):
path = listen_on[len('unix:'):]
if not path.startswith('@'):
if path.startswith('~'):
listen_on = f'unix:{os.path.expanduser(path)}'
elif not os.path.isabs(path):
import tempfile
listen_on = f'unix:{os.path.join(tempfile.gettempdir(), path)}'
return listen_on
def setup_environment(opts: OptionsStub, cli_opts: CLIOptions) -> None:
extra_env = opts.env.copy()
if opts.editor == '.':
@@ -241,7 +257,12 @@ def setup_environment(opts: OptionsStub, cli_opts: CLIOptions) -> None:
os.environ['EDITOR'] = editor
else:
os.environ['EDITOR'] = opts.editor
if cli_opts.listen_on:
from_config_file = False
if not cli_opts.listen_on and opts.listen_on.startswith('unix:'):
cli_opts.listen_on = opts.listen_on
from_config_file = True
if cli_opts.listen_on and opts.allow_remote_control != 'n':
cli_opts.listen_on = expand_listen_on(cli_opts.listen_on, from_config_file)
os.environ['KITTY_LISTEN_ON'] = cli_opts.listen_on
set_default_env(extra_env)

View File

@@ -15,6 +15,8 @@
#include "control-codes.h"
#include "monotonic.h"
extern PyTypeObject Screen_Type;
static MouseShape mouse_cursor_shape = BEAM;
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
@@ -95,37 +97,37 @@ encode_mouse_event(Window *w, int button, MouseAction action, int mods) {
// }}}
static inline double
window_left(Window *w, OSWindow *os_window) {
return w->geometry.left - OPT(window_padding_width) * (os_window->logical_dpi_x / 72.0);
static inline unsigned int
window_left(Window *w) {
return w->geometry.left - w->padding.left;
}
static inline double
window_right(Window *w, OSWindow *os_window) {
return w->geometry.right + OPT(window_padding_width) * (os_window->logical_dpi_x / 72.0);
static inline unsigned int
window_right(Window *w) {
return w->geometry.right + w->padding.right;
}
static inline double
window_top(Window *w, OSWindow *os_window) {
return w->geometry.top - OPT(window_padding_width) * (os_window->logical_dpi_y / 72.0);
static inline unsigned int
window_top(Window *w) {
return w->geometry.top - w->padding.top;
}
static inline double
window_bottom(Window *w, OSWindow *os_window) {
return w->geometry.bottom + OPT(window_padding_width) * (os_window->logical_dpi_y / 72.0);
static inline unsigned int
window_bottom(Window *w) {
return w->geometry.bottom + w->padding.bottom;
}
static inline bool
contains_mouse(Window *w, OSWindow *os_window) {
contains_mouse(Window *w) {
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
return (w->visible && window_left(w, os_window) <= x && x <= window_right(w, os_window) && window_top(w, os_window) <= y && y <= window_bottom(w, os_window));
return (w->visible && window_left(w) <= x && x <= window_right(w) && window_top(w) <= y && y <= window_bottom(w));
}
static inline double
distance_to_window(Window *w, OSWindow *os_window) {
distance_to_window(Window *w) {
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
double cx = (window_left(w, os_window) + window_right(w, os_window)) / 2.0;
double cy = (window_top(w, os_window) + window_bottom(w, os_window)) / 2.0;
double cx = (window_left(w) + window_right(w)) / 2.0;
double cy = (window_top(w) + window_bottom(w)) / 2.0;
return (x - cx) * (x - cx) + (y - cy) * (y - cy);
}
@@ -140,7 +142,7 @@ cell_for_pos(Window *w, unsigned int *x, unsigned int *y, bool *in_left_half_of_
bool in_left_half = true;
double mouse_x = global_state.callback_os_window->mouse_x;
double mouse_y = global_state.callback_os_window->mouse_y;
double left = window_left(w, os_window), top = window_top(w, os_window), right = window_right(w, os_window), bottom = window_bottom(w, os_window);
double left = window_left(w), top = window_top(w), right = window_right(w), bottom = window_bottom(w);
if (clamp_to_window) {
mouse_x = MIN(MAX(mouse_x, left), right);
mouse_y = MIN(MAX(mouse_y, top), bottom);
@@ -511,7 +513,7 @@ window_for_event(unsigned int *window_idx, bool *in_tab_bar) {
if (!*in_tab_bar && global_state.callback_os_window->num_tabs > 0) {
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
for (unsigned int i = 0; i < t->num_windows; i++) {
if (contains_mouse(t->windows + i, global_state.callback_os_window) && t->windows[i].render_data.screen) {
if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) {
*window_idx = i; return t->windows + i;
}
}
@@ -527,7 +529,7 @@ closest_window_for_event(unsigned int *window_idx) {
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
for (unsigned int i = 0; i < t->num_windows; i++) {
Window *w = t->windows + i;
double d = distance_to_window(w, global_state.callback_os_window);
double d = distance_to_window(w);
if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; }
}
}
@@ -698,6 +700,25 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
}
}
static PyObject*
send_mouse_event(PyObject *self UNUSED, PyObject *args) {
Screen *screen;
unsigned int x, y;
int button, action, mods;
if (!PyArg_ParseTuple(args, "O!IIiii", &Screen_Type, &screen, &x, &y, &button, &action, &mods)) return NULL;
MouseTrackingMode mode = screen->modes.mouse_tracking_mode;
if (mode == ANY_MODE || (mode == MOTION_MODE && action != MOVE) || (mode == BUTTON_MODE && (action == PRESS || action == RELEASE))) {
int sz = encode_mouse_event_impl(x + 1, y + 1, screen->modes.mouse_tracking_protocol, button, action, mods);
if (sz > 0) {
mouse_event_buf[sz] = 0;
write_escape_code_to_child(screen, CSI, mouse_event_buf);
Py_RETURN_TRUE;
}
}
Py_RETURN_FALSE;
}
static PyObject*
test_encode_mouse(PyObject *self UNUSED, PyObject *args) {
unsigned int x, y;
@@ -732,6 +753,7 @@ send_mock_mouse_event_to_window(PyObject *self UNUSED, PyObject *args) {
}
static PyMethodDef module_methods[] = {
METHODB(send_mouse_event, METH_VARARGS),
METHODB(test_encode_mouse, METH_VARARGS),
METHODB(send_mock_mouse_event_to_window, METH_VARARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
@@ -739,6 +761,10 @@ static PyMethodDef module_methods[] = {
bool
init_mouse(PyObject *module) {
PyModule_AddIntMacro(module, PRESS);
PyModule_AddIntMacro(module, RELEASE);
PyModule_AddIntMacro(module, DRAG);
PyModule_AddIntMacro(module, MOVE);
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
return true;
}

View File

@@ -11,7 +11,7 @@ static inline void parse_graphics_code(Screen *screen,
unsigned int i, code;
uint64_t lcode;
bool is_negative;
zero_at_ptr(&g);
memset(&g, 0, sizeof(g));
size_t sz;
static uint8_t payload[4096];
@@ -39,6 +39,8 @@ static inline void parse_graphics_code(Screen *screen,
};
enum KEYS key = 'a';
if (screen->parser_buf[pos] == ';')
state = AFTER_VALUE;
while (pos < screen->parser_buf_pos) {
switch (state) {
@@ -129,8 +131,8 @@ static inline void parse_graphics_code(Screen *screen,
case action: {
g.action = screen->parser_buf[pos++] & 0xff;
if (g.action != 'q' && g.action != 'd' && g.action != 't' &&
g.action != 'T' && g.action != 'p') {
if (g.action != 't' && g.action != 'd' && g.action != 'p' &&
g.action != 'q' && g.action != 'T') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
"value for action: 0x%x",
g.action);
@@ -140,14 +142,14 @@ static inline void parse_graphics_code(Screen *screen,
case delete_action: {
g.delete_action = screen->parser_buf[pos++] & 0xff;
if (g.delete_action != 'q' && g.delete_action != 'c' &&
g.delete_action != 'Z' && g.delete_action != 'z' &&
g.delete_action != 'x' && g.delete_action != 'X' &&
g.delete_action != 'I' && g.delete_action != 'i' &&
g.delete_action != 'P' && g.delete_action != 'y' &&
g.delete_action != 'a' && g.delete_action != 'A' &&
g.delete_action != 'Q' && g.delete_action != 'C' &&
g.delete_action != 'p' && g.delete_action != 'Y') {
if (g.delete_action != 'X' && g.delete_action != 'y' &&
g.delete_action != 'i' && g.delete_action != 'I' &&
g.delete_action != 'A' && g.delete_action != 'p' &&
g.delete_action != 'Y' && g.delete_action != 'z' &&
g.delete_action != 'a' && g.delete_action != 'P' &&
g.delete_action != 'x' && g.delete_action != 'q' &&
g.delete_action != 'Z' && g.delete_action != 'Q' &&
g.delete_action != 'c' && g.delete_action != 'C') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
"value for delete_action: 0x%x",
g.delete_action);
@@ -157,8 +159,8 @@ static inline void parse_graphics_code(Screen *screen,
case transmission_type: {
g.transmission_type = screen->parser_buf[pos++] & 0xff;
if (g.transmission_type != 's' && g.transmission_type != 'f' &&
g.transmission_type != 't' && g.transmission_type != 'd') {
if (g.transmission_type != 'f' && g.transmission_type != 'd' &&
g.transmission_type != 's' && g.transmission_type != 't') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
"value for transmission_type: 0x%x",
g.transmission_type);

View File

@@ -26,7 +26,9 @@ class Kitten(RemoteCommand):
'Run a kitten over the specified window (active window by default).'
' The :italic:`kitten_name` can be either the name of a builtin kitten'
' or the path to a python file containing a custom kitten. If a relative path'
' is used it is searched for in the kitty config directory.'
' is used it is searched for in the kitty config directory. If the kitten is a'
' no_ui kitten and its handle response method returns a string or boolean, this'
' is printed out to stdout.'
)
options_spec = MATCH_WINDOW_OPTION
argspec = 'kitten_name'
@@ -43,10 +45,13 @@ class Kitten(RemoteCommand):
windows = list(boss.match_windows(match))
if not windows:
raise MatchError(match)
retval = None
for window in windows:
if window:
boss._run_kitten(payload_get('kitten'), args=tuple(payload_get('args') or ()), window=window)
retval = boss._run_kitten(payload_get('kitten'), args=tuple(payload_get('args') or ()), window=window)
break
if isinstance(retval, (str, bool)):
return retval
kitten = Kitten()

View File

@@ -5,6 +5,7 @@
from typing import TYPE_CHECKING, Optional
from kitty.cli_stub import LaunchCLIOptions
from kitty.launch import (
launch as do_launch, options_spec as launch_options_spec,
parse_launch_args
@@ -16,7 +17,7 @@ from .base import (
)
if TYPE_CHECKING:
from kitty.cli_stub import LaunchRCOptions as CLIOptions, LaunchCLIOptions
from kitty.cli_stub import LaunchRCOptions as CLIOptions
class Launch(RemoteCommand):

View File

@@ -4,7 +4,7 @@
import os
from typing import TYPE_CHECKING, Dict, Optional
from typing import TYPE_CHECKING, Dict, Optional, Union
from kitty.config import parse_config
from kitty.fast_data_types import patch_color_profiles
@@ -23,7 +23,7 @@ class SetColors(RemoteCommand):
'''
colors+: An object mapping names to colors as 24-bit RGB integers
cursor_text_color: A 24-bit color for text under the cursor
cursor_text_color: A 24-bit color for text under the cursor, or null to use background.
match_window: Window to change colors in
match_tab: Tab to change colors in
all: Boolean indicating change colors everywhere or not
@@ -57,10 +57,11 @@ Restore all colors to the values they had at kitty startup. Note that if you spe
this option, any color arguments are ignored and --configured and --all are implied.
''' + '\n\n' + MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')
argspec = 'COLOR_OR_FILE ...'
args_completion = {'files': ('CONF files', ('*.conf',))}
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
final_colors: Dict[str, int] = {}
cursor_text_color: Optional[int] = None
cursor_text_color: Optional[Union[int, bool]] = False
if not opts.reset:
colors: Dict[str, Optional[Color]] = {}
for spec in args:
@@ -69,16 +70,18 @@ this option, any color arguments are ignored and --configured and --all are impl
else:
with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:
colors.update(parse_config(f))
ctc = colors.pop('cursor_text_color')
ctc = colors.pop('cursor_text_color', False)
if isinstance(ctc, Color):
cursor_text_color = color_as_int(ctc)
elif ctc is None:
cursor_text_color = None
final_colors = {k: color_as_int(v) for k, v in colors.items() if isinstance(v, Color)}
ans = {
'match_window': opts.match, 'match_tab': opts.match_tab,
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset,
'colors': final_colors, 'reset': opts.reset, 'dummy': 0
}
if cursor_text_color is not None:
if cursor_text_color is not False:
ans['cursor_text_color'] = cursor_text_color
del ans['dummy']
return ans
@@ -86,7 +89,7 @@ this option, any color arguments are ignored and --configured and --all are impl
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
windows = self.windows_for_payload(boss, window, payload_get)
colors = payload_get('colors')
cursor_text_color = payload_get('cursor_text_color') or False
cursor_text_color = payload_get('cursor_text_color', missing=False)
if payload_get('reset'):
colors = {k: color_as_int(v) for k, v in boss.startup_colors.items()}
cursor_text_color = boss.startup_cursor_text_color

124
kitty/rc/set_spacing.py Normal file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from typing import TYPE_CHECKING, Dict, Optional, List
from .base import (
MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType,
PayloadType, RCOptions, RemoteCommand, ResponseType, Window
)
if TYPE_CHECKING:
from kitty.cli_stub import SetSpacingRCOptions as CLIOptions
from kitty.options_stub import Options
def patch_window_edges(w: Window, s: Dict[str, Optional[float]]) -> None:
for k, v in s.items():
which, edge = k.lower().split('-', 1)
if edge == 'left':
w.patch_edge_width(which, 'left', v)
elif edge == 'right':
w.patch_edge_width(which, 'right', v)
elif edge == 'top':
w.patch_edge_width(which, 'top', v)
elif edge == 'bottom':
w.patch_edge_width(which, 'bottom', v)
def patch_configured_edges(opts: 'Options', s: Dict[str, Optional[float]]) -> None:
for k, val in s.items():
if val is None:
continue
which, edge = k.lower().split('-', 1)
q = f'window_{which}_width'
new_edges = getattr(opts, q)._replace(**{edge: val})
setattr(opts, q, new_edges)
class SetSpacing(RemoteCommand):
'''
settings+: An object mapping margins/paddings using canonical form {'margin-top': 50, 'padding-left': null} etc
match_window: Window to change colors in
match_tab: Tab to change colors in
all: Boolean indicating change colors everywhere or not
configured: Boolean indicating whether to change the configured colors. Must be True if reset is True
'''
short_desc = 'Set window padding and margins'
desc = (
'Set the padding and margins for the specified windows (defaults to active window).'
' For example: margin=20 or padding-left=10 or margin-h=30. The shorthand form sets'
' all values, the :code:`*-h` and :code:`*-v` variants set horizontal and vertical values.'
' The special value "default" resets to using the default value.'
' If you specify a tab rather than a window, all windows in that tab are affected.'
)
options_spec = '''\
--all -a
type=bool-set
By default, settings are only changed for the currently active window. This option will
cause colors to be changed in all windows.
--configured -c
type=bool-set
Also change the configured padding and margins (i.e. the settings kitty will use for new
windows).
''' + '\n\n' + MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')
argspec = 'MARGIN_OR_PADDING ...'
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
settings: Dict[str, Optional[float]] = {}
mapper: Dict[str, List[str]] = {}
for q in ('margin', 'padding'):
mapper[q] = f'{q}-left {q}-top {q}-right {q}-bottom'.split()
mapper[f'{q}-h'] = mapper[f'{q}-horizontal'] = f'{q}-left {q}-right'.split()
mapper[f'{q}-v'] = mapper[f'{q}-vertical'] = f'{q}-top {q}-bottom'.split()
for edge in ('left', 'top', 'right', 'bottom'):
mapper[f'{q}-{edge}'] = [f'{q}-{edge}']
if not args:
self.fatal('At least one setting must be specified')
for spec in args:
parts = spec.split('=', 1)
if len(parts) != 2:
self.fatal(f'{spec} is not a valid setting')
which = mapper.get(parts[0].lower())
if not which:
self.fatal(f'{parts[0]} is not a valid edge specification')
if parts[1].lower() == 'default':
val = None
else:
try:
val = float(parts[1])
except Exception:
self.fatal(f'{parts[1]} is not a number')
for q in which:
settings[q] = val
ans = {
'match_window': opts.match, 'match_tab': opts.match_tab,
'all': opts.all, 'configured': opts.configured,
'settings': settings
}
return ans
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
windows = self.windows_for_payload(boss, window, payload_get)
settings: Dict[str, Optional[float]] = payload_get('settings')
dirtied_tabs = {}
if payload_get('configured'):
patch_configured_edges(boss.opts, settings)
for w in windows:
patch_window_edges(w, settings)
tab = w.tabref()
if tab is not None:
dirtied_tabs[tab.id] = tab
for tab in dirtied_tabs.values():
tab.relayout()
set_spacing = SetSpacing()

View File

@@ -357,6 +357,31 @@ selection_has_screen_line(Selection *s, int y) {
return top <= y && y <= bottom;
}
static inline bool is_flag_pair(char_type a, char_type b) {
return is_flag_codepoint(a) && is_flag_codepoint(b);
}
static inline bool
draw_second_flag_codepoint(Screen *self, char_type ch) {
index_type xpos = 0, ypos = 0;
if (self->cursor->x > 1) {
ypos = self->cursor->y;
xpos = self->cursor->x - 2;
} else if (self->cursor->y > 0 && self->columns > 1) {
ypos = self->cursor->y - 1;
xpos = self->columns - 2;
} else return false;
linebuf_init_line(self->linebuf, ypos);
CPUCell *cell = self->linebuf->line->cpu_cells + xpos;
if (!is_flag_pair(cell->ch, ch) || cell->cc_idx[0]) return false;
line_add_combining_char(self->linebuf->line, ch, xpos);
self->is_dirty = true;
if (selection_has_screen_line(&self->selection, ypos)) self->selection = EMPTY_SELECTION;
linebuf_mark_line_dirty(self->linebuf, ypos);
return true;
}
static inline void
draw_combining_char(Screen *self, char_type ch) {
bool has_prev_char = false;
@@ -372,6 +397,17 @@ draw_combining_char(Screen *self, char_type ch) {
xpos = self->columns - 1;
has_prev_char = true;
}
if (self->cursor->x > 0) {
ypos = self->cursor->y;
linebuf_init_line(self->linebuf, ypos);
xpos = self->cursor->x - 1;
has_prev_char = true;
} else if (self->cursor->y > 0) {
ypos = self->cursor->y - 1;
linebuf_init_line(self->linebuf, ypos);
xpos = self->columns - 1;
has_prev_char = true;
}
if (has_prev_char) {
line_add_combining_char(self->linebuf->line, ch, xpos);
self->is_dirty = true;
@@ -412,6 +448,10 @@ screen_draw(Screen *self, uint32_t och) {
draw_combining_char(self, ch);
return;
}
bool is_flag = is_flag_codepoint(ch);
if (UNLIKELY(is_flag)) {
if (draw_second_flag_codepoint(self, ch)) return;
}
int char_width = wcwidth_std(ch);
if (UNLIKELY(char_width < 1)) {
if (char_width == 0) return;
@@ -1827,37 +1867,54 @@ screen_wcswidth(PyObject UNUSED *self, PyObject *str) {
unsigned long ans = 0;
char_type prev_ch = 0;
int prev_width = 0;
bool in_sgr = false;
typedef enum {NORMAL, IN_SGR, FLAG_PAIR_STARTED} WCSState;
WCSState state = NORMAL;
for (i = 0; i < len; i++) {
char_type ch = PyUnicode_READ(kind, data, i);
if (in_sgr) {
if (ch == 'm') in_sgr = false;
continue;
}
if (ch == 0x1b && i + 1 < len && PyUnicode_READ(kind, data, i + 1) == '[') { in_sgr = true; continue; }
if (ch == 0xfe0f) {
if (is_emoji_presentation_base(prev_ch) && prev_width == 1) {
ans += 1;
prev_width = 2;
} else prev_width = 0;
} else if (ch == 0xfe0e) {
if (is_emoji_presentation_base(prev_ch) && prev_width == 2) {
ans -= 1;
prev_width = 1;
} else prev_width = 0;
} else {
int w = wcwidth_std(ch);
switch(w) {
case -1:
case 0:
prev_width = 0; break;
case 2:
prev_width = 2; break;
default:
prev_width = 1; break;
}
ans += prev_width;
}
switch(state) {
case IN_SGR: {
if (ch == 'm') state = NORMAL;
} continue;
case FLAG_PAIR_STARTED: {
state = NORMAL;
if (is_flag_pair(prev_ch, ch)) break;
} /* fallthrough */
case NORMAL: {
if (ch == 0x1b && i + 1 < len && PyUnicode_READ(kind, data, i + 1) == '[') { state = IN_SGR; continue; }
switch(ch) {
case 0xfe0f: {
if (is_emoji_presentation_base(prev_ch) && prev_width == 1) {
ans += 1;
prev_width = 2;
} else prev_width = 0;
} break;
case 0xfe0e: {
if (is_emoji_presentation_base(prev_ch) && prev_width == 2) {
ans -= 1;
prev_width = 1;
} else prev_width = 0;
} break;
default: {
if (is_flag_codepoint(ch)) state = FLAG_PAIR_STARTED;
int w = wcwidth_std(ch);
switch(w) {
case -1:
case 0:
prev_width = 0; break;
case 2:
prev_width = 2; break;
default:
prev_width = 1; break;
}
ans += prev_width;
} break;
} break; // switch(ch)
} break; // case NORMAL
} // switch(state)
prev_ch = ch;
}
return PyLong_FromUnsignedLong(ans);

View File

@@ -8,7 +8,7 @@ from typing import Generator, List, NamedTuple, Optional, Tuple, Union
from .cli_stub import CLIOptions
from .config_data import to_layout_names
from .constants import kitty_exe
from .constants import FloatEdges, kitty_exe
from .layout import all_layouts
from .options_stub import Options
from .typing import SpecialWindowInstance
@@ -19,8 +19,8 @@ class WindowSizeOpts(NamedTuple):
initial_window_width: Tuple[int, str]
initial_window_height: Tuple[int, str]
window_margin_width: float
window_padding_width: float
window_margin_width: FloatEdges
window_padding_width: FloatEdges
remember_window_size: bool
@@ -53,7 +53,7 @@ class Session:
self.tabs[-1].next_title = title.strip()
def set_layout(self, val: str) -> None:
if val not in all_layouts:
if val.partition(':')[0] not in all_layouts:
raise ValueError('{} is not a valid layout'.format(val))
self.tabs[-1].layout = val

View File

@@ -27,13 +27,25 @@ GlobalState global_state = {{0}};
#define END_WITH_OS_WINDOW break; }}
#define WITH_TAB(os_window_id, tab_id) \
for (size_t o = 0; o < global_state.num_os_windows; o++) { \
for (size_t o = 0, tab_found = 0; o < global_state.num_os_windows && !tab_found; o++) { \
OSWindow *osw = global_state.os_windows + o; \
if (osw->id == os_window_id) { \
for (size_t t = 0; t < osw->num_tabs; t++) { \
if (osw->tabs[t].id == tab_id) { \
Tab *tab = osw->tabs + t;
#define END_WITH_TAB break; }}}}
#define END_WITH_TAB tab_found = 1; break; }}}}
#define WITH_WINDOW(os_window_id, tab_id, window_id) \
for (size_t o = 0, window_found = 0; o < global_state.num_os_windows && !window_found; o++) { \
OSWindow *osw = global_state.os_windows + o; \
if (osw->id == os_window_id) { \
for (size_t t = 0; t < osw->num_tabs && !window_found; t++) { \
if (osw->tabs[t].id == tab_id) { \
Tab *tab = osw->tabs + t; \
for (size_t w = 0; w < tab->num_windows; w++) { \
Window *window = tab->windows + w;
#define END_WITH_WINDOW break; }}}}}
#define WITH_OS_WINDOW_REFS \
id_type cb_window_id = 0, focused_window_id = 0; \
@@ -577,14 +589,14 @@ PYWRAP1(handle_for_window_id) {
PYWRAP1(set_options) {
PyObject *ret, *opts;
int is_wayland = 0, debug_gl = 0, debug_font_fallback = 0;
PA("O|ppp", &opts, &is_wayland, &debug_gl, &debug_font_fallback);
int is_wayland = 0, debug_rendering = 0, debug_font_fallback = 0;
PA("O|ppp", &opts, &is_wayland, &debug_rendering, &debug_font_fallback);
global_state.is_wayland = is_wayland ? true : false;
#ifdef __APPLE__
global_state.has_render_frames = true;
#endif
if (global_state.is_wayland) global_state.has_render_frames = true;
global_state.debug_gl = debug_gl ? true : false;
global_state.debug_rendering = debug_rendering ? true : false;
global_state.debug_font_fallback = debug_font_fallback ? true : false;
#define GA(name) ret = PyObject_GetAttrString(opts, #name); if (ret == NULL) return NULL;
#define SS(name, dest, convert) { GA(name); dest = convert(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; }
@@ -603,7 +615,6 @@ PYWRAP1(set_options) {
S(dim_opacity, PyFloat_AsFloat);
S(dynamic_background_opacity, PyObject_IsTrue);
S(inactive_text_alpha, PyFloat_AsFloat);
S(window_padding_width, PyFloat_AsFloat);
S(scrollback_pager_history_size, PyLong_AsUnsignedLong);
S(cursor_shape, PyLong_AsLong);
S(cursor_beam_thickness, PyFloat_AsFloat);
@@ -839,6 +850,16 @@ fix_window_idx(Tab *tab, id_type window_id, unsigned int *window_idx) {
return false;
}
PYWRAP1(set_window_padding) {
id_type os_window_id, tab_id, window_id;
unsigned int left, top, right, bottom;
PA("KKKIIII", &os_window_id, &tab_id, &window_id, &left, &top, &right, &bottom);
WITH_WINDOW(os_window_id, tab_id, window_id);
window->padding.left = left; window->padding.top = top; window->padding.right = right; window->padding.bottom = bottom;
END_WITH_WINDOW;
Py_RETURN_NONE;
}
PYWRAP1(set_window_render_data) {
#define A(name) &(d.name)
#define B(name) &(g.name)
@@ -1080,6 +1101,7 @@ static PyMethodDef module_methods[] = {
MW(add_borders_rect, METH_VARARGS),
MW(set_tab_bar_render_data, METH_VARARGS),
MW(set_window_render_data, METH_VARARGS),
MW(set_window_padding, METH_VARARGS),
MW(viewport_for_window, METH_VARARGS),
MW(cell_size_for_window, METH_VARARGS),
MW(os_window_has_background_image, METH_VARARGS),

View File

@@ -53,7 +53,6 @@ typedef struct {
bool dynamic_background_opacity;
float inactive_text_alpha;
float window_padding_width;
Edge tab_bar_edge;
unsigned long tab_bar_min_tabs;
DisableLigature disable_ligatures;
@@ -106,6 +105,9 @@ typedef struct {
double x, y;
bool in_left_half_of_cell;
} mouse_pos;
struct {
unsigned int left, top, right, bottom;
} padding;
WindowGeometry geometry;
ClickQueue click_queue;
monotonic_t last_drag_scroll_at;
@@ -196,7 +198,7 @@ typedef struct {
bool terminate;
bool is_wayland;
bool has_render_frames;
bool debug_gl, debug_font_fallback;
bool debug_rendering, debug_font_fallback;
bool has_pending_resizes, has_pending_closes;
bool in_sequence_mode;
bool tab_bar_hidden;

View File

@@ -66,6 +66,8 @@ def draw_title(draw_data: DrawData, screen: Screen, tab: TabBarData, index: int)
def draw_tab_with_separator(draw_data: DrawData, screen: Screen, tab: TabBarData, before: int, max_title_length: int, index: int, is_last: bool) -> int:
tab_bg = draw_data.active_bg if tab.is_active else draw_data.inactive_bg
screen.cursor.bg = as_rgb(color_as_int(tab_bg))
if draw_data.leading_spaces:
screen.draw(' ' * draw_data.leading_spaces)
draw_title(draw_data, screen, tab, index)
@@ -79,8 +81,11 @@ def draw_tab_with_separator(draw_data: DrawData, screen: Screen, tab: TabBarData
screen.draw(' ' * trailing_spaces)
end = screen.cursor.x
screen.cursor.bold = screen.cursor.italic = False
screen.cursor.fg = screen.cursor.bg = 0
screen.cursor.fg = 0
if not is_last:
screen.cursor.bg = as_rgb(color_as_int(draw_data.inactive_bg))
screen.draw(draw_data.sep)
screen.cursor.bg = 0
return end
@@ -135,9 +140,11 @@ def draw_tab_with_powerline(draw_data: DrawData, screen: Screen, tab: TabBarData
screen.draw('')
screen.cursor.fg = tab_fg
elif screen.cursor.x == 0:
screen.cursor.bg = tab_bg
screen.draw(' ')
start_draw = 1
screen.cursor.bg = tab_bg
if min_title_length >= max_title_length:
screen.draw('')
else:
@@ -179,7 +186,7 @@ class TabBar:
s.color_profile.update_ansi_color_table(build_ansi_color_table(opts))
s.color_profile.set_configured_colors(
color_as_int(opts.inactive_tab_foreground),
color_as_int(opts.inactive_tab_background)
color_as_int(opts.tab_bar_background or opts.background)
)
self.blank_rects: Tuple[Rect, ...] = ()
sep = opts.tab_separator
@@ -223,10 +230,13 @@ class TabBar:
self.draw_data = self.draw_data._replace(default_bg=color_from_int(spec['tab_bar_background']))
elif 'background' in spec and not self.opts.tab_bar_background:
self.draw_data = self.draw_data._replace(default_bg=color_from_int(spec['background']))
self.screen.color_profile.set_configured_colors(
spec.get('inactive_tab_foreground', color_as_int(self.opts.inactive_tab_foreground)),
spec.get('inactive_tab_background', color_as_int(self.opts.inactive_tab_background))
)
fg = spec.get('inactive_tab_foreground', color_as_int(self.opts.inactive_tab_foreground))
bg = spec.get('tab_bar_background', False)
if bg is None:
bg = color_as_int(self.opts.background)
elif bg is False:
bg = color_as_int(self.opts.tab_bar_background or self.opts.background)
self.screen.color_profile.set_configured_colors(fg, bg)
def layout(self) -> None:
central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id)

View File

@@ -7,8 +7,8 @@ from collections import deque
from contextlib import suppress
from functools import partial
from typing import (
Deque, Dict, Generator, Iterator, List, NamedTuple,
Optional, Pattern, Sequence, Tuple, cast
Any, Deque, Dict, Generator, Iterator, List, NamedTuple, Optional, Pattern,
Sequence, Tuple, cast
)
from .borders import Borders
@@ -17,17 +17,17 @@ from .cli_stub import CLIOptions
from .constants import appname, is_macos, is_wayland
from .fast_data_types import (
add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty,
next_window_id, pt_to_px, remove_tab, remove_window, ring_bell,
set_active_tab, swap_tabs, x11_window_id
next_window_id, remove_tab, remove_window, ring_bell, set_active_tab,
swap_tabs, x11_window_id
)
from .layout import (
Layout, Rect, create_layout_object_for, evict_cached_layouts
)
from .options_stub import Options
from .tab_bar import TabBar, TabBarData
from .typing import SessionTab, SessionType, TypedDict
from .utils import log_error, resolved_shell
from .window import Window, WindowDict
from .typing import TypedDict, SessionTab, SessionType
from .window import Watchers, Window, WindowDict
class TabDict(TypedDict):
@@ -87,11 +87,10 @@ class Tab: # {{{
if not self.id:
raise Exception('No OS window with id {} found, or tab counter has wrapped'.format(self.os_window_id))
self.opts, self.args = tab_manager.opts, tab_manager.args
self.recalculate_sizes(update_layout=False)
self.name = getattr(session_tab, 'name', '')
self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or self.opts.enabled_layouts]
self.borders = Borders(self.os_window_id, self.id, self.opts)
self.windows: Deque[Window] = deque()
self.windows: List[Window] = []
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
setattr(self, which + '_window', partial(self.nth_window, num=i))
self._last_used_layout: Optional[str] = None
@@ -113,15 +112,6 @@ class Tab: # {{{
self._set_current_layout(l0)
self.startup(session_tab)
def recalculate_sizes(self, update_layout: bool = True) -> None:
self.margin_width, self.padding_width, self.single_window_margin_width = map(
lambda x: pt_to_px(getattr(self.opts, x), self.os_window_id), (
'window_margin_width', 'window_padding_width', 'single_window_margin_width'))
self.border_width = pt_to_px(self.opts.window_border_width, self.os_window_id)
if update_layout and self.current_layout:
self.current_layout.update_sizes(
self.margin_width, self.single_window_margin_width, self.padding_width, self.border_width)
def take_over_from(self, other_tab: 'Tab') -> None:
self.name, self.cwd = other_tab.name, other_tab.cwd
self.enabled_layouts = list(other_tab.enabled_layouts)
@@ -129,12 +119,12 @@ class Tab: # {{{
self._set_current_layout(other_tab._current_layout_name)
self._last_used_layout = other_tab._last_used_layout
orig_windows = deque(other_tab.windows)
orig_windows = list(other_tab.windows)
orig_history = deque(other_tab.active_window_history)
orig_active = other_tab._active_window_idx
for window in other_tab.windows:
detach_window(other_tab.os_window_id, other_tab.id, window.id)
other_tab.windows = deque()
other_tab.windows = []
other_tab._active_window_idx = 0
self.active_window_history = orig_history
self.windows = orig_windows
@@ -157,6 +147,18 @@ class Tab: # {{{
self.new_window(cmd=cmd)
self.set_active_window_idx(session_tab.active_window_idx)
def serialize_state(self) -> Dict[str, Any]:
return {
'version': 1,
'id': self.id,
'active_window_idx': self.active_window_idx,
'windows': [w.serialize_state() for w in self],
'current_layout': self._current_layout_name,
'last_used_layout': self._last_used_layout,
'active_window_history': list(self.active_window_history),
'name': self.name,
}
@property
def active_window_idx(self) -> int:
return self._active_window_idx
@@ -227,20 +229,17 @@ class Tab: # {{{
if tm is not None:
visible_windows = [w for w in self.windows if w.is_visible_in_layout]
w = self.active_window
ly = self.current_layout
self.borders(
windows=visible_windows, active_window=w,
current_layout=self.current_layout, extra_blank_rects=tm.blank_rects,
padding_width=self.padding_width, border_width=self.border_width,
draw_window_borders=self.current_layout.needs_window_borders and len(visible_windows) > 1
current_layout=ly, extra_blank_rects=tm.blank_rects,
draw_window_borders=(ly.needs_window_borders and len(visible_windows) > 1) or ly.must_draw_borders
)
if w is not None:
w.change_titlebar_color()
def create_layout_object(self, name: str) -> Layout:
return create_layout_object_for(
name, self.os_window_id, self.id, self.margin_width,
self.single_window_margin_width, self.padding_width,
self.border_width)
return create_layout_object_for(name, self.os_window_id, self.id)
def next_layout(self) -> None:
if len(self.enabled_layouts) > 1:
@@ -345,11 +344,15 @@ class Tab: # {{{
location: Optional[str] = None,
copy_colors_from: Optional[Window] = None,
allow_remote_control: bool = False,
marker: Optional[str] = None
marker: Optional[str] = None,
watchers: Optional[Watchers] = None
) -> Window:
child = self.launch_child(
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env, allow_remote_control=allow_remote_control)
window = Window(self, child, self.opts, self.args, override_title=override_title, copy_colors_from=copy_colors_from)
window = Window(
self, child, self.opts, self.args, override_title=override_title,
copy_colors_from=copy_colors_from, watchers=watchers
)
if overlay_for is not None:
overlaid = next(w for w in self.windows if w.id == overlay_for)
window.overlay_for = overlay_for
@@ -384,6 +387,13 @@ class Tab: # {{{
if self.windows:
self.remove_window(self.windows[self.active_window_idx])
def close_other_windows_in_tab(self) -> None:
if len(self.windows) > 1:
active_window = self.windows[self.active_window_idx]
for window in tuple(self.windows):
if window is not active_window:
self.remove_window(window)
def previous_active_window_idx(self, num: int) -> Optional[int]:
try:
old_window_id = self.active_window_history[-num]
@@ -535,7 +545,7 @@ class Tab: # {{{
evict_cached_layouts(self.id)
for w in self.windows:
w.destroy()
self.windows = deque()
self.windows = []
def __repr__(self) -> str:
return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self)))
@@ -629,10 +639,6 @@ class TabManager: # {{{
def update_tab_bar_data(self) -> None:
self.tab_bar.update(self.tab_bar_data)
def update_dpi_based_sizes(self) -> None:
for tab in self.tabs:
tab.recalculate_sizes()
def resize(self, only_tabs: bool = False) -> None:
if not only_tabs:
if not self.tab_bar_hidden:
@@ -692,6 +698,14 @@ class TabManager: # {{{
'active_window_history': list(tab.active_window_history),
}
def serialize_state(self) -> Dict[str, Any]:
return {
'version': 1,
'id': self.os_window_id,
'tabs': [tab.serialize_state() for tab in self],
'active_tab_idx': self.active_tab_idx,
}
@property
def active_tab(self) -> Optional[Tab]:
return self.tabs[self.active_tab_idx] if self.tabs else None

View File

@@ -198,6 +198,8 @@ string_capabilities = {
'rmso': r'\E[27m',
# Exit underline mode
'rmul': r'\E[24m',
# Exit strikethrough mode
'rmxx': r'\E[29m',
# Reset string1 (empty OSC sequence to exit OSC/OTH modes, and regular reset)
'rs1': r'\E]\E\\\Ec',
# Save cursor
@@ -224,6 +226,8 @@ string_capabilities = {
'smso': r'\E[7m',
# Enter underline mode
'smul': r'\E[4m',
# Enter strikethrough mode
'smxx': r'\E[9m',
# Clear all tab stops
'tbc': r'\E[3g',
# To status line (used to set window titles)
@@ -351,6 +355,7 @@ termcap_aliases.update({
'ei': 'rmir',
'se': 'rmso',
'ue': 'rmul',
'Te': 'rmxx',
'r1': 'rs1',
'sc': 'sc',
'AB': 'setab',
@@ -363,6 +368,7 @@ termcap_aliases.update({
'im': 'smir',
'so': 'smso',
'us': 'smul',
'Ts': 'smxx',
'ct': 'tbc',
'cv': 'vpa',
'ZH': 'sitm',

557
kitty/unicode-data.c generated

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
#pragma once
#include "data-types.h"
#define VS15 1280
#define VS16 1281
#define VS15 1285
#define VS16 1286
bool is_combining_char(char_type ch);
bool is_ignored_char(char_type ch);
@@ -29,3 +29,9 @@ static inline bool
is_private_use(char_type ch) {
return (0xe000 <= ch && ch <= 0xf8ff) || (0xF0000 <= ch && ch <= 0xFFFFF) || (0x100000 <= ch && ch <= 0x10FFFF);
}
static inline bool
is_flag_codepoint(char_type ch) {
return 0x1F1E6 <= ch && ch <= 0x1F1FF;
}

395
kitty/wcwidth-std.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2019-10-01
// unicode data, built from the unicode standard on: 2020-04-06
// see gen-wcwidth.py
#pragma once
#include "data-types.h"
@@ -8,7 +8,12 @@ START_ALLOW_CASE_RANGE
static int
wcwidth_std(int32_t code) {
switch(code) {
// Marks (2275 codepoints) {{{
// Flags (26 codepoints) {{{
case 0x1f1e6 ... 0x1f1ff:
return 2;
// }}}
// Marks (2302 codepoints) {{{
case 0x0:
return 0;
case 0x300 ... 0x36f:
@@ -127,7 +132,7 @@ wcwidth_std(int32_t code) {
return 0;
case 0xb4b ... 0xb4d:
return 0;
case 0xb56 ... 0xb57:
case 0xb55 ... 0xb57:
return 0;
case 0xb62 ... 0xb63:
return 0;
@@ -181,7 +186,7 @@ wcwidth_std(int32_t code) {
return 0;
case 0xd62 ... 0xd63:
return 0;
case 0xd82 ... 0xd83:
case 0xd81 ... 0xd83:
return 0;
case 0xdca:
return 0;
@@ -275,7 +280,7 @@ wcwidth_std(int32_t code) {
return 0;
case 0x1a7f:
return 0;
case 0x1ab0 ... 0x1abe:
case 0x1ab0 ... 0x1ac0:
return 0;
case 0x1b00 ... 0x1b04:
return 0;
@@ -335,6 +340,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0xa823 ... 0xa827:
return 0;
case 0xa82c:
return 0;
case 0xa880 ... 0xa881:
return 0;
case 0xa8b4 ... 0xa8c5:
@@ -405,6 +412,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0x10d24 ... 0x10d27:
return 0;
case 0x10eab ... 0x10eac:
return 0;
case 0x10f46 ... 0x10f50:
return 0;
case 0x11000 ... 0x11002:
@@ -429,6 +438,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0x111c9 ... 0x111cc:
return 0;
case 0x111ce ... 0x111cf:
return 0;
case 0x1122c ... 0x11237:
return 0;
case 0x1123e:
@@ -473,6 +484,16 @@ wcwidth_std(int32_t code) {
return 0;
case 0x1182c ... 0x1183a:
return 0;
case 0x11930 ... 0x11935:
return 0;
case 0x11937 ... 0x11938:
return 0;
case 0x1193b ... 0x1193e:
return 0;
case 0x11940:
return 0;
case 0x11942 ... 0x11943:
return 0;
case 0x119d1 ... 0x119d7:
return 0;
case 0x119da ... 0x119e0:
@@ -527,6 +548,10 @@ wcwidth_std(int32_t code) {
return 0;
case 0x16f8f ... 0x16f92:
return 0;
case 0x16fe4:
return 0;
case 0x16ff0 ... 0x16ff1:
return 0;
case 0x1bc9d ... 0x1bc9e:
return 0;
case 0x1d165 ... 0x1d169:
@@ -637,7 +662,7 @@ wcwidth_std(int32_t code) {
return -3;
// }}}
// Text Presentation (218 codepoints) {{{
// Text Presentation (219 codepoints) {{{
case 0x23:
return 1;
case 0x2a:
@@ -728,6 +753,8 @@ wcwidth_std(int32_t code) {
return 1;
case 0x26a0:
return 1;
case 0x26a7:
return 1;
case 0x26b0 ... 0x26b1:
return 1;
case 0x26c8:
@@ -1227,7 +1254,7 @@ wcwidth_std(int32_t code) {
return -2;
// }}}
// East Asian double width (181868 codepoints) {{{
// East Asian double width (182418 codepoints) {{{
case 0x1100 ... 0x115f:
return 2;
case 0x231a ... 0x231b:
@@ -1320,9 +1347,7 @@ wcwidth_std(int32_t code) {
return 2;
case 0x3131 ... 0x318e:
return 2;
case 0x3190 ... 0x31ba:
return 2;
case 0x31c0 ... 0x31e3:
case 0x3190 ... 0x31e3:
return 2;
case 0x31f0 ... 0x321e:
return 2;
@@ -1360,7 +1385,9 @@ wcwidth_std(int32_t code) {
return 2;
case 0x17000 ... 0x187f7:
return 2;
case 0x18800 ... 0x18af2:
case 0x18800 ... 0x18cd5:
return 2;
case 0x18d00 ... 0x18d08:
return 2;
case 0x1b000 ... 0x1b11e:
return 2;
@@ -1434,33 +1461,37 @@ wcwidth_std(int32_t code) {
return 2;
case 0x1f6d0 ... 0x1f6d2:
return 2;
case 0x1f6d5:
case 0x1f6d5 ... 0x1f6d7:
return 2;
case 0x1f6eb ... 0x1f6ec:
return 2;
case 0x1f6f4 ... 0x1f6fa:
case 0x1f6f4 ... 0x1f6fc:
return 2;
case 0x1f7e0 ... 0x1f7eb:
return 2;
case 0x1f90d ... 0x1f971:
case 0x1f90c ... 0x1f93a:
return 2;
case 0x1f973 ... 0x1f976:
case 0x1f93c ... 0x1f945:
return 2;
case 0x1f97a ... 0x1f9a2:
case 0x1f947 ... 0x1f978:
return 2;
case 0x1f9a5 ... 0x1f9aa:
return 2;
case 0x1f9ae ... 0x1f9ca:
case 0x1f97a ... 0x1f9cb:
return 2;
case 0x1f9cd ... 0x1f9ff:
return 2;
case 0x1fa70 ... 0x1fa73:
case 0x1fa70 ... 0x1fa74:
return 2;
case 0x1fa78 ... 0x1fa7a:
return 2;
case 0x1fa80 ... 0x1fa82:
case 0x1fa80 ... 0x1fa86:
return 2;
case 0x1fa90 ... 0x1fa95:
case 0x1fa90 ... 0x1faa8:
return 2;
case 0x1fab0 ... 0x1fab6:
return 2;
case 0x1fac0 ... 0x1fac2:
return 2;
case 0x1fad0 ... 0x1fad6:
return 2;
case 0x20000 ... 0x2fffd:
return 2;
@@ -1468,12 +1499,10 @@ wcwidth_std(int32_t code) {
return 2;
// }}}
// Emoji Presentation (26 codepoints) {{{
case 0x1f1e6 ... 0x1f1ff:
return 2;
// Emoji Presentation (0 codepoints) {{{
// }}}
// Not assigned in the unicode character database (766326 codepoints) {{{
// Not assigned in the unicode character database (765365 codepoints) {{{
case 0x378 ... 0x379:
return -4;
case 0x380 ... 0x383:
@@ -1520,7 +1549,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x8b5:
return -4;
case 0x8be ... 0x8d2:
case 0x8c8 ... 0x8d2:
return -4;
case 0x984:
return -4;
@@ -1628,7 +1657,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0xb49 ... 0xb4a:
return -4;
case 0xb4e ... 0xb55:
case 0xb4e ... 0xb54:
return -4;
case 0xb58 ... 0xb5b:
return -4;
@@ -1718,8 +1747,6 @@ wcwidth_std(int32_t code) {
return -4;
case 0xcf3 ... 0xcff:
return -4;
case 0xd04:
return -4;
case 0xd0d:
return -4;
case 0xd11:
@@ -1732,7 +1759,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0xd64 ... 0xd65:
return -4;
case 0xd80 ... 0xd81:
case 0xd80:
return -4;
case 0xd84:
return -4;
@@ -1906,7 +1933,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1aae ... 0x1aaf:
return -4;
case 0x1abf ... 0x1aff:
case 0x1ac1 ... 0x1aff:
return -4;
case 0x1b4c ... 0x1b4f:
return -4;
@@ -1980,7 +2007,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x2b74 ... 0x2b75:
return -4;
case 0x2b96 ... 0x2b97:
case 0x2b96:
return -4;
case 0x2c2f:
return -4;
@@ -2016,7 +2043,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x2ddf:
return -4;
case 0x2e50 ... 0x2e7f:
case 0x2e53 ... 0x2e7f:
return -4;
case 0x2e9a:
return -4;
@@ -2036,8 +2063,6 @@ wcwidth_std(int32_t code) {
return -4;
case 0x318f:
return -4;
case 0x31bb ... 0x31bf:
return -4;
case 0x31e4 ... 0x31ef:
return -4;
case 0x321f:
@@ -2052,9 +2077,9 @@ wcwidth_std(int32_t code) {
return -4;
case 0xa7c0 ... 0xa7c1:
return -4;
case 0xa7c7 ... 0xa7f6:
case 0xa7cb ... 0xa7f4:
return -4;
case 0xa82c ... 0xa82f:
case 0xa82d ... 0xa82f:
return -4;
case 0xa83a ... 0xa83f:
return -4;
@@ -2094,7 +2119,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0xab2f:
return -4;
case 0xab68 ... 0xab6f:
case 0xab6c ... 0xab6f:
return -4;
case 0xabee ... 0xabef:
return -4;
@@ -2180,7 +2205,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1018f:
return -4;
case 0x1019c ... 0x1019f:
case 0x1019d ... 0x1019f:
return -4;
case 0x101a1 ... 0x101cf:
return -4;
@@ -2298,11 +2323,19 @@ wcwidth_std(int32_t code) {
return -4;
case 0x10d3a ... 0x10e5f:
return -4;
case 0x10e7f ... 0x10eff:
case 0x10e7f:
return -4;
case 0x10eaa:
return -4;
case 0x10eae ... 0x10eaf:
return -4;
case 0x10eb2 ... 0x10eff:
return -4;
case 0x10f28 ... 0x10f2f:
return -4;
case 0x10f5a ... 0x10fdf:
case 0x10f5a ... 0x10faf:
return -4;
case 0x10fcc ... 0x10fdf:
return -4;
case 0x10ff7 ... 0x10fff:
return -4;
@@ -2320,12 +2353,10 @@ wcwidth_std(int32_t code) {
return -4;
case 0x11135:
return -4;
case 0x11147 ... 0x1114f:
case 0x11148 ... 0x1114f:
return -4;
case 0x11177 ... 0x1117f:
return -4;
case 0x111ce ... 0x111cf:
return -4;
case 0x111e0:
return -4;
case 0x111f5 ... 0x111ff:
@@ -2378,11 +2409,9 @@ wcwidth_std(int32_t code) {
return -4;
case 0x11375 ... 0x113ff:
return -4;
case 0x1145a:
return -4;
case 0x1145c:
return -4;
case 0x11460 ... 0x1147f:
case 0x11462 ... 0x1147f:
return -4;
case 0x114c8 ... 0x114cf:
return -4;
@@ -2412,7 +2441,21 @@ wcwidth_std(int32_t code) {
return -4;
case 0x118f3 ... 0x118fe:
return -4;
case 0x11900 ... 0x1199f:
case 0x11907 ... 0x11908:
return -4;
case 0x1190a ... 0x1190b:
return -4;
case 0x11914:
return -4;
case 0x11917:
return -4;
case 0x11936:
return -4;
case 0x11939 ... 0x1193a:
return -4;
case 0x11947 ... 0x1194f:
return -4;
case 0x1195a ... 0x1199f:
return -4;
case 0x119a8 ... 0x119a9:
return -4;
@@ -2466,7 +2509,9 @@ wcwidth_std(int32_t code) {
return -4;
case 0x11daa ... 0x11edf:
return -4;
case 0x11ef9 ... 0x11fbf:
case 0x11ef9 ... 0x11faf:
return -4;
case 0x11fb1 ... 0x11fbf:
return -4;
case 0x11ff2 ... 0x11ffe:
return -4;
@@ -2514,11 +2559,15 @@ wcwidth_std(int32_t code) {
return -4;
case 0x16fa0 ... 0x16fdf:
return -4;
case 0x16fe4 ... 0x16fff:
case 0x16fe5 ... 0x16fef:
return -4;
case 0x16ff2 ... 0x16fff:
return -4;
case 0x187f8 ... 0x187ff:
return -4;
case 0x18af3 ... 0x1afff:
case 0x18cd6 ... 0x18cff:
return -4;
case 0x18d09 ... 0x1afff:
return -4;
case 0x1b11f ... 0x1b14f:
return -4;
@@ -2714,11 +2763,7 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1f0f6 ... 0x1f0ff:
return -4;
case 0x1f10d ... 0x1f10f:
return -4;
case 0x1f16d ... 0x1f16f:
return -4;
case 0x1f1ad ... 0x1f1e5:
case 0x1f1ae ... 0x1f1e5:
return -4;
case 0x1f203 ... 0x1f20f:
return -4;
@@ -2730,11 +2775,11 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1f266 ... 0x1f2ff:
return -4;
case 0x1f6d6 ... 0x1f6df:
case 0x1f6d8 ... 0x1f6df:
return -4;
case 0x1f6ed ... 0x1f6ef:
return -4;
case 0x1f6fb ... 0x1f6ff:
case 0x1f6fd ... 0x1f6ff:
return -4;
case 0x1f774 ... 0x1f77f:
return -4;
@@ -2750,31 +2795,37 @@ wcwidth_std(int32_t code) {
return -4;
case 0x1f888 ... 0x1f88f:
return -4;
case 0x1f8ae ... 0x1f8ff:
case 0x1f8ae ... 0x1f8af:
return -4;
case 0x1f90c:
case 0x1f8b2 ... 0x1f8ff:
return -4;
case 0x1f972:
case 0x1f979:
return -4;
case 0x1f977 ... 0x1f979:
return -4;
case 0x1f9a3 ... 0x1f9a4:
return -4;
case 0x1f9ab ... 0x1f9ad:
return -4;
case 0x1f9cb ... 0x1f9cc:
case 0x1f9cc:
return -4;
case 0x1fa54 ... 0x1fa5f:
return -4;
case 0x1fa6e ... 0x1fa6f:
return -4;
case 0x1fa74 ... 0x1fa77:
case 0x1fa75 ... 0x1fa77:
return -4;
case 0x1fa7b ... 0x1fa7f:
return -4;
case 0x1fa83 ... 0x1fa8f:
case 0x1fa87 ... 0x1fa8f:
return -4;
case 0x1fa96 ... 0x1ffff:
case 0x1faa9 ... 0x1faaf:
return -4;
case 0x1fab7 ... 0x1fabf:
return -4;
case 0x1fac3 ... 0x1facf:
return -4;
case 0x1fad7 ... 0x1faff:
return -4;
case 0x1fb93:
return -4;
case 0x1fbcb ... 0x1fbef:
return -4;
case 0x1fbfa ... 0x1ffff:
return -4;
case 0x2fffe ... 0x2ffff:
return -4;
@@ -2827,11 +2878,7 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x23cf:
return true;
case 0x23e9 ... 0x23ea:
return true;
case 0x23ed ... 0x23ef:
return true;
case 0x23f1 ... 0x23f3:
case 0x23e9 ... 0x23f3:
return true;
case 0x23f8 ... 0x23fa:
return true;
@@ -2895,6 +2942,8 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x26a0 ... 0x26a1:
return true;
case 0x26a7:
return true;
case 0x26aa ... 0x26ab:
return true;
case 0x26b0 ... 0x26b1:
@@ -2905,7 +2954,7 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x26c8:
return true;
case 0x26cf:
case 0x26ce ... 0x26cf:
return true;
case 0x26d1:
return true;
@@ -2921,9 +2970,9 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x2702:
return true;
case 0x2708 ... 0x2709:
case 0x2705:
return true;
case 0x270c ... 0x270d:
case 0x2708 ... 0x270d:
return true;
case 0x270f:
return true;
@@ -2937,20 +2986,32 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x2721:
return true;
case 0x2728:
return true;
case 0x2733 ... 0x2734:
return true;
case 0x2744:
return true;
case 0x2747:
return true;
case 0x2753:
case 0x274c:
return true;
case 0x274e:
return true;
case 0x2753 ... 0x2755:
return true;
case 0x2757:
return true;
case 0x2763 ... 0x2764:
return true;
case 0x2795 ... 0x2797:
return true;
case 0x27a1:
return true;
case 0x27b0:
return true;
case 0x27bf:
return true;
case 0x2934 ... 0x2935:
return true;
case 0x2b05 ... 0x2b07:
@@ -2971,125 +3032,51 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x1f004:
return true;
case 0x1f0cf:
return true;
case 0x1f170 ... 0x1f171:
return true;
case 0x1f17e ... 0x1f17f:
return true;
case 0x1f202:
case 0x1f18e:
return true;
case 0x1f191 ... 0x1f19a:
return true;
case 0x1f1e6 ... 0x1f1ff:
return true;
case 0x1f201 ... 0x1f202:
return true;
case 0x1f21a:
return true;
case 0x1f22f:
return true;
case 0x1f237:
case 0x1f232 ... 0x1f23a:
return true;
case 0x1f30d ... 0x1f30f:
case 0x1f250 ... 0x1f251:
return true;
case 0x1f315:
case 0x1f300 ... 0x1f321:
return true;
case 0x1f31c:
return true;
case 0x1f321:
return true;
case 0x1f324 ... 0x1f32c:
return true;
case 0x1f336:
return true;
case 0x1f378:
return true;
case 0x1f37d:
return true;
case 0x1f393:
case 0x1f324 ... 0x1f393:
return true;
case 0x1f396 ... 0x1f397:
return true;
case 0x1f399 ... 0x1f39b:
return true;
case 0x1f39e ... 0x1f39f:
case 0x1f39e ... 0x1f3f0:
return true;
case 0x1f3a7:
case 0x1f3f3 ... 0x1f3f5:
return true;
case 0x1f3ac ... 0x1f3ae:
case 0x1f3f7 ... 0x1f4fd:
return true;
case 0x1f3c2:
case 0x1f4ff ... 0x1f53d:
return true;
case 0x1f3c4:
return true;
case 0x1f3c6:
return true;
case 0x1f3ca ... 0x1f3ce:
return true;
case 0x1f3d4 ... 0x1f3e0:
return true;
case 0x1f3ed:
return true;
case 0x1f3f3:
return true;
case 0x1f3f5:
return true;
case 0x1f3f7:
return true;
case 0x1f408:
return true;
case 0x1f415:
return true;
case 0x1f41f:
return true;
case 0x1f426:
return true;
case 0x1f43f:
return true;
case 0x1f441 ... 0x1f442:
return true;
case 0x1f446 ... 0x1f449:
return true;
case 0x1f44d ... 0x1f44e:
return true;
case 0x1f453:
return true;
case 0x1f46a:
return true;
case 0x1f47d:
return true;
case 0x1f4a3:
return true;
case 0x1f4b0:
return true;
case 0x1f4b3:
return true;
case 0x1f4bb:
return true;
case 0x1f4bf:
return true;
case 0x1f4cb:
return true;
case 0x1f4da:
return true;
case 0x1f4df:
return true;
case 0x1f4e4 ... 0x1f4e6:
return true;
case 0x1f4ea ... 0x1f4ed:
return true;
case 0x1f4f7:
return true;
case 0x1f4f9 ... 0x1f4fb:
return true;
case 0x1f4fd:
return true;
case 0x1f508:
return true;
case 0x1f50d:
return true;
case 0x1f512 ... 0x1f513:
return true;
case 0x1f549 ... 0x1f54a:
case 0x1f549 ... 0x1f54e:
return true;
case 0x1f550 ... 0x1f567:
return true;
case 0x1f56f ... 0x1f570:
return true;
case 0x1f573 ... 0x1f579:
case 0x1f573 ... 0x1f57a:
return true;
case 0x1f587:
return true;
@@ -3097,7 +3084,9 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x1f590:
return true;
case 0x1f5a5:
case 0x1f595 ... 0x1f596:
return true;
case 0x1f5a4 ... 0x1f5a5:
return true;
case 0x1f5a8:
return true;
@@ -3121,39 +3110,49 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x1f5f3:
return true;
case 0x1f5fa:
case 0x1f5fa ... 0x1f64f:
return true;
case 0x1f610:
case 0x1f680 ... 0x1f6c5:
return true;
case 0x1f687:
case 0x1f6cb ... 0x1f6d2:
return true;
case 0x1f68d:
return true;
case 0x1f691:
return true;
case 0x1f694:
return true;
case 0x1f698:
return true;
case 0x1f6ad:
return true;
case 0x1f6b2:
return true;
case 0x1f6b9 ... 0x1f6ba:
return true;
case 0x1f6bc:
return true;
case 0x1f6cb:
return true;
case 0x1f6cd ... 0x1f6cf:
case 0x1f6d5 ... 0x1f6d7:
return true;
case 0x1f6e0 ... 0x1f6e5:
return true;
case 0x1f6e9:
return true;
case 0x1f6eb ... 0x1f6ec:
return true;
case 0x1f6f0:
return true;
case 0x1f6f3:
case 0x1f6f3 ... 0x1f6fc:
return true;
case 0x1f7e0 ... 0x1f7eb:
return true;
case 0x1f90c ... 0x1f93a:
return true;
case 0x1f93c ... 0x1f945:
return true;
case 0x1f947 ... 0x1f978:
return true;
case 0x1f97a ... 0x1f9cb:
return true;
case 0x1f9cd ... 0x1f9ff:
return true;
case 0x1fa70 ... 0x1fa74:
return true;
case 0x1fa78 ... 0x1fa7a:
return true;
case 0x1fa80 ... 0x1fa86:
return true;
case 0x1fa90 ... 0x1faa8:
return true;
case 0x1fab0 ... 0x1fab6:
return true;
case 0x1fac0 ... 0x1fac2:
return true;
case 0x1fad0 ... 0x1fad6:
return true;
default: return false;
}

View File

@@ -10,8 +10,8 @@ from collections import deque
from enum import IntEnum
from itertools import chain
from typing import (
Any, Callable, Deque, Dict, List, Optional, Pattern, Sequence, Tuple,
Union
Any, Callable, Deque, Dict, Iterable, List, Optional, Pattern, Sequence,
Tuple, Union
)
from .child import ProcessDesc
@@ -25,14 +25,15 @@ from .fast_data_types import (
MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
STRIKETHROUGH, TINT_PROGRAM, Screen, add_window, cell_size_for_window,
compile_program, get_boss, get_clipboard_string, init_cell_program,
set_clipboard_string, set_titlebar_color, set_window_render_data,
update_window_title, update_window_visibility, viewport_for_window
pt_to_px, set_clipboard_string, set_titlebar_color, set_window_padding,
set_window_render_data, update_window_title, update_window_visibility,
viewport_for_window
)
from .keys import defines, extended_key_event, keyboard_mode_name
from .options_stub import Options
from .rgb import to_color
from .terminfo import get_capabilities
from .typing import ChildType, TabType, TypedDict
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
from .utils import (
color_as_int, get_primary_selection, load_shaders, open_cmd, open_url,
parse_color_set, read_shell_environment, sanitize_title,
@@ -77,6 +78,19 @@ DYNAMIC_COLOR_CODES = {
DYNAMIC_COLOR_CODES.update({k+100: v for k, v in DYNAMIC_COLOR_CODES.items()})
class Watcher:
def __call__(self, boss: BossType, window: 'Window', data: Dict[str, Any]) -> None:
pass
class Watchers:
def __init__(self) -> None:
self.on_resize: List[Watcher] = []
self.on_close: List[Watcher] = []
def calculate_gl_geometry(window_geometry: WindowGeometry, viewport_width: int, viewport_height: int, cell_width: int, cell_height: int) -> ScreenGeometry:
dx, dy = 2 * cell_width / viewport_width, 2 * cell_height / viewport_height
xmargin = window_geometry.left / viewport_width
@@ -172,6 +186,25 @@ def text_sanitizer(as_ansi: bool, add_wrap_markers: bool) -> Callable[[str], str
return remove_wrap_markers
class EdgeWidths:
left: Optional[float]
top: Optional[float]
right: Optional[float]
bottom: Optional[float]
def __init__(self, serialized: Optional[Dict[str, Optional[float]]] = None):
if serialized is not None:
self.left = serialized['left']
self.right = serialized['right']
self.top = serialized['top']
self.bottom = serialized['bottom']
else:
self.left = self.top = self.right = self.bottom = None
def serialize(self) -> Dict[str, Optional[float]]:
return {'left': self.left, 'right': self.right, 'top': self.top, 'bottom': self.bottom}
class Window:
def __init__(
@@ -181,8 +214,10 @@ class Window:
opts: Options,
args: CLIOptions,
override_title: Optional[str] = None,
copy_colors_from: Optional['Window'] = None
copy_colors_from: Optional['Window'] = None,
watchers: Optional[Watchers] = None
):
self.watchers = watchers or Watchers()
self.action_on_close: Optional[Callable] = None
self.action_on_removal: Optional[Callable] = None
self.current_marker_spec: Optional[Tuple[str, Union[str, Tuple[Tuple[int, str], ...]]]] = None
@@ -196,6 +231,8 @@ class Window:
self.title_stack: Deque[str] = deque(maxlen=10)
self.allow_remote_control = child.allow_remote_control
self.id = add_window(tab.os_window_id, tab.id, self.title)
self.margin = EdgeWidths()
self.padding = EdgeWidths()
if not self.id:
raise Exception('No tab with id: {} in OS Window: {} was found, or the window counter wrapped'.format(tab.id, tab.os_window_id))
self.tab_id = tab.id
@@ -214,11 +251,47 @@ class Window:
else:
setup_colors(self.screen, opts)
def on_dpi_change(self, font_sz: float) -> None:
self.update_effective_padding()
def change_tab(self, tab: TabType) -> None:
self.tab_id = tab.id
self.os_window_id = tab.os_window_id
self.tabref = weakref.ref(tab)
def effective_margin(self, edge: EdgeLiteral, is_single_window: bool = False) -> int:
q = getattr(self.margin, edge)
if q is not None:
return pt_to_px(q, self.os_window_id)
if is_single_window:
q = getattr(self.opts.single_window_margin_width, edge)
if q > -0.1:
return pt_to_px(q, self.os_window_id)
q = getattr(self.opts.window_margin_width, edge)
return pt_to_px(q, self.os_window_id)
def effective_padding(self, edge: EdgeLiteral) -> int:
q = getattr(self.padding, edge)
if q is not None:
return pt_to_px(q, self.os_window_id)
q = getattr(self.opts.window_padding_width, edge)
return pt_to_px(q, self.os_window_id)
def update_effective_padding(self) -> None:
set_window_padding(
self.os_window_id, self.tab_id, self.id,
self.effective_padding('left'), self.effective_padding('top'),
self.effective_padding('right'), self.effective_padding('bottom'))
def patch_edge_width(self, which: str, edge: EdgeLiteral, val: Optional[float]) -> None:
q = self.padding if which == 'padding' else self.margin
setattr(q, edge, val)
if q is self.padding:
self.update_effective_padding()
def effective_border(self) -> int:
return pt_to_px(self.opts.window_border_width, self.os_window_id)
@property
def title(self) -> str:
return self.override_title or self.child_title
@@ -239,6 +312,24 @@ class Window:
foreground_processes=self.child.foreground_processes
)
def serialize_state(self) -> Dict[str, Any]:
return {
'version': 1,
'id': self.id,
'child_title': self.child_title,
'override_title': self.override_title,
'default_title': self.default_title,
'title_stack': list(self.title_stack),
'allow_remote_control': self.allow_remote_control,
'overlay_window_id': self.overlay_window_id,
'overlay_for': self.overlay_for,
'cwd': self.child.current_cwd or self.child.cwd,
'env': self.child.environ,
'cmdline': self.child.cmdline,
'margin': self.margin.serialize(),
'padding': self.padding.serialize(),
}
@property
def current_colors(self) -> Dict:
return self.screen.color_profile.as_dict()
@@ -303,10 +394,12 @@ class Window:
if not self.pty_resized_once:
self.pty_resized_once = True
self.child.mark_terminal_ready()
self.call_watchers(self.watchers.on_resize, {'old_geometry': self.geometry, 'new_geometry': new_geometry})
else:
sg = self.update_position(new_geometry)
self.geometry = g = new_geometry
set_window_render_data(self.os_window_id, self.tab_id, self.id, window_idx, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen, *g[:4])
self.update_effective_padding()
def contains(self, x: int, y: int) -> bool:
g = self.geometry
@@ -536,7 +629,17 @@ class Window:
return ''.join((l.rstrip() or '\n') for l in lines)
return ''.join(lines)
def call_watchers(self, which: Iterable[Watcher], data: Dict[str, Any]) -> None:
boss = get_boss()
for w in which:
try:
w(boss, self, data)
except Exception:
import traceback
traceback.print_exc()
def destroy(self) -> None:
self.call_watchers(self.watchers.on_close, {})
self.destroyed = True
if hasattr(self, 'screen'):
# Remove cycles so that screen is de-allocated immediately

View File

@@ -359,6 +359,10 @@ class TestDataTypes(BaseTest):
self.ae(wcswidth('\033a\033[2mb'), 2)
self.ae(wcswidth('\u25b6\ufe0f'), 2)
self.ae(wcswidth('\U0001f610\ufe0e'), 1)
self.ae(wcswidth('\U0001f1e6a'), 3)
self.ae(wcswidth('\U0001F1E6a\U0001F1E8a'), 6)
self.ae(wcswidth('\U0001F1E6\U0001F1E8a'), 3)
self.ae(wcswidth('\U0001F1E6\U0001F1E8\U0001F1E6'), 4)
# Regional indicator symbols (unicode flags) are defined as having
# Emoji_Presentation so must have width 2
self.ae(tuple(map(w, '\U0001f1ee\U0001f1f3')), (2, 2))

View File

@@ -80,26 +80,25 @@ class TestParser(BaseTest):
km(modify_key_bytes(base_key, num).decode('ascii')[1:], key)
def test_encode_mouse_event(self):
PRESS, RELEASE, DRAG, MOVE = range(4)
NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL = range(4)
L, M, R = defines.GLFW_MOUSE_BUTTON_LEFT, defines.GLFW_MOUSE_BUTTON_MIDDLE, defines.GLFW_MOUSE_BUTTON_RIGHT
protocol = SGR_PROTOCOL
def enc(button=L, action=PRESS, mods=0, x=1, y=1):
def enc(button=L, action=defines.PRESS, mods=0, x=1, y=1):
return defines.test_encode_mouse(x, y, protocol, button, action, mods)
self.ae(enc(), '<0;1;1M')
self.ae(enc(action=RELEASE), '<0;1;1m')
self.ae(enc(action=MOVE), '<35;1;1M')
self.ae(enc(action=DRAG), '<32;1;1M')
self.ae(enc(action=defines.RELEASE), '<0;1;1m')
self.ae(enc(action=defines.MOVE), '<35;1;1M')
self.ae(enc(action=defines.DRAG), '<32;1;1M')
self.ae(enc(R), '<2;1;1M')
self.ae(enc(R, action=RELEASE), '<2;1;1m')
self.ae(enc(R, action=DRAG), '<34;1;1M')
self.ae(enc(R, action=defines.RELEASE), '<2;1;1m')
self.ae(enc(R, action=defines.DRAG), '<34;1;1M')
self.ae(enc(M), '<1;1;1M')
self.ae(enc(M, action=RELEASE), '<1;1;1m')
self.ae(enc(M, action=DRAG), '<33;1;1M')
self.ae(enc(M, action=defines.RELEASE), '<1;1;1m')
self.ae(enc(M, action=defines.DRAG), '<33;1;1M')
self.ae(enc(x=1234, y=5678), '<0;1234;5678M')
self.ae(enc(mods=defines.GLFW_MOD_SHIFT), '<4;1;1M')

View File

@@ -3,9 +3,9 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from kitty.config import defaults
from kitty.fast_data_types import pt_to_px
from kitty.layout import Horizontal, Stack, Tall, Grid, idx_for_id, Splits
from kitty.constants import WindowGeometry
from kitty.layout import Grid, Horizontal, Splits, Stack, Tall, idx_for_id
from kitty.window import EdgeWidths
from . import BaseTest
@@ -18,6 +18,17 @@ class Window:
self.overlay_window_id = overlay_window_id
self.is_visible_in_layout = True
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
self.padding = EdgeWidths()
self.margin = EdgeWidths()
def effective_border(self):
return 1
def effective_padding(self, edge):
return 1
def effective_margin(self, edge, is_single_window=False):
return 0 if is_single_window else 1
def set_visible_in_layout(self, idx, val):
self.is_visible_in_layout = bool(val)
@@ -29,8 +40,7 @@ class Window:
def create_layout(cls, opts=None, border_width=2):
if opts is None:
opts = defaults
mw, pw = map(pt_to_px, (opts.window_margin_width, opts.window_padding_width))
ans = cls(1, 1, mw, mw, pw, border_width)
ans = cls(1, 1)
ans.set_active_window_in_os_window = lambda idx: None
ans.swap_windows_in_os_window = lambda a, b: None
return ans

View File

@@ -456,12 +456,16 @@ class TestScreen(BaseTest):
self.ae(as_text(True), '\x1b[mababa\x1b[mbabab\n\x1b[mc\n\n')
def test_user_marking(self):
def cells(*a, y=0, mark=3):
return [(x, y, mark) for x in a]
s = self.create_screen()
s.draw('abaa')
s.carriage_return(), s.linefeed()
s.draw('xyxyx')
s.set_marker(marker_from_regex('a', 3))
self.ae(s.marked_cells(), [(0, 0, 3), (2, 0, 3), (3, 0, 3)])
self.ae(s.marked_cells(), cells(0, 2, 3))
s.set_marker()
self.ae(s.marked_cells(), [])
@@ -488,3 +492,19 @@ class TestScreen(BaseTest):
self.assertTrue(s.scroll_to_next_mark(0, False))
self.ae(s.scrolled_by, 10 - i - 1)
self.ae(s.scrolled_by, 0)
s = self.create_screen()
s.draw('🐈ab')
s.set_marker(marker_from_regex('🐈', 3))
self.ae(s.marked_cells(), cells(0, 1))
s.set_marker(marker_from_regex('🐈a', 3))
self.ae(s.marked_cells(), cells(0, 1, 2))
s.set_marker(marker_from_regex('a', 3))
self.ae(s.marked_cells(), cells(2))
s = self.create_screen(cols=20)
s.tab()
s.draw('ab')
s.set_marker(marker_from_regex('a', 3))
self.ae(s.marked_cells(), cells(8))
s.set_marker(marker_from_regex('\t', 3))
self.ae(s.marked_cells(), cells(*range(8)))

View File

@@ -31,6 +31,7 @@
#define KITTY_LIB_DIR_NAME "lib"
#endif
#ifndef __FreeBSD__
static inline bool
safe_realpath(const char* src, char *buf, size_t buf_sz) {
char* ans = realpath(src, NULL);
@@ -39,6 +40,7 @@ safe_realpath(const char* src, char *buf, size_t buf_sz) {
free(ans);
return true;
}
#endif
static inline void
set_bundle_exe_dir(const wchar_t *exe_dir) {

View File

@@ -141,9 +141,12 @@ def cc_version() -> Tuple[str, Tuple[int, int]]:
else:
cc = 'cc'
raw = subprocess.check_output([cc, '-dumpversion']).decode('utf-8')
ver_ = raw.split('.')[:2]
ver_ = raw.strip().split('.')[:2]
try:
ver = int(ver_[0]), int(ver_[1])
if len(ver_) == 1:
ver = int(ver_[0]), 0
else:
ver = int(ver_[0]), int(ver_[1])
except Exception:
ver = (0, 0)
return cc, ver

View File

@@ -215,6 +215,7 @@ xterm-kitty|KovIdTTY,
rmkx=\E[?1l,
rmso=\E[27m,
rmul=\E[24m,
rmxx=\E[29m,
rs1=\E]\E\\\Ec,
sc=\E7,
setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
@@ -231,6 +232,7 @@ xterm-kitty|KovIdTTY,
smkx=\E[?1h,
smso=\E[7m,
smul=\E[4m,
smxx=\E[9m,
tbc=\E[3g,
tsl=\E]2;,
vpa=\E[%i%p1%dd,

Binary file not shown.