Compare commits
285 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97af84a063 | ||
|
|
18faee6250 | ||
|
|
fd90a3ddc3 | ||
|
|
a08931d84d | ||
|
|
2f0b6e24c9 | ||
|
|
33c5fc0fb6 | ||
|
|
71adb2dcf8 | ||
|
|
1ae324691d | ||
|
|
689d059517 | ||
|
|
18f0ab9e02 | ||
|
|
088ab5f137 | ||
|
|
3dd83975ba | ||
|
|
a796a6cbef | ||
|
|
4766d7de82 | ||
|
|
4a3686c16c | ||
|
|
b49105495c | ||
|
|
ca2eda2c5b | ||
|
|
4fc7c4aafc | ||
|
|
0981828493 | ||
|
|
6bc4f18a59 | ||
|
|
79532a315c | ||
|
|
7472a992ed | ||
|
|
e40616a0db | ||
|
|
dd8b97a86c | ||
|
|
5eff3897de | ||
|
|
530cf2eb14 | ||
|
|
2baa34beb8 | ||
|
|
87e2f7f86d | ||
|
|
e142083d53 | ||
|
|
2e850a0d0d | ||
|
|
aec9c31bca | ||
|
|
598a147500 | ||
|
|
ba201c4c92 | ||
|
|
5bc7cfaa43 | ||
|
|
2804e1ff81 | ||
|
|
ae27d36902 | ||
|
|
08182fbe4d | ||
|
|
778474f436 | ||
|
|
c6698ce305 | ||
|
|
21bc215313 | ||
|
|
88a9cc42d2 | ||
|
|
28f33a67cf | ||
|
|
6742fabfba | ||
|
|
b3806f4533 | ||
|
|
6d5fb4ccc6 | ||
|
|
2487f18f24 | ||
|
|
f59afff1d1 | ||
|
|
b92f95b092 | ||
|
|
d68ae01448 | ||
|
|
87eda834d3 | ||
|
|
7bf0afa621 | ||
|
|
e3e02c7271 | ||
|
|
24e17cb7d8 | ||
|
|
769998adca | ||
|
|
9b5d88e92d | ||
|
|
6d7bebee3a | ||
|
|
1c38654321 | ||
|
|
da7a78691f | ||
|
|
1eaf7083ef | ||
|
|
79a6fcdc5c | ||
|
|
90bcbbf426 | ||
|
|
41049e2a40 | ||
|
|
c9b8510e9c | ||
|
|
d854fc1cd0 | ||
|
|
5f7bcae072 | ||
|
|
652eec3033 | ||
|
|
a9928ec98a | ||
|
|
e1dfb608ca | ||
|
|
2cee3a8809 | ||
|
|
804f72b31a | ||
|
|
e106dcc83b | ||
|
|
9347be639b | ||
|
|
ffc0c42a63 | ||
|
|
f5e855b3ad | ||
|
|
9af4dc4bff | ||
|
|
6f6180c34a | ||
|
|
6ddd751713 | ||
|
|
2c72b06012 | ||
|
|
88854c1aa4 | ||
|
|
5ad704469f | ||
|
|
c140e17a6f | ||
|
|
4fc733a9c8 | ||
|
|
c5c59e4c55 | ||
|
|
e0edef6719 | ||
|
|
0f46ce49e2 | ||
|
|
f9fd39b8c1 | ||
|
|
bd75157428 | ||
|
|
f987f3ba77 | ||
|
|
1c16806efd | ||
|
|
29155fef23 | ||
|
|
c05cbed5b2 | ||
|
|
4f85d4db71 | ||
|
|
468468ab9f | ||
|
|
1b4a6da577 | ||
|
|
aed7a197c7 | ||
|
|
c257b7ecfd | ||
|
|
d6072f6218 | ||
|
|
4ea72a2892 | ||
|
|
89b07a1c6b | ||
|
|
db0cd61807 | ||
|
|
6feb906953 | ||
|
|
7cbf8c63bc | ||
|
|
fae51d4e00 | ||
|
|
c510584afd | ||
|
|
738878c2ff | ||
|
|
d5682fe49a | ||
|
|
d8dd22048a | ||
|
|
737d8b96b6 | ||
|
|
c8627c9713 | ||
|
|
65fa7da24a | ||
|
|
c4c6ef799f | ||
|
|
0d18b91895 | ||
|
|
42089aaadc | ||
|
|
432d0f3318 | ||
|
|
4ade6587e8 | ||
|
|
1724f11429 | ||
|
|
75d66d1048 | ||
|
|
feccef67b9 | ||
|
|
a7d30a199d | ||
|
|
37bbfb11d3 | ||
|
|
79c80b4043 | ||
|
|
18a1402ae2 | ||
|
|
5d639b91b0 | ||
|
|
080c09d22e | ||
|
|
00c2670039 | ||
|
|
f113184165 | ||
|
|
d69f8af107 | ||
|
|
ad2bd1bf63 | ||
|
|
059786ab66 | ||
|
|
72dfa5e5e6 | ||
|
|
d84da6b7b8 | ||
|
|
6aa82d82ad | ||
|
|
a649eb2a48 | ||
|
|
83932c8099 | ||
|
|
95e36cece1 | ||
|
|
7075a7f1c0 | ||
|
|
3093610d26 | ||
|
|
86eed7f895 | ||
|
|
5dc14c5861 | ||
|
|
cab87f6fb2 | ||
|
|
fb3acd1f4a | ||
|
|
b247cef85c | ||
|
|
70445f33e9 | ||
|
|
cadb4f1389 | ||
|
|
bf75a4de61 | ||
|
|
400ab584ac | ||
|
|
9edad62144 | ||
|
|
e94205098f | ||
|
|
35ab7eb472 | ||
|
|
18619b14ac | ||
|
|
97e0b9b65a | ||
|
|
5175d9f4a6 | ||
|
|
a5aed0f4ab | ||
|
|
14d693a8ff | ||
|
|
645963cc24 | ||
|
|
20751c2807 | ||
|
|
dda886b97c | ||
|
|
b796ceecdd | ||
|
|
4def89ebb5 | ||
|
|
bd1c1839dd | ||
|
|
8739f698f9 | ||
|
|
b8c8482f20 | ||
|
|
4348ca8cc5 | ||
|
|
d1aa59080f | ||
|
|
bf0ffa80be | ||
|
|
2a89a0c87e | ||
|
|
de84489a20 | ||
|
|
40901778ad | ||
|
|
c1febea659 | ||
|
|
b42f4ac288 | ||
|
|
fae57a5868 | ||
|
|
7a02d09508 | ||
|
|
8ec6fe960d | ||
|
|
61a77e19ef | ||
|
|
82e88b54c7 | ||
|
|
ab8975f4da | ||
|
|
5e7fd21c8d | ||
|
|
30cd9b2ae4 | ||
|
|
4f7d12f3fd | ||
|
|
62da158823 | ||
|
|
2d19b87208 | ||
|
|
54ca775006 | ||
|
|
a64a0d64cb | ||
|
|
6634dc81fe | ||
|
|
a2d5eef398 | ||
|
|
8184ba246a | ||
|
|
54ad1d103a | ||
|
|
d92f8a90ef | ||
|
|
d33c287344 | ||
|
|
782eebf976 | ||
|
|
0fbf75b95a | ||
|
|
5781facf4b | ||
|
|
b710ffc403 | ||
|
|
7d8edccb04 | ||
|
|
3750d70173 | ||
|
|
025e0bb546 | ||
|
|
033367de91 | ||
|
|
f93b605b75 | ||
|
|
7b4141b0c8 | ||
|
|
90acef72dc | ||
|
|
43f200be77 | ||
|
|
2fbb167803 | ||
|
|
2b396262f0 | ||
|
|
74deca52b9 | ||
|
|
058d956464 | ||
|
|
0f38dc1bd1 | ||
|
|
576b03c047 | ||
|
|
59cb1ad1e0 | ||
|
|
f77acdce14 | ||
|
|
43a98a1df1 | ||
|
|
d1cf771c14 | ||
|
|
10e5fcc375 | ||
|
|
2112932d04 | ||
|
|
be505ce6e0 | ||
|
|
4d6472128c | ||
|
|
26b7b1ec63 | ||
|
|
9a8f0a9b5a | ||
|
|
16dbbf3a1e | ||
|
|
8492d0a264 | ||
|
|
d8f8f1f590 | ||
|
|
b762e81da0 | ||
|
|
8b3b68ea69 | ||
|
|
28525766a5 | ||
|
|
d8fc23c38f | ||
|
|
8cae70fa36 | ||
|
|
7f2b98fad7 | ||
|
|
2b6dde2ac5 | ||
|
|
0c254fa7c7 | ||
|
|
fe7375becc | ||
|
|
b6ee88056e | ||
|
|
96f691ad64 | ||
|
|
8cf3380a77 | ||
|
|
e8d7e3e98b | ||
|
|
8af0a8476f | ||
|
|
2de4248f76 | ||
|
|
8c73ab6afb | ||
|
|
ee9bd77dc4 | ||
|
|
81f31ca215 | ||
|
|
51eff372f8 | ||
|
|
087376b44d | ||
|
|
e49047ef7d | ||
|
|
c117f36009 | ||
|
|
5ec83ee759 | ||
|
|
410165c14d | ||
|
|
973ef9fcda | ||
|
|
396bfb8540 | ||
|
|
aaf597bffd | ||
|
|
63c4f64bf5 | ||
|
|
5745dc34f7 | ||
|
|
114b8cff93 | ||
|
|
261571bb69 | ||
|
|
497e2c9163 | ||
|
|
ac01ffaa54 | ||
|
|
eb9fe06d10 | ||
|
|
506f2f7db7 | ||
|
|
f476c56f91 | ||
|
|
474688888d | ||
|
|
7ddbb613fe | ||
|
|
adc1c06089 | ||
|
|
1f8631cc99 | ||
|
|
f9e4e71285 | ||
|
|
5529194c8d | ||
|
|
5b008163af | ||
|
|
802da7087c | ||
|
|
53ab7617ca | ||
|
|
4369255a25 | ||
|
|
b709ee6842 | ||
|
|
4a8f9efe79 | ||
|
|
19d36fbe69 | ||
|
|
d37e73db54 | ||
|
|
a8f6615380 | ||
|
|
97fe38fbbc | ||
|
|
3378175450 | ||
|
|
9f60d2aea3 | ||
|
|
46d49e18c0 | ||
|
|
43fc335be2 | ||
|
|
f68339c571 | ||
|
|
0d68b7078c | ||
|
|
2e71dde780 | ||
|
|
51ddd584d6 | ||
|
|
4b090259cd | ||
|
|
f3b9ff5f9f | ||
|
|
4ec1a8d9c3 | ||
|
|
aadab38487 | ||
|
|
53275c9137 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -3,11 +3,11 @@ kitty/emoji.h linguist-generated=true
|
||||
kitty/keys.h linguist-generated=true
|
||||
kitty/charsets.c linguist-generated=true
|
||||
kitty/key_encoding.py linguist-generated=true
|
||||
kitty/unicode-data.c
|
||||
kitty/unicode-data.c linguist-generated=true
|
||||
kitty/rgb.py linguist-generated=true
|
||||
kitty/gl-wrapper.* linguist-generated=true
|
||||
kitty/khrplatform.h linguist-generated=true
|
||||
kitty/glfw-wrapper.* linguist-generated=true
|
||||
kitty/parse-graphics-command.h linguist-generated=true
|
||||
glfw/*.c linguist-vendored=true
|
||||
glfw/*.h linguist-vendored=true
|
||||
kittens/unicode_input/names.h linguist-generated=true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
= kitty - the fast, featureful, GPU based, terminal emulator
|
||||
|
||||
See https://sw.kovidgoyal.net/kitty
|
||||
See https://sw.kovidgoyal.net/kitty/
|
||||
|
||||
image:https://circleci.com/gh/kovidgoyal/kitty.svg?style=svg["Build status", link="https://circleci.com/gh/kovidgoyal/kitty"]
|
||||
|
||||
@@ -92,8 +92,10 @@ def setup_openssl_environment():
|
||||
if 'SSL_CERT_FILE' not in os.environ and 'SSL_CERT_DIR' not in os.environ:
|
||||
if os.access('/etc/pki/tls/certs/ca-bundle.crt', os.R_OK):
|
||||
os.environ['SSL_CERT_FILE'] = '/etc/pki/tls/certs/ca-bundle.crt'
|
||||
sys.kitty_ssl_env_var = 'SSL_CERT_FILE'
|
||||
elif os.path.isdir('/etc/ssl/certs'):
|
||||
os.environ['SSL_CERT_DIR'] = '/etc/ssl/certs'
|
||||
sys.kitty_ssl_env_var = 'SSL_CERT_DIR'
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -1,2 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
cloc --exclude-list-file <(echo -e 'kitty/wcwidth-std.h\nkitty/glfw.c\nkitty/keys.h\nkitty/charsets.c\nkitty/unicode-data.c\nkitty/key_encoding.py\nkitty/rgb.py\nkitty/gl.h\nkitty/gl-wrapper.h\nkitty/gl-wrapper.c\nkitty/khrplatform.h\nkitty/glfw-wrapper.h\nkitty/glfw-wrapper.c\nkitty/emoji.h\nkittens/unicode_input/names.h') kitty kittens
|
||||
#!/usr/bin/env python
|
||||
|
||||
import subprocess
|
||||
|
||||
files_to_exclude = '''\
|
||||
kitty/wcwidth-std.h
|
||||
kitty/glfw.c
|
||||
kitty/keys.h
|
||||
kitty/charsets.c
|
||||
kitty/unicode-data.c
|
||||
kitty/key_encoding.py
|
||||
kitty/rgb.py
|
||||
kitty/gl.h
|
||||
kitty/gl-wrapper.h
|
||||
kitty/gl-wrapper.c
|
||||
kitty/glfw-wrapper.h
|
||||
kitty/glfw-wrapper.c
|
||||
kitty/emoji.h
|
||||
kittens/unicode_input/names.h
|
||||
kitty/parse-graphics-command.h
|
||||
'''
|
||||
|
||||
p = subprocess.Popen([
|
||||
'cloc', '--exclude-list-file', '/dev/stdin', 'kitty', 'kittens'
|
||||
], stdin=subprocess.PIPE)
|
||||
p.communicate(files_to_exclude.encode('utf-8'))
|
||||
raise SystemExit(p.wait())
|
||||
|
||||
2
docs/_static/custom.css
vendored
2
docs/_static/custom.css
vendored
@@ -78,6 +78,8 @@ body div.document {
|
||||
div.sphinxsidebar {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#sidebartoc li {
|
||||
|
||||
@@ -29,7 +29,7 @@ Build-time dependencies:
|
||||
* gcc or clang
|
||||
* pkg-config
|
||||
* For building on Linux in addition to the above dependencies you might also need to install the ``-dev`` packages for:
|
||||
``libdbus-1-dev``, ``libxcursor-dev``, ``libxrandr-dev``, ``libxi-dev``, ``libxinerama-dev``, ``libgl1-mesa-dev``, ``libxkbcommon-x11-dev``, ``libfontconfig-dev``, ``libcanberra-dev`` and ``libpython-dev``.
|
||||
``libdbus-1-dev``, ``libxcursor-dev``, ``libxrandr-dev``, ``libxi-dev``, ``libxinerama-dev``, ``libgl1-mesa-dev``, ``libxkbcommon-x11-dev``, ``libfontconfig-dev``, and ``libpython-dev``,
|
||||
if they are not already installed by your distro.
|
||||
|
||||
Install and run from source
|
||||
|
||||
@@ -4,6 +4,81 @@ Changelog
|
||||
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||
To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
|
||||
0.15.1 [2019-12-21]
|
||||
--------------------
|
||||
|
||||
- Fix a crash/incorrect rendering when detaching a window in some circumstances
|
||||
(:iss:`2173`)
|
||||
|
||||
- hints kitten: Add an option :option:`kitty +kitten hints --ascending` to
|
||||
control if the hints numbers increase or decrease from top to bottom
|
||||
|
||||
- Fix :opt:`background_opacity` incorrectly applying to selected text and
|
||||
reverse video text (:iss:`2177`)
|
||||
|
||||
- Add a new option :opt:`tab_bar_background` to specify a different color
|
||||
for the tab bar (:iss:`2198`)
|
||||
|
||||
- Add a new option :opt:`active_tab_title_template` to specify a different
|
||||
template for active tab titles (:iss:`2198`)
|
||||
|
||||
- Fix lines at the edge of the window at certain windows sizes when drawing
|
||||
images on a transparent window (:iss:`2079`)
|
||||
|
||||
- Fix window not being rendered for the first time until some input has been
|
||||
received from child process (:iss:`2216`)
|
||||
|
||||
|
||||
0.15.0 [2019-11-27]
|
||||
--------------------
|
||||
|
||||
- Add a new action :ref:`detach_window <detach_window>` that can be used to move the current
|
||||
window into a different tab (:iss:`1310`)
|
||||
|
||||
- Add a new action :doc:`launch <launch>` that unifies launching of processes
|
||||
in new kitty windows/tabs.
|
||||
|
||||
- Add a new style ``powerline`` for tab bar rendering, see :opt:`tab_bar_style` (:pull:`2021`)
|
||||
|
||||
- Allow changing colors by mapping a keyboard shortcut to read a kitty config
|
||||
file with color definitions. See the :doc:`FAQ <faq>` for details
|
||||
(:iss:`2083`)
|
||||
|
||||
- hints kitten: Allow completely customizing the matching and actions performed
|
||||
by the kitten using your own script (:iss:`2124`)
|
||||
|
||||
- Wayland: Fix key repeat not being stopped when focus leaves window. This is
|
||||
expected behavior on Wayland, apparently (:iss:`2014`)
|
||||
|
||||
- When drawing unicode symbols that are followed by spaces, use multiple cells
|
||||
to avoid resized or cut-off glyphs (:iss:`1452`)
|
||||
|
||||
- diff kitten: Allow diffing remote files easily via ssh (:iss:`727`)
|
||||
|
||||
- unicode input kitten: Add an option :option:`kitty +kitten unicode_input
|
||||
--emoji-variation` to control the presentation variant of selected emojis
|
||||
(:iss:`2139`)
|
||||
|
||||
- Add specialised rendering for a few more box powerline and unicode symbols
|
||||
(:pull:`2074` and :pull:`2021`)
|
||||
|
||||
- Add a new socket only mode for :opt:`allow_remote_control`. This makes
|
||||
it possible for programs running on the local machine to control kitty
|
||||
but not programs running over ssh.
|
||||
|
||||
- hints kitten: Allow using named groups in the regular expression. The named
|
||||
groups are passed to the invoked program for further processing.
|
||||
|
||||
- Fix a regression in 0.14.5 that caused rendering of private use glyphs
|
||||
with and without spaces to be identical (:iss:`2117`)
|
||||
|
||||
- Wayland: Fix incorrect scale used when first creating an OS window
|
||||
(:iss:`2133`)
|
||||
|
||||
- macOS: Disable mouse hiding by default as getting it to work robustly
|
||||
on Cocoa is too much effort (:iss:`2158`)
|
||||
|
||||
|
||||
0.14.6 [2019-09-25]
|
||||
---------------------
|
||||
|
||||
|
||||
12
docs/conf.py
12
docs/conf.py
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:fileencoding=utf-8
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
@@ -243,7 +243,17 @@ def add_html_context(app, pagename, templatename, context, *args):
|
||||
|
||||
# CLI docs {{{
|
||||
def write_cli_docs(all_kitten_names):
|
||||
from kitty.launch import options_spec as launch_options_spec
|
||||
from kitty.cli import option_spec_as_rst
|
||||
with open('generated/launch.rst', 'w') as f:
|
||||
f.write(option_spec_as_rst(
|
||||
appname='launch', ospec=launch_options_spec, heading_char='_',
|
||||
message='''\
|
||||
Launch an arbitrary program in a new kitty window/tab. Note that
|
||||
if you specify a program-to-run you can use the special placeholder
|
||||
:code:`@selection` which will be replaced by the current selection.
|
||||
'''
|
||||
))
|
||||
with open('generated/cli-kitty.rst', 'w') as f:
|
||||
f.write(option_spec_as_rst(appname='kitty').replace(
|
||||
'kitty --to', 'kitty @ --to'))
|
||||
|
||||
14
docs/faq.rst
14
docs/faq.rst
@@ -109,8 +109,13 @@ How do I change the colors in a running kitty instance?
|
||||
|
||||
You can either use the
|
||||
`OSC terminal escape codes <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands>`_
|
||||
to set colors or you can enable :doc:`remote control <remote-control>`
|
||||
for |kitty| and use :ref:`at_set-colors`.
|
||||
to set colors or you can define keyboard shortcuts to set colors, for example::
|
||||
|
||||
map f1 set_colors --configured /path/to/some/config/file/colors.conf
|
||||
|
||||
Or you can enable :doc:`remote control <remote-control>` for |kitty| and use :ref:`at_set-colors`.
|
||||
The shortcut mapping technique has the same syntax as the remote control
|
||||
command, for details, see :ref:`at_set-colors`.
|
||||
|
||||
A list of pre-made color themes for kitty is available at:
|
||||
`kitty-themes <https://github.com/dexpota/kitty-themes>`_
|
||||
@@ -184,6 +189,11 @@ following :file:`~/.config/fontconfig/fonts.conf`::
|
||||
</match>
|
||||
</fontconfig>
|
||||
|
||||
After creating (or modifying) this file, you may need to run the following
|
||||
command to rebuild your fontconfig cache::
|
||||
|
||||
fc-cache -r
|
||||
|
||||
Then, the font will be available in ``kitty list-fonts``.
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ See the :doc:`binary install instructions </binary>`. You can also
|
||||
You can also use your favorite package manager to install the |kitty| package.
|
||||
|kitty| packages are available for:
|
||||
`macOS with Homebrew (Cask) <https://formulae.brew.sh/cask/kitty>`_,
|
||||
`macOS and Linux with Nix <https://nixos.org/nixos/packages.html#kitty>`_,
|
||||
`macOS and Linux with Nix <https://nixos.org/nixos/packages.html?channel=nixpkgs-unstable&query=kitty>`_,
|
||||
`Ubuntu <https://launchpad.net/ubuntu/+source/kitty>`_,
|
||||
`Debian <https://packages.debian.org/buster/kitty>`_,
|
||||
`openSUSE <https://build.opensuse.org/package/show/X11:terminals/kitty>`_,
|
||||
@@ -173,6 +173,19 @@ You can also define a shortcut to switch to the previously active window::
|
||||
``nth_window`` will focus the nth window for positive numbers and the
|
||||
previously active windows for negative numbers.
|
||||
|
||||
.. _detach_window:
|
||||
|
||||
You can define shortcuts to detach the current window and
|
||||
move it to another tab or another OS window::
|
||||
|
||||
map ctrl+f2 detach_window # moves the window into a new OS window
|
||||
map ctrl+f3 detach_window new-tab # moves the window into a new Tab
|
||||
map ctrl+f4 detach_window ask # asks which tab to move the window into
|
||||
|
||||
Similarly, you can detach the current tab, with::
|
||||
|
||||
map ctrl+f2 detach_tab # moves the tab into a new OS window
|
||||
map ctrl+f4 detach_tab ask # asks which OS Window to move the tab into
|
||||
|
||||
Other keyboard shortcuts
|
||||
----------------------------------
|
||||
@@ -402,7 +415,7 @@ comfortably within the pager.
|
||||
Additionally, you can pipe the contents of the scrollback buffer to an
|
||||
arbitrary, command running in a new window, tab or overlay, for example::
|
||||
|
||||
map f1 pipe @ansi window less +G -R
|
||||
map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting less +G -R
|
||||
|
||||
Would open the scrollback buffer in a new window when you press the :kbd:`F1`
|
||||
key. See :sc:`show_scrollback` for details.
|
||||
@@ -478,7 +491,8 @@ completions happens after the call to :file:`compinit`.
|
||||
fish
|
||||
~~~~~~~~
|
||||
|
||||
Add the following to your :file:`~/.config/fish/config.fish`
|
||||
For versions of fish earlier than 3.0.0, add the following to your
|
||||
:file:`~/.config/fish/config.fish`. Later versions source completions by default.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
|
||||
@@ -128,7 +128,27 @@ layout, by simply adding the line::
|
||||
boss.toggle_fullscreen()
|
||||
|
||||
|
||||
to the ``handle_result()`` function, above.
|
||||
To the ``handle_result()`` function, above.
|
||||
|
||||
|
||||
Debugging kittens
|
||||
--------------------
|
||||
|
||||
The part of the kitten that runs in ``main()`` is just a normal program and
|
||||
the output of print statements will be visible in the kitten window. Or
|
||||
alternately, you can use::
|
||||
|
||||
from kittens.tui.loop import debug
|
||||
debug('whatever')
|
||||
|
||||
The ``debug()`` function is just like ``print()`` except that the output
|
||||
will appear in the ``STDOUT`` of the kitty process inside which the kitten is
|
||||
running.
|
||||
|
||||
The ``handle_result()`` part of the kitten runs inside the kitty process.
|
||||
The output of print statements will go to the ``STDOUT`` of the kitty process.
|
||||
So if you run kitty from another kitty instance, the output will be visible
|
||||
in the first kitty instance.
|
||||
|
||||
.. _external_kittens:
|
||||
|
||||
|
||||
@@ -23,6 +23,56 @@ options and modes of operation, see below. You can use these options to
|
||||
create mappings in :file:`kitty.conf` to select various different text
|
||||
snippets. See :sc:`insert_selected_path` for examples.
|
||||
|
||||
Completely customizing the matching and actions of the kitten
|
||||
---------------------------------------------------------------
|
||||
|
||||
The hints kitten supports writing simple python scripts that can be used to
|
||||
completely customize how it finds matches and what happens when a match is
|
||||
selected. This allows the hints kitten to provide the user interface, while
|
||||
you can provide the logic for finding matches and performing actions on them.
|
||||
This is best illustrated with an example. Create the file
|
||||
:file:`custom-hints.py` in the kitty config directory with the following
|
||||
contents:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import re
|
||||
|
||||
def mark(text, args, Mark, extra_cli_args, *a):
|
||||
# This function is responsible for finding all
|
||||
# matching text. extra_cli_args are any extra arguments
|
||||
# passed on the command line when invoking the kitten.
|
||||
# We mark all individual word for potential selection
|
||||
for idx, m in enumerate(re.finditer(r'\w+', text)):
|
||||
start, end = m.span()
|
||||
mark_text = text[start:end].replace('\n', '').replace('\0', '')
|
||||
# The empty dictionary below will be available as groupdicts
|
||||
# in handle_result() and can contain arbitrary data.
|
||||
yield Mark(idx, start, end, mark_text, {})
|
||||
|
||||
|
||||
def handle_result(args, data, target_window_id, boss, extra_cli_args, *a):
|
||||
# This function is responsible for performing some
|
||||
# action on the selected text.
|
||||
# matches is a list of the selected entries and groupdicts contains
|
||||
# the arbitrary data associated with each entry in mark() above
|
||||
matches, groupdicts = [], []
|
||||
for m, g in zip(data['match'], data['groupdicts']):
|
||||
if m:
|
||||
matches.append(m), groupdicts.append(g)
|
||||
for word, match_data in zip(matches, groupdicts):
|
||||
# Lookup the word in a dictionary, the open_url function
|
||||
# will open the provided url in the system browser
|
||||
boss.open_url(f'https://www.google.com/search?q=define:{word}')
|
||||
|
||||
Now run kitty with::
|
||||
|
||||
kitty -o 'map f1 kitten hints --customize-processing custom-hints.py'
|
||||
|
||||
When you press the :kbd:`F1` key you will be able to select a word to
|
||||
look it up in the Google dictionary.
|
||||
|
||||
|
||||
Command Line Interface
|
||||
-------------------------
|
||||
|
||||
|
||||
@@ -22,3 +22,8 @@ In :guilabel:`Name` mode you instead type words from the character name and use
|
||||
keys/tab to select the character from the displayed matches. You can also type
|
||||
a leading period and the index for the match if you don't like to use arrow
|
||||
keys.
|
||||
|
||||
Command Line Interface
|
||||
-------------------------
|
||||
|
||||
.. include:: ../generated/cli-kitten-unicode_input.rst
|
||||
|
||||
56
docs/launch.rst
Normal file
56
docs/launch.rst
Normal file
@@ -0,0 +1,56 @@
|
||||
Launching programs in new windows/tabs
|
||||
========================================
|
||||
|
||||
.. program:: launch
|
||||
|
||||
|
||||
|kitty| has a :code:`launch` action that can be used to run arbitrary programs
|
||||
in news windows/tabs. It can be mapped to user defined shortcuts in kitty.conf.
|
||||
It is very powerful and allows sending the contents of
|
||||
the current window to the launched program, as well as many other options.
|
||||
|
||||
In the simplest form, you can use it to open a new kitty window running the
|
||||
shell, as shown below::
|
||||
|
||||
map f1 launch
|
||||
|
||||
To run a different program simply pass the command line as arguments to
|
||||
launch::
|
||||
|
||||
map f1 launch vim path/to/some/file
|
||||
|
||||
|
||||
To open a new window with the same working directory as the currently
|
||||
active window::
|
||||
|
||||
map f1 launch --cwd=current
|
||||
|
||||
To open the new window in a new tab::
|
||||
|
||||
map f1 launch --type=tab
|
||||
|
||||
To pass the contents of the current screen and scrollback to the started process::
|
||||
|
||||
map f1 launch --stdin-source=@screen_scrollback less
|
||||
|
||||
There are many more powerful options, refer to the complete list below.
|
||||
|
||||
The piping environment
|
||||
--------------------------
|
||||
|
||||
When using :option:`launch --stdin-source`, the program to which the data is
|
||||
piped has a special environment variable declared, ``KITTY_PIPE_DATA`` whose
|
||||
contents are::
|
||||
|
||||
KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}
|
||||
|
||||
where ``scrolled_by`` is the number of lines kitty is currently scrolled by,
|
||||
``cursor_(x|y)`` is the position of the cursor on the screen with ``(1,1)``
|
||||
being the top left corner and ``{lines},{columns}`` being the number of rows
|
||||
and columns of the screen.
|
||||
|
||||
|
||||
Syntax reference
|
||||
------------------
|
||||
|
||||
.. include:: /generated/launch.rst
|
||||
@@ -1,6 +1,9 @@
|
||||
Working with the screen and history buffer contents
|
||||
======================================================
|
||||
|
||||
.. warning::
|
||||
The pipe action has been deprecated in favor of the
|
||||
:doc:`launch <launch>` action which is more powerful.
|
||||
|
||||
You can pipe the contents of the current screen and history buffer as
|
||||
:file:`STDIN` to an arbitrary program using the ``pipe`` function. The program
|
||||
@@ -56,6 +59,9 @@ Input placeholders
|
||||
|
||||
There are various different kinds of placeholders
|
||||
|
||||
``@selection``
|
||||
Plain text, currently selected text
|
||||
|
||||
``@text``
|
||||
Plain text, current screen + scrollback buffer
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ If you do not want to allow all programs running in |kitty| to control it, you c
|
||||
enable remote control for only some |kitty| windows. Simply create a shortcut
|
||||
such as::
|
||||
|
||||
map ctrl+k new_window @ some_program
|
||||
map ctrl+k launch --allow-remote-control some_program
|
||||
|
||||
Then programs running in windows created with that shortcut can use ``kitty @``
|
||||
to control kitty. Note that any program with the right level of permissions can
|
||||
|
||||
@@ -43,6 +43,7 @@ def get_data(fname, folder='UCD'):
|
||||
|
||||
# Map of class names to set of codepoints in class
|
||||
class_maps = {}
|
||||
all_symbols = set()
|
||||
name_map = {}
|
||||
word_search_map = defaultdict(set)
|
||||
zwj = 0x200d
|
||||
@@ -89,6 +90,8 @@ def parse_ucd():
|
||||
not_assigned.discard(codepoint)
|
||||
if category.startswith('M'):
|
||||
marks.add(codepoint)
|
||||
elif category.startswith('S'):
|
||||
all_symbols.add(codepoint)
|
||||
|
||||
# Some common synonyms
|
||||
word_search_map['bee'] |= word_search_map['honeybee']
|
||||
@@ -194,6 +197,7 @@ def gen_emoji():
|
||||
p('\t\tdefault: return false;')
|
||||
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'])):
|
||||
@@ -203,6 +207,15 @@ def gen_emoji():
|
||||
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)):
|
||||
write_case(spec, p)
|
||||
p('\t\t\treturn true;')
|
||||
p('\t\tdefault: return false;')
|
||||
p('\t}')
|
||||
p('\treturn false;\n}')
|
||||
|
||||
|
||||
def category_test(name, p, classes, comment, static=False, extra_chars=frozenset(), exclude=frozenset()):
|
||||
static = 'static inline ' if static else ''
|
||||
|
||||
53
glfw/backend_utils.c
vendored
53
glfw/backend_utils.c
vendored
@@ -22,19 +22,6 @@
|
||||
#define ppoll pollts
|
||||
#endif
|
||||
|
||||
static inline double
|
||||
monotonic(void) {
|
||||
struct timespec ts = {0};
|
||||
#ifdef CLOCK_HIGHRES
|
||||
clock_gettime(CLOCK_HIGHRES, &ts);
|
||||
#elif CLOCK_MONOTONIC_RAW
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
#else
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
#endif
|
||||
return (((double)ts.tv_nsec) / 1e9) + (double)ts.tv_sec;
|
||||
}
|
||||
|
||||
void
|
||||
update_fds(EventLoopData *eld) {
|
||||
for (nfds_t i = 0; i < eld->watches_count; i++) {
|
||||
@@ -109,7 +96,7 @@ update_timers(EventLoopData *eld) {
|
||||
}
|
||||
|
||||
id_type
|
||||
addTimer(EventLoopData *eld, const char *name, double interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) {
|
||||
addTimer(EventLoopData *eld, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) {
|
||||
if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added");
|
||||
return 0;
|
||||
@@ -117,7 +104,7 @@ addTimer(EventLoopData *eld, const char *name, double interval, int enabled, boo
|
||||
Timer *t = eld->timers + eld->timers_count++;
|
||||
t->interval = interval;
|
||||
t->name = name;
|
||||
t->trigger_at = enabled ? monotonic() + interval : DBL_MAX;
|
||||
t->trigger_at = enabled ? monotonic() + interval : MONOTONIC_T_MAX;
|
||||
t->repeats = repeats;
|
||||
t->callback = cb;
|
||||
t->callback_data = cb_data;
|
||||
@@ -144,7 +131,7 @@ void
|
||||
toggleTimer(EventLoopData *eld, id_type timer_id, int enabled) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
double trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : DBL_MAX;
|
||||
monotonic_t trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : MONOTONIC_T_MAX;
|
||||
if (trigger_at != eld->timers[i].trigger_at) {
|
||||
eld->timers[i].trigger_at = trigger_at;
|
||||
update_timers(eld);
|
||||
@@ -155,7 +142,7 @@ toggleTimer(EventLoopData *eld, id_type timer_id, int enabled) {
|
||||
}
|
||||
|
||||
void
|
||||
changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval) {
|
||||
changeTimerInterval(EventLoopData *eld, id_type timer_id, monotonic_t interval) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
eld->timers[i].interval = interval;
|
||||
@@ -165,22 +152,28 @@ changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval) {
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
prepareForPoll(EventLoopData *eld, double timeout) {
|
||||
monotonic_t
|
||||
prepareForPoll(EventLoopData *eld, monotonic_t timeout) {
|
||||
for (nfds_t i = 0; i < eld->watches_count; i++) eld->fds[i].revents = 0;
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return timeout;
|
||||
double now = monotonic(), next_repeat_at = eld->timers[0].trigger_at;
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return timeout;
|
||||
monotonic_t now = monotonic(), next_repeat_at = eld->timers[0].trigger_at;
|
||||
if (timeout < 0 || now + timeout > next_repeat_at) {
|
||||
timeout = next_repeat_at <= now ? 0 : next_repeat_at - now;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static inline struct timespec
|
||||
calc_time(monotonic_t nsec) {
|
||||
struct timespec result;
|
||||
result.tv_sec = nsec / (1000LL * 1000LL * 1000LL);
|
||||
result.tv_nsec = nsec % (1000LL * 1000LL * 1000LL);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout) {
|
||||
const long seconds = (long) timeout;
|
||||
const long nanoseconds = (long) ((timeout - seconds) * 1e9);
|
||||
struct timespec tv = { seconds, nanoseconds };
|
||||
pollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout) {
|
||||
struct timespec tv = calc_time(timeout);
|
||||
return ppoll(fds, nfds, &tv, NULL);
|
||||
}
|
||||
|
||||
@@ -198,10 +191,10 @@ dispatchEvents(EventLoopData *eld) {
|
||||
|
||||
unsigned
|
||||
dispatchTimers(EventLoopData *eld) {
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return 0;
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return 0;
|
||||
static struct { timer_callback_func func; id_type id; void* data; bool repeats; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])];
|
||||
unsigned num_dispatches = 0;
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
for (nfds_t i = 0; i < eld->timers_count && eld->timers[i].trigger_at <= now; i++) {
|
||||
eld->timers[i].trigger_at = now + eld->timers[i].interval;
|
||||
dispatches[num_dispatches].func = eld->timers[i].callback;
|
||||
@@ -299,12 +292,12 @@ finalizePollData(EventLoopData *eld) {
|
||||
}
|
||||
|
||||
int
|
||||
pollForEvents(EventLoopData *eld, double timeout) {
|
||||
pollForEvents(EventLoopData *eld, monotonic_t timeout) {
|
||||
int read_ok = 0;
|
||||
timeout = prepareForPoll(eld, timeout);
|
||||
EVDBG("pollForEvents final timeout: %.3f", timeout);
|
||||
EVDBG("pollForEvents final timeout: %.3f", monotonic_t_to_s_double(timeout));
|
||||
int result;
|
||||
double end_time = monotonic() + timeout;
|
||||
monotonic_t end_time = monotonic() + timeout;
|
||||
eld->wakeup_fd_ready = false;
|
||||
|
||||
while(1) {
|
||||
|
||||
13
glfw/backend_utils.h
vendored
13
glfw/backend_utils.h
vendored
@@ -25,6 +25,7 @@
|
||||
//========================================================================
|
||||
|
||||
#pragma once
|
||||
#include "../kitty/monotonic.h"
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
@@ -55,7 +56,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
id_type id;
|
||||
double interval, trigger_at;
|
||||
monotonic_t interval, trigger_at;
|
||||
timer_callback_func callback;
|
||||
void *callback_data;
|
||||
GLFWuserdatafreefun free;
|
||||
@@ -82,14 +83,14 @@ void check_for_wakeup_events(EventLoopData *eld);
|
||||
id_type addWatch(EventLoopData *eld, const char *name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data);
|
||||
void removeWatch(EventLoopData *eld, id_type watch_id);
|
||||
void toggleWatch(EventLoopData *eld, id_type watch_id, int enabled);
|
||||
id_type addTimer(EventLoopData *eld, const char *name, double interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free);
|
||||
id_type addTimer(EventLoopData *eld, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free);
|
||||
void removeTimer(EventLoopData *eld, id_type timer_id);
|
||||
void removeAllTimers(EventLoopData *eld);
|
||||
void toggleTimer(EventLoopData *eld, id_type timer_id, int enabled);
|
||||
void changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval);
|
||||
double prepareForPoll(EventLoopData *eld, double timeout);
|
||||
int pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout);
|
||||
int pollForEvents(EventLoopData *eld, double timeout);
|
||||
void changeTimerInterval(EventLoopData *eld, id_type timer_id, monotonic_t interval);
|
||||
monotonic_t prepareForPoll(EventLoopData *eld, monotonic_t timeout);
|
||||
int pollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout);
|
||||
int pollForEvents(EventLoopData *eld, monotonic_t timeout);
|
||||
unsigned dispatchTimers(EventLoopData *eld);
|
||||
void finalizePollData(EventLoopData *eld);
|
||||
bool initPollData(EventLoopData *eld, int display_fd);
|
||||
|
||||
@@ -27,9 +27,13 @@
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
#include <sys/param.h> // For MAXPATHLEN
|
||||
#include <pthread.h>
|
||||
|
||||
// Needed for _NSGetProgname
|
||||
#include <crt_externs.h>
|
||||
|
||||
// Change to our application bundle's resources directory, if present
|
||||
//
|
||||
static void changeToResourcesDirectory(void)
|
||||
@@ -66,14 +70,119 @@ static void changeToResourcesDirectory(void)
|
||||
chdir(resourcesPath);
|
||||
}
|
||||
|
||||
// Set up the menu bar (manually)
|
||||
// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that
|
||||
// could go away at any moment, lots of stuff that really should be
|
||||
// localize(d|able), etc. Add a nib to save us this horror.
|
||||
//
|
||||
static void createMenuBar(void)
|
||||
{
|
||||
size_t i;
|
||||
NSString* appName = nil;
|
||||
NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString* nameKeys[] =
|
||||
{
|
||||
@"CFBundleDisplayName",
|
||||
@"CFBundleName",
|
||||
@"CFBundleExecutable",
|
||||
};
|
||||
|
||||
// Try to figure out what the calling application is called
|
||||
|
||||
for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++)
|
||||
{
|
||||
id name = bundleInfo[nameKeys[i]];
|
||||
if (name &&
|
||||
[name isKindOfClass:[NSString class]] &&
|
||||
![name isEqualToString:@""])
|
||||
{
|
||||
appName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!appName)
|
||||
{
|
||||
char** progname = _NSGetProgname();
|
||||
if (progname && *progname)
|
||||
appName = @(*progname);
|
||||
else
|
||||
appName = @"GLFW Application";
|
||||
}
|
||||
|
||||
NSMenu* bar = [[NSMenu alloc] init];
|
||||
[NSApp setMainMenu:bar];
|
||||
|
||||
NSMenuItem* appMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
NSMenu* appMenu = [[NSMenu alloc] init];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
|
||||
action:@selector(orderFrontStandardAboutPanel:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
NSMenu* servicesMenu = [[NSMenu alloc] init];
|
||||
[NSApp setServicesMenu:servicesMenu];
|
||||
[[appMenu addItemWithTitle:@"Services"
|
||||
action:NULL
|
||||
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||
[servicesMenu release];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
|
||||
action:@selector(hide:)
|
||||
keyEquivalent:@"h"];
|
||||
[[appMenu addItemWithTitle:@"Hide Others"
|
||||
action:@selector(hideOtherApplications:)
|
||||
keyEquivalent:@"h"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand];
|
||||
[appMenu addItemWithTitle:@"Show All"
|
||||
action:@selector(unhideAllApplications:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
|
||||
action:@selector(terminate:)
|
||||
keyEquivalent:@"q"];
|
||||
|
||||
NSMenuItem* windowMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
[bar release];
|
||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
|
||||
[windowMenu addItemWithTitle:@"Minimize"
|
||||
action:@selector(performMiniaturize:)
|
||||
keyEquivalent:@"m"];
|
||||
[windowMenu addItemWithTitle:@"Zoom"
|
||||
action:@selector(performZoom:)
|
||||
keyEquivalent:@""];
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[windowMenu addItemWithTitle:@"Bring All to Front"
|
||||
action:@selector(arrangeInFront:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
// TODO: Make this appear at the bottom of the menu (for consistency)
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
||||
action:@selector(toggleFullScreen:)
|
||||
keyEquivalent:@"f"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
|
||||
// Prior to Snow Leopard, we need to use this oddly-named semi-private API
|
||||
// to get the application menu working properly.
|
||||
SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:");
|
||||
[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
|
||||
}
|
||||
|
||||
// Create key code translation tables
|
||||
//
|
||||
static void createKeyTables(void)
|
||||
{
|
||||
int scancode;
|
||||
int keycode;
|
||||
|
||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
||||
memset(_glfw.ns.key_to_keycode, -1, sizeof(_glfw.ns.key_to_keycode));
|
||||
|
||||
_glfw.ns.keycodes[0x1D] = GLFW_KEY_0;
|
||||
_glfw.ns.keycodes[0x12] = GLFW_KEY_1;
|
||||
@@ -190,11 +299,11 @@ static void createKeyTables(void)
|
||||
_glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY;
|
||||
_glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT;
|
||||
|
||||
for (scancode = 0; scancode < 256; scancode++)
|
||||
for (keycode = 0; keycode < 256; keycode++)
|
||||
{
|
||||
// Store the reverse translation for faster key name lookup
|
||||
if (_glfw.ns.keycodes[scancode] >= 0)
|
||||
_glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode;
|
||||
if (_glfw.ns.keycodes[keycode] >= 0)
|
||||
_glfw.ns.key_to_keycode[_glfw.ns.keycodes[keycode]] = keycode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +385,17 @@ static bool initializeTIS(void)
|
||||
return updateUnicodeDataNS();
|
||||
}
|
||||
|
||||
static void
|
||||
display_reconfigured(CGDirectDisplayID display UNUSED, CGDisplayChangeSummaryFlags flags, void *userInfo UNUSED)
|
||||
{
|
||||
if (flags & kCGDisplayBeginConfigurationFlag) {
|
||||
return;
|
||||
}
|
||||
if (flags & kCGDisplaySetModeFlag) {
|
||||
// GPU possibly changed
|
||||
}
|
||||
}
|
||||
|
||||
@interface GLFWHelper : NSObject
|
||||
@end
|
||||
|
||||
@@ -294,6 +414,102 @@ static bool initializeTIS(void)
|
||||
|
||||
@end // GLFWHelper
|
||||
|
||||
// Delegate for application related notifications {{{
|
||||
|
||||
@interface GLFWApplicationDelegate : NSObject <NSApplicationDelegate>
|
||||
@end
|
||||
|
||||
@implementation GLFWApplicationDelegate
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
(void)sender;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
_glfwInputWindowCloseRequest(window);
|
||||
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
|
||||
{
|
||||
(void)sender;
|
||||
if (!handle_reopen_callback) return YES;
|
||||
if (handle_reopen_callback(flag)) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)applicationDidChangeScreenParameters:(NSNotification *) notification
|
||||
{
|
||||
(void)notification;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
{
|
||||
if (window->context.client != GLFW_NO_API)
|
||||
[window->context.nsgl.object update];
|
||||
}
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
}
|
||||
|
||||
static GLFWapplicationwillfinishlaunchingfun finish_launching_callback = NULL;
|
||||
|
||||
- (void)applicationWillFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
if (_glfw.hints.init.ns.menubar)
|
||||
{
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
// Menu bar setup must go between sharedApplication and finishLaunching
|
||||
// in order to properly emulate the behavior of NSApplicationMain
|
||||
|
||||
if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"])
|
||||
{
|
||||
[[NSBundle mainBundle] loadNibNamed:@"MainMenu"
|
||||
owner:NSApp
|
||||
topLevelObjects:&_glfw.ns.nibObjects];
|
||||
}
|
||||
else
|
||||
createMenuBar();
|
||||
}
|
||||
if (finish_launching_callback)
|
||||
finish_launching_callback();
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
[NSApp stop:nil];
|
||||
|
||||
CGDisplayRegisterReconfigurationCallback(display_reconfigured, NULL);
|
||||
_glfwCocoaPostEmptyEvent();
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
(void)aNotification;
|
||||
CGDisplayRemoveReconfigurationCallback(display_reconfigured, NULL);
|
||||
}
|
||||
|
||||
- (void)applicationDidHide:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
_glfwRestoreVideoModeNS(_glfw.monitors[i]);
|
||||
}
|
||||
|
||||
@end // GLFWApplicationDelegate
|
||||
// }}}
|
||||
|
||||
|
||||
@interface GLFWApplication : NSApplication
|
||||
- (void)tick_callback;
|
||||
- (void)render_frame_received:(id)displayIDAsID;
|
||||
@@ -335,6 +551,18 @@ is_cmd_period(NSEvent *event, NSEventModifierFlags modifierFlags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) {
|
||||
GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback;
|
||||
handle_reopen_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback) {
|
||||
GLFWapplicationwillfinishlaunchingfun previous = finish_launching_callback;
|
||||
finish_launching_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
@@ -345,8 +573,21 @@ int _glfwPlatformInit(void)
|
||||
toTarget:_glfw.ns.helper
|
||||
withObject:nil];
|
||||
|
||||
if (NSApp)
|
||||
_glfw.ns.finishedLaunching = true;
|
||||
|
||||
[GLFWApplication sharedApplication];
|
||||
|
||||
_glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init];
|
||||
if (_glfw.ns.delegate == nil)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to create application delegate");
|
||||
return false;
|
||||
}
|
||||
|
||||
[NSApp setDelegate:_glfw.ns.delegate];
|
||||
|
||||
NSEvent* (^keydown_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
|
||||
{
|
||||
NSEventModifierFlags modifierFlags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
@@ -385,6 +626,10 @@ int _glfwPlatformInit(void)
|
||||
if (_glfw.hints.init.ns.chdir)
|
||||
changeToResourcesDirectory();
|
||||
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO};
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:_glfw.ns.helper
|
||||
selector:@selector(selectedKeyboardInputSourceChanged:)
|
||||
@@ -402,7 +647,6 @@ int _glfwPlatformInit(void)
|
||||
if (!initializeTIS())
|
||||
return false;
|
||||
|
||||
_glfwInitTimerNS();
|
||||
_glfwInitJoysticksNS();
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
@@ -531,7 +775,7 @@ typedef struct {
|
||||
NSTimer *os_timer;
|
||||
unsigned long long id;
|
||||
bool repeats;
|
||||
double interval;
|
||||
monotonic_t interval;
|
||||
GLFWuserdatafun callback;
|
||||
void *callback_data;
|
||||
GLFWuserdatafun free_callback_data;
|
||||
@@ -551,7 +795,7 @@ remove_timer_at(size_t idx) {
|
||||
}
|
||||
|
||||
static void schedule_timer(Timer *t) {
|
||||
t->os_timer = [NSTimer scheduledTimerWithTimeInterval:t->interval repeats:(t->repeats ? YES: NO) block:^(NSTimer *os_timer) {
|
||||
t->os_timer = [NSTimer scheduledTimerWithTimeInterval:monotonic_t_to_s_double(t->interval) repeats:(t->repeats ? YES: NO) block:^(NSTimer *os_timer) {
|
||||
for (size_t i = 0; i < num_timers; i++) {
|
||||
if (timers[i].os_timer == os_timer) {
|
||||
timers[i].callback(timers[i].id, timers[i].callback_data);
|
||||
@@ -562,7 +806,7 @@ static void schedule_timer(Timer *t) {
|
||||
}];
|
||||
}
|
||||
|
||||
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback) {
|
||||
unsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback) {
|
||||
static unsigned long long timer_counter = 0;
|
||||
if (num_timers >= sizeof(timers)/sizeof(timers[0]) - 1) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added");
|
||||
@@ -588,7 +832,7 @@ void _glfwPlatformRemoveTimer(unsigned long long timer_id) {
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, bool enabled) {
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {
|
||||
for (size_t i = 0; i < num_timers; i++) {
|
||||
if (timers[i].id == timer_id) {
|
||||
Timer *t = timers + i;
|
||||
|
||||
@@ -328,7 +328,7 @@ void _glfwPollMonitorsNS(void)
|
||||
if (!name)
|
||||
name = _glfw_strdup("Unknown");
|
||||
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height);
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor(name, (int)size.width, (int)size.height);
|
||||
monitor->ns.displayID = displays[i];
|
||||
monitor->ns.unitNumber = unitNumber;
|
||||
createDisplayLink(monitor->ns.displayID);
|
||||
@@ -354,8 +354,9 @@ void _glfwPollMonitorsNS(void)
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
|
||||
{
|
||||
GLFWvidmode current;
|
||||
const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
|
||||
_glfwPlatformGetVideoMode(monitor, ¤t);
|
||||
|
||||
const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
|
||||
if (_glfwCompareVideoModes(¤t, best) == 0)
|
||||
return;
|
||||
|
||||
@@ -454,13 +455,13 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
|
||||
const NSRect frameRect = [monitor->ns.screen visibleFrame];
|
||||
|
||||
if (xpos)
|
||||
*xpos = frameRect.origin.x;
|
||||
*xpos = (int)frameRect.origin.x;
|
||||
if (ypos)
|
||||
*ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
|
||||
*ypos = (int)_glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
|
||||
if (width)
|
||||
*width = frameRect.size.width;
|
||||
*width = (int)frameRect.size.width;
|
||||
if (height)
|
||||
*height = frameRect.size.height;
|
||||
*height = (int)frameRect.size.height;
|
||||
|
||||
}
|
||||
|
||||
@@ -505,7 +506,6 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
|
||||
{
|
||||
CVDisplayLinkRef link;
|
||||
|
||||
CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
|
||||
|
||||
CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
|
||||
|
||||
16
glfw/cocoa_platform.h
vendored
16
glfw/cocoa_platform.h
vendored
@@ -36,6 +36,9 @@ typedef void* id;
|
||||
typedef void* CVDisplayLinkRef;
|
||||
#endif
|
||||
|
||||
// NOTE: Many Cocoa enum values have been renamed and we need to build across
|
||||
// SDK versions where one is unavailable or the other deprecated
|
||||
// We use the newer names in code and these macros to handle compatibility
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
|
||||
#define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat
|
||||
#define NSEventMaskAny NSAnyEventMask
|
||||
@@ -64,8 +67,9 @@ typedef void* CVDisplayLinkRef;
|
||||
|
||||
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
|
||||
typedef struct VkMacOSSurfaceCreateInfoMVK
|
||||
@@ -144,7 +148,7 @@ typedef struct _GLFWDisplayLinkNS
|
||||
{
|
||||
CVDisplayLinkRef displayLink;
|
||||
CGDirectDisplayID displayID;
|
||||
double lastRenderFrameRequestedAt;
|
||||
monotonic_t lastRenderFrameRequestedAt;
|
||||
} _GLFWDisplayLinkNS;
|
||||
|
||||
// Cocoa-specific global data
|
||||
@@ -153,6 +157,7 @@ typedef struct _GLFWlibraryNS
|
||||
{
|
||||
CGEventSourceRef eventSource;
|
||||
id delegate;
|
||||
bool finishedLaunching;
|
||||
bool cursorHidden;
|
||||
TISInputSourceRef inputSource;
|
||||
IOHIDManagerRef hidManager;
|
||||
@@ -160,11 +165,12 @@ typedef struct _GLFWlibraryNS
|
||||
id helper;
|
||||
id keyUpMonitor;
|
||||
id keyDownMonitor;
|
||||
id nibObjects;
|
||||
|
||||
char keyName[64];
|
||||
char text[256];
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
short int key_to_keycode[GLFW_KEY_LAST + 1];
|
||||
char* clipboardString;
|
||||
CGPoint cascadePoint;
|
||||
// Where to place the cursor when re-enabled
|
||||
@@ -215,8 +221,6 @@ typedef struct _GLFWtimerNS
|
||||
} _GLFWtimerNS;
|
||||
|
||||
|
||||
void _glfwInitTimerNS(void);
|
||||
|
||||
void _glfwPollMonitorsNS(void);
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||
|
||||
62
glfw/cocoa_time.c
vendored
62
glfw/cocoa_time.c
vendored
@@ -1,62 +0,0 @@
|
||||
//========================================================================
|
||||
// GLFW 3.4 macOS - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialise timer
|
||||
//
|
||||
void _glfwInitTimerNS(void)
|
||||
{
|
||||
mach_timebase_info_data_t info;
|
||||
mach_timebase_info(&info);
|
||||
|
||||
_glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint64_t _glfwPlatformGetTimerValue(void)
|
||||
{
|
||||
return mach_absolute_time();
|
||||
}
|
||||
|
||||
uint64_t _glfwPlatformGetTimerFrequency(void)
|
||||
{
|
||||
return _glfw.timer.ns.frequency;
|
||||
}
|
||||
|
||||
@@ -27,13 +27,11 @@
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
|
||||
// Needed for _NSGetProgname
|
||||
#include <crt_externs.h>
|
||||
|
||||
|
||||
#define PARAGRAPH_UTF_8 0xc2a7 // §
|
||||
#define MASCULINE_UTF_8 0xc2ba // º
|
||||
@@ -93,15 +91,14 @@
|
||||
//
|
||||
static NSUInteger getStyleMask(_GLFWwindow* window)
|
||||
{
|
||||
NSUInteger styleMask = 0;
|
||||
NSUInteger styleMask = NSWindowStyleMaskMiniaturizable;
|
||||
|
||||
if (window->monitor || !window->decorated)
|
||||
styleMask |= NSWindowStyleMaskBorderless;
|
||||
else
|
||||
{
|
||||
styleMask |= NSWindowStyleMaskTitled |
|
||||
NSWindowStyleMaskClosable |
|
||||
NSWindowStyleMaskMiniaturizable;
|
||||
NSWindowStyleMaskClosable;
|
||||
|
||||
if (window->resizable)
|
||||
styleMask |= NSWindowStyleMaskResizable;
|
||||
@@ -120,7 +117,7 @@ CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
|
||||
}
|
||||
|
||||
static unsigned long long display_link_shutdown_timer = 0;
|
||||
#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL 30
|
||||
#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll)
|
||||
|
||||
void
|
||||
_glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data UNUSED) {
|
||||
@@ -147,7 +144,7 @@ requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
|
||||
} else {
|
||||
display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL);
|
||||
}
|
||||
double now = glfwGetTime();
|
||||
monotonic_t now = glfwGetTime();
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
|
||||
if (dl->displayID == displayID) {
|
||||
@@ -329,8 +326,8 @@ format_text(const char *src) {
|
||||
}
|
||||
|
||||
static const char*
|
||||
safe_name_for_scancode(unsigned int scancode) {
|
||||
const char *ans = _glfwPlatformGetScancodeName(scancode);
|
||||
safe_name_for_keycode(unsigned int keycode) {
|
||||
const char *ans = _glfwPlatformGetNativeKeyName(keycode);
|
||||
if (!ans) return "<noname>";
|
||||
if ((1 <= ans[0] && ans[0] <= 31) || ans[0] == 127) ans = "<cc>";
|
||||
return ans;
|
||||
@@ -343,7 +340,7 @@ static int translateKey(unsigned int key, bool apply_keymap)
|
||||
{
|
||||
if (apply_keymap) {
|
||||
// Look for the effective key name after applying any keyboard layouts/mappings
|
||||
const char *name_chars = _glfwPlatformGetScancodeName(key);
|
||||
const char *name_chars = _glfwPlatformGetNativeKeyName(key);
|
||||
uint32_t name = 0;
|
||||
if (name_chars) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -480,17 +477,6 @@ static int translateKey(unsigned int key, bool apply_keymap)
|
||||
return _glfw.ns.keycodes[key];
|
||||
}
|
||||
|
||||
static void
|
||||
display_reconfigured(CGDirectDisplayID display UNUSED, CGDisplayChangeSummaryFlags flags, void *userInfo UNUSED)
|
||||
{
|
||||
if (flags & kCGDisplayBeginConfigurationFlag) {
|
||||
return;
|
||||
}
|
||||
if (flags & kCGDisplaySetModeFlag) {
|
||||
// GPU possibly changed
|
||||
}
|
||||
}
|
||||
|
||||
// Translate a GLFW keycode to a Cocoa modifier flag
|
||||
//
|
||||
static NSUInteger translateKeyToModifierFlag(int key)
|
||||
@@ -572,17 +558,17 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
if (fbRect.size.width != window->ns.fbWidth ||
|
||||
fbRect.size.height != window->ns.fbHeight)
|
||||
{
|
||||
window->ns.fbWidth = fbRect.size.width;
|
||||
window->ns.fbHeight = fbRect.size.height;
|
||||
_glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
|
||||
window->ns.fbWidth = (int)fbRect.size.width;
|
||||
window->ns.fbHeight = (int)fbRect.size.height;
|
||||
_glfwInputFramebufferSize(window, (int)fbRect.size.width, (int)fbRect.size.height);
|
||||
}
|
||||
|
||||
if (contentRect.size.width != window->ns.width ||
|
||||
contentRect.size.height != window->ns.height)
|
||||
{
|
||||
window->ns.width = contentRect.size.width;
|
||||
window->ns.height = contentRect.size.height;
|
||||
_glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height);
|
||||
window->ns.width = (int)contentRect.size.width;
|
||||
window->ns.height = (int)contentRect.size.height;
|
||||
_glfwInputWindowSize(window, (int)contentRect.size.width, (int)contentRect.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,77 +647,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end // }}}
|
||||
|
||||
// Delegate for application related notifications {{{
|
||||
|
||||
@interface GLFWApplicationDelegate : NSObject
|
||||
@end
|
||||
|
||||
@implementation GLFWApplicationDelegate
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
(void)sender;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
_glfwInputWindowCloseRequest(window);
|
||||
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
|
||||
{
|
||||
(void)sender;
|
||||
if (!handle_reopen_callback) return YES;
|
||||
if (handle_reopen_callback(flag)) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)applicationDidChangeScreenParameters:(NSNotification *) notification
|
||||
{
|
||||
(void)notification;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
{
|
||||
if (window->context.client != GLFW_NO_API)
|
||||
[window->context.nsgl.object update];
|
||||
}
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
[NSApp stop:nil];
|
||||
|
||||
CGDisplayRegisterReconfigurationCallback(display_reconfigured, NULL);
|
||||
_glfwCocoaPostEmptyEvent();
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
(void)aNotification;
|
||||
CGDisplayRemoveReconfigurationCallback(display_reconfigured, NULL);
|
||||
}
|
||||
|
||||
- (void)applicationDidHide:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
_glfwRestoreVideoModeNS(_glfw.monitors[i]);
|
||||
}
|
||||
|
||||
@end
|
||||
// }}}
|
||||
|
||||
// Content view class for the GLFW window {{{
|
||||
|
||||
@@ -960,9 +877,9 @@ static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;
|
||||
if (fbRect.size.width != window->ns.fbWidth ||
|
||||
fbRect.size.height != window->ns.fbHeight)
|
||||
{
|
||||
window->ns.fbWidth = fbRect.size.width;
|
||||
window->ns.fbHeight = fbRect.size.height;
|
||||
_glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
|
||||
window->ns.fbWidth = (int)fbRect.size.width;
|
||||
window->ns.fbHeight = (int)fbRect.size.height;
|
||||
_glfwInputFramebufferSize(window, (int)fbRect.size.width, (int)fbRect.size.height);
|
||||
}
|
||||
|
||||
const float xscale = fbRect.size.width / contentRect.size.width;
|
||||
@@ -1047,14 +964,16 @@ is_ascii_control_char(char x) {
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
const unsigned int scancode = [event keyCode];
|
||||
const unsigned int keycode = [event keyCode];
|
||||
const NSUInteger flags = [event modifierFlags];
|
||||
const int mods = translateFlags(flags);
|
||||
const int key = translateKey(scancode, true);
|
||||
const bool process_text = !window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, scancode, flags) != 1;
|
||||
const int key = translateKey(keycode, true);
|
||||
const bool process_text = !window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, keycode, flags) != 1;
|
||||
const bool previous_has_marked_text = [self hasMarkedText];
|
||||
[self unmarkText];
|
||||
_glfw.ns.text[0] = 0;
|
||||
GLFWkeyevent glfw_keyevent;
|
||||
_glfwInitializeKeyEvent(&glfw_keyevent, key, keycode, GLFW_PRESS, mods);
|
||||
if (!_glfw.ns.unicodeData) {
|
||||
// Using the cocoa API for key handling is disabled, as there is no
|
||||
// reliable way to handle dead keys using it. Only use it if the
|
||||
@@ -1069,7 +988,7 @@ is_ascii_control_char(char x) {
|
||||
const bool in_compose_sequence = window->ns.deadKeyState != 0;
|
||||
if (UCKeyTranslate(
|
||||
[(NSData*) _glfw.ns.unicodeData bytes],
|
||||
scancode,
|
||||
keycode,
|
||||
kUCKeyActionDown,
|
||||
convert_cocoa_to_carbon_modifiers(flags),
|
||||
LMGetKbdType(),
|
||||
@@ -1079,30 +998,32 @@ is_ascii_control_char(char x) {
|
||||
&char_count,
|
||||
text
|
||||
) != noErr) {
|
||||
debug_key(@"UCKeyTranslate failed for scancode: 0x%x (%@) %@\n",
|
||||
scancode, @(safe_name_for_scancode(scancode)), @(format_mods(mods)));
|
||||
debug_key(@"UCKeyTranslate failed for keycode: 0x%x (%@) %@\n",
|
||||
keycode, @(safe_name_for_keycode(keycode)), @(format_mods(mods)));
|
||||
window->ns.deadKeyState = 0;
|
||||
return;
|
||||
}
|
||||
debug_key(@"scancode: 0x%x (%@) %@char_count: %lu deadKeyState: %u repeat: %d",
|
||||
scancode, @(safe_name_for_scancode(scancode)), @(format_mods(mods)), char_count, window->ns.deadKeyState, event.ARepeat);
|
||||
debug_key(@"keycode: 0x%x (%@) %@char_count: %lu deadKeyState: %u repeat: %d",
|
||||
keycode, @(safe_name_for_keycode(keycode)), @(format_mods(mods)), char_count, window->ns.deadKeyState, event.ARepeat);
|
||||
if (process_text) {
|
||||
// this will call insertText which will fill up _glfw.ns.text
|
||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||
} else {
|
||||
window->ns.deadKeyState = 0;
|
||||
}
|
||||
if (window->ns.deadKeyState && (char_count == 0 || scancode == 0x75)) {
|
||||
if (window->ns.deadKeyState && (char_count == 0 || keycode == 0x75)) {
|
||||
// 0x75 is the delete key which needs to be ignored during a compose sequence
|
||||
debug_key(@"Sending pre-edit text for dead key (text: %@ markedText: %@).\n", @(format_text(_glfw.ns.text)), markedText);
|
||||
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods,
|
||||
[[markedText string] UTF8String], 1); // update pre-edit text
|
||||
glfw_keyevent.text = [[markedText string] UTF8String];
|
||||
glfw_keyevent.ime_state = 1;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // update pre-edit text
|
||||
return;
|
||||
}
|
||||
if (in_compose_sequence) {
|
||||
debug_key(@"Clearing pre-edit text at end of compose sequence\n");
|
||||
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods,
|
||||
NULL, 1); // clear pre-edit text
|
||||
glfw_keyevent.text = NULL;
|
||||
glfw_keyevent.ime_state = 1;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text
|
||||
}
|
||||
}
|
||||
if (is_ascii_control_char(_glfw.ns.text[0])) _glfw.ns.text[0] = 0; // don't send text for ascii control codes
|
||||
@@ -1110,18 +1031,22 @@ is_ascii_control_char(char x) {
|
||||
@(format_text(_glfw.ns.text)), @(_glfwGetKeyName(key)), markedText);
|
||||
if (!window->ns.deadKeyState) {
|
||||
if ([self hasMarkedText]) {
|
||||
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods,
|
||||
[[markedText string] UTF8String], 1); // update pre-edit text
|
||||
glfw_keyevent.text = [[markedText string] UTF8String];
|
||||
glfw_keyevent.ime_state = 1;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // update pre-edit text
|
||||
} else if (previous_has_marked_text) {
|
||||
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods,
|
||||
NULL, 1); // clear pre-edit text
|
||||
glfw_keyevent.text = NULL;
|
||||
glfw_keyevent.ime_state = 1;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text
|
||||
}
|
||||
if (([self hasMarkedText] || previous_has_marked_text) && !_glfw.ns.text[0]) {
|
||||
// do not pass keys like BACKSPACE while there's pre-edit text, let IME handle it
|
||||
return;
|
||||
}
|
||||
}
|
||||
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods, _glfw.ns.text, 0);
|
||||
glfw_keyevent.text = _glfw.ns.text;
|
||||
glfw_keyevent.ime_state = 0;
|
||||
_glfwInputKeyboard(window, &glfw_keyevent);
|
||||
}
|
||||
|
||||
- (void)flagsChanged:(NSEvent *)event
|
||||
@@ -1143,14 +1068,19 @@ is_ascii_control_char(char x) {
|
||||
else
|
||||
action = GLFW_RELEASE;
|
||||
|
||||
_glfwInputKeyboard(window, key, [event keyCode], action, mods, "", 0);
|
||||
GLFWkeyevent glfw_keyevent;
|
||||
_glfwInitializeKeyEvent(&glfw_keyevent, key, [event keyCode], action, mods);
|
||||
_glfwInputKeyboard(window, &glfw_keyevent);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
const int key = translateKey([event keyCode], true);
|
||||
const int mods = translateFlags([event modifierFlags]);
|
||||
_glfwInputKeyboard(window, key, [event keyCode], GLFW_RELEASE, mods, "", 0);
|
||||
|
||||
GLFWkeyevent glfw_keyevent;
|
||||
_glfwInitializeKeyEvent(&glfw_keyevent, key, [event keyCode], GLFW_RELEASE, mods);
|
||||
_glfwInputKeyboard(window, &glfw_keyevent);
|
||||
}
|
||||
|
||||
- (void)scrollWheel:(NSEvent *)event
|
||||
@@ -1341,7 +1271,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c,
|
||||
@end
|
||||
// }}}
|
||||
|
||||
// GLFW Window class {{{
|
||||
// GLFW window class {{{
|
||||
|
||||
@interface GLFWWindow : NSWindow {
|
||||
_GLFWwindow* glfw_window;
|
||||
@@ -1394,154 +1324,6 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c,
|
||||
@end
|
||||
// }}}
|
||||
|
||||
// Set up the menu bar (manually)
|
||||
// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that
|
||||
// could go away at any moment, lots of stuff that really should be
|
||||
// localize(d|able), etc. Add a nib to save us this horror.
|
||||
//
|
||||
static void createMenuBar(void)
|
||||
{
|
||||
size_t i;
|
||||
NSString* appName = nil;
|
||||
NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString* nameKeys[] =
|
||||
{
|
||||
@"CFBundleDisplayName",
|
||||
@"CFBundleName",
|
||||
@"CFBundleExecutable",
|
||||
};
|
||||
|
||||
// Try to figure out what the calling application is called
|
||||
|
||||
for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++)
|
||||
{
|
||||
id name = bundleInfo[nameKeys[i]];
|
||||
if (name &&
|
||||
[name isKindOfClass:[NSString class]] &&
|
||||
![name isEqualToString:@""])
|
||||
{
|
||||
appName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!appName)
|
||||
{
|
||||
char** progname = _NSGetProgname();
|
||||
if (progname && *progname)
|
||||
appName = @(*progname);
|
||||
else
|
||||
appName = @"GLFW Application";
|
||||
}
|
||||
|
||||
NSMenu* bar = [[NSMenu alloc] init];
|
||||
[NSApp setMainMenu:bar];
|
||||
|
||||
NSMenuItem* appMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
NSMenu* appMenu = [[NSMenu alloc] init];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
|
||||
action:@selector(orderFrontStandardAboutPanel:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
NSMenu* servicesMenu = [[NSMenu alloc] init];
|
||||
[NSApp setServicesMenu:servicesMenu];
|
||||
[[appMenu addItemWithTitle:@"Services"
|
||||
action:NULL
|
||||
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||
[servicesMenu release];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
|
||||
action:@selector(hide:)
|
||||
keyEquivalent:@"h"];
|
||||
[[appMenu addItemWithTitle:@"Hide Others"
|
||||
action:@selector(hideOtherApplications:)
|
||||
keyEquivalent:@"h"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand];
|
||||
[appMenu addItemWithTitle:@"Show All"
|
||||
action:@selector(unhideAllApplications:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
|
||||
action:@selector(terminate:)
|
||||
keyEquivalent:@"q"];
|
||||
|
||||
NSMenuItem* windowMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
[bar release];
|
||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
|
||||
[windowMenu addItemWithTitle:@"Minimize"
|
||||
action:@selector(performMiniaturize:)
|
||||
keyEquivalent:@"m"];
|
||||
[windowMenu addItemWithTitle:@"Zoom"
|
||||
action:@selector(performZoom:)
|
||||
keyEquivalent:@""];
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[windowMenu addItemWithTitle:@"Bring All to Front"
|
||||
action:@selector(arrangeInFront:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
// TODO: Make this appear at the bottom of the menu (for consistency)
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
||||
action:@selector(toggleFullScreen:)
|
||||
keyEquivalent:@"f"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
|
||||
// Prior to Snow Leopard, we need to use this oddly-named semi-private API
|
||||
// to get the application menu working properly.
|
||||
SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:");
|
||||
[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
|
||||
}
|
||||
|
||||
// Initialize the Cocoa Application Kit
|
||||
//
|
||||
static bool initializeAppKit(void)
|
||||
{
|
||||
if (_glfw.ns.delegate)
|
||||
return true;
|
||||
|
||||
// There can only be one application delegate, but we allocate it the
|
||||
// first time a window is created to keep all window code in this file
|
||||
_glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init];
|
||||
if (_glfw.ns.delegate == nil)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to create application delegate");
|
||||
return false;
|
||||
}
|
||||
[NSApp setDelegate:_glfw.ns.delegate];
|
||||
|
||||
if (_glfw.hints.init.ns.menubar)
|
||||
{
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
// Menu bar setup must go between sharedApplication above and
|
||||
// finishLaunching below, in order to properly emulate the behavior
|
||||
// of NSApplicationMain
|
||||
|
||||
// disabled by Kovid
|
||||
/* if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) */
|
||||
/* [NSApp loadMainMenu]; */
|
||||
/* else */
|
||||
createMenuBar();
|
||||
}
|
||||
|
||||
[NSApp run];
|
||||
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO};
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create the Cocoa window
|
||||
//
|
||||
@@ -1589,7 +1371,7 @@ static bool createNativeWindow(_GLFWwindow* window,
|
||||
[window->ns.object setLevel:NSMainMenuWindowLevel + 1];
|
||||
else
|
||||
{
|
||||
[(NSWindow*)window->ns.object center];
|
||||
[(NSWindow*) window->ns.object center];
|
||||
_glfw.ns.cascadePoint =
|
||||
NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint:
|
||||
NSPointFromCGPoint(_glfw.ns.cascadePoint)]);
|
||||
@@ -1646,8 +1428,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
window->ns.deadKeyState = 0;
|
||||
if (!initializeAppKit())
|
||||
return false;
|
||||
if (!_glfw.ns.finishedLaunching)
|
||||
{
|
||||
[NSApp run];
|
||||
_glfw.ns.finishedLaunching = true;
|
||||
}
|
||||
|
||||
if (!createNativeWindow(window, wndconfig, fbconfig))
|
||||
return false;
|
||||
@@ -1713,7 +1498,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
window->ns.object = nil;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowTitle(_GLFWwindow* window UNUSED, const char *title)
|
||||
void _glfwPlatformSetWindowTitle(_GLFWwindow* window UNUSED, const char* title)
|
||||
{
|
||||
NSString* string = @(title);
|
||||
[window->ns.object setTitle:string];
|
||||
@@ -1734,9 +1519,9 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
|
||||
[window->ns.object contentRectForFrameRect:[window->ns.object frame]];
|
||||
|
||||
if (xpos)
|
||||
*xpos = contentRect.origin.x;
|
||||
*xpos = (int)contentRect.origin.x;
|
||||
if (ypos)
|
||||
*ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1);
|
||||
*ypos = (int)_glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y)
|
||||
@@ -1752,9 +1537,9 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
|
||||
const NSRect contentRect = [window->ns.view frame];
|
||||
|
||||
if (width)
|
||||
*width = contentRect.size.width;
|
||||
*width = (int)contentRect.size.width;
|
||||
if (height)
|
||||
*height = contentRect.size.height;
|
||||
*height = (int)contentRect.size.height;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
||||
@@ -1817,15 +1602,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect];
|
||||
|
||||
if (left)
|
||||
*left = contentRect.origin.x - frameRect.origin.x;
|
||||
*left = (int)(contentRect.origin.x - frameRect.origin.x);
|
||||
if (top)
|
||||
*top = frameRect.origin.y + frameRect.size.height -
|
||||
contentRect.origin.y - contentRect.size.height;
|
||||
*top = (int)(frameRect.origin.y + frameRect.size.height -
|
||||
contentRect.origin.y - contentRect.size.height);
|
||||
if (right)
|
||||
*right = frameRect.origin.x + frameRect.size.width -
|
||||
contentRect.origin.x - contentRect.size.width;
|
||||
*right = (int)(frameRect.origin.x + frameRect.size.width -
|
||||
contentRect.origin.x - contentRect.size.width);
|
||||
if (bottom)
|
||||
*bottom = contentRect.origin.y - frameRect.origin.y;
|
||||
*bottom = (int)(contentRect.origin.y - frameRect.origin.y);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||
@@ -1840,9 +1625,9 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||
*yscale = (float) (pixels.size.height / points.size.height);
|
||||
}
|
||||
|
||||
double _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return [NSEvent doubleClickInterval];
|
||||
return s_double_to_monotonic_t([NSEvent doubleClickInterval]);
|
||||
}
|
||||
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
|
||||
@@ -2110,14 +1895,14 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode UNUSED)
|
||||
updateCursorMode(window);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
const char* _glfwPlatformGetNativeKeyName(int keycode)
|
||||
{
|
||||
UInt32 deadKeyState = 0;
|
||||
UniChar characters[8];
|
||||
UniCharCount characterCount = 0;
|
||||
|
||||
if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes],
|
||||
scancode,
|
||||
keycode,
|
||||
kUCKeyActionDisplay,
|
||||
0,
|
||||
LMGetKbdType(),
|
||||
@@ -2137,9 +1922,9 @@ const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
return _glfw.ns.keyName;
|
||||
}
|
||||
|
||||
int _glfwPlatformGetKeyScancode(int key)
|
||||
int _glfwPlatformGetNativeKeyForKey(int key)
|
||||
{
|
||||
return _glfw.ns.scancodes[key];
|
||||
return _glfw.ns.key_to_keycode[key];
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
@@ -2389,12 +2174,6 @@ GLFWAPI GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWw
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) {
|
||||
GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback;
|
||||
handle_reopen_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback) {
|
||||
requestRenderFrame((_GLFWwindow*)w, callback);
|
||||
}
|
||||
|
||||
3
glfw/dbus_glfw.c
vendored
3
glfw/dbus_glfw.c
vendored
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "internal.h"
|
||||
#include "dbus_glfw.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -107,7 +108,7 @@ on_dbus_timer_ready(id_type timer_id UNUSED, void *data) {
|
||||
static dbus_bool_t
|
||||
add_dbus_timeout(DBusTimeout *timeout, void *data) {
|
||||
int enabled = dbus_timeout_get_enabled(timeout) ? 1 : 0;
|
||||
double interval = ((double)dbus_timeout_get_interval(timeout)) / 1000.0;
|
||||
monotonic_t interval = ms_to_monotonic_t(dbus_timeout_get_interval(timeout));
|
||||
if (interval < 0) return FALSE;
|
||||
id_type timer_id = addTimer(dbus_data->eld, data, interval, enabled, true, on_dbus_timer_ready, timeout, NULL);
|
||||
if (!timer_id) return FALSE;
|
||||
|
||||
31
glfw/egl_context.c
vendored
31
glfw/egl_context.c
vendored
@@ -123,23 +123,24 @@ static bool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
|
||||
continue;
|
||||
|
||||
#if defined(_GLFW_X11)
|
||||
XVisualInfo vi = {0};
|
||||
|
||||
// Only consider EGLConfigs with associated Visuals
|
||||
vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
|
||||
if (!vi.visualid)
|
||||
continue;
|
||||
|
||||
if (desired->transparent)
|
||||
{
|
||||
int count;
|
||||
XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display,
|
||||
VisualIDMask, &vi,
|
||||
&count);
|
||||
if (vis)
|
||||
XVisualInfo vi = {0};
|
||||
|
||||
// Only consider EGLConfigs with associated Visuals
|
||||
vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
|
||||
if (!vi.visualid)
|
||||
continue;
|
||||
|
||||
if (desired->transparent)
|
||||
{
|
||||
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
|
||||
XFree(vis);
|
||||
int count;
|
||||
XVisualInfo* vis =
|
||||
XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count);
|
||||
if (vis)
|
||||
{
|
||||
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
|
||||
XFree(vis);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _GLFW_X11
|
||||
|
||||
15
glfw/glfw.py
15
glfw/glfw.py
@@ -165,13 +165,14 @@ def generate_wrappers(glfw_header):
|
||||
GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback)
|
||||
GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *window, GLFWcocoatogglefullscreenfun callback)
|
||||
GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback)
|
||||
GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)
|
||||
void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, void* cocoa_key, void* cocoa_mods)
|
||||
void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)
|
||||
void* glfwGetX11Display(void)
|
||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
|
||||
const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)
|
||||
int glfwGetXKBScancode(const char* key_name, int case_sensitive)
|
||||
int glfwGetNativeKeyForName(const char* key_name, int case_sensitive)
|
||||
void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)
|
||||
unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, \
|
||||
const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *data)
|
||||
@@ -185,15 +186,23 @@ const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callbac
|
||||
p = src.find('*/', p)
|
||||
preamble = src[p + 2:first]
|
||||
header = '''\
|
||||
//
|
||||
// THIS FILE IS GENERATED BY glfw.py
|
||||
//
|
||||
// SAVE YOURSELF SOME TIME, DO NOT MANUALLY EDIT
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "monotonic.h"
|
||||
|
||||
{}
|
||||
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
|
||||
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
|
||||
|
||||
695
glfw/glfw3.h
vendored
695
glfw/glfw3.h
vendored
File diff suppressed because it is too large
Load Diff
29
glfw/ibus_glfw.c
vendored
29
glfw/ibus_glfw.c
vendored
@@ -107,10 +107,14 @@ get_ibus_text_from_message(DBusMessage *msg) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, int state) {
|
||||
send_text(const char *text, int ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
w->callbacks.keyboard((GLFWwindow*) w, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0, text, state);
|
||||
GLFWkeyevent fake_ev;
|
||||
_glfwInitializeKeyEvent(&fake_ev, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0);
|
||||
fake_ev.text = text;
|
||||
fake_ev.ime_state = ime_state;
|
||||
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,31 +397,36 @@ ibus_key_state(unsigned int glfw_modifiers, int action) {
|
||||
void
|
||||
key_event_processed(DBusMessage *msg, const char* errmsg, void *data) {
|
||||
uint32_t handled = 0;
|
||||
KeyEvent *ev = (KeyEvent*)data;
|
||||
bool is_release = ev->action == GLFW_RELEASE;
|
||||
_GLFWIBUSKeyEvent *ev = (_GLFWIBUSKeyEvent*)data;
|
||||
// Restore key's text from the text embedded in the structure.
|
||||
ev->glfw_ev.text = ev->__embedded_text;
|
||||
bool is_release = ev->glfw_ev.action == GLFW_RELEASE;
|
||||
bool failed = false;
|
||||
if (errmsg) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "IBUS: Failed to process key with error: %s", errmsg);
|
||||
failed = true;
|
||||
} else {
|
||||
glfw_dbus_get_args(msg, "Failed to get IBUS handled key from reply", DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID);
|
||||
debug("IBUS processed scancode: 0x%x release: %d handled: %u\n", ev->keycode, is_release, handled);
|
||||
debug("IBUS processed native_key: 0x%x release: %d handled: %u\n", ev->glfw_ev.native_key, is_release, handled);
|
||||
}
|
||||
glfw_xkb_key_from_ime(ev, handled ? true : false, failed);
|
||||
free(ev);
|
||||
}
|
||||
|
||||
bool
|
||||
ibus_process_key(const KeyEvent *ev_, _GLFWIBUSData *ibus) {
|
||||
ibus_process_key(const _GLFWIBUSKeyEvent *ev_, _GLFWIBUSData *ibus) {
|
||||
if (!check_connection(ibus)) return false;
|
||||
KeyEvent *ev = malloc(sizeof(KeyEvent));
|
||||
_GLFWIBUSKeyEvent *ev = calloc(1, sizeof(_GLFWIBUSKeyEvent));
|
||||
if (!ev) return false;
|
||||
memcpy(ev, ev_, sizeof(KeyEvent));
|
||||
uint32_t state = ibus_key_state(ev->glfw_modifiers, ev->action);
|
||||
memcpy(ev, ev_, sizeof(_GLFWIBUSKeyEvent));
|
||||
// Put the key's text in a field IN the structure, for proper serialization.
|
||||
if (ev->glfw_ev.text) strncpy(ev->__embedded_text, ev->glfw_ev.text, sizeof(ev->__embedded_text) - 1);
|
||||
ev->glfw_ev.text = NULL;
|
||||
uint32_t state = ibus_key_state(ev->glfw_ev.mods, ev->glfw_ev.action);
|
||||
if (!glfw_dbus_call_method_with_reply(
|
||||
ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "ProcessKeyEvent",
|
||||
3000, key_event_processed, ev,
|
||||
DBUS_TYPE_UINT32, &ev->ibus_sym, DBUS_TYPE_UINT32, &ev->ibus_keycode, DBUS_TYPE_UINT32,
|
||||
DBUS_TYPE_UINT32, &ev->ibus_keysym, DBUS_TYPE_UINT32, &ev->ibus_keycode, DBUS_TYPE_UINT32,
|
||||
&state, DBUS_TYPE_INVALID)) {
|
||||
free(ev);
|
||||
return false;
|
||||
|
||||
15
glfw/ibus_glfw.h
vendored
15
glfw/ibus_glfw.h
vendored
@@ -27,6 +27,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
#include "dbus_glfw.h"
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
@@ -38,18 +39,16 @@ typedef struct {
|
||||
} _GLFWIBUSData;
|
||||
|
||||
typedef struct {
|
||||
xkb_keycode_t keycode, ibus_keycode;
|
||||
xkb_keysym_t keysym, ibus_sym;
|
||||
unsigned int glfw_modifiers;
|
||||
int action;
|
||||
xkb_keycode_t ibus_keycode;
|
||||
xkb_keysym_t ibus_keysym;
|
||||
GLFWid window_id;
|
||||
int glfw_keycode;
|
||||
char text[64];
|
||||
} KeyEvent;
|
||||
GLFWkeyevent glfw_ev;
|
||||
char __embedded_text[64];
|
||||
} _GLFWIBUSKeyEvent;
|
||||
|
||||
void glfw_connect_to_ibus(_GLFWIBUSData *ibus);
|
||||
void glfw_ibus_terminate(_GLFWIBUSData *ibus);
|
||||
void glfw_ibus_set_focused(_GLFWIBUSData *ibus, bool focused);
|
||||
void glfw_ibus_dispatch(_GLFWIBUSData *ibus);
|
||||
bool ibus_process_key(const KeyEvent *ev_, _GLFWIBUSData *ibus);
|
||||
bool ibus_process_key(const _GLFWIBUSKeyEvent *ev_, _GLFWIBUSData *ibus);
|
||||
void glfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h);
|
||||
|
||||
10
glfw/init.c
vendored
10
glfw/init.c
vendored
@@ -201,7 +201,7 @@ _glfwDebug(const char *format, ...) {
|
||||
{
|
||||
va_list vl;
|
||||
|
||||
fprintf(stderr, "[%.4f] ", glfwGetTime());
|
||||
fprintf(stderr, "[%.4f] ", monotonic_t_to_s_double(glfwGetTime()));
|
||||
va_start(vl, format);
|
||||
vfprintf(stderr, format, vl);
|
||||
va_end(vl);
|
||||
@@ -214,10 +214,11 @@ _glfwDebug(const char *format, ...) {
|
||||
////// GLFW public API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI int glfwInit(void)
|
||||
GLFWAPI int glfwInit(monotonic_t start_time)
|
||||
{
|
||||
if (_glfw.initialized)
|
||||
return true;
|
||||
monotonic_start_time = start_time;
|
||||
|
||||
memset(&_glfw, 0, sizeof(_glfw));
|
||||
_glfw.hints.init = _glfwInitHints;
|
||||
@@ -239,7 +240,6 @@ GLFWAPI int glfwInit(void)
|
||||
_glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError);
|
||||
|
||||
_glfw.initialized = true;
|
||||
_glfw.timer.offset = _glfwPlatformGetTimerValue();
|
||||
|
||||
glfwDefaultWindowHints();
|
||||
|
||||
@@ -350,13 +350,13 @@ GLFWAPI void glfwStopMainLoop(void) {
|
||||
}
|
||||
|
||||
GLFWAPI unsigned long long glfwAddTimer(
|
||||
double interval, bool repeats, GLFWuserdatafun callback,
|
||||
monotonic_t interval, bool repeats, GLFWuserdatafun callback,
|
||||
void *callback_data, GLFWuserdatafun free_callback)
|
||||
{
|
||||
return _glfwPlatformAddTimer(interval, repeats, callback, callback_data, free_callback);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, double interval, bool enabled) {
|
||||
GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {
|
||||
_glfwPlatformUpdateTimer(timer_id, interval, enabled);
|
||||
}
|
||||
|
||||
|
||||
73
glfw/input.c
vendored
73
glfw/input.c
vendored
@@ -28,6 +28,7 @@
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
@@ -256,33 +257,44 @@ static bool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods)
|
||||
{
|
||||
ev->key = key;
|
||||
ev->native_key = native_key;
|
||||
ev->action = action;
|
||||
ev->mods = mods;
|
||||
ev->text = NULL;
|
||||
ev->ime_state = 0;
|
||||
}
|
||||
|
||||
// Notifies shared code of a physical key event
|
||||
//
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state)
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
|
||||
{
|
||||
if (key >= 0 && key <= GLFW_KEY_LAST)
|
||||
if (ev->key >= 0 && ev->key <= GLFW_KEY_LAST)
|
||||
{
|
||||
bool repeated = false;
|
||||
|
||||
if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE)
|
||||
if (ev->action == GLFW_RELEASE && window->keys[ev->key] == GLFW_RELEASE)
|
||||
return;
|
||||
|
||||
if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS)
|
||||
if (ev->action == GLFW_PRESS && window->keys[ev->key] == GLFW_PRESS)
|
||||
repeated = true;
|
||||
|
||||
if (action == GLFW_RELEASE && window->stickyKeys)
|
||||
window->keys[key] = _GLFW_STICK;
|
||||
if (ev->action == GLFW_RELEASE && window->stickyKeys)
|
||||
window->keys[ev->key] = _GLFW_STICK;
|
||||
else
|
||||
window->keys[key] = (char) action;
|
||||
window->keys[ev->key] = (char) ev->action;
|
||||
|
||||
if (repeated)
|
||||
action = GLFW_REPEAT;
|
||||
ev->action = GLFW_REPEAT;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: will need to update ev->virtual_mods here too?
|
||||
if (window->callbacks.keyboard) {
|
||||
if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
window->callbacks.keyboard((GLFWwindow*) window, key, scancode, action, mods, text, state);
|
||||
if (!window->lockKeyMods) ev->mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
window->callbacks.keyboard((GLFWwindow*) window, ev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,7 +756,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetKeyName(int key, int scancode)
|
||||
GLFWAPI const char* glfwGetKeyName(int key, int native_key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
@@ -757,13 +769,13 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scancode = _glfwPlatformGetKeyScancode(key);
|
||||
native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
}
|
||||
|
||||
return _glfwPlatformGetScancodeName(scancode);
|
||||
return _glfwPlatformGetNativeKeyName(native_key);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKeyScancode(int key)
|
||||
GLFWAPI int glfwGetNativeKeyForKey(int key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(-1);
|
||||
|
||||
@@ -773,7 +785,7 @@ GLFWAPI int glfwGetKeyScancode(int key)
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
|
||||
return _glfwPlatformGetKeyScancode(key);
|
||||
return _glfwPlatformGetNativeKeyForKey(key);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKey(GLFWwindow* handle, int key)
|
||||
@@ -1466,35 +1478,8 @@ GLFWAPI const char* glfwGetPrimarySelectionString(GLFWwindow* handle UNUSED)
|
||||
}
|
||||
#endif
|
||||
|
||||
GLFWAPI double glfwGetTime(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0.0);
|
||||
return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) /
|
||||
_glfwPlatformGetTimerFrequency();
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetTime(double time)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (time != time || time < 0.0 || time > 18446744073.0)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time);
|
||||
return;
|
||||
}
|
||||
|
||||
_glfw.timer.offset = _glfwPlatformGetTimerValue() -
|
||||
(uint64_t) (time * _glfwPlatformGetTimerFrequency());
|
||||
}
|
||||
|
||||
GLFWAPI uint64_t glfwGetTimerValue(void)
|
||||
GLFWAPI monotonic_t glfwGetTime(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||
return _glfwPlatformGetTimerValue();
|
||||
}
|
||||
|
||||
GLFWAPI uint64_t glfwGetTimerFrequency(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||
return _glfwPlatformGetTimerFrequency();
|
||||
return monotonic();
|
||||
}
|
||||
|
||||
26
glfw/internal.h
vendored
26
glfw/internal.h
vendored
@@ -27,6 +27,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#if defined(_GLFW_USE_CONFIG_H)
|
||||
#include "glfw_config.h"
|
||||
#endif
|
||||
@@ -572,12 +574,6 @@ struct _GLFWlibrary
|
||||
_GLFWtls contextSlot;
|
||||
_GLFWmutex errorLock;
|
||||
|
||||
struct {
|
||||
uint64_t offset;
|
||||
// This is defined in the platform's time.h
|
||||
_GLFW_PLATFORM_LIBRARY_TIMER_STATE;
|
||||
} timer;
|
||||
|
||||
struct {
|
||||
bool available;
|
||||
void* handle;
|
||||
@@ -638,8 +634,8 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape
|
||||
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
|
||||
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode);
|
||||
int _glfwPlatformGetKeyScancode(int key);
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key);
|
||||
int _glfwPlatformGetNativeKeyForKey(int key);
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||
@@ -661,9 +657,6 @@ const char* _glfwPlatformGetPrimarySelectionString(void);
|
||||
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
|
||||
void _glfwPlatformUpdateGamepadGUID(char* guid);
|
||||
|
||||
uint64_t _glfwPlatformGetTimerValue(void);
|
||||
uint64_t _glfwPlatformGetTimerFrequency(void);
|
||||
|
||||
int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
@@ -687,7 +680,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
int* right, int* bottom);
|
||||
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||
float* xscale, float* yscale);
|
||||
double _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window);
|
||||
monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window);
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformRestoreWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
|
||||
@@ -716,7 +709,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c,
|
||||
|
||||
void _glfwPlatformPollEvents(void);
|
||||
void _glfwPlatformWaitEvents(void);
|
||||
void _glfwPlatformWaitEventsTimeout(double timeout);
|
||||
void _glfwPlatformWaitEventsTimeout(monotonic_t timeout);
|
||||
void _glfwPlatformPostEmptyEvent(void);
|
||||
|
||||
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions);
|
||||
@@ -756,7 +749,8 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
|
||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state);
|
||||
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods);
|
||||
void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags);
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
@@ -823,8 +817,8 @@ _GLFWwindow* _glfwFocusedWindow(void);
|
||||
_GLFWwindow* _glfwWindowForId(GLFWid id);
|
||||
void _glfwPlatformRunMainLoop(GLFWtickcallback, void*);
|
||||
void _glfwPlatformStopMainLoop(void);
|
||||
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback);
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, bool enabled);
|
||||
unsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback);
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled);
|
||||
void _glfwPlatformRemoveTimer(unsigned long long timer_id);
|
||||
|
||||
char* _glfw_strdup(const char* source);
|
||||
|
||||
3
glfw/linux_joystick.c
vendored
3
glfw/linux_joystick.c
vendored
@@ -222,7 +222,8 @@ static bool openJoystickDevice(const char* path)
|
||||
}
|
||||
}
|
||||
|
||||
_GLFWjoystick* js = _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
|
||||
_GLFWjoystick* js =
|
||||
_glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
|
||||
if (!js)
|
||||
{
|
||||
close(linjs.fd);
|
||||
|
||||
5
glfw/main_loop.h
vendored
5
glfw/main_loop.h
vendored
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#ifndef GLFW_LOOP_BACKEND
|
||||
#define GLFW_LOOP_BACKEND x11
|
||||
@@ -36,7 +37,7 @@ void _glfwPlatformRunMainLoop(GLFWtickcallback tick_callback, void* data) {
|
||||
EVDBG("main loop exiting");
|
||||
}
|
||||
|
||||
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback) {
|
||||
unsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback) {
|
||||
return addTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, "user timer", interval, 1, repeats, callback, callback_data, free_callback);
|
||||
}
|
||||
|
||||
@@ -44,7 +45,7 @@ void _glfwPlatformRemoveTimer(unsigned long long timer_id) {
|
||||
removeTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id);
|
||||
}
|
||||
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, bool enabled) {
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {
|
||||
changeTimerInterval(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id, interval);
|
||||
toggleTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id, enabled);
|
||||
}
|
||||
|
||||
10
glfw/monotonic.c
vendored
Normal file
10
glfw/monotonic.c
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* monotonic.c
|
||||
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#define MONOTONIC_IMPLEMENTATION
|
||||
#include "../kitty/monotonic.h"
|
||||
4
glfw/nsgl_context.h
vendored
4
glfw/nsgl_context.h
vendored
@@ -32,8 +32,8 @@
|
||||
//
|
||||
typedef struct _GLFWcontextNSGL
|
||||
{
|
||||
id pixelFormat;
|
||||
id object;
|
||||
id pixelFormat;
|
||||
id object;
|
||||
|
||||
} _GLFWcontextNSGL;
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ bool _glfwCreateContextNSGL(_GLFWwindow* window,
|
||||
return false;
|
||||
}
|
||||
|
||||
NSOpenGLContext* share = NULL;
|
||||
NSOpenGLContext* share = nil;
|
||||
|
||||
if (ctxconfig->share)
|
||||
share = ctxconfig->share->context.nsgl.object;
|
||||
@@ -293,8 +293,7 @@ bool _glfwCreateContextNSGL(_GLFWwindow* window,
|
||||
forParameter:NSOpenGLContextParameterSurfaceOpacity];
|
||||
}
|
||||
|
||||
if (window->ns.retina)
|
||||
[window->ns.view setWantsBestResolutionOpenGLSurface:YES];
|
||||
[window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina];
|
||||
|
||||
GLint interval = 0;
|
||||
[window->context.nsgl.object setValues:&interval
|
||||
@@ -325,7 +324,7 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)
|
||||
if (window->context.client == GLFW_NO_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return NULL;
|
||||
return nil;
|
||||
}
|
||||
|
||||
return window->context.nsgl.object;
|
||||
|
||||
2
glfw/null_init.c
vendored
2
glfw/null_init.c
vendored
@@ -36,7 +36,6 @@
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
_glfwInitTimerPOSIX();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,4 +48,3 @@ const char* _glfwPlatformGetVersionString(void)
|
||||
{
|
||||
return _GLFW_VERSION_NUMBER " null OSMesa";
|
||||
}
|
||||
|
||||
|
||||
11
glfw/null_window.c
vendored
11
glfw/null_window.c
vendored
@@ -28,6 +28,7 @@
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
|
||||
static int createNativeWindow(_GLFWwindow* window,
|
||||
@@ -150,9 +151,9 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,
|
||||
*yscale = 1.f;
|
||||
}
|
||||
|
||||
double _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return 0.5;
|
||||
return ms_to_monotonic_t(500ll);
|
||||
}
|
||||
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window UNUSED)
|
||||
@@ -257,7 +258,7 @@ void _glfwPlatformWaitEvents(void)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformWaitEventsTimeout(double timeout UNUSED)
|
||||
void _glfwPlatformWaitEventsTimeout(monotonic_t timeout UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -306,12 +307,12 @@ const char* _glfwPlatformGetClipboardString(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode UNUSED)
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key UNUSED)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
int _glfwPlatformGetKeyScancode(int key UNUSED)
|
||||
int _glfwPlatformGetNativeKeyForKey(int key UNUSED)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
87
glfw/posix_time.c
vendored
87
glfw/posix_time.c
vendored
@@ -1,87 +0,0 @@
|
||||
//========================================================================
|
||||
// GLFW 3.4 POSIX - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialise timer
|
||||
//
|
||||
void _glfwInitTimerPOSIX(void)
|
||||
{
|
||||
#if defined(CLOCK_MONOTONIC)
|
||||
struct timespec ts;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
|
||||
{
|
||||
_glfw.timer.posix.monotonic = true;
|
||||
_glfw.timer.posix.frequency = 1000000000;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
_glfw.timer.posix.monotonic = false;
|
||||
_glfw.timer.posix.frequency = 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint64_t _glfwPlatformGetTimerValue(void)
|
||||
{
|
||||
#if defined(CLOCK_MONOTONIC)
|
||||
if (_glfw.timer.posix.monotonic)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t _glfwPlatformGetTimerFrequency(void)
|
||||
{
|
||||
return _glfw.timer.posix.frequency;
|
||||
}
|
||||
|
||||
44
glfw/posix_time.h
vendored
44
glfw/posix_time.h
vendored
@@ -1,44 +0,0 @@
|
||||
//========================================================================
|
||||
// GLFW 3.4 POSIX - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
|
||||
#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// POSIX-specific global timer data
|
||||
//
|
||||
typedef struct _GLFWtimerPOSIX
|
||||
{
|
||||
bool monotonic;
|
||||
uint64_t frequency;
|
||||
|
||||
} _GLFWtimerPOSIX;
|
||||
|
||||
|
||||
void _glfwInitTimerPOSIX(void);
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"cocoa_joystick.m",
|
||||
"cocoa_monitor.m",
|
||||
"cocoa_window.m",
|
||||
"cocoa_time.c",
|
||||
"posix_thread.c",
|
||||
"nsgl_context.m",
|
||||
"egl_context.c",
|
||||
@@ -31,6 +30,7 @@
|
||||
"input.c",
|
||||
"monitor.c",
|
||||
"vulkan.c",
|
||||
"monotonic.c",
|
||||
"window.c"
|
||||
]
|
||||
},
|
||||
@@ -38,7 +38,6 @@
|
||||
"headers": [
|
||||
"null_platform.h",
|
||||
"null_joystick.h",
|
||||
"posix_time.h",
|
||||
"posix_thread.h",
|
||||
"osmesa_context.h"
|
||||
],
|
||||
@@ -47,7 +46,6 @@
|
||||
"null_monitor.c",
|
||||
"null_window.c",
|
||||
"null_joystick.c",
|
||||
"posix_time.c",
|
||||
"posix_thread.c",
|
||||
"osmesa_context.c"
|
||||
]
|
||||
@@ -55,7 +53,6 @@
|
||||
"wayland": {
|
||||
"headers": [
|
||||
"wl_platform.h",
|
||||
"posix_time.h",
|
||||
"posix_thread.h",
|
||||
"xkb_glfw.h",
|
||||
"dbus_glfw.h",
|
||||
@@ -81,7 +78,6 @@
|
||||
"wl_init.c",
|
||||
"wl_monitor.c",
|
||||
"wl_window.c",
|
||||
"posix_time.c",
|
||||
"posix_thread.c",
|
||||
"xkb_glfw.c",
|
||||
"dbus_glfw.c",
|
||||
@@ -125,7 +121,6 @@
|
||||
"dbus_glfw.h",
|
||||
"ibus_glfw.h",
|
||||
"backend_utils.h",
|
||||
"posix_time.h",
|
||||
"posix_thread.h",
|
||||
"glx_context.h",
|
||||
"egl_context.h",
|
||||
@@ -142,7 +137,6 @@
|
||||
"xkb_glfw.c",
|
||||
"dbus_glfw.c",
|
||||
"ibus_glfw.c",
|
||||
"posix_time.c",
|
||||
"posix_thread.c",
|
||||
"glx_context.c",
|
||||
"egl_context.c",
|
||||
|
||||
11
glfw/window.c
vendored
11
glfw/window.c
vendored
@@ -29,6 +29,7 @@
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
@@ -56,8 +57,10 @@ void _glfwInputWindowFocus(_GLFWwindow* window, bool focused)
|
||||
{
|
||||
if (window->keys[key] == GLFW_PRESS)
|
||||
{
|
||||
const int scancode = _glfwPlatformGetKeyScancode(key);
|
||||
_glfwInputKeyboard(window, key, scancode, GLFW_RELEASE, 0, "", 0);
|
||||
const int native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
GLFWkeyevent ev;
|
||||
_glfwInitializeKeyEvent(&ev, key, native_key, GLFW_RELEASE, 0);
|
||||
_glfwInputKeyboard(window, &ev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -736,12 +739,12 @@ GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle,
|
||||
_glfwPlatformGetWindowContentScale(window, xscale, yscale);
|
||||
}
|
||||
|
||||
GLFWAPI double glfwGetDoubleClickInterval(GLFWwindow* handle)
|
||||
GLFWAPI monotonic_t glfwGetDoubleClickInterval(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0.5f);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(ms_to_monotonic_t(500ll));
|
||||
return _glfwPlatformGetDoubleClickInterval(window);
|
||||
}
|
||||
|
||||
|
||||
58
glfw/wl_init.c
vendored
58
glfw/wl_init.c
vendored
@@ -29,6 +29,7 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "internal.h"
|
||||
#include "backend_utils.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@@ -136,6 +137,7 @@ static void pointerHandleLeave(void* data UNUSED,
|
||||
_glfw.wl.pointerSerial = serial;
|
||||
_glfw.wl.pointerFocus = NULL;
|
||||
_glfwInputCursorEnter(window, false);
|
||||
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
|
||||
}
|
||||
|
||||
static void setCursor(GLFWCursorShape shape)
|
||||
@@ -164,6 +166,7 @@ static void setCursor(GLFWCursorShape shape)
|
||||
wl_surface_damage(surface, 0, 0,
|
||||
image->width, image->height);
|
||||
wl_surface_commit(surface);
|
||||
_glfw.wl.cursorPreviousShape = shape;
|
||||
}
|
||||
|
||||
static void pointerHandleMotion(void* data UNUSED,
|
||||
@@ -174,43 +177,46 @@ static void pointerHandleMotion(void* data UNUSED,
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
GLFWCursorShape cursorShape = GLFW_ARROW_CURSOR;
|
||||
double x, y;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
return;
|
||||
window->wl.cursorPosX = wl_fixed_to_double(sx);
|
||||
window->wl.cursorPosY = wl_fixed_to_double(sy);
|
||||
x = wl_fixed_to_double(sx);
|
||||
y = wl_fixed_to_double(sy);
|
||||
|
||||
switch (window->wl.decorations.focus)
|
||||
{
|
||||
case mainWindow:
|
||||
_glfwInputCursorPos(window,
|
||||
window->wl.cursorPosX, window->wl.cursorPosY);
|
||||
window->wl.cursorPosX = x;
|
||||
window->wl.cursorPosY = y;
|
||||
_glfwInputCursorPos(window, x, y);
|
||||
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
|
||||
return;
|
||||
case topDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
cursorShape = GLFW_VRESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_ARROW_CURSOR;
|
||||
break;
|
||||
case leftDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
cursorShape = GLFW_NW_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_HRESIZE_CURSOR;
|
||||
break;
|
||||
case rightDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
cursorShape = GLFW_NE_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_HRESIZE_CURSOR;
|
||||
break;
|
||||
case bottomDecoration:
|
||||
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||
if (x < _GLFW_DECORATION_WIDTH)
|
||||
cursorShape = GLFW_SW_RESIZE_CURSOR;
|
||||
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
else if (x > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
cursorShape = GLFW_SE_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_VRESIZE_CURSOR;
|
||||
@@ -218,7 +224,8 @@ static void pointerHandleMotion(void* data UNUSED,
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
setCursor(cursorShape);
|
||||
if (_glfw.wl.cursorPreviousShape != cursorShape)
|
||||
setCursor(cursorShape);
|
||||
}
|
||||
|
||||
static void pointerHandleButton(void* data UNUSED,
|
||||
@@ -284,8 +291,8 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
{
|
||||
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
|
||||
_glfw.wl.seat, serial,
|
||||
window->wl.cursorPosX,
|
||||
window->wl.cursorPosY);
|
||||
(int32_t)window->wl.cursorPosX,
|
||||
(int32_t)window->wl.cursorPosY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -367,7 +374,7 @@ static void keyboardHandleEnter(void* data UNUSED,
|
||||
struct wl_keyboard* keyboard UNUSED,
|
||||
uint32_t serial UNUSED,
|
||||
struct wl_surface* surface,
|
||||
struct wl_array* keys UNUSED)
|
||||
struct wl_array* keys)
|
||||
{
|
||||
// Happens in the case we just destroyed the surface.
|
||||
if (!surface)
|
||||
@@ -383,6 +390,15 @@ static void keyboardHandleEnter(void* data UNUSED,
|
||||
|
||||
_glfw.wl.keyboardFocus = window;
|
||||
_glfwInputWindowFocus(window, true);
|
||||
uint32_t* key;
|
||||
if (keys && _glfw.wl.keyRepeatInfo.key) {
|
||||
wl_array_for_each(key, keys) {
|
||||
if (*key == _glfw.wl.keyRepeatInfo.key) {
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboardHandleLeave(void* data UNUSED,
|
||||
@@ -397,13 +413,14 @@ static void keyboardHandleLeave(void* data UNUSED,
|
||||
|
||||
_glfw.wl.keyboardFocus = NULL;
|
||||
_glfwInputWindowFocus(window, false);
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatchPendingKeyRepeats(id_type timer_id UNUSED, void *data UNUSED) {
|
||||
if (_glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||
glfw_xkb_handle_key_event(_glfw.wl.keyRepeatInfo.keyboardFocus, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, (1.0 / (double)_glfw.wl.keyboardRepeatRate));
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, (s_to_monotonic_t(1ll) / (monotonic_t)_glfw.wl.keyboardRepeatRate));
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
|
||||
}
|
||||
|
||||
@@ -421,6 +438,7 @@ static void keyboardHandleKey(void* data UNUSED,
|
||||
int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
|
||||
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);
|
||||
bool repeatable = false;
|
||||
_glfw.wl.keyRepeatInfo.key = 0;
|
||||
|
||||
if (action == GLFW_PRESS && _glfw.wl.keyboardRepeatRate > 0 && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))
|
||||
{
|
||||
@@ -429,7 +447,7 @@ static void keyboardHandleKey(void* data UNUSED,
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
|
||||
}
|
||||
if (repeatable) {
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, (double)(_glfw.wl.keyboardRepeatDelay) / 1000.0);
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, _glfw.wl.keyboardRepeatDelay);
|
||||
}
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, repeatable ? 1 : 0);
|
||||
}
|
||||
@@ -454,7 +472,7 @@ static void keyboardHandleRepeatInfo(void* data UNUSED,
|
||||
return;
|
||||
|
||||
_glfw.wl.keyboardRepeatRate = rate;
|
||||
_glfw.wl.keyboardRepeatDelay = delay;
|
||||
_glfw.wl.keyboardRepeatDelay = ms_to_monotonic_t(delay);
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboardListener = {
|
||||
@@ -607,8 +625,7 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
_glfwSetupWaylandDataDevice();
|
||||
}
|
||||
}
|
||||
else if (strcmp(interface, "zwp_primary_selection_device_manager_v1") == 0 ||
|
||||
strcmp(interface, "gtk_primary_selection_device_manager") == 0)
|
||||
else if (strcmp(interface, "zwp_primary_selection_device_manager_v1") == 0)
|
||||
{
|
||||
_glfw.wl.primarySelectionDeviceManager =
|
||||
wl_registry_bind(registry, name,
|
||||
@@ -728,8 +745,8 @@ int _glfwPlatformInit(void)
|
||||
"Wayland: Failed to initialize event loop data");
|
||||
}
|
||||
glfw_dbus_init(&_glfw.wl.dbus, &_glfw.wl.eventLoopData);
|
||||
_glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", 0.5, 0, true, dispatchPendingKeyRepeats, NULL, NULL);
|
||||
_glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", 0.5, 0, true, animateCursorImage, NULL, NULL);
|
||||
_glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", ms_to_monotonic_t(500ll), 0, true, dispatchPendingKeyRepeats, NULL, NULL);
|
||||
_glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", ms_to_monotonic_t(500ll), 0, true, animateCursorImage, NULL, NULL);
|
||||
|
||||
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
|
||||
wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL);
|
||||
@@ -749,7 +766,6 @@ int _glfwPlatformInit(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
_glfwInitTimerPOSIX();
|
||||
if (!_glfw.wl.wmBase)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
|
||||
5
glfw/wl_platform.h
vendored
5
glfw/wl_platform.h
vendored
@@ -43,7 +43,6 @@ typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWa
|
||||
typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*);
|
||||
|
||||
#include "posix_thread.h"
|
||||
#include "posix_time.h"
|
||||
#ifdef __linux__
|
||||
#include "linux_joystick.h"
|
||||
#else
|
||||
@@ -156,6 +155,7 @@ typedef struct _GLFWwindowWayland
|
||||
// We need to track the monitors the window spans on to calculate the
|
||||
// optimal scaling factor.
|
||||
int scale;
|
||||
bool initial_scale_notified;
|
||||
_GLFWmonitor** monitors;
|
||||
int monitorsCount;
|
||||
int monitorsSize;
|
||||
@@ -247,10 +247,11 @@ typedef struct _GLFWlibraryWayland
|
||||
|
||||
struct wl_cursor_theme* cursorTheme;
|
||||
struct wl_surface* cursorSurface;
|
||||
GLFWCursorShape cursorPreviousShape;
|
||||
uint32_t pointerSerial;
|
||||
|
||||
int32_t keyboardRepeatRate;
|
||||
int32_t keyboardRepeatDelay;
|
||||
monotonic_t keyboardRepeatDelay;
|
||||
struct {
|
||||
uint32_t key;
|
||||
id_type keyRepeatTimer;
|
||||
|
||||
52
glfw/wl_window.c
vendored
52
glfw/wl_window.c
vendored
@@ -32,6 +32,7 @@
|
||||
#include "backend_utils.h"
|
||||
#include "memfd.h"
|
||||
#include "linux_notify.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -54,13 +55,18 @@ static bool checkScaleChange(_GLFWwindow* window)
|
||||
if (_glfw.wl.compositorVersion < 3)
|
||||
return false;
|
||||
|
||||
// Get the scale factor from the highest scale monitor.
|
||||
// Get the scale factor from the highest scale monitor that this window is on
|
||||
for (i = 0; i < window->wl.monitorsCount; ++i)
|
||||
{
|
||||
monitorScale = window->wl.monitors[i]->wl.scale;
|
||||
if (scale < monitorScale)
|
||||
scale = monitorScale;
|
||||
}
|
||||
if (window->wl.monitorsCount < 1 && _glfw.monitorCount > 0) {
|
||||
// The window has not yet been assigned to any monitors, use the primary monitor
|
||||
_GLFWmonitor *m = _glfw.monitors[0];
|
||||
if (m && m->wl.scale > scale) scale = m->wl.scale;
|
||||
}
|
||||
|
||||
// Only change the framebuffer size if the scale changed.
|
||||
if (scale != window->wl.scale)
|
||||
@@ -69,6 +75,10 @@ static bool checkScaleChange(_GLFWwindow* window)
|
||||
wl_surface_set_buffer_scale(window->wl.surface, scale);
|
||||
return true;
|
||||
}
|
||||
if (window->wl.monitorsCount > 0 && !window->wl.initial_scale_notified) {
|
||||
window->wl.initial_scale_notified = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -241,8 +251,8 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image)
|
||||
if (fd < 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Creating a buffer file for %d B failed: %%m",
|
||||
length);
|
||||
"Wayland: Creating a buffer file for %d B failed: %s",
|
||||
length, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -250,7 +260,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image)
|
||||
if (data == MAP_FAILED)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: mmap failed: %%m");
|
||||
"Wayland: mmap failed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
@@ -545,9 +555,9 @@ static void xdgToplevelHandleConfigure(void* data,
|
||||
aspectRatio = (float)width / (float)height;
|
||||
targetRatio = (float)window->numer / (float)window->denom;
|
||||
if (aspectRatio < targetRatio)
|
||||
height = width / targetRatio;
|
||||
height = (int32_t)((float)width / targetRatio);
|
||||
else if (aspectRatio > targetRatio)
|
||||
width = height * targetRatio;
|
||||
width = (int32_t)((float)height * targetRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -684,7 +694,7 @@ setCursorImage(_GLFWcursorWayland* cursorWayland)
|
||||
image = cursorWayland->cursor->images[cursorWayland->currentImage];
|
||||
buffer = wl_cursor_image_get_buffer(image);
|
||||
if (image->delay) {
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, ((double)image->delay) / 1000.0);
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, ms_to_monotonic_t(image->delay));
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 1);
|
||||
} else {
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
|
||||
@@ -745,11 +755,11 @@ abortOnFatalError(int last_error) {
|
||||
}
|
||||
|
||||
static void
|
||||
handleEvents(double timeout)
|
||||
handleEvents(monotonic_t timeout)
|
||||
{
|
||||
struct wl_display* display = _glfw.wl.display;
|
||||
errno = 0;
|
||||
EVDBG("starting handleEvents(%.2f)", timeout);
|
||||
EVDBG("starting handleEvents(%.2f)", monotonic_t_to_s_double(timeout));
|
||||
|
||||
while (wl_display_prepare_read(display) != 0) {
|
||||
while(1) {
|
||||
@@ -1052,9 +1062,9 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||
*yscale = (float) window->wl.scale;
|
||||
}
|
||||
|
||||
double _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return 0.5;
|
||||
return ms_to_monotonic_t(500ll);
|
||||
}
|
||||
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
|
||||
@@ -1227,11 +1237,11 @@ void _glfwPlatformPollEvents(void)
|
||||
|
||||
void _glfwPlatformWaitEvents(void)
|
||||
{
|
||||
double timeout = wl_display_dispatch_pending(_glfw.wl.display) > 0 ? 0 : -1;
|
||||
monotonic_t timeout = wl_display_dispatch_pending(_glfw.wl.display) > 0 ? 0 : -1;
|
||||
handleEvents(timeout);
|
||||
}
|
||||
|
||||
void _glfwPlatformWaitEventsTimeout(double timeout)
|
||||
void _glfwPlatformWaitEventsTimeout(monotonic_t timeout)
|
||||
{
|
||||
if (wl_display_dispatch_pending(_glfw.wl.display) > 0) timeout = 0;
|
||||
handleEvents(timeout);
|
||||
@@ -1268,12 +1278,12 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode UNUSED)
|
||||
_glfwPlatformSetCursor(window, window->wl.currentCursor);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key)
|
||||
{
|
||||
return glfw_xkb_keysym_name(scancode);
|
||||
return glfw_xkb_keysym_name(native_key);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetKeyScancode(int key)
|
||||
int _glfwPlatformGetNativeKeyForKey(int key)
|
||||
{
|
||||
return glfw_xkb_sym_for_key(key);
|
||||
}
|
||||
@@ -1462,8 +1472,8 @@ static void send_text(char *text, int fd)
|
||||
{
|
||||
if (text) {
|
||||
size_t len = strlen(text), pos = 0;
|
||||
double start = glfwGetTime();
|
||||
while (pos < len && glfwGetTime() - start < 2.0) {
|
||||
monotonic_t start = glfwGetTime();
|
||||
while (pos < len && glfwGetTime() - start < s_to_monotonic_t(2ll)) {
|
||||
ssize_t ret = write(fd, text + pos, len - pos);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR) continue;
|
||||
@@ -1496,7 +1506,7 @@ static char* read_offer_string(int data_pipe) {
|
||||
struct pollfd fds;
|
||||
fds.fd = data_pipe;
|
||||
fds.events = POLLIN;
|
||||
double start = glfwGetTime();
|
||||
monotonic_t start = glfwGetTime();
|
||||
#define bail(...) { \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
||||
free(buf); buf = NULL; \
|
||||
@@ -1504,7 +1514,7 @@ static char* read_offer_string(int data_pipe) {
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
while (glfwGetTime() - start < 2) {
|
||||
while (glfwGetTime() - start < s_to_monotonic_t(2ll)) {
|
||||
int ret = poll(&fds, 1, 2000);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
@@ -2031,7 +2041,7 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
|
||||
return window->wl.surface;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetXKBScancode(const char* keyName, bool caseSensitive) {
|
||||
GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {
|
||||
return glfw_xkb_keysym_from_name(keyName, caseSensitive);
|
||||
}
|
||||
|
||||
|
||||
14
glfw/x11_init.c
vendored
14
glfw/x11_init.c
vendored
@@ -433,12 +433,10 @@ static bool initExtensions(void)
|
||||
//
|
||||
void _glfwGetSystemContentScaleX11(float* xscale, float* yscale, bool bypass_cache)
|
||||
{
|
||||
// NOTE: Default to the display-wide DPI as we don't currently have a policy
|
||||
// for which monitor a window is considered to be on
|
||||
float xdpi = DisplayWidth(_glfw.x11.display, _glfw.x11.screen) *
|
||||
25.4f / DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);
|
||||
float ydpi = DisplayHeight(_glfw.x11.display, _glfw.x11.screen) *
|
||||
25.4f / DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);
|
||||
// Start by assuming the default X11 DPI
|
||||
// NOTE: Some desktop environments (KDE) may remove the Xft.dpi field when it
|
||||
// would be set to 96, so assume that is the case if we cannot find it
|
||||
float xdpi = 96.f, ydpi = 96.f;
|
||||
|
||||
// NOTE: Basing the scale on Xft.dpi where available should provide the most
|
||||
// consistent user experience (matches Qt, Gtk, etc), although not
|
||||
@@ -468,7 +466,7 @@ void _glfwGetSystemContentScaleX11(float* xscale, float* yscale, bool bypass_cac
|
||||
if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value))
|
||||
{
|
||||
if (type && strcmp(type, "String") == 0)
|
||||
xdpi = ydpi = atof(value.addr);
|
||||
xdpi = ydpi = (float)atof(value.addr);
|
||||
}
|
||||
|
||||
XrmDestroyDatabase(db);
|
||||
@@ -637,8 +635,6 @@ int _glfwPlatformInit(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
_glfwInitTimerPOSIX();
|
||||
|
||||
_glfwPollMonitorsX11();
|
||||
return true;
|
||||
}
|
||||
|
||||
10
glfw/x11_monitor.c
vendored
10
glfw/x11_monitor.c
vendored
@@ -166,6 +166,16 @@ void _glfwPollMonitorsX11(void)
|
||||
heightMM = oi->mm_height;
|
||||
}
|
||||
|
||||
if (widthMM <= 0 || heightMM <= 0)
|
||||
{
|
||||
// HACK: If RandR does not provide a physical size, assume the
|
||||
// X11 default 96 DPI and calcuate from the CRTC viewport
|
||||
// NOTE: These members are affected by rotation, unlike the mode
|
||||
// info and output info members
|
||||
widthMM = (int) (ci->width * 25.4f / 96.f);
|
||||
heightMM = (int) (ci->height * 25.4f / 96.f);
|
||||
}
|
||||
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM);
|
||||
monitor->x11.output = sr->outputs[i];
|
||||
monitor->x11.crtc = oi->crtc;
|
||||
|
||||
1
glfw/x11_platform.h
vendored
1
glfw/x11_platform.h
vendored
@@ -152,7 +152,6 @@ typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSur
|
||||
typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t);
|
||||
|
||||
#include "posix_thread.h"
|
||||
#include "posix_time.h"
|
||||
#include "glx_context.h"
|
||||
#include "egl_context.h"
|
||||
#include "osmesa_context.h"
|
||||
|
||||
205
glfw/x11_window.c
vendored
205
glfw/x11_window.c
vendored
@@ -31,6 +31,7 @@
|
||||
#include "internal.h"
|
||||
#include "backend_utils.h"
|
||||
#include "linux_notify.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xmd.h>
|
||||
@@ -51,6 +52,10 @@
|
||||
#define Button6 6
|
||||
#define Button7 7
|
||||
|
||||
// Motif WM hints flags
|
||||
#define MWM_HINTS_DECORATIONS 2
|
||||
#define MWM_DECOR_ALL 1
|
||||
|
||||
#define _GLFW_XDND_VERSION 5
|
||||
|
||||
|
||||
@@ -61,8 +66,8 @@
|
||||
static unsigned _glfwDispatchX11Events(void);
|
||||
|
||||
static void
|
||||
handleEvents(double timeout) {
|
||||
EVDBG("starting handleEvents(%.2f)", timeout);
|
||||
handleEvents(monotonic_t timeout) {
|
||||
EVDBG("starting handleEvents(%.2f)", monotonic_t_to_s_double(timeout));
|
||||
int display_read_ok = pollForEvents(&_glfw.x11.eventLoopData, timeout);
|
||||
EVDBG("display_read_ok: %d", display_read_ok);
|
||||
if (display_read_ok) {
|
||||
@@ -77,9 +82,9 @@ handleEvents(double timeout) {
|
||||
}
|
||||
|
||||
static bool
|
||||
waitForX11Event(double timeout) {
|
||||
waitForX11Event(monotonic_t timeout) {
|
||||
// returns true if there is X11 data waiting to be read, does not run watches and timers
|
||||
double end_time = glfwGetTime() + timeout;
|
||||
monotonic_t end_time = glfwGetTime() + timeout;
|
||||
while(true) {
|
||||
if (timeout >= 0) {
|
||||
const int result = pollWithTimeout(_glfw.x11.eventLoopData.fds, 1, timeout);
|
||||
@@ -109,7 +114,7 @@ static bool waitForVisibilityNotify(_GLFWwindow* window)
|
||||
VisibilityNotify,
|
||||
&dummy))
|
||||
{
|
||||
if (!waitForX11Event(0.1))
|
||||
if (!waitForX11Event(ms_to_monotonic_t(100ll)))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -508,8 +513,8 @@ static bool createNativeWindow(_GLFWwindow* window,
|
||||
|
||||
if (wndconfig->scaleToMonitor)
|
||||
{
|
||||
width *= _glfw.x11.contentScaleX;
|
||||
height *= _glfw.x11.contentScaleY;
|
||||
width *= (int)_glfw.x11.contentScaleX;
|
||||
height *= (int)_glfw.x11.contentScaleY;
|
||||
}
|
||||
|
||||
// Create a colormap based on the visual used by the current context
|
||||
@@ -589,7 +594,7 @@ static bool createNativeWindow(_GLFWwindow* window,
|
||||
{
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
|
||||
PropModeReplace, (unsigned char*) &states, count);
|
||||
PropModeReplace, (unsigned char*) states, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,7 +885,7 @@ static const char* getSelectionString(Atom selection)
|
||||
Atom actualType;
|
||||
int actualFormat;
|
||||
unsigned long itemCount, bytesAfter;
|
||||
double start = glfwGetTime();
|
||||
monotonic_t start = glfwGetTime();
|
||||
XEvent notification, dummy;
|
||||
|
||||
XConvertSelection(_glfw.x11.display,
|
||||
@@ -895,10 +900,10 @@ static const char* getSelectionString(Atom selection)
|
||||
SelectionNotify,
|
||||
¬ification))
|
||||
{
|
||||
double time = glfwGetTime();
|
||||
if (time - start > 2)
|
||||
monotonic_t time = glfwGetTime();
|
||||
if (time - start > s_to_monotonic_t(2ll))
|
||||
return "";
|
||||
waitForX11Event(2.0 - (time - start));
|
||||
waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
|
||||
}
|
||||
|
||||
if (notification.xselection.property == None)
|
||||
@@ -935,10 +940,10 @@ static const char* getSelectionString(Atom selection)
|
||||
isSelPropNewValueNotify,
|
||||
(XPointer) ¬ification))
|
||||
{
|
||||
double time = glfwGetTime();
|
||||
if (time - start > 2)
|
||||
monotonic_t time = glfwGetTime();
|
||||
if (time - start > s_to_monotonic_t(2ll))
|
||||
return "";
|
||||
waitForX11Event(2.0 - (time - start));
|
||||
waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
|
||||
}
|
||||
|
||||
XFree(data);
|
||||
@@ -2066,7 +2071,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
isFrameExtentsEvent,
|
||||
(XPointer) window))
|
||||
{
|
||||
if (!waitForX11Event(0.5))
|
||||
if (!waitForX11Event(ms_to_monotonic_t(500ll)))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
|
||||
@@ -2103,9 +2108,9 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,
|
||||
*yscale = _glfw.x11.contentScaleY;
|
||||
}
|
||||
|
||||
double _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return 0.5;
|
||||
return ms_to_monotonic_t(500ll);
|
||||
}
|
||||
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
|
||||
@@ -2141,18 +2146,67 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
|
||||
|
||||
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (_glfw.x11.NET_WM_STATE &&
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
|
||||
if (!_glfw.x11.NET_WM_STATE ||
|
||||
!_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
|
||||
!_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_glfwPlatformWindowVisible(window))
|
||||
{
|
||||
sendEventToWM(window,
|
||||
_glfw.x11.NET_WM_STATE,
|
||||
_NET_WM_STATE_ADD,
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
|
||||
1, 0);
|
||||
XFlush(_glfw.x11.display);
|
||||
_glfw.x11.NET_WM_STATE,
|
||||
_NET_WM_STATE_ADD,
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
|
||||
1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Atom* states = NULL;
|
||||
unsigned long count =
|
||||
_glfwGetWindowPropertyX11(window->x11.handle,
|
||||
_glfw.x11.NET_WM_STATE,
|
||||
XA_ATOM,
|
||||
(unsigned char**) &states);
|
||||
|
||||
// NOTE: We don't check for failure as this property may not exist yet
|
||||
// and that's fine (and we'll create it implicitly with append)
|
||||
|
||||
Atom missing[2] =
|
||||
{
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
|
||||
_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
|
||||
};
|
||||
unsigned long missingCount = 2;
|
||||
|
||||
for (unsigned long i = 0; i < count; i++)
|
||||
{
|
||||
for (unsigned long j = 0; j < missingCount; j++)
|
||||
{
|
||||
if (states[i] == missing[j])
|
||||
{
|
||||
missing[j] = missing[missingCount - 1];
|
||||
missingCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (states)
|
||||
XFree(states);
|
||||
|
||||
if (!missingCount)
|
||||
return;
|
||||
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
|
||||
PropModeAppend,
|
||||
(unsigned char*) missing,
|
||||
missingCount);
|
||||
}
|
||||
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window)
|
||||
@@ -2172,6 +2226,9 @@ void _glfwPlatformHideWindow(_GLFWwindow* window)
|
||||
|
||||
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
{
|
||||
if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
|
||||
return;
|
||||
|
||||
sendEventToWM(window,
|
||||
_glfw.x11.NET_WM_STATE,
|
||||
_NET_WM_STATE_ADD,
|
||||
@@ -2350,33 +2407,24 @@ void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled UNUSED)
|
||||
|
||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
struct
|
||||
{
|
||||
XDeleteProperty(_glfw.x11.display,
|
||||
window->x11.handle,
|
||||
_glfw.x11.MOTIF_WM_HINTS);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long functions;
|
||||
unsigned long decorations;
|
||||
long input_mode;
|
||||
unsigned long status;
|
||||
} hints;
|
||||
unsigned long flags;
|
||||
unsigned long functions;
|
||||
unsigned long decorations;
|
||||
long input_mode;
|
||||
unsigned long status;
|
||||
} hints = {0};
|
||||
|
||||
hints.flags = 2; // Set decorations
|
||||
hints.decorations = 0; // No decorations
|
||||
hints.flags = MWM_HINTS_DECORATIONS;
|
||||
hints.decorations = enabled ? MWM_DECOR_ALL : 0;
|
||||
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.MOTIF_WM_HINTS,
|
||||
_glfw.x11.MOTIF_WM_HINTS, 32,
|
||||
PropModeReplace,
|
||||
(unsigned char*) &hints,
|
||||
sizeof(hints) / sizeof(long));
|
||||
}
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.MOTIF_WM_HINTS,
|
||||
_glfw.x11.MOTIF_WM_HINTS, 32,
|
||||
PropModeReplace,
|
||||
(unsigned char*) &hints,
|
||||
sizeof(hints) / sizeof(long));
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
|
||||
@@ -2386,7 +2434,7 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
|
||||
|
||||
if (_glfwPlatformWindowVisible(window))
|
||||
{
|
||||
const Atom action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
|
||||
const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
|
||||
sendEventToWM(window,
|
||||
_glfw.x11.NET_WM_STATE,
|
||||
action,
|
||||
@@ -2395,15 +2443,16 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
|
||||
}
|
||||
else
|
||||
{
|
||||
Atom* states;
|
||||
Atom* states = NULL;
|
||||
unsigned long i, count;
|
||||
|
||||
count = _glfwGetWindowPropertyX11(window->x11.handle,
|
||||
_glfw.x11.NET_WM_STATE,
|
||||
XA_ATOM,
|
||||
(unsigned char**) &states);
|
||||
if (!states)
|
||||
return;
|
||||
|
||||
// NOTE: We don't check for failure as this property may not exist yet
|
||||
// and that's fine (and we'll create it implicitly with append)
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
@@ -2413,32 +2462,36 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == count)
|
||||
{
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
|
||||
PropModeAppend,
|
||||
(unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
|
||||
1);
|
||||
}
|
||||
if (i < count)
|
||||
return;
|
||||
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
|
||||
PropModeAppend,
|
||||
(unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
|
||||
1);
|
||||
}
|
||||
else
|
||||
else if (states)
|
||||
{
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
|
||||
{
|
||||
states[i] = states[count - 1];
|
||||
count--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == count)
|
||||
return;
|
||||
|
||||
states[i] = states[count - 1];
|
||||
count--;
|
||||
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
|
||||
PropModeReplace, (unsigned char*) &states, count);
|
||||
PropModeReplace, (unsigned char*) states, count);
|
||||
}
|
||||
|
||||
XFree(states);
|
||||
if (states)
|
||||
XFree(states);
|
||||
}
|
||||
|
||||
XFlush(_glfw.x11.display);
|
||||
@@ -2527,11 +2580,11 @@ void _glfwPlatformPollEvents(void)
|
||||
|
||||
void _glfwPlatformWaitEvents(void)
|
||||
{
|
||||
double timeout = _glfwDispatchX11Events() ? 0 : -1;
|
||||
monotonic_t timeout = _glfwDispatchX11Events() ? 0 : -1;
|
||||
handleEvents(timeout);
|
||||
}
|
||||
|
||||
void _glfwPlatformWaitEventsTimeout(double timeout)
|
||||
void _glfwPlatformWaitEventsTimeout(monotonic_t timeout)
|
||||
{
|
||||
if (_glfwDispatchX11Events()) timeout = 0;
|
||||
handleEvents(timeout);
|
||||
@@ -2585,13 +2638,13 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key)
|
||||
{
|
||||
|
||||
return glfw_xkb_keysym_name(scancode);
|
||||
return glfw_xkb_keysym_name(native_key);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetKeyScancode(int key)
|
||||
int _glfwPlatformGetNativeKeyForKey(int key)
|
||||
{
|
||||
return glfw_xkb_sym_for_key(key);
|
||||
}
|
||||
@@ -2870,7 +2923,7 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
|
||||
return window->x11.handle;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetXKBScancode(const char* keyName, bool caseSensitive) {
|
||||
GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {
|
||||
return glfw_xkb_keysym_from_name(keyName, caseSensitive);
|
||||
}
|
||||
|
||||
|
||||
119
glfw/xkb_glfw.c
vendored
119
glfw/xkb_glfw.c
vendored
@@ -437,24 +437,22 @@ glfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_m
|
||||
}
|
||||
|
||||
bool
|
||||
glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t scancode) {
|
||||
glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t keycode) {
|
||||
#ifdef _GLFW_WAYLAND
|
||||
scancode += 8;
|
||||
keycode += 8;
|
||||
#endif
|
||||
return xkb_keymap_key_repeats(xkb->keymap, scancode);
|
||||
return xkb_keymap_key_repeats(xkb->keymap, keycode);
|
||||
}
|
||||
|
||||
|
||||
static KeyEvent key_event = {0};
|
||||
|
||||
static inline xkb_keysym_t
|
||||
compose_symbol(struct xkb_compose_state *composeState, xkb_keysym_t sym, int *compose_completed) {
|
||||
compose_symbol(struct xkb_compose_state *composeState, xkb_keysym_t sym, int *compose_completed, char *key_text, int n) {
|
||||
*compose_completed = 0;
|
||||
if (sym == XKB_KEY_NoSymbol || !composeState) return sym;
|
||||
if (xkb_compose_state_feed(composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym;
|
||||
switch (xkb_compose_state_get_status(composeState)) {
|
||||
case XKB_COMPOSE_COMPOSED:
|
||||
xkb_compose_state_get_utf8(composeState, key_event.text, sizeof(key_event.text));
|
||||
xkb_compose_state_get_utf8(composeState, key_text, n);
|
||||
*compose_completed = 1;
|
||||
return xkb_compose_state_get_one_sym(composeState);
|
||||
case XKB_COMPOSE_COMPOSING:
|
||||
@@ -534,11 +532,14 @@ glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, i
|
||||
}
|
||||
|
||||
void
|
||||
glfw_xkb_key_from_ime(KeyEvent *ev, bool handled_by_ime, bool failed) {
|
||||
glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) {
|
||||
_GLFWwindow *window = _glfwWindowForId(ev->window_id);
|
||||
if (failed && window && window->callbacks.keyboard) {
|
||||
// notify application to remove any existing pre-edit text
|
||||
window->callbacks.keyboard((GLFWwindow*) window, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0, "", 1);
|
||||
GLFWkeyevent fake_ev;
|
||||
_glfwInitializeKeyEvent(&fake_ev, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0);
|
||||
fake_ev.ime_state = 1;
|
||||
window->callbacks.keyboard((GLFWwindow*) window, &fake_ev);
|
||||
}
|
||||
static xkb_keycode_t last_handled_press_keycode = 0;
|
||||
// We filter out release events that correspond to the last press event
|
||||
@@ -547,53 +548,58 @@ glfw_xkb_key_from_ime(KeyEvent *ev, bool handled_by_ime, bool failed) {
|
||||
// you'd need to implement a ring buffer to store pending key presses.
|
||||
xkb_keycode_t prev_handled_press = last_handled_press_keycode;
|
||||
last_handled_press_keycode = 0;
|
||||
bool is_release = ev->action == GLFW_RELEASE;
|
||||
debug("From IBUS: scancode: 0x%x name: %s is_release: %d\n", ev->keycode, glfw_xkb_keysym_name(ev->keysym), is_release);
|
||||
if (window && !handled_by_ime && !(is_release && ev->keycode == prev_handled_press)) {
|
||||
bool is_release = ev->glfw_ev.action == GLFW_RELEASE;
|
||||
debug("From IBUS: native_key: 0x%x name: %s is_release: %d\n", ev->glfw_ev.native_key, glfw_xkb_keysym_name(ev->glfw_ev.key), is_release);
|
||||
if (window && !handled_by_ime && !(is_release && ev->glfw_ev.native_key == (int) prev_handled_press)) {
|
||||
debug("↳ to application: glfw_keycode: 0x%x (%s) keysym: 0x%x (%s) action: %s %s text: %s\n",
|
||||
ev->glfw_keycode, _glfwGetKeyName(ev->glfw_keycode), ev->keysym, glfw_xkb_keysym_name(ev->keysym),
|
||||
(ev->action == GLFW_RELEASE ? "RELEASE" : (ev->action == GLFW_PRESS ? "PRESS" : "REPEAT")),
|
||||
format_mods(ev->glfw_modifiers), ev->text
|
||||
ev->glfw_ev.native_key, _glfwGetKeyName(ev->glfw_ev.native_key), ev->glfw_ev.key, glfw_xkb_keysym_name(ev->glfw_ev.key),
|
||||
(ev->glfw_ev.action == GLFW_RELEASE ? "RELEASE" : (ev->glfw_ev.action == GLFW_PRESS ? "PRESS" : "REPEAT")),
|
||||
format_mods(ev->glfw_ev.mods), ev->glfw_ev.text
|
||||
);
|
||||
_glfwInputKeyboard(window, ev->glfw_keycode, ev->keysym, ev->action, ev->glfw_modifiers, ev->text, 0);
|
||||
|
||||
ev->glfw_ev.ime_state = 0;
|
||||
_glfwInputKeyboard(window, &ev->glfw_ev);
|
||||
} else debug("↳ discarded\n");
|
||||
if (!is_release && handled_by_ime) last_handled_press_keycode = ev->keycode;
|
||||
if (!is_release && handled_by_ime)
|
||||
last_handled_press_keycode = ev->glfw_ev.native_key;
|
||||
}
|
||||
|
||||
void
|
||||
glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action) {
|
||||
glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t xkb_keycode, int action) {
|
||||
static char key_text[64] = {0};
|
||||
const xkb_keysym_t *syms, *clean_syms, *default_syms;
|
||||
xkb_keysym_t glfw_sym;
|
||||
xkb_keycode_t code_for_sym = scancode;
|
||||
key_event.ibus_keycode = scancode;
|
||||
xkb_keysym_t xkb_sym;
|
||||
xkb_keycode_t code_for_sym = xkb_keycode, ibus_keycode = xkb_keycode;
|
||||
GLFWkeyevent glfw_ev;
|
||||
_glfwInitializeKeyEvent(&glfw_ev, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0); // init with default values
|
||||
#ifdef _GLFW_WAYLAND
|
||||
code_for_sym += 8;
|
||||
#else
|
||||
key_event.ibus_keycode -= 8;
|
||||
ibus_keycode -= 8;
|
||||
#endif
|
||||
debug("%s scancode: 0x%x ", action == GLFW_RELEASE ? "Release" : "Press", scancode);
|
||||
debug("%s xkb_keycode: 0x%x ", action == GLFW_RELEASE ? "Release" : "Press", xkb_keycode);
|
||||
XKBStateGroup *sg = &xkb->states;
|
||||
int num_syms = xkb_state_key_get_syms(sg->state, code_for_sym, &syms);
|
||||
int num_clean_syms = xkb_state_key_get_syms(sg->clean_state, code_for_sym, &clean_syms);
|
||||
key_event.text[0] = 0;
|
||||
key_text[0] = 0;
|
||||
// According to the documentation of xkb_compose_state_feed it does not
|
||||
// support multi-sym events, so we ignore them
|
||||
if (num_syms != 1 || num_clean_syms != 1) {
|
||||
debug("num_syms: %d num_clean_syms: %d ignoring event\n", num_syms, num_clean_syms);
|
||||
return;
|
||||
}
|
||||
glfw_sym = clean_syms[0];
|
||||
xkb_sym = clean_syms[0];
|
||||
debug("clean_sym: %s ", glfw_xkb_keysym_name(clean_syms[0]));
|
||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||
const char *text_type = "composed_text";
|
||||
int compose_completed;
|
||||
glfw_sym = compose_symbol(sg->composeState, syms[0], &compose_completed);
|
||||
if (glfw_sym == XKB_KEY_NoSymbol && !compose_completed) {
|
||||
xkb_sym = compose_symbol(sg->composeState, syms[0], &compose_completed, key_text, sizeof(key_text));
|
||||
if (xkb_sym == XKB_KEY_NoSymbol && !compose_completed) {
|
||||
debug("compose not complete, ignoring.\n");
|
||||
return;
|
||||
}
|
||||
debug("composed_sym: %s ", glfw_xkb_keysym_name(glfw_sym));
|
||||
if (glfw_sym == syms[0]) { // composed sym is the same as non-composed sym
|
||||
debug("composed_sym: %s ", glfw_xkb_keysym_name(xkb_sym));
|
||||
if (xkb_sym == syms[0]) { // composed sym is the same as non-composed sym
|
||||
// Only use the clean_sym if no mods other than the mods we report
|
||||
// are active (for example if ISO_Shift_Level_* mods are active
|
||||
// they are not reported by GLFW so the key should be the shifted
|
||||
@@ -601,37 +607,58 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
|
||||
xkb_mod_mask_t consumed_unknown_mods = xkb_state_key_get_consumed_mods(sg->state, code_for_sym) & sg->activeUnknownModifiers;
|
||||
if (sg->activeUnknownModifiers) debug("%s", format_xkb_mods(xkb, "active_unknown_mods", sg->activeUnknownModifiers));
|
||||
if (consumed_unknown_mods) { debug("%s", format_xkb_mods(xkb, "consumed_unknown_mods", consumed_unknown_mods)); }
|
||||
else glfw_sym = clean_syms[0];
|
||||
else xkb_sym = clean_syms[0];
|
||||
// xkb returns text even if alt and/or super are pressed
|
||||
if ( ((GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) & sg->modifiers) == 0) xkb_state_key_get_utf8(sg->state, code_for_sym, key_event.text, sizeof(key_event.text));
|
||||
if ( ((GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) & sg->modifiers) == 0) {
|
||||
xkb_state_key_get_utf8(sg->state, code_for_sym, key_text, sizeof(key_text));
|
||||
}
|
||||
text_type = "text";
|
||||
}
|
||||
if ((1 <= key_event.text[0] && key_event.text[0] <= 31) || key_event.text[0] == 127) key_event.text[0] = 0; // don't send text for ascii control codes
|
||||
if (key_event.text[0]) { debug("%s: %s ", text_type, key_event.text); }
|
||||
if ((1 <= key_text[0] && key_text[0] <= 31) || key_text[0] == 127) {
|
||||
key_text[0] = 0; // don't send text for ascii control codes
|
||||
}
|
||||
if (key_text[0]) { debug("%s: %s ", text_type, key_text); }
|
||||
}
|
||||
int glfw_keycode = glfw_key_for_sym(glfw_sym);
|
||||
int glfw_sym = glfw_key_for_sym(xkb_sym);
|
||||
bool is_fallback = false;
|
||||
if (glfw_keycode == GLFW_KEY_UNKNOWN && !key_event.text[0]) {
|
||||
if (glfw_sym == GLFW_KEY_UNKNOWN && !key_text[0]) {
|
||||
int num_default_syms = xkb_state_key_get_syms(sg->default_state, code_for_sym, &default_syms);
|
||||
if (num_default_syms > 0) {
|
||||
glfw_sym = default_syms[0];
|
||||
glfw_keycode = glfw_key_for_sym(glfw_sym);
|
||||
xkb_sym = default_syms[0];
|
||||
glfw_sym = glfw_key_for_sym(xkb_sym);
|
||||
is_fallback = true;
|
||||
}
|
||||
}
|
||||
debug(
|
||||
"%s%s: %d (%s) xkb_key: %d (%s)\n",
|
||||
format_mods(sg->modifiers),
|
||||
is_fallback ? "glfw_fallback_key" : "glfw_key", glfw_keycode, _glfwGetKeyName(glfw_keycode),
|
||||
glfw_sym, glfw_xkb_keysym_name(glfw_sym)
|
||||
is_fallback ? "glfw_fallback_key" : "glfw_key", glfw_sym, _glfwGetKeyName(glfw_sym),
|
||||
xkb_sym, glfw_xkb_keysym_name(xkb_sym)
|
||||
);
|
||||
key_event.action = action; key_event.glfw_modifiers = sg->modifiers;
|
||||
key_event.keycode = scancode; key_event.keysym = glfw_sym;
|
||||
key_event.window_id = window->id; key_event.glfw_keycode = glfw_keycode;
|
||||
key_event.ibus_sym = syms[0];
|
||||
if (ibus_process_key(&key_event, &xkb->ibus)) {
|
||||
debug("↳ to IBUS: keycode: 0x%x keysym: 0x%x (%s) %s\n", key_event.ibus_keycode, key_event.ibus_sym, glfw_xkb_keysym_name(key_event.ibus_sym), format_mods(key_event.glfw_modifiers));
|
||||
|
||||
// NOTE: On linux, the reported native key identifier is the XKB keysym value.
|
||||
// Do not confuse `native_key` with `xkb_keycode` (the native keycode reported for the
|
||||
// glfw event VS the X internal code for a key).
|
||||
//
|
||||
// We use the XKB keysym instead of the X keycode to be able to go back-and-forth between
|
||||
// the GLFW keysym and the XKB keysym when needed, which is not possible using the X keycode,
|
||||
// because of the lost information when resolving the keycode to the keysym, like consumed
|
||||
// mods.
|
||||
glfw_ev.native_key = xkb_sym;
|
||||
|
||||
glfw_ev.action = action;
|
||||
glfw_ev.key = glfw_sym;
|
||||
glfw_ev.mods = sg->modifiers;
|
||||
glfw_ev.text = key_text;
|
||||
|
||||
_GLFWIBUSKeyEvent ibus_ev;
|
||||
ibus_ev.glfw_ev = glfw_ev;
|
||||
ibus_ev.ibus_keycode = ibus_keycode;
|
||||
ibus_ev.window_id = window->id;
|
||||
ibus_ev.ibus_keysym = syms[0];
|
||||
if (ibus_process_key(&ibus_ev, &xkb->ibus)) {
|
||||
debug("↳ to IBUS: keycode: 0x%x keysym: 0x%x (%s) %s\n", ibus_ev.ibus_keycode, ibus_ev.ibus_keysym, glfw_xkb_keysym_name(ibus_ev.ibus_keysym), format_mods(ibus_ev.glfw_ev.mods));
|
||||
} else {
|
||||
_glfwInputKeyboard(window, glfw_keycode, glfw_sym, action, sg->modifiers, key_event.text, 0);
|
||||
_glfwInputKeyboard(window, &glfw_ev);
|
||||
}
|
||||
}
|
||||
|
||||
6
glfw/xkb_glfw.h
vendored
6
glfw/xkb_glfw.h
vendored
@@ -87,10 +87,10 @@ void glfw_xkb_release(_GLFWXKBData *xkb);
|
||||
bool glfw_xkb_create_context(_GLFWXKBData *xkb);
|
||||
bool glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str);
|
||||
void glfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_mask_t latched, xkb_mod_mask_t locked, xkb_layout_index_t base_group, xkb_layout_index_t latched_group, xkb_layout_index_t locked_group);
|
||||
bool glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t scancode);
|
||||
bool glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t keycode);
|
||||
const char* glfw_xkb_keysym_name(xkb_keysym_t sym);
|
||||
xkb_keysym_t glfw_xkb_sym_for_key(int key);
|
||||
void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action);
|
||||
void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t keycode, int action);
|
||||
int glfw_xkb_keysym_from_name(const char *name, bool case_sensitive);
|
||||
void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d);
|
||||
void glfw_xkb_key_from_ime(KeyEvent *ev, bool handled_by_ime, bool failed);
|
||||
void glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed);
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import atexit
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
from contextlib import suppress
|
||||
from functools import partial
|
||||
from gettext import gettext as _
|
||||
from contextlib import suppress
|
||||
|
||||
from kitty.cli import CONFIG_HELP, parse_args
|
||||
from kitty.constants import appname
|
||||
@@ -506,7 +509,7 @@ def showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
|
||||
|
||||
showwarning.warnings = []
|
||||
help_text = 'Show a side-by-side diff of the specified files/directories'
|
||||
help_text = 'Show a side-by-side diff of the specified files/directories. You can also use ssh:hostname:remote-file-path to diff remote files.'
|
||||
usage = 'file_or_directory_left file_or_directory_right'
|
||||
|
||||
|
||||
@@ -516,6 +519,20 @@ def terminate_processes(processes):
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
|
||||
|
||||
def get_remote_file(path):
|
||||
if path.startswith('ssh:'):
|
||||
parts = path.split(':', 2)
|
||||
if len(parts) == 3:
|
||||
hostname, rpath = parts[1:]
|
||||
with tempfile.NamedTemporaryFile(suffix='-' + os.path.basename(rpath), prefix='remote:', delete=False) as tf:
|
||||
atexit.register(os.remove, tf.name)
|
||||
p = subprocess.Popen(['ssh', hostname, 'cat', rpath], stdout=tf)
|
||||
if p.wait() != 0:
|
||||
raise SystemExit(p.returncode)
|
||||
return tf.name
|
||||
return path
|
||||
|
||||
|
||||
def main(args):
|
||||
warnings.showwarning = showwarning
|
||||
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten diff')
|
||||
@@ -528,6 +545,7 @@ def main(args):
|
||||
opts = init_config(args)
|
||||
set_diff_command(opts.diff_cmd)
|
||||
lines_for_path.replace_tab_by = opts.replace_tab_by
|
||||
left, right = map(get_remote_file, (left, right))
|
||||
for f in left, right:
|
||||
if not os.path.exists(f):
|
||||
raise SystemExit('{} does not exist'.format(f))
|
||||
|
||||
@@ -26,11 +26,12 @@ screen_size = screen_size_function()
|
||||
|
||||
class Mark:
|
||||
|
||||
__slots__ = ('index', 'start', 'end', 'text')
|
||||
__slots__ = ('index', 'start', 'end', 'text', 'groupdict')
|
||||
|
||||
def __init__(self, index, start, end, text):
|
||||
def __init__(self, index, start, end, text, groupdict):
|
||||
self.index, self.start, self.end = index, start, end
|
||||
self.text = text
|
||||
self.groupdict = groupdict
|
||||
|
||||
|
||||
@lru_cache(maxsize=2048)
|
||||
@@ -94,6 +95,14 @@ class Hints(Handler):
|
||||
self.chosen = []
|
||||
self.reset()
|
||||
|
||||
@property
|
||||
def text_matches(self):
|
||||
return [m.text + self.match_suffix for m in self.chosen]
|
||||
|
||||
@property
|
||||
def groupdicts(self):
|
||||
return [m.groupdict for m in self.chosen]
|
||||
|
||||
def get_match_suffix(self, args):
|
||||
if args.add_trailing_space == 'always':
|
||||
return ' '
|
||||
@@ -126,7 +135,7 @@ class Hints(Handler):
|
||||
if encode_hint(idx, self.alphabet).startswith(self.current_input)
|
||||
]
|
||||
if len(matches) == 1:
|
||||
self.chosen.append(matches[0].text + self.match_suffix)
|
||||
self.chosen.append(matches[0])
|
||||
if self.multiple:
|
||||
self.ignore_mark_indices.add(matches[0].index)
|
||||
self.reset()
|
||||
@@ -144,7 +153,7 @@ class Hints(Handler):
|
||||
elif key_event is enter_key and self.current_input:
|
||||
try:
|
||||
idx = decode_hint(self.current_input, self.alphabet)
|
||||
self.chosen.append(self.index_map[idx].text + self.match_suffix)
|
||||
self.chosen.append(self.index_map[idx])
|
||||
self.ignore_mark_indices.add(idx)
|
||||
except Exception:
|
||||
self.current_input = ''
|
||||
@@ -176,12 +185,13 @@ class Hints(Handler):
|
||||
|
||||
|
||||
def regex_finditer(pat, minimum_match_length, text):
|
||||
has_named_groups = bool(pat.groupindex)
|
||||
for m in pat.finditer(text):
|
||||
s, e = m.span(pat.groups)
|
||||
s, e = m.span(0 if has_named_groups else pat.groups)
|
||||
while e > s + 1 and text[e-1] == '\0':
|
||||
e -= 1
|
||||
if e - s >= minimum_match_length:
|
||||
yield s, e
|
||||
yield s, e, m.groupdict()
|
||||
|
||||
|
||||
closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>', '*': '*', '"': '"', "'": "'"}
|
||||
@@ -240,21 +250,23 @@ def quotes(text, s, e):
|
||||
|
||||
def mark(pattern, post_processors, text, args):
|
||||
pat = re.compile(pattern)
|
||||
for idx, (s, e) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
||||
for idx, (s, e, groupdict) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
||||
for func in post_processors:
|
||||
s, e = func(text, s, e)
|
||||
mark_text = text[s:e].replace('\n', '').replace('\0', '')
|
||||
yield Mark(idx, s, e, mark_text)
|
||||
yield Mark(idx, s, e, mark_text, groupdict)
|
||||
|
||||
|
||||
def run_loop(args, text, all_marks, index_map):
|
||||
def run_loop(args, text, all_marks, index_map, extra_cli_args=()):
|
||||
loop = Loop()
|
||||
handler = Hints(text, all_marks, index_map, args)
|
||||
loop.loop(handler)
|
||||
if handler.chosen and loop.return_code == 0:
|
||||
return {'match': handler.chosen, 'programs': args.program,
|
||||
'multiple_joiner': args.multiple_joiner,
|
||||
'type': args.type}
|
||||
return {
|
||||
'match': handler.text_matches, 'programs': args.program,
|
||||
'multiple_joiner': args.multiple_joiner, 'customize_processing': args.customize_processing,
|
||||
'type': args.type, 'groupdicts': handler.groupdicts, 'extra_cli_args': extra_cli_args
|
||||
}
|
||||
raise SystemExit(loop.return_code)
|
||||
|
||||
|
||||
@@ -311,11 +323,33 @@ def parse_input(text):
|
||||
return convert_text(text, cols)
|
||||
|
||||
|
||||
def run(args, text):
|
||||
def load_custom_processor(customize_processing):
|
||||
if customize_processing.startswith('::import::'):
|
||||
import importlib
|
||||
m = importlib.import_module(customize_processing[len('::import::'):])
|
||||
return {k: getattr(m, k) for k in dir(m)}
|
||||
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)
|
||||
import runpy
|
||||
return runpy.run_path(custom_path, run_name='__main__')
|
||||
|
||||
|
||||
def run(args, text, extra_cli_args=()):
|
||||
try:
|
||||
pattern, post_processors = functions_for(args)
|
||||
text = parse_input(text)
|
||||
all_marks = tuple(mark(pattern, post_processors, text, args))
|
||||
pattern, post_processors = functions_for(args)
|
||||
if args.customize_processing:
|
||||
m = load_custom_processor(args.customize_processing)
|
||||
if 'mark' in m:
|
||||
all_marks = tuple(m['mark'](text, args, Mark, extra_cli_args))
|
||||
else:
|
||||
all_marks = tuple(mark(pattern, post_processors, text, args))
|
||||
else:
|
||||
all_marks = tuple(mark(pattern, post_processors, text, args))
|
||||
if not all_marks:
|
||||
input(_('No {} found, press Enter to quit.').format(
|
||||
'URLs' if args.type == 'url' else 'matches'
|
||||
@@ -325,7 +359,10 @@ def run(args, text):
|
||||
largest_index = all_marks[-1].index
|
||||
offset = max(0, args.hints_offset)
|
||||
for m in all_marks:
|
||||
m.index = largest_index - m.index + offset
|
||||
if args.ascending:
|
||||
m.index += offset
|
||||
else:
|
||||
m.index = largest_index - m.index + offset
|
||||
index_map = {m.index: m for m in all_marks}
|
||||
except Exception:
|
||||
import traceback
|
||||
@@ -333,7 +370,7 @@ def run(args, text):
|
||||
input('Press Enter to quit.')
|
||||
raise SystemExit(1)
|
||||
|
||||
return run_loop(args, text, all_marks, index_map)
|
||||
return run_loop(args, text, all_marks, index_map, extra_cli_args)
|
||||
|
||||
|
||||
# CLI {{{
|
||||
@@ -356,11 +393,14 @@ The type of text to search for.
|
||||
--regex
|
||||
default=(?m)^\s*(.+)\s*$
|
||||
The regular expression to use when :option:`kitty +kitten hints --type`=regex.
|
||||
The regular expression is in python syntax. If you specify a group in
|
||||
The regular expression is in python syntax. If you specify a numbered group in
|
||||
the regular expression only the group will be matched. This allow you to match
|
||||
text ignoring a prefix/suffix, as needed. The default expression matches lines.
|
||||
To match text over multiple lines you should prefix the regular expression with
|
||||
:code:`(?ms)`, which turns on MULTILINE and DOTALL modes for the regex engine.
|
||||
If you specify named groups and a :option:`kitty +kitten hints --program` then
|
||||
the program will be passed arguments corresponding to each named group of
|
||||
the form key=value.
|
||||
|
||||
|
||||
--url-prefixes
|
||||
@@ -418,6 +458,18 @@ unless you specify the hints offset as zero the first match will be highlighted
|
||||
the second character you specify.
|
||||
|
||||
|
||||
--ascending
|
||||
type=bool-set
|
||||
Have the hints increase from top to bottom instead of decreasing from top to bottom.
|
||||
|
||||
|
||||
--customize-processing
|
||||
Name of a python file in the kitty config directory which will be imported to provide
|
||||
custom implementations for pattern finding and performing actions
|
||||
on selected matches. See https://sw.kovidgoyal.net/kitty/kittens/hints.html
|
||||
for details. You can also specify absolute paths to load the script from elsewhere.
|
||||
|
||||
|
||||
'''.format(','.join(sorted(URL_PREFIXES))).format
|
||||
help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
|
||||
usage = ''
|
||||
@@ -444,15 +496,23 @@ def main(args):
|
||||
print(e.args[0], file=sys.stderr)
|
||||
input(_('Press Enter to quit'))
|
||||
return
|
||||
if items:
|
||||
if items and not args.customize_processing:
|
||||
print('Extra command line arguments present: {}'.format(' '.join(items)), file=sys.stderr)
|
||||
input(_('Press Enter to quit'))
|
||||
return run(args, text)
|
||||
return run(args, text, items)
|
||||
|
||||
|
||||
def handle_result(args, data, target_window_id, boss):
|
||||
if data['customize_processing']:
|
||||
m = load_custom_processor(data['customize_processing'])
|
||||
if 'handle_result' in m:
|
||||
return m['handle_result'](args, data, target_window_id, boss, data['extra_cli_args'])
|
||||
|
||||
programs = data['programs'] or ('default',)
|
||||
matches = tuple(filter(None, data['match']))
|
||||
matches, groupdicts = [], []
|
||||
for m, g in zip(data['match'], data['groupdicts']):
|
||||
if m:
|
||||
matches.append(m), groupdicts.append(g)
|
||||
joiner = data['multiple_joiner']
|
||||
try:
|
||||
is_int = int(joiner)
|
||||
@@ -489,7 +549,11 @@ def handle_result(args, data, target_window_id, boss):
|
||||
if w is not None:
|
||||
cwd = w.cwd_of_child
|
||||
program = None if program == 'default' else program
|
||||
for m in matches:
|
||||
for m, groupdict in zip(matches, groupdicts):
|
||||
if groupdict:
|
||||
m = []
|
||||
for k, v in groupdict.items():
|
||||
m.append('{}={}'.format(k, v or ''))
|
||||
boss.open_url(m, program, cwd=cwd)
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ from ..tui.images import (
|
||||
)
|
||||
from ..tui.operations import clear_images_on_screen, serialize_gr_command
|
||||
|
||||
screen_size = None
|
||||
OPTIONS = '''\
|
||||
--align
|
||||
type=choices
|
||||
@@ -97,6 +96,21 @@ Do not print out anything to stdout during operation.
|
||||
'''
|
||||
|
||||
|
||||
screen_size = None
|
||||
|
||||
|
||||
def get_screen_size_function():
|
||||
global screen_size
|
||||
if screen_size is None:
|
||||
screen_size = screen_size_function()
|
||||
return screen_size
|
||||
|
||||
|
||||
def get_screen_size():
|
||||
screen_size = get_screen_size_function()
|
||||
return screen_size()
|
||||
|
||||
|
||||
def options_spec():
|
||||
if not hasattr(options_spec, 'ans'):
|
||||
options_spec.ans = OPTIONS.format(
|
||||
@@ -122,7 +136,7 @@ def calculate_in_cell_x_offset(width, cell_width, align):
|
||||
|
||||
|
||||
def set_cursor(cmd, width, height, align):
|
||||
ss = screen_size()
|
||||
ss = get_screen_size()
|
||||
cw = int(ss.width / ss.cols)
|
||||
num_of_cells_needed = int(ceil(width / cw))
|
||||
if num_of_cells_needed > ss.cols:
|
||||
@@ -143,7 +157,7 @@ def set_cursor(cmd, width, height, align):
|
||||
|
||||
def set_cursor_for_place(place, cmd, width, height, align):
|
||||
x = place.left + 1
|
||||
ss = screen_size()
|
||||
ss = get_screen_size()
|
||||
cw = int(ss.width / ss.cols)
|
||||
num_of_cells_needed = int(ceil(width / cw))
|
||||
cmd['X'] = calculate_in_cell_x_offset(width, cw, align)
|
||||
@@ -189,7 +203,7 @@ def show(outfile, width, height, fmt, transmit_mode='t', align='center', place=N
|
||||
|
||||
def process(path, args, is_tempfile):
|
||||
m = identify(path)
|
||||
ss = screen_size()
|
||||
ss = get_screen_size()
|
||||
available_width = args.place.width * (ss.width / ss.cols) if args.place else ss.width
|
||||
available_height = args.place.height * (ss.height / ss.rows) if args.place else 10 * m.height
|
||||
needs_scaling = m.width > available_width or m.height > available_height
|
||||
@@ -314,7 +328,6 @@ def process_single_item(item, args, url_pat=None, maybe_dir=True):
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
global screen_size
|
||||
args, items = parse_args(args[1:], options_spec, usage, help_text, '{} +kitten icat'.format(appname))
|
||||
|
||||
if args.print_window_size:
|
||||
@@ -334,7 +347,7 @@ def main(args=sys.argv):
|
||||
sys.stdin.close()
|
||||
sys.stdin = open(os.ctermid(), 'r')
|
||||
|
||||
screen_size = screen_size_function()
|
||||
screen_size = get_screen_size_function()
|
||||
signal.signal(signal.SIGWINCH, lambda signum, frame: setattr(screen_size, 'changed', True))
|
||||
if screen_size().width == 0:
|
||||
if args.detect_support:
|
||||
|
||||
@@ -130,8 +130,8 @@ def main(sys_args):
|
||||
if not items:
|
||||
raise SystemExit('You must specify the program to run')
|
||||
sys.argv = ['kitty']
|
||||
if args.config:
|
||||
sys.argv.append('--config={}'.format(args.config))
|
||||
for config in args.config:
|
||||
sys.argv.append('--config={}'.format(config))
|
||||
for override in args.override:
|
||||
sys.argv.append('--override={}'.format(override))
|
||||
sys.argv.extend(items)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
|
||||
from kitty.cli import parse_args
|
||||
|
||||
@@ -30,9 +31,8 @@ def real_main(args):
|
||||
|
||||
def main(args):
|
||||
try:
|
||||
real_main(args)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
with suppress(KeyboardInterrupt):
|
||||
real_main(args)
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -76,6 +76,9 @@ def parse_ssh_args(args):
|
||||
if arg in boolean_ssh_args:
|
||||
ssh_args.append(arg)
|
||||
continue
|
||||
if arg.startswith('-p') and arg[2:].isdigit():
|
||||
ssh_args.append(arg)
|
||||
continue
|
||||
if arg in other_ssh_args:
|
||||
if i != len(all_args) - 1:
|
||||
raise SystemExit('Option {} cannot occur in the middle'.format(arg))
|
||||
|
||||
@@ -5,25 +5,27 @@
|
||||
import os
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
from functools import lru_cache
|
||||
from gettext import gettext as _
|
||||
from contextlib import suppress
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.config import cached_values_for
|
||||
from kitty.constants import config_dir
|
||||
from kitty.utils import get_editor
|
||||
from kitty.fast_data_types import wcswidth
|
||||
from kitty.fast_data_types import wcswidth, is_emoji_presentation_base
|
||||
from kitty.key_encoding import (
|
||||
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
|
||||
enter_key
|
||||
)
|
||||
from kitty.utils import get_editor
|
||||
|
||||
from ..tui.line_edit import LineEdit
|
||||
from ..tui.handler import Handler
|
||||
from ..tui.line_edit import LineEdit
|
||||
from ..tui.loop import Loop
|
||||
from ..tui.operations import (
|
||||
clear_screen, colored, cursor, faint, set_line_wrapping,
|
||||
set_window_title, sgr, styled
|
||||
clear_screen, colored, cursor, faint, set_line_wrapping, set_window_title,
|
||||
sgr, styled
|
||||
)
|
||||
|
||||
HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
|
||||
@@ -131,7 +133,8 @@ def decode_hint(x):
|
||||
|
||||
class Table:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, emoji_variation):
|
||||
self.emoji_variation = emoji_variation
|
||||
self.layout_dirty = True
|
||||
self.last_rows = self.last_cols = -1
|
||||
self.codepoints = []
|
||||
@@ -161,7 +164,10 @@ class Table:
|
||||
self.layout_dirty = False
|
||||
|
||||
def safe_chr(codepoint):
|
||||
return chr(codepoint).encode('utf-8', 'replace').decode('utf-8')
|
||||
ans = chr(codepoint).encode('utf-8', 'replace').decode('utf-8')
|
||||
if self.emoji_variation and is_emoji_presentation_base(codepoint):
|
||||
ans += self.emoji_variation
|
||||
return ans
|
||||
|
||||
if self.mode is NAME:
|
||||
def as_parts(i, codepoint):
|
||||
@@ -248,8 +254,13 @@ def is_index(w):
|
||||
|
||||
class UnicodeInput(Handler):
|
||||
|
||||
def __init__(self, cached_values):
|
||||
def __init__(self, cached_values, emoji_variation='none'):
|
||||
self.cached_values = cached_values
|
||||
self.emoji_variation = ''
|
||||
if emoji_variation == 'text':
|
||||
self.emoji_variation = '\ufe0e'
|
||||
elif emoji_variation == 'graphic':
|
||||
self.emoji_variation = '\ufe0f'
|
||||
self.line_edit = LineEdit()
|
||||
self.recent = list(self.cached_values.get('recent', DEFAULT_SET))
|
||||
self.current_char = None
|
||||
@@ -257,9 +268,17 @@ class UnicodeInput(Handler):
|
||||
self.last_updated_code_point_at = None
|
||||
self.choice_line = ''
|
||||
self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX')
|
||||
self.table = Table()
|
||||
self.table = Table(self.emoji_variation)
|
||||
self.update_prompt()
|
||||
|
||||
@property
|
||||
def resolved_current_char(self):
|
||||
ans = self.current_char
|
||||
if ans:
|
||||
if self.emoji_variation and is_emoji_presentation_base(ord(ans[0])):
|
||||
ans += self.emoji_variation
|
||||
return ans
|
||||
|
||||
def update_codepoints(self):
|
||||
codepoints = None
|
||||
if self.mode is HEX:
|
||||
@@ -320,8 +339,10 @@ class UnicodeInput(Handler):
|
||||
self.choice_line = ''
|
||||
else:
|
||||
c, color = self.current_char, 'green'
|
||||
if self.emoji_variation and is_emoji_presentation_base(ord(c[0])):
|
||||
c += self.emoji_variation
|
||||
self.choice_line = _('Chosen:') + ' {} U+{} {}'.format(
|
||||
colored(c, 'green'), hex(ord(c))[2:], faint(styled(name(c) or '', italic=True)))
|
||||
colored(c, 'green'), hex(ord(c[0]))[2:], faint(styled(name(c) or '', italic=True)))
|
||||
self.prompt = self.prompt_template.format(colored(c, color))
|
||||
|
||||
def init_terminal_state(self):
|
||||
@@ -475,17 +496,43 @@ class UnicodeInput(Handler):
|
||||
self.refresh()
|
||||
|
||||
|
||||
help_text = 'Input a unicode character'
|
||||
usage = ''
|
||||
OPTIONS = '''
|
||||
--emoji-variation
|
||||
type=choices
|
||||
default=none
|
||||
choices=none,graphic,text
|
||||
Whether to use the textual or the graphical form for emoji. By default the
|
||||
default form specified in the unicode standard for the symbol is used.
|
||||
|
||||
|
||||
'''.format
|
||||
|
||||
|
||||
def parse_unicode_input_args(args):
|
||||
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten unicode_input')
|
||||
|
||||
|
||||
def main(args):
|
||||
try:
|
||||
args, items = parse_unicode_input_args(args[1:])
|
||||
except SystemExit as e:
|
||||
if e.code != 0:
|
||||
print(e.args[0], file=sys.stderr)
|
||||
input(_('Press Enter to quit'))
|
||||
return
|
||||
|
||||
loop = Loop()
|
||||
with cached_values_for('unicode-input') as cached_values:
|
||||
handler = UnicodeInput(cached_values)
|
||||
handler = UnicodeInput(cached_values, args.emoji_variation)
|
||||
loop.loop(handler)
|
||||
if handler.current_char and loop.return_code == 0:
|
||||
with suppress(Exception):
|
||||
handler.recent.remove(ord(handler.current_char))
|
||||
recent = [ord(handler.current_char)] + handler.recent
|
||||
cached_values['recent'] = recent[:len(DEFAULT_SET)]
|
||||
return handler.current_char
|
||||
return handler.resolved_current_char
|
||||
if loop.return_code != 0:
|
||||
raise SystemExit(loop.return_code)
|
||||
|
||||
@@ -497,10 +544,10 @@ def handle_result(args, current_char, target_window_id, boss):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
if '-h' in sys.argv or '--help' in sys.argv:
|
||||
print('Choose a unicode character to input into the terminal')
|
||||
raise SystemExit(0)
|
||||
ans = main(sys.argv)
|
||||
if ans:
|
||||
print(ans)
|
||||
elif __name__ == '__doc__':
|
||||
sys.cli_docs['usage'] = usage
|
||||
sys.cli_docs['options'] = OPTIONS
|
||||
sys.cli_docs['help_text'] = help_text
|
||||
|
||||
2
kittens/unicode_input/names.h
generated
2
kittens/unicode_input/names.h
generated
@@ -1,4 +1,4 @@
|
||||
// unicode data, built from the unicode standard on: 2019-08-02
|
||||
// unicode data, built from the unicode standard on: 2019-10-01
|
||||
// see gen-wcwidth.py
|
||||
#pragma once
|
||||
#include "data-types.h"
|
||||
|
||||
285
kitty/boss.py
285
kitty/boss.py
@@ -23,10 +23,10 @@ from .constants import (
|
||||
from .fast_data_types import (
|
||||
ChildMonitor, background_opacity_of, change_background_opacity,
|
||||
change_os_window_state, create_os_window, current_os_window,
|
||||
destroy_global_data, get_clipboard_string, global_font_size,
|
||||
mark_os_window_for_close, os_window_font_size, patch_global_colors,
|
||||
safe_pipe, set_clipboard_string, set_in_sequence_mode, thread_write,
|
||||
toggle_fullscreen, toggle_maximized
|
||||
destroy_global_data, focus_os_window, get_clipboard_string,
|
||||
global_font_size, mark_os_window_for_close, os_window_font_size,
|
||||
patch_global_colors, safe_pipe, set_clipboard_string, set_in_sequence_mode,
|
||||
thread_write, toggle_fullscreen, toggle_maximized
|
||||
)
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
from .layout import set_layout_options
|
||||
@@ -58,9 +58,9 @@ def data_for_at(w, arg, add_wrap_markers=False):
|
||||
|
||||
if arg == '@selection':
|
||||
return w.text_for_selection()
|
||||
if arg == '@ansi':
|
||||
if arg in ('@ansi', '@ansi_screen_scrollback'):
|
||||
return as_text(as_ansi=True, add_history=True)
|
||||
if arg == '@text':
|
||||
if arg in ('@text', '@screen_scrollback'):
|
||||
return as_text(add_history=True)
|
||||
if arg == '@screen':
|
||||
return as_text()
|
||||
@@ -121,7 +121,7 @@ class Boss:
|
||||
talk_fd = getattr(single_instance, 'socket', None)
|
||||
talk_fd = -1 if talk_fd is None else talk_fd.fileno()
|
||||
listen_fd = -1
|
||||
if opts.allow_remote_control and args.listen_on:
|
||||
if args.listen_on and (opts.allow_remote_control in ('y', 'socket-only')):
|
||||
listen_fd = listen_on(args.listen_on)
|
||||
self.child_monitor = ChildMonitor(
|
||||
self.on_child_death,
|
||||
@@ -146,9 +146,9 @@ class Boss:
|
||||
from .fast_data_types import cocoa_set_notification_activated_callback
|
||||
cocoa_set_notification_activated_callback(self.notification_activated)
|
||||
|
||||
def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, opts_for_size=None, startup_id=None):
|
||||
def add_os_window(self, startup_session=None, os_window_id=None, wclass=None, wname=None, opts_for_size=None, startup_id=None):
|
||||
if os_window_id is None:
|
||||
opts_for_size = opts_for_size or startup_session.os_window_size or self.opts
|
||||
opts_for_size = opts_for_size or getattr(startup_session, 'os_window_size', None) or self.opts
|
||||
cls = wclass or self.args.cls or appname
|
||||
with startup_notification_handler(do_notify=startup_id is not None, startup_id=startup_id) as pre_show_callback:
|
||||
os_window_id = create_os_window(
|
||||
@@ -235,7 +235,7 @@ class Boss:
|
||||
if tab:
|
||||
yield tab
|
||||
|
||||
def set_active_window(self, window):
|
||||
def set_active_window(self, window, switch_os_window_if_needed=False):
|
||||
for os_window_id, tm in self.os_window_map.items():
|
||||
for tab in tm:
|
||||
for w in tab:
|
||||
@@ -243,6 +243,8 @@ class Boss:
|
||||
if tab is not self.active_tab:
|
||||
tm.set_active_tab(tab)
|
||||
tab.set_active_window(w)
|
||||
if switch_os_window_if_needed and current_os_window() != os_window_id:
|
||||
focus_os_window(os_window_id, True)
|
||||
return os_window_id
|
||||
|
||||
def _new_os_window(self, args, cwd_from=None):
|
||||
@@ -276,9 +278,9 @@ class Boss:
|
||||
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
|
||||
self.window_id_map[window.id] = window
|
||||
|
||||
def _handle_remote_command(self, cmd, window=None):
|
||||
def _handle_remote_command(self, cmd, window=None, from_peer=False):
|
||||
response = None
|
||||
if self.opts.allow_remote_control or getattr(window, 'allow_remote_control', False):
|
||||
if self.opts.allow_remote_control == 'y' or from_peer or getattr(window, 'allow_remote_control', False):
|
||||
try:
|
||||
response = handle_cmd(self, window, cmd)
|
||||
except Exception as err:
|
||||
@@ -287,7 +289,7 @@ class Boss:
|
||||
if not getattr(err, 'hide_traceback', False):
|
||||
response['tb'] = traceback.format_exc()
|
||||
else:
|
||||
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control yes to your kitty.conf'}
|
||||
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control to your kitty.conf'}
|
||||
return response
|
||||
|
||||
def peer_message_received(self, msg):
|
||||
@@ -295,7 +297,7 @@ class Boss:
|
||||
cmd_prefix = '\x1bP@kitty-cmd'
|
||||
if msg.startswith(cmd_prefix):
|
||||
cmd = msg[len(cmd_prefix):-2]
|
||||
response = self._handle_remote_command(cmd)
|
||||
response = self._handle_remote_command(cmd, from_peer=True)
|
||||
if response is not None:
|
||||
response = (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8')
|
||||
return response
|
||||
@@ -321,6 +323,16 @@ class Boss:
|
||||
if window is not None:
|
||||
window.send_cmd_response(response)
|
||||
|
||||
def _cleanup_tab_after_window_removal(self, src_tab):
|
||||
if len(src_tab) < 1:
|
||||
tm = src_tab.tab_manager_ref()
|
||||
if tm is not None:
|
||||
tm.remove(src_tab)
|
||||
src_tab.destroy()
|
||||
if len(tm) == 0:
|
||||
if not self.shutting_down:
|
||||
mark_os_window_for_close(src_tab.os_window_id)
|
||||
|
||||
def on_child_death(self, window_id):
|
||||
window = self.window_id_map.pop(window_id, None)
|
||||
if window is None:
|
||||
@@ -334,20 +346,22 @@ class Boss:
|
||||
os_window_id = window.os_window_id
|
||||
window.destroy()
|
||||
tm = self.os_window_map.get(os_window_id)
|
||||
if tm is None:
|
||||
return
|
||||
for tab in tm:
|
||||
if window in tab:
|
||||
break
|
||||
else:
|
||||
return
|
||||
tab.remove_window(window)
|
||||
if len(tab) == 0:
|
||||
tm.remove(tab)
|
||||
tab.destroy()
|
||||
if len(tm) == 0:
|
||||
if not self.shutting_down:
|
||||
mark_os_window_for_close(os_window_id)
|
||||
tab = None
|
||||
if tm is not None:
|
||||
for q in tm:
|
||||
if window in q:
|
||||
tab = q
|
||||
break
|
||||
if tab is not None:
|
||||
tab.remove_window(window)
|
||||
self._cleanup_tab_after_window_removal(tab)
|
||||
if window.action_on_removal:
|
||||
try:
|
||||
window.action_on_removal(window)
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
window.action_on_close = window.action_on_removal = None
|
||||
|
||||
def close_window(self, window=None):
|
||||
if window is None:
|
||||
@@ -512,27 +526,27 @@ class Boss:
|
||||
if t is not None:
|
||||
return t.active_window
|
||||
|
||||
def dispatch_special_key(self, key, scancode, action, mods):
|
||||
def dispatch_special_key(self, key, native_key, action, mods):
|
||||
# Handles shortcuts, return True if the key was consumed
|
||||
key_action = get_shortcut(self.keymap, mods, key, scancode)
|
||||
key_action = get_shortcut(self.keymap, mods, key, native_key)
|
||||
if key_action is None:
|
||||
sequences = get_shortcut(self.opts.sequence_map, mods, key, scancode)
|
||||
sequences = get_shortcut(self.opts.sequence_map, mods, key, native_key)
|
||||
if sequences:
|
||||
self.pending_sequences = sequences
|
||||
set_in_sequence_mode(True)
|
||||
return True
|
||||
else:
|
||||
self.current_key_press_info = key, scancode, action, mods
|
||||
self.current_key_press_info = key, native_key, action, mods
|
||||
return self.dispatch_action(key_action)
|
||||
|
||||
def process_sequence(self, key, scancode, action, mods):
|
||||
def process_sequence(self, key, native_key, action, mods):
|
||||
if not self.pending_sequences:
|
||||
set_in_sequence_mode(False)
|
||||
|
||||
remaining = {}
|
||||
matched_action = None
|
||||
for seq, key_action in self.pending_sequences.items():
|
||||
if shortcut_matches(seq[0], mods, key, scancode):
|
||||
if shortcut_matches(seq[0], mods, key, native_key):
|
||||
seq = seq[1:]
|
||||
if seq:
|
||||
remaining[seq] = key_action
|
||||
@@ -670,7 +684,7 @@ class Boss:
|
||||
output += str(s.linebuf.line(i))
|
||||
return output
|
||||
|
||||
def _run_kitten(self, kitten, args=(), input_data=None, window=None):
|
||||
def _run_kitten(self, kitten, args=(), input_data=None, window=None, custom_callback=None, action_on_removal=None):
|
||||
orig_args, args = list(args), list(args)
|
||||
from kittens.runner import create_kitten_handler
|
||||
end_kitten = create_kitten_handler(kitten, orig_args)
|
||||
@@ -719,7 +733,10 @@ class Boss:
|
||||
),
|
||||
copy_colors_from=w
|
||||
)
|
||||
overlay_window.action_on_close = partial(self.on_kitten_finish, w.id, end_kitten)
|
||||
wid = w.id
|
||||
overlay_window.action_on_close = partial(self.on_kitten_finish, wid, custom_callback or end_kitten)
|
||||
if action_on_removal is not None:
|
||||
overlay_window.action_on_removal = lambda *a: action_on_removal(wid, self)
|
||||
return overlay_window
|
||||
|
||||
def kitten(self, kitten, *args):
|
||||
@@ -881,6 +898,9 @@ class Boss:
|
||||
stdin = stdin.encode('utf-8')
|
||||
return env, stdin
|
||||
|
||||
def data_for_at(self, which, window=None, add_wrap_markers=False):
|
||||
return data_for_at(window or self.active_window, which, add_wrap_markers=add_wrap_markers)
|
||||
|
||||
def special_window_for_cmd(self, cmd, window=None, stdin=None, cwd_from=None, as_overlay=False):
|
||||
w = window or self.active_window
|
||||
env, stdin = self.process_stdin_source(w, stdin)
|
||||
@@ -894,6 +914,25 @@ class Boss:
|
||||
overlay_for = w.id if as_overlay and w.overlay_for is None else None
|
||||
return SpecialWindow(cmd, stdin, cwd_from=cwd_from, overlay_for=overlay_for, env=env)
|
||||
|
||||
def run_background_process(self, cmd, cwd=None, env=None, stdin=None, cwd_from=None):
|
||||
import subprocess
|
||||
if cwd_from:
|
||||
with suppress(Exception):
|
||||
cwd = cwd_of_process(cwd_from)
|
||||
|
||||
if stdin:
|
||||
r, w = safe_pipe(False)
|
||||
try:
|
||||
subprocess.Popen(cmd, env=env, stdin=r, cwd=cwd)
|
||||
except Exception:
|
||||
os.close(w)
|
||||
else:
|
||||
thread_write(w, stdin)
|
||||
finally:
|
||||
os.close(r)
|
||||
else:
|
||||
subprocess.Popen(cmd, env=env, cwd=cwd)
|
||||
|
||||
def pipe(self, source, dest, exe, *args):
|
||||
cmd = [exe] + list(args)
|
||||
window = self.active_window
|
||||
@@ -919,24 +958,8 @@ class Boss:
|
||||
func = set_clipboard_string if dest == 'clipboard' else set_primary_selection
|
||||
func(stdin)
|
||||
else:
|
||||
import subprocess
|
||||
env, stdin = self.process_stdin_source(stdin=source, window=window)
|
||||
cwd = None
|
||||
if cwd_from:
|
||||
with suppress(Exception):
|
||||
cwd = cwd_of_process(cwd_from)
|
||||
if stdin:
|
||||
r, w = safe_pipe(False)
|
||||
try:
|
||||
subprocess.Popen(cmd, env=env, stdin=r, cwd=cwd)
|
||||
except Exception:
|
||||
os.close(w)
|
||||
else:
|
||||
thread_write(w, stdin)
|
||||
finally:
|
||||
os.close(r)
|
||||
else:
|
||||
subprocess.Popen(cmd, env=env, cwd=cwd)
|
||||
self.run_background_process(cmd, cwd_from=cwd_from, stdin=stdin, env=env)
|
||||
|
||||
def args_to_special_window(self, args, cwd_from=None):
|
||||
args = list(args)
|
||||
@@ -991,14 +1014,20 @@ class Boss:
|
||||
def _new_window(self, args, cwd_from=None):
|
||||
tab = self.active_tab
|
||||
if tab is not None:
|
||||
allow_remote_control = False
|
||||
location = None
|
||||
if args and args[0].startswith('!'):
|
||||
location = args[0][1:].lower()
|
||||
args = args[1:]
|
||||
if args and args[0] == '@':
|
||||
args = args[1:]
|
||||
allow_remote_control = True
|
||||
if args:
|
||||
return tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from), location=location)
|
||||
return tab.new_special_window(
|
||||
self.args_to_special_window(args, cwd_from=cwd_from),
|
||||
location=location, allow_remote_control=allow_remote_control)
|
||||
else:
|
||||
return tab.new_window(cwd_from=cwd_from, location=location)
|
||||
return tab.new_window(cwd_from=cwd_from, location=location, allow_remote_control=allow_remote_control)
|
||||
|
||||
def new_window(self, *args):
|
||||
self._new_window(args)
|
||||
@@ -1010,6 +1039,11 @@ class Boss:
|
||||
cwd_from = w.child.pid_for_cwd if w is not None else None
|
||||
self._new_window(args, cwd_from=cwd_from)
|
||||
|
||||
def launch(self, *args):
|
||||
from kitty.launch import parse_launch_args, launch
|
||||
opts, args = parse_launch_args(args)
|
||||
launch(self, opts, args)
|
||||
|
||||
def move_tab_forward(self):
|
||||
tm = self.active_tab_manager
|
||||
if tm is not None:
|
||||
@@ -1096,3 +1130,144 @@ class Boss:
|
||||
|
||||
msg = '\n'.join(map(format_bad_line, bad_lines)).rstrip()
|
||||
self.show_error(_('Errors in kitty.conf'), msg)
|
||||
|
||||
def set_colors(self, *args):
|
||||
from .cmds import parse_subcommand_cli, cmd_set_colors, set_colors
|
||||
opts, items = parse_subcommand_cli(cmd_set_colors, ['set-colors'] + list(args))
|
||||
payload = cmd_set_colors(None, opts, items)
|
||||
set_colors(self, self.active_window, payload)
|
||||
|
||||
def _move_window_to(self, window=None, target_tab_id=None, target_os_window_id=None):
|
||||
window = window or self.active_window
|
||||
if not window:
|
||||
return
|
||||
src_tab = self.tab_for_window(window)
|
||||
if src_tab is None:
|
||||
return
|
||||
if target_os_window_id == 'new':
|
||||
target_os_window_id = self.add_os_window()
|
||||
tm = self.os_window_map[target_os_window_id]
|
||||
target_tab = tm.new_tab(empty_tab=True)
|
||||
else:
|
||||
target_os_window_id = target_os_window_id or current_os_window()
|
||||
if target_tab_id == 'new':
|
||||
tm = self.os_window_map[target_os_window_id]
|
||||
target_tab = tm.new_tab(empty_tab=True)
|
||||
else:
|
||||
for tab in self.all_tabs:
|
||||
if tab.id == target_tab_id:
|
||||
target_tab = tab
|
||||
target_os_window_id = tab.os_window_id
|
||||
break
|
||||
else:
|
||||
return
|
||||
|
||||
underlaid_window, overlaid_window = src_tab.detach_window(window)
|
||||
if underlaid_window:
|
||||
target_tab.attach_window(underlaid_window)
|
||||
if overlaid_window:
|
||||
target_tab.attach_window(overlaid_window)
|
||||
self._cleanup_tab_after_window_removal(src_tab)
|
||||
target_tab.make_active()
|
||||
|
||||
def _move_tab_to(self, tab=None, target_os_window_id=None):
|
||||
tab = tab or self.active_tab
|
||||
if tab is None:
|
||||
return
|
||||
if target_os_window_id is None:
|
||||
target_os_window_id = self.add_os_window()
|
||||
tm = self.os_window_map[target_os_window_id]
|
||||
target_tab = tm.new_tab(empty_tab=True)
|
||||
target_tab.take_over_from(tab)
|
||||
self._cleanup_tab_after_window_removal(tab)
|
||||
target_tab.make_active()
|
||||
|
||||
def detach_window(self, *args):
|
||||
if not args or args[0] == 'new':
|
||||
return self._move_window_to(target_os_window_id='new')
|
||||
if args[0] == 'new-tab':
|
||||
return self._move_window_to(target_tab_id='new')
|
||||
lines = [
|
||||
'Choose a tab to move the window to',
|
||||
''
|
||||
]
|
||||
fmt = ': {1}'
|
||||
tab_id_map = {}
|
||||
current_tab = self.active_tab
|
||||
for i, tab in enumerate(self.all_tabs):
|
||||
if tab is not current_tab:
|
||||
tab_id_map[len(tab_id_map)] = tab.id
|
||||
lines.append(fmt.format(i + 1, tab.title))
|
||||
new_idx = len(tab_id_map) + 1
|
||||
tab_id_map[new_idx - 1] = 'new'
|
||||
lines.append(fmt.format(new_idx, 'New tab'))
|
||||
new_idx = len(tab_id_map) + 1
|
||||
tab_id_map[new_idx - 1] = None
|
||||
lines.append(fmt.format(new_idx, 'New OS Window'))
|
||||
|
||||
def done(data, target_window_id, self):
|
||||
done.tab_id = tab_id_map[int(data['groupdicts'][0]['index'])]
|
||||
|
||||
def done2(target_window_id, self):
|
||||
if not hasattr(done, 'tab_id'):
|
||||
return
|
||||
tab_id = done.tab_id
|
||||
target_window = None
|
||||
for w in self.all_windows:
|
||||
if w.id == target_window_id:
|
||||
target_window = w
|
||||
break
|
||||
if tab_id is None:
|
||||
self._move_window_to(window=target_window, target_os_window_id='new')
|
||||
else:
|
||||
self._move_window_to(window=target_window, target_tab_id=tab_id)
|
||||
|
||||
self._run_kitten(
|
||||
'hints', args=(
|
||||
'--ascending', '--customize-processing=::import::kitty.choose_entry',
|
||||
r'--regex=(?m)^:\s+.+$',
|
||||
), input_data='\r\n'.join(lines).encode('utf-8'), custom_callback=done, action_on_removal=done2
|
||||
)
|
||||
|
||||
def detach_tab(self, *args):
|
||||
if not args or args[0] == 'new':
|
||||
return self._move_tab_to()
|
||||
|
||||
lines = [
|
||||
'Choose an OS window to move the tab to',
|
||||
''
|
||||
]
|
||||
fmt = ': {1}'
|
||||
os_window_id_map = {}
|
||||
current_os_window = getattr(self.active_tab, 'os_window_id', 0)
|
||||
for i, osw in enumerate(self.os_window_map):
|
||||
tm = self.os_window_map[osw]
|
||||
if current_os_window != osw and tm.active_tab and tm.active_tab:
|
||||
os_window_id_map[len(os_window_id_map)] = osw
|
||||
lines.append(fmt.format(i + 1, tm.active_tab.title))
|
||||
new_idx = len(os_window_id_map) + 1
|
||||
os_window_id_map[new_idx - 1] = None
|
||||
lines.append(fmt.format(new_idx, 'New OS Window'))
|
||||
|
||||
def done(data, target_window_id, self):
|
||||
done.os_window_id = os_window_id_map[int(data['groupdicts'][0]['index'])]
|
||||
|
||||
def done2(target_window_id, self):
|
||||
if not hasattr(done, 'os_window_id'):
|
||||
return
|
||||
os_window_id = done.os_window_id
|
||||
target_tab = self.active_tab
|
||||
for w in self.all_windows:
|
||||
if w.id == target_window_id:
|
||||
target_tab = w.tabref()
|
||||
break
|
||||
if target_tab and target_tab.os_window_id == os_window_id:
|
||||
return
|
||||
self._move_tab_to(tab=target_tab, target_os_window_id=os_window_id)
|
||||
|
||||
self._run_kitten(
|
||||
'hints', args=(
|
||||
'--ascending', '--customize-processing=::import::kitty.choose_entry',
|
||||
r'--regex=(?m)^:\s+.+$',
|
||||
), input_data='\r\n'.join(lines).encode('utf-8'), custom_callback=done, action_on_removal=done2
|
||||
)
|
||||
|
||||
@@ -151,7 +151,8 @@ void main() {
|
||||
// set cell color indices {{{
|
||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||
uint text_attrs = sprite_coords[3];
|
||||
uint is_inverted = ((text_attrs >> REVERSE_SHIFT) & ONE) + inverted;
|
||||
uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & ONE);
|
||||
uint is_inverted = is_reversed + inverted;
|
||||
int fg_index = fg_index_map[is_inverted];
|
||||
int bg_index = 1 - fg_index;
|
||||
float cell_has_cursor = is_cursor(c, r);
|
||||
@@ -200,21 +201,28 @@ void main() {
|
||||
background = bg;
|
||||
#endif
|
||||
|
||||
#if defined(TRANSPARENT) && !defined(SPECIAL)
|
||||
// If the background color is default, set its opacity to background_opacity, otherwise it should be opaque
|
||||
bg_alpha = step(0.5, float(colors[bg_index] & BYTE_MASK));
|
||||
// Cursor must not be affected by background_opacity
|
||||
bg_alpha = mix(bg_alpha, 1.0, cell_has_block_cursor);
|
||||
#if defined(TRANSPARENT)
|
||||
// Set bg_alpha to background_opacity on cells that have the default background color
|
||||
// Which means they must not have a block cursor or a selection or reverse video
|
||||
// On other cells it should be 1. For the SPECIAL program it should be 1 on cells with
|
||||
// selections/block cursor and 0 everywhere else.
|
||||
float is_special_cell = cell_has_block_cursor + float(is_selected & ONE);
|
||||
#ifndef SPECIAL
|
||||
is_special_cell += float(colors[bg_index] & BYTE_MASK) + float(is_reversed);
|
||||
#endif
|
||||
bg_alpha = step(0.5, is_special_cell);
|
||||
#ifndef SPECIAL
|
||||
bg_alpha = bg_alpha + (1.0f - bg_alpha) * background_opacity;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(SPECIAL) || defined(SIMPLE)
|
||||
// Selection and cursor
|
||||
bg = choose_color(float(is_selected & ONE), color_to_vec(highlight_bg), bg);
|
||||
background = choose_color(cell_has_block_cursor, color_to_vec(cursor_color), bg);
|
||||
#ifdef SPECIAL
|
||||
// bg_alpha should be 1 if cursor/selection otherwise 0
|
||||
bg_alpha = mix(0.0, 1.0, step(0.5, float(is_selected & ONE) + cell_has_block_cursor));
|
||||
#if !defined(TRANSPARENT) && defined(SPECIAL)
|
||||
float is_special_cell = cell_has_block_cursor + float(is_selected & ONE);
|
||||
bg_alpha = step(0.5, is_special_cell);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "screen.h"
|
||||
#include "fonts.h"
|
||||
#include "charsets.h"
|
||||
#include "monotonic.h"
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <float.h>
|
||||
@@ -34,7 +35,7 @@ extern PyTypeObject Screen_Type;
|
||||
#endif
|
||||
#define USE_RENDER_FRAMES (global_state.has_render_frames && OPT(sync_to_monitor))
|
||||
|
||||
static void (*parse_func)(Screen*, PyObject*, double);
|
||||
static void (*parse_func)(Screen*, PyObject*, monotonic_t);
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
@@ -108,12 +109,12 @@ static size_t reaped_pids_count = 0;
|
||||
#define INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF)
|
||||
#define DECREF_CHILD(x) XREF_CHILD(x, Py_DECREF)
|
||||
|
||||
// The max time (in secs) to wait for events from the window system
|
||||
// The max time to wait for events from the window system
|
||||
// before ticking over the main loop. Negative values mean wait forever.
|
||||
static double maximum_wait = -1.0;
|
||||
static monotonic_t maximum_wait = -1;
|
||||
|
||||
static inline void
|
||||
set_maximum_wait(double val) {
|
||||
set_maximum_wait(monotonic_t val) {
|
||||
if (val >= 0 && (val < maximum_wait || maximum_wait < 0)) maximum_wait = val;
|
||||
}
|
||||
|
||||
@@ -296,11 +297,11 @@ shutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {
|
||||
}
|
||||
|
||||
static inline bool
|
||||
do_parse(ChildMonitor *self, Screen *screen, double now) {
|
||||
do_parse(ChildMonitor *self, Screen *screen, monotonic_t now) {
|
||||
bool input_read = false;
|
||||
screen_mutex(lock, read);
|
||||
if (screen->read_buf_sz || screen->pending_mode.used) {
|
||||
double time_since_new_input = now - screen->new_input_at;
|
||||
monotonic_t time_since_new_input = now - screen->new_input_at;
|
||||
if (time_since_new_input >= OPT(input_delay)) {
|
||||
bool read_buf_full = screen->read_buf_sz >= READ_BUF_SZ;
|
||||
input_read = true;
|
||||
@@ -308,7 +309,7 @@ do_parse(ChildMonitor *self, Screen *screen, double now) {
|
||||
if (read_buf_full) wakeup_io_loop(self, false); // Ensure the read fd has POLLIN set
|
||||
screen->new_input_at = 0;
|
||||
if (screen->pending_mode.activated_at) {
|
||||
double time_since_pending = MAX(0, now - screen->pending_mode.activated_at);
|
||||
monotonic_t time_since_pending = MAX(0, now - screen->pending_mode.activated_at);
|
||||
set_maximum_wait(screen->pending_mode.wait_time - time_since_pending);
|
||||
}
|
||||
} else set_maximum_wait(OPT(input_delay) - time_since_new_input);
|
||||
@@ -322,7 +323,7 @@ parse_input(ChildMonitor *self) {
|
||||
// Parse all available input that was read in the I/O thread.
|
||||
size_t count = 0, remove_count = 0;
|
||||
bool input_read = false;
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
PyObject *msg = NULL;
|
||||
children_mutex(lock);
|
||||
while (remove_queue_count) {
|
||||
@@ -485,22 +486,22 @@ pyset_iutf8(ChildMonitor *self, PyObject *args) {
|
||||
extern void cocoa_update_menu_bar_title(PyObject*);
|
||||
|
||||
static inline void
|
||||
collect_cursor_info(CursorRenderInfo *ans, Window *w, double now, OSWindow *os_window) {
|
||||
collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow *os_window) {
|
||||
ScreenRenderData *rd = &w->render_data;
|
||||
Cursor *cursor = rd->screen->cursor;
|
||||
ans->x = cursor->x; ans->y = cursor->y;
|
||||
ans->is_visible = false;
|
||||
if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) return;
|
||||
double time_since_start_blink = now - os_window->cursor_blink_zero_time;
|
||||
monotonic_t time_since_start_blink = now - os_window->cursor_blink_zero_time;
|
||||
bool cursor_blinking = OPT(cursor_blink_interval) > 0 && os_window->is_focused && (OPT(cursor_stop_blinking_after) == 0 || time_since_start_blink <= OPT(cursor_stop_blinking_after));
|
||||
bool do_draw_cursor = true;
|
||||
if (cursor_blinking) {
|
||||
int t = (int)(time_since_start_blink * 1000);
|
||||
int d = (int)(OPT(cursor_blink_interval) * 1000);
|
||||
int t = monotonic_t_to_ms(time_since_start_blink);
|
||||
int d = monotonic_t_to_ms(OPT(cursor_blink_interval));
|
||||
int n = t / d;
|
||||
do_draw_cursor = n % 2 == 0 ? true : false;
|
||||
double bucket = (n + 1) * d;
|
||||
double delay = (bucket / 1000.0) - time_since_start_blink;
|
||||
monotonic_t bucket = ms_to_monotonic_t((n + 1) * d);
|
||||
monotonic_t delay = bucket - time_since_start_blink;
|
||||
set_maximum_wait(delay);
|
||||
}
|
||||
if (!do_draw_cursor) { ans->is_visible = false; return; }
|
||||
@@ -526,7 +527,7 @@ update_window_title(Window *w, OSWindow *os_window) {
|
||||
}
|
||||
|
||||
static inline bool
|
||||
prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg) {
|
||||
prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg) {
|
||||
#define TD os_window->tab_bar_render_data
|
||||
bool needs_render = os_window->needs_render;
|
||||
os_window->needs_render = false;
|
||||
@@ -555,10 +556,10 @@ prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *activ
|
||||
if (*num_visible_windows == 1) first_window_bg = window_bg;
|
||||
if (first_window_bg != window_bg) all_windows_have_same_bg = false;
|
||||
if (w->last_drag_scroll_at > 0) {
|
||||
if (now - w->last_drag_scroll_at >= 0.02) {
|
||||
if (now - w->last_drag_scroll_at >= ms_to_monotonic_t(20ll)) {
|
||||
if (drag_scroll(w, os_window)) {
|
||||
w->last_drag_scroll_at = now;
|
||||
set_maximum_wait(0.02);
|
||||
set_maximum_wait(ms_to_monotonic_t(20ll));
|
||||
needs_render = true;
|
||||
} else w->last_drag_scroll_at = 0;
|
||||
} else set_maximum_wait(now - w->last_drag_scroll_at);
|
||||
@@ -579,7 +580,7 @@ prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *activ
|
||||
}
|
||||
|
||||
static inline void
|
||||
render_os_window(OSWindow *os_window, double now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {
|
||||
render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {
|
||||
// ensure all pixels are cleared to background color at least once in every buffer
|
||||
if (os_window->clear_count++ < 3) blank_os_window(os_window);
|
||||
Tab *tab = os_window->tabs + os_window->active_tab;
|
||||
@@ -587,8 +588,8 @@ render_os_window(OSWindow *os_window, double now, unsigned int active_window_id,
|
||||
bool static_live_resize_in_progress = os_window->live_resize.in_progress && OPT(resize_draw_strategy) == RESIZE_DRAW_STATIC;
|
||||
float x_ratio = 1, y_ratio = 1;
|
||||
if (static_live_resize_in_progress) {
|
||||
x_ratio = os_window->viewport_width / (double) os_window->live_resize.width;
|
||||
y_ratio = os_window->viewport_height / (double) os_window->live_resize.height;
|
||||
x_ratio = (float) os_window->viewport_width / (float) os_window->live_resize.width;
|
||||
y_ratio = (float) os_window->viewport_height / (float) os_window->live_resize.height;
|
||||
}
|
||||
if (!static_live_resize_in_progress) {
|
||||
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);
|
||||
@@ -601,7 +602,7 @@ render_os_window(OSWindow *os_window, double now, unsigned int active_window_id,
|
||||
bool is_active_window = i == tab->active_window;
|
||||
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx * x_ratio, WD.dy * y_ratio, WD.screen, os_window, is_active_window, true);
|
||||
if (WD.screen->start_visual_bell_at != 0) {
|
||||
double bell_left = OPT(visual_bell_duration) - (now - WD.screen->start_visual_bell_at);
|
||||
monotonic_t bell_left = OPT(visual_bell_duration) - (now - WD.screen->start_visual_bell_at);
|
||||
set_maximum_wait(bell_left);
|
||||
}
|
||||
w->cursor_visible_at_last_render = WD.screen->cursor_render_info.is_visible; w->last_cursor_x = WD.screen->cursor_render_info.x; w->last_cursor_y = WD.screen->cursor_render_info.y; w->last_cursor_shape = WD.screen->cursor_render_info.shape;
|
||||
@@ -638,17 +639,17 @@ draw_resizing_text(OSWindow *w) {
|
||||
}
|
||||
|
||||
static inline bool
|
||||
no_render_frame_received_recently(OSWindow *w, double now, double max_wait) {
|
||||
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 %f seconds, re-requesting at: %f", max_wait, now);
|
||||
if (ans) log_error("No render frame received in %f seconds, re-requesting at: %f", monotonic_t_to_s_double(max_wait), monotonic_t_to_s_double(now));
|
||||
return ans;
|
||||
}
|
||||
|
||||
static inline void
|
||||
render(double now, bool input_read) {
|
||||
render(monotonic_t now, bool input_read) {
|
||||
EVDBG("input_read: %d", input_read);
|
||||
static double last_render_at = -DBL_MAX;
|
||||
double time_since_last_render = now - last_render_at;
|
||||
static monotonic_t last_render_at = MONOTONIC_T_MIN;
|
||||
monotonic_t time_since_last_render = last_render_at == MONOTONIC_T_MIN ? OPT(repaint_delay) : now - last_render_at;
|
||||
if (!input_read && time_since_last_render < OPT(repaint_delay)) {
|
||||
set_maximum_wait(OPT(repaint_delay) - time_since_last_render);
|
||||
return;
|
||||
@@ -662,7 +663,7 @@ render(double now, bool input_read) {
|
||||
continue;
|
||||
}
|
||||
if (USE_RENDER_FRAMES && w->render_state != RENDER_FRAME_READY) {
|
||||
if (w->render_state == RENDER_FRAME_NOT_REQUESTED || no_render_frame_received_recently(w, now, 0.25)) request_frame_render(w);
|
||||
if (w->render_state == RENDER_FRAME_NOT_REQUESTED || no_render_frame_received_recently(w, now, ms_to_monotonic_t(250ll))) request_frame_render(w);
|
||||
continue;
|
||||
}
|
||||
make_os_window_context_current(w);
|
||||
@@ -806,7 +807,7 @@ add_python_timer(PyObject *self UNUSED, PyObject *args) {
|
||||
double interval;
|
||||
int repeats = 1;
|
||||
if (!PyArg_ParseTuple(args, "Od|p", &callback, &interval, &repeats)) return NULL;
|
||||
unsigned long long timer_id = add_main_loop_timer(interval, repeats ? true: false, python_timer_callback, callback, python_timer_cleanup);
|
||||
unsigned long long timer_id = add_main_loop_timer(s_double_to_monotonic_t(interval), repeats ? true: false, python_timer_callback, callback, python_timer_cleanup);
|
||||
Py_INCREF(callback);
|
||||
return Py_BuildValue("K", timer_id);
|
||||
}
|
||||
@@ -821,7 +822,7 @@ remove_python_timer(PyObject *self UNUSED, PyObject *args) {
|
||||
|
||||
|
||||
static inline void
|
||||
process_pending_resizes(double now) {
|
||||
process_pending_resizes(monotonic_t now) {
|
||||
global_state.has_pending_resizes = false;
|
||||
for (size_t i = 0; i < global_state.num_os_windows; i++) {
|
||||
OSWindow *w = global_state.os_windows + i;
|
||||
@@ -830,11 +831,11 @@ process_pending_resizes(double now) {
|
||||
if (w->live_resize.from_os_notification) {
|
||||
if (w->live_resize.os_says_resize_complete || (now - w->live_resize.last_resize_event_at) > 1) update_viewport = true;
|
||||
} else {
|
||||
double debounce_time = OPT(resize_debounce_time);
|
||||
monotonic_t debounce_time = OPT(resize_debounce_time);
|
||||
// if more than one resize event has occurred, wait at least 0.2 secs
|
||||
// before repainting, to avoid rapid transitions between the cells banner
|
||||
// and the normal screen
|
||||
if (w->live_resize.num_of_resize_events > 1 && OPT(resize_draw_strategy) == RESIZE_DRAW_SIZE) debounce_time = MAX(0.2, debounce_time);
|
||||
if (w->live_resize.num_of_resize_events > 1 && OPT(resize_draw_strategy) == RESIZE_DRAW_SIZE) debounce_time = MAX(ms_to_monotonic_t(200ll), debounce_time);
|
||||
if (now - w->live_resize.last_resize_event_at >= debounce_time) update_viewport = true;
|
||||
else {
|
||||
global_state.has_pending_resizes = true;
|
||||
@@ -915,10 +916,14 @@ process_global_state(void *data) {
|
||||
ChildMonitor *self = data;
|
||||
maximum_wait = -1;
|
||||
bool state_check_timer_enabled = false;
|
||||
bool input_read = false;
|
||||
|
||||
double now = monotonic();
|
||||
if (global_state.has_pending_resizes) process_pending_resizes(now);
|
||||
bool input_read = parse_input(self);
|
||||
monotonic_t now = monotonic();
|
||||
if (global_state.has_pending_resizes) {
|
||||
process_pending_resizes(now);
|
||||
input_read = true;
|
||||
}
|
||||
if (parse_input(self)) input_read = true;
|
||||
render(now, input_read);
|
||||
#ifdef __APPLE__
|
||||
if (cocoa_pending_actions) {
|
||||
@@ -1167,7 +1172,7 @@ io_loop(void *data) {
|
||||
size_t i;
|
||||
int ret;
|
||||
bool has_more, data_received, has_pending_wakeups = false;
|
||||
double last_main_loop_wakeup_at = -1, now = -1;
|
||||
monotonic_t last_main_loop_wakeup_at = -1, now = -1;
|
||||
Screen *screen;
|
||||
ChildMonitor *self = (ChildMonitor*)data;
|
||||
set_thread_name("KittyChildMon");
|
||||
@@ -1188,8 +1193,8 @@ io_loop(void *data) {
|
||||
}
|
||||
if (has_pending_wakeups) {
|
||||
now = monotonic();
|
||||
double time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at);
|
||||
if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, (int)ceil(1000 * time_delta));
|
||||
monotonic_t time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at);
|
||||
if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, monotonic_t_to_ms(time_delta));
|
||||
else ret = 0;
|
||||
} else {
|
||||
ret = poll(fds, self->count + EXTRA_FDS, -1);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
import fcntl
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from contextlib import suppress
|
||||
from contextlib import contextmanager, suppress
|
||||
|
||||
import kitty.fast_data_types as fast_data_types
|
||||
|
||||
@@ -125,15 +125,24 @@ def remove_blocking(fd):
|
||||
os.set_blocking(fd, False)
|
||||
|
||||
|
||||
def process_env():
|
||||
ans = os.environ
|
||||
ssl_env_var = getattr(sys, 'kitty_ssl_env_var', None)
|
||||
if ssl_env_var is not None:
|
||||
ans = ans.copy()
|
||||
ans.pop(ssl_env_var, None)
|
||||
return ans
|
||||
|
||||
|
||||
def default_env():
|
||||
try:
|
||||
return default_env.env
|
||||
except AttributeError:
|
||||
return os.environ
|
||||
return process_env()
|
||||
|
||||
|
||||
def set_default_env(val=None):
|
||||
env = os.environ.copy()
|
||||
env = process_env().copy()
|
||||
if val:
|
||||
env.update(val)
|
||||
default_env.env = env
|
||||
@@ -151,12 +160,8 @@ class Child:
|
||||
child_fd = pid = None
|
||||
forked = False
|
||||
|
||||
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None):
|
||||
self.allow_remote_control = False
|
||||
if argv and argv[0] == '@':
|
||||
self.allow_remote_control = True
|
||||
if len(argv) > 1:
|
||||
argv = argv[1:]
|
||||
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None, allow_remote_control=False):
|
||||
self.allow_remote_control = allow_remote_control
|
||||
self.argv = argv
|
||||
if cwd_from is not None:
|
||||
try:
|
||||
@@ -262,6 +267,13 @@ class Child:
|
||||
except Exception:
|
||||
return list(self.argv)
|
||||
|
||||
@property
|
||||
def foreground_cmdline(self):
|
||||
try:
|
||||
return cmdline_of_process(self.pid_for_cwd) or self.cmdline
|
||||
except Exception:
|
||||
return self.cmdline
|
||||
|
||||
@property
|
||||
def environ(self):
|
||||
try:
|
||||
@@ -287,3 +299,14 @@ class Child:
|
||||
def foreground_cwd(self):
|
||||
with suppress(Exception):
|
||||
return cwd_of_process(self.pid_for_cwd) or None
|
||||
|
||||
@property
|
||||
def foreground_environ(self):
|
||||
try:
|
||||
return environ_of_process(self.pid_for_cwd)
|
||||
except Exception:
|
||||
try:
|
||||
return environ_of_process(self.pid)
|
||||
except Exception:
|
||||
pass
|
||||
return {}
|
||||
|
||||
12
kitty/choose_entry.py
Normal file
12
kitty/choose_entry.py
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def mark(text, args, Mark, extra_cli_args, *a):
|
||||
for idx, m in enumerate(re.finditer(args.regex, text)):
|
||||
start, end = m.span()
|
||||
mark_text = text[start:end].replace('\n', '').replace('\0', '')
|
||||
yield Mark(idx, start, end, mark_text, {'index': idx})
|
||||
@@ -225,7 +225,7 @@ Run the :italic:`{appname}` terminal emulator. You can also specify the :italic:
|
||||
to run inside :italic:`{appname}` as normal arguments following the :italic:`options`.
|
||||
For example: {appname} /bin/sh
|
||||
|
||||
For comprehensive documentation for kitty, please see: https://sw.kovidgoyal.net/kitty''').format(appname=appname)
|
||||
For comprehensive documentation for kitty, please see: https://sw.kovidgoyal.net/kitty/''').format(appname=appname)
|
||||
|
||||
|
||||
def print_help_for_seq(seq, usage, message, appname):
|
||||
@@ -423,8 +423,8 @@ class Options:
|
||||
class Namespace:
|
||||
|
||||
def __init__(self, kwargs):
|
||||
for name in kwargs:
|
||||
setattr(self, name, kwargs[name])
|
||||
for name, val in kwargs.items():
|
||||
setattr(self, name, val)
|
||||
|
||||
|
||||
def parse_cmdline(oc, disabled, args=None):
|
||||
|
||||
174
kitty/cmds.py
174
kitty/cmds.py
@@ -7,10 +7,16 @@ import os
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
|
||||
from .cli import parse_args, parse_option_spec, get_defaults_from_seq
|
||||
from .cli import (
|
||||
Namespace, get_defaults_from_seq, parse_args, parse_option_spec
|
||||
)
|
||||
from .config import parse_config, parse_send_text_bytes
|
||||
from .constants import appname
|
||||
from .fast_data_types import focus_os_window
|
||||
from .launch import (
|
||||
launch as do_launch, options_spec as launch_options_spec,
|
||||
parse_launch_args
|
||||
)
|
||||
from .tabs import SpecialWindow
|
||||
from .utils import natsort_ints
|
||||
|
||||
@@ -375,6 +381,100 @@ def set_tab_title(boss, window, payload):
|
||||
# }}}
|
||||
|
||||
|
||||
# detach_window {{{
|
||||
@cmd(
|
||||
'Detach a window and place it in a different/new tab',
|
||||
'Detach the specified window and either move it into a new tab, a new OS window'
|
||||
' or add it to the specified tab. Use the special value :code:`new` for --target-tab'
|
||||
' to move to a new tab. If no target tab is specified the window is moved to a new OS window.',
|
||||
options_spec=MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--target-tab -t') + '''\n
|
||||
--self
|
||||
type=bool-set
|
||||
If specified detach the window this command is run in, rather than the active window.
|
||||
''',
|
||||
argspec=''
|
||||
)
|
||||
def cmd_detach_window(global_opts, opts, args):
|
||||
'''
|
||||
match: Which window to detach
|
||||
target: Which tab to move the detached window to
|
||||
self: Boolean indicating whether to detach the window the command is run in
|
||||
'''
|
||||
return {'match': opts.match, 'target': opts.target_tab, 'self': opts.self}
|
||||
|
||||
|
||||
def detach_window(boss, window, payload):
|
||||
pg = cmd_detach_window.payload_get
|
||||
match = pg(payload, 'match')
|
||||
if match:
|
||||
windows = tuple(boss.match_windows(match))
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
windows = [window if window and pg(payload, 'self') else boss.active_window]
|
||||
match = pg(payload, 'target_tab')
|
||||
kwargs = {}
|
||||
if match:
|
||||
if match == 'new':
|
||||
kwargs['target_tab_id'] = 'new'
|
||||
else:
|
||||
tabs = tuple(boss.match_tabs(match))
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
kwargs['target_tab_id'] = tabs[0].id
|
||||
if not kwargs:
|
||||
kwargs['target_os_window_id'] = 'new'
|
||||
|
||||
for window in windows:
|
||||
boss._move_window_to(window=window, **kwargs)
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
# detach_tab {{{
|
||||
@cmd(
|
||||
'Detach a tab and place it in a different/new OS Window',
|
||||
'Detach the specified tab and either move it into a new OS window'
|
||||
' or add it to the OS Window containing the tab specified by --target-tab',
|
||||
options_spec=MATCH_TAB_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--target-tab -t') + '''\n
|
||||
--self
|
||||
type=bool-set
|
||||
If specified detach the tab this command is run in, rather than the active tab.
|
||||
''',
|
||||
argspec=''
|
||||
)
|
||||
def cmd_detach_tab(global_opts, opts, args):
|
||||
'''
|
||||
match: Which tab to detach
|
||||
target: Which OS Window to move the detached tab to
|
||||
self: Boolean indicating whether to detach the tab the command is run in
|
||||
'''
|
||||
return {'match': opts.match, 'target': opts.target_tab, 'self': opts.self}
|
||||
|
||||
|
||||
def detach_tab(boss, window, payload):
|
||||
pg = cmd_detach_tab.payload_get
|
||||
match = pg(payload, 'match')
|
||||
if match:
|
||||
tabs = tuple(boss.match_tabs(match))
|
||||
if not tabs:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
tabs = [window.tabref() if pg(payload, 'self') and window and window.tabref() else boss.active_tab]
|
||||
match = pg(payload, 'target_tab')
|
||||
kwargs = {}
|
||||
if match:
|
||||
targets = tuple(boss.match_tabs(match))
|
||||
if not targets:
|
||||
raise MatchError(match, 'tabs')
|
||||
kwargs['target_os_window_id'] = targets[0].os_window_id
|
||||
|
||||
for tab in tabs:
|
||||
boss._move_tab_to(tab=tab, **kwargs)
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
# goto_layout {{{
|
||||
@cmd(
|
||||
'Set the window layout',
|
||||
@@ -675,6 +775,76 @@ def new_window(boss, window, payload):
|
||||
# }}}
|
||||
|
||||
|
||||
# launch {{{
|
||||
@cmd(
|
||||
'Run an arbitrary process in a new window/tab',
|
||||
' Prints out the id of the newly opened window. Any command line arguments'
|
||||
' are assumed to be the command line used to run in the new window, if none'
|
||||
' are provided, the default shell is run. For example:'
|
||||
' :italic:`kitty @ launch --title Email mutt`.',
|
||||
options_spec=MATCH_TAB_OPTION + '\n\n' + '''\
|
||||
--no-response
|
||||
type=bool-set
|
||||
Do not print out the id of the newly created window.
|
||||
|
||||
|
||||
--self
|
||||
type=bool-set
|
||||
If specified the tab containing the window this command is run in is used
|
||||
instead of the active tab
|
||||
''' + '\n\n' + launch_options_spec().replace(':option:`launch', ':option:`kitty @ launch'),
|
||||
argspec='[CMD ...]'
|
||||
)
|
||||
def cmd_launch(global_opts, opts, args):
|
||||
'''
|
||||
args+: The command line to run in the new window, as a list, use an empty list to run the default shell
|
||||
match: The tab to open the new window in
|
||||
window_title: Title for the new window
|
||||
cwd: Working directory for the new window
|
||||
env: List of environment varibles of the form NAME=VALUE
|
||||
tab_title: Title for the new tab
|
||||
type: The type of window to open
|
||||
keep_focus: Boolean indicating whether the current window should retain focus or not
|
||||
copy_colors: Boolean indicating whether to copy the colors from the current window
|
||||
copy_cmdline: Boolean indicating whether to copy the cmdline from the current window
|
||||
copy_env: Boolean indicating whether to copy the environ from the current window
|
||||
location: Where in the tab to open the new window
|
||||
allow_remote_control: Boolean indicating whether to allow remote control from the new window
|
||||
stdin_source: Where to get stdin for thew process from
|
||||
stdin_add_formatting: Boolean indicating whether to add formatting codes to stdin
|
||||
stdin_add_line_wrap_markers: Boolean indicating whether to add line wrap markers to stdin
|
||||
no_response: Boolean indicating whether to send back the window id
|
||||
'''
|
||||
if opts.no_response:
|
||||
global_opts.no_command_response = True
|
||||
ans = {'args': args or []}
|
||||
for attr, val in opts.__dict__.items():
|
||||
ans[attr] = val
|
||||
return ans
|
||||
|
||||
|
||||
def launch(boss, window, payload):
|
||||
pg = cmd_launch.payload_get
|
||||
default_opts = parse_launch_args()[0]
|
||||
opts = {}
|
||||
for key, default_value in default_opts.__dict__.items():
|
||||
opts[key] = payload.get(key, default_value)
|
||||
opts = Namespace(opts)
|
||||
match = pg(payload, 'match')
|
||||
if match:
|
||||
tabs = tuple(boss.match_tabs(match))
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
else:
|
||||
tabs = [boss.active_tab]
|
||||
if pg(payload, 'self') and window and window.tabref():
|
||||
tabs = [window.tabref()]
|
||||
tab = tabs[0]
|
||||
w = do_launch(boss, opts, pg(payload, 'args') or None, target_tab=tab)
|
||||
return None if pg(payload, 'no_response') else str(getattr(w, 'id', 0))
|
||||
# }}}
|
||||
|
||||
|
||||
# focus_window {{{
|
||||
@cmd(
|
||||
'Focus the specified window',
|
||||
@@ -882,7 +1052,7 @@ this option, any color arguments are ignored and --configured and --all are impl
|
||||
def cmd_set_colors(global_opts, opts, args):
|
||||
'''
|
||||
colors+: An object mapping names to colors as 24-bit RGB integers
|
||||
cursor_text_color: A 24-bit clor for text under the cursor
|
||||
cursor_text_color: A 24-bit color for text under the cursor
|
||||
match_window: Window to change colors in
|
||||
match_tab: Tab to change colors in
|
||||
all: Boolean indicating change colors everywhere or not
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
|
||||
#include "state.h"
|
||||
#include "monotonic.h"
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
@@ -72,12 +73,12 @@ find_app_name(void) {
|
||||
|
||||
@implementation GlobalMenuTarget
|
||||
|
||||
- (void) show_preferences : (id)sender {
|
||||
- (void)show_preferences:(id)sender {
|
||||
(void)sender;
|
||||
set_cocoa_pending_action(PREFERENCES_WINDOW, NULL);
|
||||
}
|
||||
|
||||
- (void) new_os_window : (id)sender {
|
||||
- (void)new_os_window:(id)sender {
|
||||
(void)sender;
|
||||
set_cocoa_pending_action(NEW_OS_WINDOW, NULL);
|
||||
}
|
||||
@@ -117,9 +118,10 @@ get_dock_menu(id self UNUSED, SEL _cmd UNUSED, NSApplication *sender UNUSED) {
|
||||
if (!dockMenu) {
|
||||
GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];
|
||||
dockMenu = [[NSMenu alloc] init];
|
||||
NSMenuItem *newWindowItem = [dockMenu addItemWithTitle:@"New OS window"
|
||||
action:@selector(new_os_window:)
|
||||
keyEquivalent:@""];
|
||||
NSMenuItem *newWindowItem =
|
||||
[dockMenu addItemWithTitle:@"New OS window"
|
||||
action:@selector(new_os_window:)
|
||||
keyEquivalent:@""];
|
||||
[newWindowItem setTarget:global_menu_target];
|
||||
}
|
||||
return dockMenu;
|
||||
@@ -231,21 +233,21 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
|
||||
// global menu {{{
|
||||
void
|
||||
cocoa_create_global_menu(void) {
|
||||
@autoreleasepool {
|
||||
|
||||
NSString* app_name = find_app_name();
|
||||
NSMenu* bar = [[NSMenu alloc] init];
|
||||
GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];
|
||||
[NSApp setMainMenu:bar];
|
||||
|
||||
NSMenuItem* appMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
[bar addItemWithTitle:@""
|
||||
action:NULL
|
||||
keyEquivalent:@""];
|
||||
NSMenu* appMenu = [[NSMenu alloc] init];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", app_name]
|
||||
action:@selector(orderFrontStandardAboutPanel:)
|
||||
keyEquivalent:@""];
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
NSMenuItem* preferences_menu_item = [[NSMenuItem alloc] initWithTitle:@"Preferences..." action:@selector(show_preferences:) keyEquivalent:@","], *new_os_window_menu_item = NULL;
|
||||
[preferences_menu_item setTarget:global_menu_target];
|
||||
@@ -264,8 +266,8 @@ cocoa_create_global_menu(void) {
|
||||
action:@selector(hide:)
|
||||
keyEquivalent:@"h"];
|
||||
[[appMenu addItemWithTitle:@"Hide Others"
|
||||
action:@selector(hideOtherApplications:)
|
||||
keyEquivalent:@"h"]
|
||||
action:@selector(hideOtherApplications:)
|
||||
keyEquivalent:@"h"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand];
|
||||
[appMenu addItemWithTitle:@"Show All"
|
||||
action:@selector(unhideAllApplications:)
|
||||
@@ -275,19 +277,21 @@ cocoa_create_global_menu(void) {
|
||||
NSMenu* servicesMenu = [[NSMenu alloc] init];
|
||||
[NSApp setServicesMenu:servicesMenu];
|
||||
[[appMenu addItemWithTitle:@"Services"
|
||||
action:NULL
|
||||
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||
action:NULL
|
||||
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||
[servicesMenu release];
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", app_name]
|
||||
action:@selector(terminate:)
|
||||
keyEquivalent:@"q"];
|
||||
keyEquivalent:@"q"];
|
||||
[appMenu release];
|
||||
|
||||
NSMenuItem* windowMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
[bar addItemWithTitle:@""
|
||||
action:NULL
|
||||
keyEquivalent:@""];
|
||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
|
||||
@@ -317,20 +321,18 @@ cocoa_create_global_menu(void) {
|
||||
[bar release];
|
||||
|
||||
class_addMethod(
|
||||
object_getClass([NSApp delegate]),
|
||||
@selector(applicationDockMenu:),
|
||||
(IMP)get_dock_menu,
|
||||
"@@:@");
|
||||
object_getClass([NSApp delegate]),
|
||||
@selector(applicationDockMenu:),
|
||||
(IMP)get_dock_menu,
|
||||
"@@:@");
|
||||
|
||||
|
||||
[NSApp setServicesProvider:[[[ServiceProvider alloc] init] autorelease]];
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void
|
||||
cocoa_update_menu_bar_title(PyObject *pytitle) {
|
||||
NSString *title = [[NSString alloc] initWithUTF8String:PyUnicode_AsUTF8(pytitle)];
|
||||
NSString *title = @(PyUnicode_AsUTF8(pytitle));
|
||||
NSMenu *bar = [NSApp mainMenu];
|
||||
if (title_menu != NULL) {
|
||||
[bar removeItem:title_menu];
|
||||
@@ -339,7 +341,6 @@ cocoa_update_menu_bar_title(PyObject *pytitle) {
|
||||
NSMenu *m = [[NSMenu alloc] initWithTitle:[NSString stringWithFormat:@" :: %@", title]];
|
||||
[title_menu setSubmenu:m];
|
||||
[m release];
|
||||
[title release];
|
||||
} // }}}
|
||||
|
||||
bool
|
||||
@@ -367,7 +368,7 @@ cocoa_make_window_resizable(void *w, bool resizable) {
|
||||
bool
|
||||
cocoa_alt_option_key_pressed(NSUInteger flags) {
|
||||
NSUInteger q = (OPT(macos_option_as_alt) == 1) ? NSRightAlternateKeyMask : NSLeftAlternateKeyMask;
|
||||
return ((q & flags) == q) ? true : false;
|
||||
return (q & flags) == q;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -411,7 +412,7 @@ cocoa_get_lang(PyObject UNUSED *self) {
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
double
|
||||
monotonic_t
|
||||
cocoa_cursor_blink_interval(void) {
|
||||
@autoreleasepool {
|
||||
|
||||
@@ -420,12 +421,12 @@ cocoa_cursor_blink_interval(void) {
|
||||
double off_period_ms = [defaults doubleForKey:@"NSTextInsertionPointBlinkPeriodOff"];
|
||||
double period_ms = [defaults doubleForKey:@"NSTextInsertionPointBlinkPeriod"];
|
||||
double max_value = 60 * 1000.0, ans = -1.0;
|
||||
if (on_period_ms || off_period_ms) {
|
||||
if (on_period_ms != 0. || off_period_ms != 0.) {
|
||||
ans = on_period_ms + off_period_ms;
|
||||
} else if (period_ms) {
|
||||
} else if (period_ms != 0.) {
|
||||
ans = period_ms;
|
||||
}
|
||||
return ans > max_value ? 0.0 : ans;
|
||||
return ans > max_value ? 0ll : ms_double_to_monotonic_t(ans);
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ from .shell import options_for_cmd
|
||||
To add completion for a new shell, you need to:
|
||||
|
||||
1) Add an entry to completion scripts for your shell, this is
|
||||
a simple function that call's kitty's completion code and passes the
|
||||
a simple function that calls kitty's completion code and passes the
|
||||
results to the shell's completion system. This can be output by
|
||||
`kitty +complete setup shell_name` and its output goes into
|
||||
your shell's rc file.
|
||||
|
||||
@@ -50,7 +50,8 @@ func_with_args, args_funcs = key_func()
|
||||
|
||||
@func_with_args(
|
||||
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
|
||||
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd'
|
||||
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',
|
||||
'launch'
|
||||
)
|
||||
def shlex_parse(func, rest):
|
||||
return func, to_cmdline(rest)
|
||||
@@ -92,6 +93,20 @@ def goto_tab_parse(func, rest):
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('detach_window')
|
||||
def detach_window_parse(func, rest):
|
||||
if rest not in ('new', 'new-tab', 'ask'):
|
||||
rest = 'new'
|
||||
return func, (rest,)
|
||||
|
||||
|
||||
@func_with_args('detach_tab')
|
||||
def detach_tab_parse(func, rest):
|
||||
if rest not in ('new', 'ask'):
|
||||
rest = 'new'
|
||||
return func, (rest,)
|
||||
|
||||
|
||||
@func_with_args('set_background_opacity', 'goto_layout', 'kitty_shell')
|
||||
def simple_parse(func, rest):
|
||||
return func, [rest]
|
||||
@@ -193,6 +208,15 @@ def pipe(func, rest):
|
||||
return func, rest
|
||||
|
||||
|
||||
@func_with_args('set_colors')
|
||||
def set_colors(func, rest):
|
||||
import shlex
|
||||
rest = shlex.split(rest)
|
||||
if len(rest) < 1:
|
||||
log_error('Too few arguments to set_colors function')
|
||||
return func, rest
|
||||
|
||||
|
||||
@func_with_args('nth_window')
|
||||
def nth_window(func, rest):
|
||||
try:
|
||||
|
||||
@@ -393,10 +393,13 @@ Use negative numbers to change scroll direction.'''))
|
||||
|
||||
g('mouse') # {{{
|
||||
|
||||
o('mouse_hide_wait', 3.0, option_type=float, long_text=_('''
|
||||
o('mouse_hide_wait', 0.0 if is_macos else 3.0, option_type=float, long_text=_('''
|
||||
Hide mouse cursor after the specified number of seconds
|
||||
of the mouse not being used. Set to zero to disable mouse cursor hiding.
|
||||
Set to a negative value to hide the mouse cursor immediately when typing text.'''))
|
||||
Set to a negative value to hide the mouse cursor immediately when typing text.
|
||||
Disabled by default on macOS as getting it to work robustly with
|
||||
the ever-changing sea of bugs that is Cocoa is too much effort.
|
||||
'''))
|
||||
|
||||
o('url_color', '#0087bd', option_type=to_color, long_text=_('''
|
||||
The color and style for highlighting URLs on mouse-over.
|
||||
@@ -476,6 +479,7 @@ moving the mouse around'''))
|
||||
|
||||
o('pointer_shape_when_grabbed', 'arrow', option_type=choices('arrow', 'beam', 'hand'), long_text=('''
|
||||
The shape of the mouse pointer when the program running in the terminal grabs the mouse.
|
||||
Valid values are: :code:`arrow`, :code:`beam` and :code:`hand`
|
||||
'''))
|
||||
|
||||
# }}}
|
||||
@@ -679,10 +683,10 @@ Which edge to show the tab bar on, top or bottom'''))
|
||||
o('tab_bar_margin_width', 0.0, option_type=positive_float, long_text=_('''
|
||||
The margin to the left and right of the tab bar (in pts)'''))
|
||||
|
||||
o('tab_bar_style', 'fade', option_type=choices('fade', 'separator', 'hidden'), long_text=_('''
|
||||
The tab bar style, can be one of: :code:`fade`, :code:`separator` or :code:`hidden`. In the fade style,
|
||||
each tab's edges fade into the background color, in the separator style, tabs are
|
||||
separated by a configurable separator.
|
||||
o('tab_bar_style', 'fade', option_type=choices('fade', 'separator', 'powerline', 'hidden'), long_text=_('''
|
||||
The tab bar style, can be one of: :code:`fade`, :code:`separator`, :code:`powerline`, or :code:`hidden`.
|
||||
In the fade style, each tab's edges fade into the background color, in the separator style, tabs are
|
||||
separated by a configurable separator, and the powerline shows the tabs as a continuous line.
|
||||
'''))
|
||||
|
||||
o('tab_bar_min_tabs', 2, option_type=lambda x: max(1, positive_int(x)), long_text=_('''
|
||||
@@ -712,12 +716,20 @@ entries to this list.
|
||||
o('tab_separator', '"{}"'.format(default_tab_separator), option_type=tab_separator, long_text=_('''
|
||||
The separator between tabs in the tab bar when using :code:`separator` as the :opt:`tab_bar_style`.'''))
|
||||
|
||||
|
||||
def active_tab_title_template(x):
|
||||
return None if x == 'none' else x
|
||||
|
||||
|
||||
o('tab_title_template', '{title}', long_text=_('''
|
||||
A template to render the tab title. The default just renders
|
||||
the title. If you wish to include the tab-index as well,
|
||||
use something like: :code:`{index}: {title}`. Useful
|
||||
if you have shortcuts mapped for :code:`goto_tab N`.
|
||||
'''))
|
||||
o('active_tab_title_template', 'none', option_type=active_tab_title_template, long_text=_('''
|
||||
Template to use for active tabs, if not specified falls back
|
||||
to :opt:`tab_title_template`.'''))
|
||||
|
||||
o('active_tab_foreground', '#000', option_type=to_color, long_text=_('''
|
||||
Tab bar colors and styles'''))
|
||||
@@ -726,6 +738,8 @@ o('active_tab_font_style', 'bold-italic', option_type=tab_font_style)
|
||||
o('inactive_tab_foreground', '#444', option_type=to_color)
|
||||
o('inactive_tab_background', '#999', option_type=to_color)
|
||||
o('inactive_tab_font_style', 'normal', option_type=tab_font_style)
|
||||
o('tab_bar_background', 'none', option_type=to_color_or_none, long_text=_('''
|
||||
Background color for the tab bar. Defaults to using the terminal background color.'''))
|
||||
|
||||
# }}}
|
||||
|
||||
@@ -812,9 +826,9 @@ ensure that the shell starts in interactive mode and reads its startup rc files.
|
||||
|
||||
o('editor', '.', long_text=_('''
|
||||
The console editor to use when editing the kitty config file or similar tasks.
|
||||
A value of . means to use the environment variable EDITOR. Note that this
|
||||
environment variable has to be set not just in your shell startup scripts but
|
||||
system-wide, otherwise kitty will not see it.
|
||||
A value of . means to use the environment variables VISUAL and EDITOR in that
|
||||
order. Note that this environment variable has to be set not just in your shell
|
||||
startup scripts but system-wide, otherwise kitty will not see it.
|
||||
'''))
|
||||
|
||||
o('close_on_child_death', False, long_text=_('''
|
||||
@@ -826,11 +840,23 @@ Note that setting it to yes means that any background processes still using the
|
||||
terminal can fail silently because their stdout/stderr/stdin no longer work.
|
||||
'''))
|
||||
|
||||
o('allow_remote_control', False, long_text=_('''
|
||||
|
||||
def allow_remote_control(x):
|
||||
if x != 'socket-only':
|
||||
x = 'y' if to_bool(x) else 'n'
|
||||
return x
|
||||
|
||||
|
||||
o('allow_remote_control', 'no', option_type=allow_remote_control, long_text=_('''
|
||||
Allow other programs to control kitty. If you turn this on other programs can
|
||||
control all aspects of kitty, including sending text to kitty windows,
|
||||
opening new windows, closing windows, reading the content of windows, etc.
|
||||
Note that this even works over ssh connections.
|
||||
control all aspects of kitty, including sending text to kitty windows, opening
|
||||
new windows, closing windows, reading the content of windows, etc. Note that
|
||||
this even works over ssh connections. You can chose to either allow any program
|
||||
running within kitty to control it, with :code:`yes` or only programs that
|
||||
connect to the socket specified with the :option:`kitty --listen-on` command
|
||||
line option, if you use the value :code:`socket-only`. The latter is useful if
|
||||
you want to prevent programs running on a remote computer over ssh from
|
||||
controlling kitty.
|
||||
'''))
|
||||
|
||||
o(
|
||||
@@ -1061,13 +1087,13 @@ if is_macos:
|
||||
k('show_scrollback', 'kitty_mod+h', 'show_scrollback', _('Browse scrollback buffer in less'), long_text=_('''
|
||||
|
||||
You can pipe the contents of the current screen + history buffer as
|
||||
:file:`STDIN` to an arbitrary program using the ``pipe`` function. For example,
|
||||
:file:`STDIN` to an arbitrary program using the ``launch`` function. For example,
|
||||
the following opens the scrollback buffer in less in an overlay window::
|
||||
|
||||
map f1 pipe @ansi overlay less +G -R
|
||||
map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay less +G -R
|
||||
|
||||
For more details on piping screen and buffer contents to external programs,
|
||||
see :doc:`pipe`.
|
||||
see :doc:`launch`.
|
||||
'''))
|
||||
|
||||
|
||||
@@ -1077,26 +1103,27 @@ g('shortcuts.window') # {{{
|
||||
k('new_window', 'kitty_mod+enter', 'new_window', _(''), long_text=_('''
|
||||
You can open a new window running an arbitrary program, for example::
|
||||
|
||||
map kitty_mod+y new_window mutt
|
||||
map kitty_mod+y launch mutt
|
||||
|
||||
You can open a new window with the current working directory set to the
|
||||
working directory of the current window using::
|
||||
|
||||
map ctrl+alt+enter new_window_with_cwd
|
||||
map ctrl+alt+enter launch --cwd=current
|
||||
|
||||
You can open a new window that is allowed to control kitty via
|
||||
the kitty remote control facility by prefixing the command line with @.
|
||||
Any programs running in that window will be allowed to control kitty.
|
||||
For example::
|
||||
|
||||
map ctrl+enter new_window @ some_program
|
||||
map ctrl+enter launch --allow-remote-control some_program
|
||||
|
||||
You can open a new window next to the currently active window or as the first window,
|
||||
with::
|
||||
|
||||
map ctrl+n new_window !neighbor some_program
|
||||
map ctrl+f new_window !first some_program
|
||||
map ctrl+n launch --location=neighbor some_program
|
||||
map ctrl+f launch --location=first some_program
|
||||
|
||||
For more details, see :doc:`launch`.
|
||||
'''))
|
||||
if is_macos:
|
||||
k('new_window', 'cmd+enter', 'new_window', _('New window'), add_to_docs=False)
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
import os
|
||||
import pwd
|
||||
import sys
|
||||
import errno
|
||||
from collections import namedtuple
|
||||
from contextlib import suppress
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 14, 6)
|
||||
version = (0, 15, 1)
|
||||
str_version = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
@@ -25,7 +26,7 @@ def kitty_exe():
|
||||
if ans is None:
|
||||
rpath = sys._xoptions.get('bundle_exe_dir')
|
||||
if not rpath:
|
||||
items = os.environ['PATH'].split(os.pathsep)
|
||||
items = filter(None, os.environ.get('PATH', '').split(os.pathsep))
|
||||
seen = set()
|
||||
for candidate in items:
|
||||
if candidate not in seen:
|
||||
@@ -49,22 +50,15 @@ def _get_config_dir():
|
||||
locations.append(os.path.expanduser('~/.config'))
|
||||
if is_macos:
|
||||
locations.append(os.path.expanduser('~/Library/Preferences'))
|
||||
if 'XDG_CONFIG_DIRS' in os.environ:
|
||||
for loc in os.environ['XDG_CONFIG_DIRS'].split(os.pathsep):
|
||||
locations.append(os.path.abspath(os.path.expanduser(loc)))
|
||||
for loc in filter(None, os.environ.get('XDG_CONFIG_DIRS', '').split(os.pathsep)):
|
||||
locations.append(os.path.abspath(os.path.expanduser(loc)))
|
||||
for loc in locations:
|
||||
if loc:
|
||||
q = os.path.join(loc, appname)
|
||||
if os.access(q, os.W_OK) and os.path.exists(os.path.join(q, 'kitty.conf')):
|
||||
return q
|
||||
|
||||
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or '~/.config'))
|
||||
ans = os.path.join(candidate, appname)
|
||||
try:
|
||||
os.makedirs(ans, exist_ok=True)
|
||||
except FileExistsError:
|
||||
raise SystemExit('A file {} already exists. It must be a directory, not a file.'.format(ans))
|
||||
except PermissionError:
|
||||
def make_tmp_conf():
|
||||
import tempfile
|
||||
import atexit
|
||||
ans = tempfile.mkdtemp(prefix='kitty-conf-')
|
||||
@@ -74,6 +68,19 @@ def _get_config_dir():
|
||||
with suppress(Exception):
|
||||
shutil.rmtree(ans)
|
||||
atexit.register(cleanup)
|
||||
|
||||
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or '~/.config'))
|
||||
ans = os.path.join(candidate, appname)
|
||||
try:
|
||||
os.makedirs(ans, exist_ok=True)
|
||||
except FileExistsError:
|
||||
raise SystemExit('A file {} already exists. It must be a directory, not a file.'.format(ans))
|
||||
except PermissionError:
|
||||
make_tmp_conf()
|
||||
except OSError as err:
|
||||
if err.errno != errno.EROFS: # Error other than read-only file system
|
||||
raise
|
||||
make_tmp_conf()
|
||||
return ans
|
||||
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ get_glyph_width(PyObject *s, glyph_index g) {
|
||||
CGGlyph gg = g;
|
||||
CGRect bounds;
|
||||
CTFontGetBoundingRectsForGlyphs(self->ct_font, kCTFontOrientationHorizontal, &gg, &bounds, 1);
|
||||
return bounds.size.width;
|
||||
return (int)ceil(bounds.size.width);
|
||||
}
|
||||
|
||||
static inline float
|
||||
@@ -326,7 +326,7 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
|
||||
}
|
||||
}
|
||||
*cell_width = MAX(1u, width);
|
||||
*underline_position = floor(self->ascent - self->underline_position + 0.5);
|
||||
*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;
|
||||
// float line_height = MAX(1, floor(self->ascent + self->descent + MAX(0, self->leading) + 0.5));
|
||||
@@ -457,7 +457,7 @@ render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {
|
||||
CTFontGetGlyphsForCharacters(font, chars, glyphs, num_chars);
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontOrientationDefault, glyphs, advances, num_chars);
|
||||
CGRect bounding_box = CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationDefault, glyphs, boxes, num_chars);
|
||||
StringCanvas ans = { .width = 0, .height = 2 * bounding_box.size.height };
|
||||
StringCanvas ans = { .width = 0, .height = (size_t)(2 * bounding_box.size.height) };
|
||||
for (size_t i = 0, y = 0; i < num_chars; i++) {
|
||||
positions[i] = CGPointMake(ans.width, y);
|
||||
ans.width += advances[i].width; y += advances[i].height;
|
||||
|
||||
@@ -22,31 +22,10 @@
|
||||
#ifdef WITH_PROFILER
|
||||
#include <gperftools/profiler.h>
|
||||
#endif
|
||||
|
||||
/* To millisecond (10^-3) */
|
||||
#define SEC_TO_MS 1000
|
||||
|
||||
/* To microseconds (10^-6) */
|
||||
#define MS_TO_US 1000
|
||||
#define SEC_TO_US (SEC_TO_MS * MS_TO_US)
|
||||
|
||||
/* To nanoseconds (10^-9) */
|
||||
#define US_TO_NS 1000
|
||||
#define MS_TO_NS (MS_TO_US * US_TO_NS)
|
||||
#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS)
|
||||
|
||||
/* Conversion from nanoseconds */
|
||||
#define NS_TO_MS (1000 * 1000)
|
||||
#define NS_TO_US (1000)
|
||||
#include "monotonic.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <libproc.h>
|
||||
#include <mach/mach_time.h>
|
||||
static mach_timebase_info_data_t timebase = {0};
|
||||
|
||||
static inline double monotonic_(void) {
|
||||
return ((double)(mach_absolute_time() * timebase.numer) / timebase.denom)/SEC_TO_NS;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
user_cache_dir() {
|
||||
@@ -73,25 +52,7 @@ process_group_map() {
|
||||
free(buf);
|
||||
return ans;
|
||||
}
|
||||
|
||||
#else
|
||||
#include <time.h>
|
||||
static inline double monotonic_(void) {
|
||||
struct timespec ts = {0};
|
||||
#ifdef CLOCK_HIGHRES
|
||||
clock_gettime(CLOCK_HIGHRES, &ts);
|
||||
#elif CLOCK_MONOTONIC_RAW
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
#else
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
#endif
|
||||
return (((double)ts.tv_nsec) / SEC_TO_NS) + (double)ts.tv_sec;
|
||||
}
|
||||
#endif
|
||||
|
||||
static double start_time = 0;
|
||||
|
||||
double monotonic() { return monotonic_() - start_time; }
|
||||
|
||||
static PyObject*
|
||||
redirect_std_streams(PyObject UNUSED *self, PyObject *args) {
|
||||
@@ -148,7 +109,7 @@ open_tty(PyObject *self UNUSED, PyObject *args) {
|
||||
if (!read_with_timeout) flags |= O_NONBLOCK;
|
||||
static char ctty[L_ctermid+1];
|
||||
int fd = open(ctermid(ctty), flags);
|
||||
if (fd == -1) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||
if (fd == -1) { PyErr_Format(PyExc_OSError, "Failed to open controlling terminal: %s (identified with ctermid()) with error: %s", ctty, strerror(errno)); return NULL; }
|
||||
struct termios *termios_p = calloc(1, sizeof(struct termios));
|
||||
if (!termios_p) return PyErr_NoMemory();
|
||||
if (tcgetattr(fd, termios_p) != 0) { free(termios_p); PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||
@@ -253,10 +214,7 @@ PyInit_fast_data_types(void) {
|
||||
|
||||
m = PyModule_Create(&module);
|
||||
if (m == NULL) return NULL;
|
||||
#ifdef __APPLE__
|
||||
mach_timebase_info(&timebase);
|
||||
#endif
|
||||
start_time = monotonic_();
|
||||
init_monotonic();
|
||||
|
||||
if (m != NULL) {
|
||||
if (!init_logging(m)) return NULL;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include "glfw-wrapper.h"
|
||||
// Required minimum OpenGL version
|
||||
#define OPENGL_REQUIRED_VERSION_MAJOR 3
|
||||
#define OPENGL_REQUIRED_VERSION_MINOR 3
|
||||
@@ -291,7 +292,6 @@ void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned i
|
||||
const char* cell_as_sgr(const GPUCell *, const GPUCell *);
|
||||
const char* cursor_as_sgr(const Cursor *);
|
||||
|
||||
double monotonic(void);
|
||||
PyObject* cm_thread_write(PyObject *self, PyObject *args);
|
||||
bool schedule_write_to_child(unsigned long id, unsigned int num, ...);
|
||||
bool set_iutf8(int, bool);
|
||||
@@ -309,7 +309,7 @@ void focus_in_event(void);
|
||||
void scroll_event(double, double, int);
|
||||
void fake_scroll(int, bool);
|
||||
void set_special_key_combo(int glfw_key, int mods, bool is_native);
|
||||
void on_key_input(int key, int scancode, int action, int mods, const char*, int);
|
||||
void on_key_input(GLFWkeyevent *ev);
|
||||
void request_window_attention(id_type, bool);
|
||||
#ifndef __APPLE__
|
||||
void play_canberra_sound(const char *which_sound, const char *event_id);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "data-types.h"
|
||||
#include <dlfcn.h>
|
||||
#include <canberra.h>
|
||||
|
||||
#define FUNC(name, restype, ...) typedef restype (*name##_func)(__VA_ARGS__); static name##_func name = NULL
|
||||
#define LOAD_FUNC(handle, name) {\
|
||||
@@ -90,15 +89,56 @@ static PyMethodDef module_methods[] = {
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static ca_context *canberra_ctx = NULL;
|
||||
static void* libcanberra_handle = NULL;
|
||||
static void *canberra_ctx = NULL;
|
||||
FUNC(ca_context_create, int, void**);
|
||||
FUNC(ca_context_destroy, int, void*);
|
||||
typedef int (*ca_context_play_func)(void*, uint32_t, ...); static ca_context_play_func ca_context_play = NULL;
|
||||
|
||||
static PyObject*
|
||||
load_libcanberra_functions(void) {
|
||||
LOAD_FUNC(libcanberra_handle, ca_context_create);
|
||||
LOAD_FUNC(libcanberra_handle, ca_context_play);
|
||||
LOAD_FUNC(libcanberra_handle, ca_context_destroy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
load_libcanberra(void) {
|
||||
static const char* libname = "libcanberra.so";
|
||||
// some installs are missing the .so symlink, so try the full name
|
||||
static const char* libname2 = "libcanberra.so.0";
|
||||
static const char* libname3 = "libcanberra.so.0.2.5";
|
||||
static bool done = false;
|
||||
if (done) return;
|
||||
done = true;
|
||||
libcanberra_handle = dlopen(libname, RTLD_LAZY);
|
||||
if (libcanberra_handle == NULL) libcanberra_handle = dlopen(libname2, RTLD_LAZY);
|
||||
if (libcanberra_handle == NULL) libcanberra_handle = dlopen(libname3, RTLD_LAZY);
|
||||
if (libcanberra_handle == NULL) {
|
||||
fprintf(stderr, "Failed to load %s, cannot play beep sound, with error: %s\n", libname, dlerror());
|
||||
return;
|
||||
}
|
||||
load_libcanberra_functions();
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Print();
|
||||
dlclose(libcanberra_handle); libcanberra_handle = NULL;
|
||||
}
|
||||
if (ca_context_create(&canberra_ctx) != 0) {
|
||||
fprintf(stderr, "Failed to create libcanberra context, cannot play beep sound\n");
|
||||
ca_context_destroy(canberra_ctx); canberra_ctx = NULL;
|
||||
dlclose(libcanberra_handle); libcanberra_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
play_canberra_sound(const char *which_sound, const char *event_id) {
|
||||
if (canberra_ctx == NULL) ca_context_create(&canberra_ctx);
|
||||
load_libcanberra();
|
||||
if (libcanberra_handle == NULL || canberra_ctx == NULL) return;
|
||||
ca_context_play(
|
||||
canberra_ctx, 0,
|
||||
CA_PROP_EVENT_ID, which_sound,
|
||||
CA_PROP_EVENT_DESCRIPTION, event_id,
|
||||
"event.id", which_sound,
|
||||
"event.description", event_id,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
@@ -109,6 +149,7 @@ finalize(void) {
|
||||
libsn_handle = NULL;
|
||||
if (canberra_ctx) ca_context_destroy(canberra_ctx);
|
||||
canberra_ctx = NULL;
|
||||
if (libcanberra_handle) dlclose(libcanberra_handle);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
461
kitty/emoji.h
generated
461
kitty/emoji.h
generated
@@ -1,4 +1,4 @@
|
||||
// unicode data, built from the unicode standard on: 2019-08-02
|
||||
// unicode data, built from the unicode standard on: 2019-10-01
|
||||
// see gen-wcwidth.py
|
||||
#pragma once
|
||||
#include "data-types.h"
|
||||
@@ -187,5 +187,464 @@ is_emoji_modifier(char_type code) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static inline bool
|
||||
is_symbol(char_type code) {
|
||||
switch(code) {
|
||||
case 0x24:
|
||||
return true;
|
||||
case 0x2b:
|
||||
return true;
|
||||
case 0x3c ... 0x3e:
|
||||
return true;
|
||||
case 0x5e:
|
||||
return true;
|
||||
case 0x60:
|
||||
return true;
|
||||
case 0x7c:
|
||||
return true;
|
||||
case 0x7e:
|
||||
return true;
|
||||
case 0xa2 ... 0xa6:
|
||||
return true;
|
||||
case 0xa8 ... 0xa9:
|
||||
return true;
|
||||
case 0xac:
|
||||
return true;
|
||||
case 0xae ... 0xb1:
|
||||
return true;
|
||||
case 0xb4:
|
||||
return true;
|
||||
case 0xb8:
|
||||
return true;
|
||||
case 0xd7:
|
||||
return true;
|
||||
case 0xf7:
|
||||
return true;
|
||||
case 0x2c2 ... 0x2c5:
|
||||
return true;
|
||||
case 0x2d2 ... 0x2df:
|
||||
return true;
|
||||
case 0x2e5 ... 0x2eb:
|
||||
return true;
|
||||
case 0x2ed:
|
||||
return true;
|
||||
case 0x2ef ... 0x2ff:
|
||||
return true;
|
||||
case 0x375:
|
||||
return true;
|
||||
case 0x384 ... 0x385:
|
||||
return true;
|
||||
case 0x3f6:
|
||||
return true;
|
||||
case 0x482:
|
||||
return true;
|
||||
case 0x58d ... 0x58f:
|
||||
return true;
|
||||
case 0x606 ... 0x608:
|
||||
return true;
|
||||
case 0x60b:
|
||||
return true;
|
||||
case 0x60e ... 0x60f:
|
||||
return true;
|
||||
case 0x6de:
|
||||
return true;
|
||||
case 0x6e9:
|
||||
return true;
|
||||
case 0x6fd ... 0x6fe:
|
||||
return true;
|
||||
case 0x7f6:
|
||||
return true;
|
||||
case 0x7fe ... 0x7ff:
|
||||
return true;
|
||||
case 0x9f2 ... 0x9f3:
|
||||
return true;
|
||||
case 0x9fa ... 0x9fb:
|
||||
return true;
|
||||
case 0xaf1:
|
||||
return true;
|
||||
case 0xb70:
|
||||
return true;
|
||||
case 0xbf3 ... 0xbfa:
|
||||
return true;
|
||||
case 0xc7f:
|
||||
return true;
|
||||
case 0xd4f:
|
||||
return true;
|
||||
case 0xd79:
|
||||
return true;
|
||||
case 0xe3f:
|
||||
return true;
|
||||
case 0xf01 ... 0xf03:
|
||||
return true;
|
||||
case 0xf13:
|
||||
return true;
|
||||
case 0xf15 ... 0xf17:
|
||||
return true;
|
||||
case 0xf1a ... 0xf1f:
|
||||
return true;
|
||||
case 0xf34:
|
||||
return true;
|
||||
case 0xf36:
|
||||
return true;
|
||||
case 0xf38:
|
||||
return true;
|
||||
case 0xfbe ... 0xfc5:
|
||||
return true;
|
||||
case 0xfc7 ... 0xfcc:
|
||||
return true;
|
||||
case 0xfce ... 0xfcf:
|
||||
return true;
|
||||
case 0xfd5 ... 0xfd8:
|
||||
return true;
|
||||
case 0x109e ... 0x109f:
|
||||
return true;
|
||||
case 0x1390 ... 0x1399:
|
||||
return true;
|
||||
case 0x166d:
|
||||
return true;
|
||||
case 0x17db:
|
||||
return true;
|
||||
case 0x1940:
|
||||
return true;
|
||||
case 0x19de ... 0x19ff:
|
||||
return true;
|
||||
case 0x1b61 ... 0x1b6a:
|
||||
return true;
|
||||
case 0x1b74 ... 0x1b7c:
|
||||
return true;
|
||||
case 0x1fbd:
|
||||
return true;
|
||||
case 0x1fbf ... 0x1fc1:
|
||||
return true;
|
||||
case 0x1fcd ... 0x1fcf:
|
||||
return true;
|
||||
case 0x1fdd ... 0x1fdf:
|
||||
return true;
|
||||
case 0x1fed ... 0x1fef:
|
||||
return true;
|
||||
case 0x1ffd ... 0x1ffe:
|
||||
return true;
|
||||
case 0x2044:
|
||||
return true;
|
||||
case 0x2052:
|
||||
return true;
|
||||
case 0x207a ... 0x207c:
|
||||
return true;
|
||||
case 0x208a ... 0x208c:
|
||||
return true;
|
||||
case 0x20a0 ... 0x20bf:
|
||||
return true;
|
||||
case 0x2100 ... 0x2101:
|
||||
return true;
|
||||
case 0x2103 ... 0x2106:
|
||||
return true;
|
||||
case 0x2108 ... 0x2109:
|
||||
return true;
|
||||
case 0x2114:
|
||||
return true;
|
||||
case 0x2116 ... 0x2118:
|
||||
return true;
|
||||
case 0x211e ... 0x2123:
|
||||
return true;
|
||||
case 0x2125:
|
||||
return true;
|
||||
case 0x2127:
|
||||
return true;
|
||||
case 0x2129:
|
||||
return true;
|
||||
case 0x212e:
|
||||
return true;
|
||||
case 0x213a ... 0x213b:
|
||||
return true;
|
||||
case 0x2140 ... 0x2144:
|
||||
return true;
|
||||
case 0x214a ... 0x214d:
|
||||
return true;
|
||||
case 0x214f:
|
||||
return true;
|
||||
case 0x218a ... 0x218b:
|
||||
return true;
|
||||
case 0x2190 ... 0x2307:
|
||||
return true;
|
||||
case 0x230c ... 0x2328:
|
||||
return true;
|
||||
case 0x232b ... 0x2426:
|
||||
return true;
|
||||
case 0x2440 ... 0x244a:
|
||||
return true;
|
||||
case 0x249c ... 0x24e9:
|
||||
return true;
|
||||
case 0x2500 ... 0x2767:
|
||||
return true;
|
||||
case 0x2794 ... 0x27c4:
|
||||
return true;
|
||||
case 0x27c7 ... 0x27e5:
|
||||
return true;
|
||||
case 0x27f0 ... 0x2982:
|
||||
return true;
|
||||
case 0x2999 ... 0x29d7:
|
||||
return true;
|
||||
case 0x29dc ... 0x29fb:
|
||||
return true;
|
||||
case 0x29fe ... 0x2b73:
|
||||
return true;
|
||||
case 0x2b76 ... 0x2b95:
|
||||
return true;
|
||||
case 0x2b98 ... 0x2bff:
|
||||
return true;
|
||||
case 0x2ce5 ... 0x2cea:
|
||||
return true;
|
||||
case 0x2e80 ... 0x2e99:
|
||||
return true;
|
||||
case 0x2e9b ... 0x2ef3:
|
||||
return true;
|
||||
case 0x2f00 ... 0x2fd5:
|
||||
return true;
|
||||
case 0x2ff0 ... 0x2ffb:
|
||||
return true;
|
||||
case 0x3004:
|
||||
return true;
|
||||
case 0x3012 ... 0x3013:
|
||||
return true;
|
||||
case 0x3020:
|
||||
return true;
|
||||
case 0x3036 ... 0x3037:
|
||||
return true;
|
||||
case 0x303e ... 0x303f:
|
||||
return true;
|
||||
case 0x309b ... 0x309c:
|
||||
return true;
|
||||
case 0x3190 ... 0x3191:
|
||||
return true;
|
||||
case 0x3196 ... 0x319f:
|
||||
return true;
|
||||
case 0x31c0 ... 0x31e3:
|
||||
return true;
|
||||
case 0x3200 ... 0x321e:
|
||||
return true;
|
||||
case 0x322a ... 0x3247:
|
||||
return true;
|
||||
case 0x3250:
|
||||
return true;
|
||||
case 0x3260 ... 0x327f:
|
||||
return true;
|
||||
case 0x328a ... 0x32b0:
|
||||
return true;
|
||||
case 0x32c0 ... 0x33ff:
|
||||
return true;
|
||||
case 0x4dc0 ... 0x4dff:
|
||||
return true;
|
||||
case 0xa490 ... 0xa4c6:
|
||||
return true;
|
||||
case 0xa700 ... 0xa716:
|
||||
return true;
|
||||
case 0xa720 ... 0xa721:
|
||||
return true;
|
||||
case 0xa789 ... 0xa78a:
|
||||
return true;
|
||||
case 0xa828 ... 0xa82b:
|
||||
return true;
|
||||
case 0xa836 ... 0xa839:
|
||||
return true;
|
||||
case 0xaa77 ... 0xaa79:
|
||||
return true;
|
||||
case 0xab5b:
|
||||
return true;
|
||||
case 0xfb29:
|
||||
return true;
|
||||
case 0xfbb2 ... 0xfbc1:
|
||||
return true;
|
||||
case 0xfdfc ... 0xfdfd:
|
||||
return true;
|
||||
case 0xfe62:
|
||||
return true;
|
||||
case 0xfe64 ... 0xfe66:
|
||||
return true;
|
||||
case 0xfe69:
|
||||
return true;
|
||||
case 0xff04:
|
||||
return true;
|
||||
case 0xff0b:
|
||||
return true;
|
||||
case 0xff1c ... 0xff1e:
|
||||
return true;
|
||||
case 0xff3e:
|
||||
return true;
|
||||
case 0xff40:
|
||||
return true;
|
||||
case 0xff5c:
|
||||
return true;
|
||||
case 0xff5e:
|
||||
return true;
|
||||
case 0xffe0 ... 0xffe6:
|
||||
return true;
|
||||
case 0xffe8 ... 0xffee:
|
||||
return true;
|
||||
case 0xfffc ... 0xfffd:
|
||||
return true;
|
||||
case 0x10137 ... 0x1013f:
|
||||
return true;
|
||||
case 0x10179 ... 0x10189:
|
||||
return true;
|
||||
case 0x1018c ... 0x1018e:
|
||||
return true;
|
||||
case 0x10190 ... 0x1019b:
|
||||
return true;
|
||||
case 0x101a0:
|
||||
return true;
|
||||
case 0x101d0 ... 0x101fc:
|
||||
return true;
|
||||
case 0x10877 ... 0x10878:
|
||||
return true;
|
||||
case 0x10ac8:
|
||||
return true;
|
||||
case 0x1173f:
|
||||
return true;
|
||||
case 0x11fd5 ... 0x11ff1:
|
||||
return true;
|
||||
case 0x16b3c ... 0x16b3f:
|
||||
return true;
|
||||
case 0x16b45:
|
||||
return true;
|
||||
case 0x1bc9c:
|
||||
return true;
|
||||
case 0x1d000 ... 0x1d0f5:
|
||||
return true;
|
||||
case 0x1d100 ... 0x1d126:
|
||||
return true;
|
||||
case 0x1d129 ... 0x1d164:
|
||||
return true;
|
||||
case 0x1d16a ... 0x1d16c:
|
||||
return true;
|
||||
case 0x1d183 ... 0x1d184:
|
||||
return true;
|
||||
case 0x1d18c ... 0x1d1a9:
|
||||
return true;
|
||||
case 0x1d1ae ... 0x1d1e8:
|
||||
return true;
|
||||
case 0x1d200 ... 0x1d241:
|
||||
return true;
|
||||
case 0x1d245:
|
||||
return true;
|
||||
case 0x1d300 ... 0x1d356:
|
||||
return true;
|
||||
case 0x1d6c1:
|
||||
return true;
|
||||
case 0x1d6db:
|
||||
return true;
|
||||
case 0x1d6fb:
|
||||
return true;
|
||||
case 0x1d715:
|
||||
return true;
|
||||
case 0x1d735:
|
||||
return true;
|
||||
case 0x1d74f:
|
||||
return true;
|
||||
case 0x1d76f:
|
||||
return true;
|
||||
case 0x1d789:
|
||||
return true;
|
||||
case 0x1d7a9:
|
||||
return true;
|
||||
case 0x1d7c3:
|
||||
return true;
|
||||
case 0x1d800 ... 0x1d9ff:
|
||||
return true;
|
||||
case 0x1da37 ... 0x1da3a:
|
||||
return true;
|
||||
case 0x1da6d ... 0x1da74:
|
||||
return true;
|
||||
case 0x1da76 ... 0x1da83:
|
||||
return true;
|
||||
case 0x1da85 ... 0x1da86:
|
||||
return true;
|
||||
case 0x1e14f:
|
||||
return true;
|
||||
case 0x1e2ff:
|
||||
return true;
|
||||
case 0x1ecac:
|
||||
return true;
|
||||
case 0x1ecb0:
|
||||
return true;
|
||||
case 0x1ed2e:
|
||||
return true;
|
||||
case 0x1eef0 ... 0x1eef1:
|
||||
return true;
|
||||
case 0x1f000 ... 0x1f02b:
|
||||
return true;
|
||||
case 0x1f030 ... 0x1f093:
|
||||
return true;
|
||||
case 0x1f0a0 ... 0x1f0ae:
|
||||
return true;
|
||||
case 0x1f0b1 ... 0x1f0bf:
|
||||
return true;
|
||||
case 0x1f0c1 ... 0x1f0cf:
|
||||
return true;
|
||||
case 0x1f0d1 ... 0x1f0f5:
|
||||
return true;
|
||||
case 0x1f110 ... 0x1f16c:
|
||||
return true;
|
||||
case 0x1f170 ... 0x1f1ac:
|
||||
return true;
|
||||
case 0x1f1e6 ... 0x1f202:
|
||||
return true;
|
||||
case 0x1f210 ... 0x1f23b:
|
||||
return true;
|
||||
case 0x1f240 ... 0x1f248:
|
||||
return true;
|
||||
case 0x1f250 ... 0x1f251:
|
||||
return true;
|
||||
case 0x1f260 ... 0x1f265:
|
||||
return true;
|
||||
case 0x1f300 ... 0x1f6d5:
|
||||
return true;
|
||||
case 0x1f6e0 ... 0x1f6ec:
|
||||
return true;
|
||||
case 0x1f6f0 ... 0x1f6fa:
|
||||
return true;
|
||||
case 0x1f700 ... 0x1f773:
|
||||
return true;
|
||||
case 0x1f780 ... 0x1f7d8:
|
||||
return true;
|
||||
case 0x1f7e0 ... 0x1f7eb:
|
||||
return true;
|
||||
case 0x1f800 ... 0x1f80b:
|
||||
return true;
|
||||
case 0x1f810 ... 0x1f847:
|
||||
return true;
|
||||
case 0x1f850 ... 0x1f859:
|
||||
return true;
|
||||
case 0x1f860 ... 0x1f887:
|
||||
return true;
|
||||
case 0x1f890 ... 0x1f8ad:
|
||||
return true;
|
||||
case 0x1f900 ... 0x1f90b:
|
||||
return true;
|
||||
case 0x1f90d ... 0x1f971:
|
||||
return true;
|
||||
case 0x1f973 ... 0x1f976:
|
||||
return true;
|
||||
case 0x1f97a ... 0x1f9a2:
|
||||
return true;
|
||||
case 0x1f9a5 ... 0x1f9aa:
|
||||
return true;
|
||||
case 0x1f9ae ... 0x1f9ca:
|
||||
return true;
|
||||
case 0x1f9cd ... 0x1fa53:
|
||||
return true;
|
||||
case 0x1fa60 ... 0x1fa6d:
|
||||
return true;
|
||||
case 0x1fa70 ... 0x1fa73:
|
||||
return true;
|
||||
case 0x1fa78 ... 0x1fa7a:
|
||||
return true;
|
||||
case 0x1fa80 ... 0x1fa82:
|
||||
return true;
|
||||
case 0x1fa90 ... 0x1fa95:
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
END_ALLOW_CASE_RANGE
|
||||
|
||||
@@ -409,9 +409,9 @@ calc_cell_metrics(FontGroup *fg) {
|
||||
unsigned int before_cell_height = cell_height;
|
||||
int cw = cell_width, ch = cell_height;
|
||||
if (OPT(adjust_line_height_px) != 0) ch += OPT(adjust_line_height_px);
|
||||
if (OPT(adjust_line_height_frac) != 0.f) ch *= OPT(adjust_line_height_frac);
|
||||
if (OPT(adjust_line_height_frac) != 0.f) ch = (int)(ch * OPT(adjust_line_height_frac));
|
||||
if (OPT(adjust_column_width_px != 0)) cw += OPT(adjust_column_width_px);
|
||||
if (OPT(adjust_column_width_frac) != 0.f) cw *= OPT(adjust_column_width_frac);
|
||||
if (OPT(adjust_column_width_frac) != 0.f) cw = (int)(cw * OPT(adjust_column_width_frac));
|
||||
#define MAX_DIM 1000
|
||||
#define MIN_WIDTH 2
|
||||
#define MIN_HEIGHT 4
|
||||
@@ -543,8 +543,18 @@ in_symbol_maps(FontGroup *fg, char_type ch) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
font_for_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
// Decides which 'font' to use for a given cell.
|
||||
//
|
||||
// Possible results:
|
||||
// - NO_FONT
|
||||
// - MISSING_FONT
|
||||
// - BLANK_FONT
|
||||
// - BOX_FONT
|
||||
// - an index in the fonts list
|
||||
static inline ssize_t
|
||||
font_for_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell, bool *is_fallback_font, bool *is_emoji_presentation) {
|
||||
*is_fallback_font = false;
|
||||
*is_emoji_presentation = false;
|
||||
START_ALLOW_CASE_RANGE
|
||||
ssize_t ans;
|
||||
switch(cpu_cell->ch) {
|
||||
@@ -552,12 +562,14 @@ START_ALLOW_CASE_RANGE
|
||||
case ' ':
|
||||
case '\t':
|
||||
return BLANK_FONT;
|
||||
case 0x2500 ... 0x2570:
|
||||
case 0x2500 ... 0x2573:
|
||||
case 0x2574 ... 0x259f:
|
||||
case 0xe0b0:
|
||||
case 0xe0b2:
|
||||
case 0xe0b4:
|
||||
case 0xe0b0 ... 0xe0b4:
|
||||
case 0xe0b6:
|
||||
case 0xe0b8: //
|
||||
case 0xe0ba: //
|
||||
case 0xe0bc: //
|
||||
case 0xe0be: //
|
||||
return BOX_FONT;
|
||||
default:
|
||||
ans = in_symbol_maps(fg, cpu_cell->ch);
|
||||
@@ -573,7 +585,9 @@ START_ALLOW_CASE_RANGE
|
||||
ans = fg->bi_font_idx; break;
|
||||
}
|
||||
if (ans < 0) ans = fg->medium_font_idx;
|
||||
if (!has_emoji_presentation(cpu_cell, gpu_cell) && has_cell_text(fg->fonts + ans, cpu_cell)) return ans;
|
||||
*is_emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
|
||||
if (!*is_emoji_presentation && has_cell_text(fg->fonts + ans, cpu_cell)) return ans;
|
||||
*is_fallback_font = true;
|
||||
return fallback_font(fg, cpu_cell, gpu_cell);
|
||||
}
|
||||
END_ALLOW_CASE_RANGE
|
||||
@@ -584,20 +598,15 @@ set_sprite(GPUCell *cell, sprite_index x, sprite_index y, sprite_index z) {
|
||||
cell->sprite_x = x; cell->sprite_y = y; cell->sprite_z = z;
|
||||
}
|
||||
|
||||
// Gives a unique (arbitrary) id to a box glyph
|
||||
static inline glyph_index
|
||||
box_glyph_id(char_type ch) {
|
||||
START_ALLOW_CASE_RANGE
|
||||
switch(ch) {
|
||||
case 0x2500 ... 0x259f:
|
||||
return ch - 0x2500;
|
||||
case 0xe0b0:
|
||||
return 0xfa;
|
||||
case 0xe0b2:
|
||||
return 0xfb;
|
||||
case 0xe0b4:
|
||||
return 0xfc;
|
||||
case 0xe0b6:
|
||||
return 0xfd;
|
||||
return ch - 0x2500; // IDs from 0x00 to 0x9f
|
||||
case 0xe0b0 ... 0xe0d4:
|
||||
return 0xa0 + ch - 0xe0b0; // IDs from 0xa0 to 0xc4
|
||||
default:
|
||||
return 0xff;
|
||||
}
|
||||
@@ -682,7 +691,7 @@ static inline void
|
||||
render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, bool center_glyph) {
|
||||
static SpritePosition* sprite_position[16];
|
||||
int error = 0;
|
||||
num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells);
|
||||
num_cells = MIN(arraysz(sprite_position), num_cells);
|
||||
for (unsigned int i = 0; i < num_cells; i++) {
|
||||
sprite_position[i] = sprite_position_for(fg, font, glyph, extra_glyphs, (uint8_t)i, &error);
|
||||
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; }
|
||||
@@ -717,7 +726,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
unsigned int first_glyph_idx, first_cell_idx, num_glyphs, num_cells;
|
||||
bool has_special_glyph;
|
||||
bool has_special_glyph, is_space_ligature;
|
||||
} Group;
|
||||
|
||||
typedef struct {
|
||||
@@ -955,13 +964,11 @@ merge_groups_for_pua_space_ligature(void) {
|
||||
while (G(group_idx) > 0) {
|
||||
Group *g = G(groups), *g1 = G(groups) + 1;
|
||||
g->num_cells += g1->num_cells;
|
||||
// We dont want the space glyphs rendered because some stupid
|
||||
// fonts like PowerLine dont have a space glyph
|
||||
// https://github.com/kovidgoyal/kitty/issues/1225
|
||||
/* g->num_glyphs += g1->num_glyphs; */
|
||||
/* g->num_glyphs = MIN(g->num_glyphs, MAX_NUM_EXTRA_GLYPHS + 1); */
|
||||
g->num_glyphs += g1->num_glyphs;
|
||||
g->num_glyphs = MIN(g->num_glyphs, MAX_NUM_EXTRA_GLYPHS + 1);
|
||||
G(group_idx)--;
|
||||
}
|
||||
G(groups)->is_space_ligature = true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -995,7 +1002,11 @@ render_groups(FontGroup *fg, Font *font, bool center_glyph) {
|
||||
int last = -1;
|
||||
for (i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) { last = i - 1; ed.data[last] = G(info)[group->first_glyph_idx + i].codepoint; }
|
||||
if ((size_t)(last + 1) < arraysz(ed.data)) ed.data[last + 1] = 0;
|
||||
render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed, center_glyph);
|
||||
// We dont want to render the spaces in a space ligature because
|
||||
// there exist stupid fonts like Powerline that have no space glyph,
|
||||
// so special case it: https://github.com/kovidgoyal/kitty/issues/1225
|
||||
unsigned int num_glyphs = group->is_space_ligature ? 1 : group->num_glyphs;
|
||||
render_group(fg, group->num_cells, num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed, center_glyph);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
@@ -1096,18 +1107,20 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor,
|
||||
if (prev_width == 2) { prev_width = 0; continue; }
|
||||
CPUCell *cpu_cell = line->cpu_cells + i;
|
||||
GPUCell *gpu_cell = line->gpu_cells + i;
|
||||
ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell);
|
||||
bool is_fallback_font, is_emoji_presentation;
|
||||
ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell, &is_fallback_font, &is_emoji_presentation);
|
||||
|
||||
if (is_private_use(cpu_cell->ch)
|
||||
&& cell_font_idx != BOX_FONT
|
||||
&& cell_font_idx != MISSING_FONT) {
|
||||
if (
|
||||
cell_font_idx != MISSING_FONT &&
|
||||
((is_fallback_font && !is_emoji_presentation && is_symbol(cpu_cell->ch)) || (cell_font_idx != BOX_FONT && is_private_use(cpu_cell->ch)))
|
||||
) {
|
||||
unsigned int desired_cells = 1;
|
||||
if (cell_font_idx > 0) {
|
||||
Font *font = (fg->fonts + cell_font_idx);
|
||||
glyph_index glyph_id = glyph_id_for_codepoint(font->face, cpu_cell->ch);
|
||||
|
||||
int width = get_glyph_width(font->face, glyph_id);
|
||||
desired_cells = ceilf((float)width / fg->cell_width);
|
||||
desired_cells = (unsigned int)ceilf((float)width / fg->cell_width);
|
||||
}
|
||||
|
||||
unsigned int num_spaces = 0;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
#
|
||||
# NOTE: to add a new glyph, add an entry to the `box_chars` dict, then update
|
||||
# the functions `font_for_cell` and `box_glyph_id` in `kitty/fonts.c`.
|
||||
#
|
||||
|
||||
import math
|
||||
from functools import partial as p
|
||||
from itertools import repeat
|
||||
@@ -172,6 +177,117 @@ def triangle(buf, width, height, left=True):
|
||||
fill_region(buf, width, height, xlimits)
|
||||
|
||||
|
||||
def corner_triangle(buf, width, height, corner):
|
||||
if corner == 'top-right' or corner == 'bottom-left':
|
||||
diagonal_y = line_equation(0, 0, width - 1, height - 1)
|
||||
if corner == 'top-right':
|
||||
xlimits = [(0, diagonal_y(x)) for x in range(width)]
|
||||
elif corner == 'bottom-left':
|
||||
xlimits = [(diagonal_y(x), height - 1) for x in range(width)]
|
||||
else:
|
||||
diagonal_y = line_equation(width - 1, 0, 0, height - 1)
|
||||
if corner == 'top-left':
|
||||
xlimits = [(0, diagonal_y(x)) for x in range(width)]
|
||||
elif corner == 'bottom-right':
|
||||
xlimits = [(diagonal_y(x), height - 1) for x in range(width)]
|
||||
fill_region(buf, width, height, xlimits)
|
||||
|
||||
|
||||
def antialiased_1px_line(buf, width, height, p1, p2):
|
||||
# Draw an antialiased line using the Wu algorithm
|
||||
x1, y1 = p1
|
||||
x2, y2 = p2
|
||||
dx, dy = x2 - x1, y2 - y1
|
||||
off_limit = height * width
|
||||
steep = abs(dx) < abs(dy)
|
||||
|
||||
if steep:
|
||||
x1, y1, x2, y2, dx, dy = y1, x1, y2, x2, dy, dx
|
||||
|
||||
def p(x, y):
|
||||
return y, x
|
||||
else:
|
||||
def p(x, y):
|
||||
return x, y
|
||||
|
||||
if x2 < x1:
|
||||
x1, x2, y1, y2 = x2, x1, y2, y1
|
||||
|
||||
def fpart(x):
|
||||
return x - int(x)
|
||||
|
||||
def rfpart(x):
|
||||
return 1 - fpart(x)
|
||||
|
||||
def putpixel(p, alpha):
|
||||
x, y = p
|
||||
off = int(x + y * width)
|
||||
if 0 <= off < off_limit:
|
||||
buf[off] = int(min(buf[off] + (alpha * 255), 255))
|
||||
|
||||
def draw_endpoint(pt):
|
||||
x, y = pt
|
||||
xend = round(x)
|
||||
yend = y + grad * (xend - x)
|
||||
xgap = rfpart(x + 0.5)
|
||||
px, py = int(xend), int(yend)
|
||||
putpixel(p(px, py), rfpart(yend) * xgap)
|
||||
putpixel(p(px, py+1), fpart(yend) * xgap)
|
||||
return px
|
||||
|
||||
grad = dy/dx
|
||||
intery = y1 + rfpart(x1) * grad
|
||||
|
||||
xstart = draw_endpoint(p(*p1))
|
||||
xend = draw_endpoint(p(*p2))
|
||||
|
||||
if xstart > xend:
|
||||
xstart, xend = xend, xstart
|
||||
xstart += 1
|
||||
|
||||
for x in range(xstart, xend):
|
||||
y = int(intery)
|
||||
putpixel(p(x, y), rfpart(intery))
|
||||
putpixel(p(x, y+1), fpart(intery))
|
||||
intery += grad
|
||||
|
||||
|
||||
def antialiased_line(buf, width, height, p1, p2, level=1):
|
||||
th = thickness(level)
|
||||
if th < 2:
|
||||
return antialiased_1px_line(buf, width, height, p1, p2)
|
||||
(x1, y1), (x2, y2) = p1, p2
|
||||
dh = th // 2
|
||||
items = range(-dh, dh + (th % 2))
|
||||
for delta in items:
|
||||
antialiased_1px_line(buf, width, height, (x1, y1 + delta), (x2, y2 + delta))
|
||||
|
||||
|
||||
def cross_line(buf, width, height, left=True, level=1):
|
||||
if left:
|
||||
p1, p2 = (0, 0), (width - 1, height - 1)
|
||||
else:
|
||||
p1, p2 = (width - 1, 0), (0, height - 1)
|
||||
antialiased_line(buf, width, height, p1, p2, level=level)
|
||||
|
||||
|
||||
def half_cross_line(buf, width, height, which='tl', level=1):
|
||||
my = (height - 1) // 2
|
||||
if which == 'tl':
|
||||
p1 = 0, 0
|
||||
p2 = width - 1, my
|
||||
elif which == 'bl':
|
||||
p2 = 0, height - 1
|
||||
p1 = width - 1, my
|
||||
elif which == 'tr':
|
||||
p1 = width - 1, 0
|
||||
p2 = 0, my
|
||||
else:
|
||||
p2 = width - 1, height - 1
|
||||
p1 = 0, my
|
||||
antialiased_line(buf, width, height, p1, p2, level=level)
|
||||
|
||||
|
||||
def cubic_bezier(start, end, c1, c2):
|
||||
|
||||
def bezier_eq(p0, p1, p2, p3):
|
||||
@@ -459,6 +575,12 @@ box_chars = {
|
||||
'': [p(triangle, left=False)],
|
||||
'': [D],
|
||||
'': [p(D, left=False)],
|
||||
'': [p(half_cross_line, which='tl'), p(half_cross_line, which='bl')],
|
||||
'': [p(half_cross_line, which='tr'), p(half_cross_line, which='br')],
|
||||
'': [p(corner_triangle, corner='bottom-left')],
|
||||
'': [p(corner_triangle, corner='bottom-right')],
|
||||
'': [p(corner_triangle, corner='top-left')],
|
||||
'': [p(corner_triangle, corner='top-right')],
|
||||
'═': [dhline],
|
||||
'║': [dvline],
|
||||
'╞': [vline, p(half_dhline, which='right')],
|
||||
@@ -472,6 +594,9 @@ box_chars = {
|
||||
'╣': [p(inner_corner, which='tl'), p(inner_corner, which='bl'), p(dvline, only='right')],
|
||||
'╦': [p(inner_corner, which='bl'), p(inner_corner, which='br'), p(dhline, only='top')],
|
||||
'╩': [p(inner_corner, which='tl'), p(inner_corner, which='tr'), p(dhline, only='bottom')],
|
||||
'╱': [p(cross_line, left=False)],
|
||||
'╲': [cross_line],
|
||||
'╳': [cross_line, p(cross_line, left=False)],
|
||||
'▀': [p(vblock, frac=1/2)],
|
||||
'▁': [p(vblock, frac=1/8, gravity='bottom')],
|
||||
'▂': [p(vblock, frac=1/4, gravity='bottom')],
|
||||
@@ -551,60 +676,60 @@ def render_missing_glyph(buf, width, height):
|
||||
|
||||
|
||||
def test_char(ch, sz=48):
|
||||
# kitty +runpy "from kitty.fonts.box_drawing import test_char; import sys; test_char('XXX')"
|
||||
# kitty +runpy "from kitty.fonts.box_drawing import test_char; test_char('XXX')"
|
||||
from .render import display_bitmap, setup_for_testing
|
||||
from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
|
||||
width, height = setup_for_testing('monospace', sz)[1:]
|
||||
buf = bytearray(width * height)
|
||||
try:
|
||||
render_box_char(ch, buf, width, height)
|
||||
with setup_for_testing('monospace', sz) as (_, width, height):
|
||||
buf = bytearray(width * height)
|
||||
try:
|
||||
render_box_char(ch, buf, width, height)
|
||||
|
||||
def join_cells(*cells):
|
||||
cells = tuple(bytes(x) for x in cells)
|
||||
return concat_cells(width, height, False, cells)
|
||||
def join_cells(*cells):
|
||||
cells = tuple(bytes(x) for x in cells)
|
||||
return concat_cells(width, height, False, cells)
|
||||
|
||||
rgb_data = join_cells(buf)
|
||||
display_bitmap(rgb_data, width, height)
|
||||
print()
|
||||
finally:
|
||||
set_send_sprite_to_gpu(None)
|
||||
rgb_data = join_cells(buf)
|
||||
display_bitmap(rgb_data, width, height)
|
||||
print()
|
||||
finally:
|
||||
set_send_sprite_to_gpu(None)
|
||||
|
||||
|
||||
def test_drawing(sz=48, family='monospace'):
|
||||
from .render import display_bitmap, setup_for_testing
|
||||
from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
|
||||
|
||||
width, height = setup_for_testing(family, sz)[1:]
|
||||
space = bytearray(width * height)
|
||||
with setup_for_testing(family, sz) as (_, width, height):
|
||||
space = bytearray(width * height)
|
||||
|
||||
def join_cells(cells):
|
||||
cells = tuple(bytes(x) for x in cells)
|
||||
return concat_cells(width, height, False, cells)
|
||||
def join_cells(cells):
|
||||
cells = tuple(bytes(x) for x in cells)
|
||||
return concat_cells(width, height, False, cells)
|
||||
|
||||
def render_chr(ch):
|
||||
if ch in box_chars:
|
||||
cell = bytearray(len(space))
|
||||
render_box_char(ch, cell, width, height)
|
||||
return cell
|
||||
return space
|
||||
def render_chr(ch):
|
||||
if ch in box_chars:
|
||||
cell = bytearray(len(space))
|
||||
render_box_char(ch, cell, width, height)
|
||||
return cell
|
||||
return space
|
||||
|
||||
pos = 0x2500
|
||||
rows = []
|
||||
space_row = join_cells(repeat(space, 32))
|
||||
pos = 0x2500
|
||||
rows = []
|
||||
space_row = join_cells(repeat(space, 32))
|
||||
|
||||
try:
|
||||
for r in range(10):
|
||||
row = []
|
||||
for i in range(16):
|
||||
row.append(render_chr(chr(pos)))
|
||||
row.append(space)
|
||||
pos += 1
|
||||
rows.append(join_cells(row))
|
||||
rows.append(space_row)
|
||||
rgb_data = b''.join(rows)
|
||||
width *= 32
|
||||
height *= len(rows)
|
||||
assert len(rgb_data) == width * height * 4, '{} != {}'.format(len(rgb_data), width * height * 4)
|
||||
display_bitmap(rgb_data, width, height)
|
||||
finally:
|
||||
set_send_sprite_to_gpu(None)
|
||||
try:
|
||||
for r in range(10):
|
||||
row = []
|
||||
for i in range(16):
|
||||
row.append(render_chr(chr(pos)))
|
||||
row.append(space)
|
||||
pos += 1
|
||||
rows.append(join_cells(row))
|
||||
rows.append(space_row)
|
||||
rgb_data = b''.join(rows)
|
||||
width *= 32
|
||||
height *= len(rows)
|
||||
assert len(rgb_data) == width * height * 4, '{} != {}'.format(len(rgb_data), width * height * 4)
|
||||
display_bitmap(rgb_data, width, height)
|
||||
finally:
|
||||
set_send_sprite_to_gpu(None)
|
||||
|
||||
@@ -25,8 +25,29 @@ else:
|
||||
current_faces = None
|
||||
|
||||
|
||||
def coalesce_symbol_maps(maps):
|
||||
if not maps:
|
||||
return maps
|
||||
items = tuple((k, maps[k]) for k in sorted(maps))
|
||||
ans = [items[0]]
|
||||
|
||||
def merge(prev_item, item):
|
||||
s, e = item[0]
|
||||
pe = prev_item[0][1]
|
||||
ans[-1] = ((prev_item[0][0], max(pe, e)), prev_item[1])
|
||||
|
||||
for item in items[1:]:
|
||||
current_item = ans[-1]
|
||||
if current_item[1] != item[1] or item[0][0] > current_item[0][1] + 1:
|
||||
ans.append(item)
|
||||
else:
|
||||
merge(current_item, item)
|
||||
|
||||
return dict(ans)
|
||||
|
||||
|
||||
def create_symbol_map(opts):
|
||||
val = opts.symbol_map
|
||||
val = coalesce_symbol_maps(opts.symbol_map)
|
||||
family_map = {}
|
||||
count = 0
|
||||
for family in val.values():
|
||||
|
||||
@@ -77,12 +77,12 @@ static FT_Library library;
|
||||
|
||||
static inline int
|
||||
font_units_to_pixels_y(Face *self, int x) {
|
||||
return ceil((double)FT_MulFix(x, self->face->size->metrics.y_scale) / 64.0);
|
||||
return (int)ceil((double)FT_MulFix(x, self->face->size->metrics.y_scale) / 64.0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
font_units_to_pixels_x(Face *self, int x) {
|
||||
return ceil((double)FT_MulFix(x, self->face->size->metrics.x_scale) / 64.0);
|
||||
return (int)ceil((double)FT_MulFix(x, self->face->size->metrics.x_scale) / 64.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
|
||||
if (!error) {
|
||||
unsigned int ch = calc_cell_height(self, false);
|
||||
if (desired_height && ch != desired_height) {
|
||||
FT_F26Dot6 h = floor((double)char_height * (double)desired_height / (double) ch);
|
||||
FT_F26Dot6 h = (FT_F26Dot6)floor((double)char_height * (double)desired_height / (double) ch);
|
||||
return set_font_size(self, 0, h, xdpi, ydpi, 0, cell_height);
|
||||
}
|
||||
self->char_width = char_width; self->char_height = char_height; self->xdpi = xdpi; self->ydpi = ydpi;
|
||||
@@ -160,8 +160,8 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
|
||||
int32_t min_diff = INT32_MAX;
|
||||
if (desired_height == 0) desired_height = cell_height;
|
||||
if (desired_height == 0) {
|
||||
desired_height = ceil(((double)char_height / 64.) * (double)ydpi / 72.);
|
||||
desired_height += ceil(0.2 * desired_height);
|
||||
desired_height = (unsigned int)ceil(((double)char_height / 64.) * (double)ydpi / 72.);
|
||||
desired_height += (unsigned int)ceil(0.2 * desired_height);
|
||||
}
|
||||
FT_Int strike_index = -1;
|
||||
for (FT_Int i = 0; i < self->face->num_fixed_sizes; i++) {
|
||||
@@ -190,7 +190,7 @@ set_size_for_face(PyObject *s, unsigned int desired_height, bool force, FONTS_DA
|
||||
FT_F26Dot6 w = (FT_F26Dot6)(ceil(fg->font_sz_in_pts * 64.0));
|
||||
FT_UInt xdpi = (FT_UInt)fg->logical_dpi_x, ydpi = (FT_UInt)fg->logical_dpi_y;
|
||||
if (!force && (self->char_width == w && self->char_height == w && self->xdpi == xdpi && self->ydpi == ydpi)) return true;
|
||||
((Face*)self)->size_in_pts = fg->font_sz_in_pts;
|
||||
((Face*)self)->size_in_pts = (float)fg->font_sz_in_pts;
|
||||
return set_font_size(self, w, w, xdpi, ydpi, desired_height, fg->cell_height);
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ downsample_bitmap(ProcessedBitmap *bm, unsigned int width, unsigned int cell_hei
|
||||
// better with bi-cubic or lanczos, but at these small sizes I don't think
|
||||
// it matters
|
||||
float ratio = MAX((float)bm->width / width, (float)bm->rows / cell_height);
|
||||
int factor = ceilf(ratio);
|
||||
int factor = (int)ceilf(ratio);
|
||||
uint8_t *dest = calloc(4, width * cell_height);
|
||||
if (dest == NULL) fatal("Out of memory");
|
||||
uint8_t *d = dest;
|
||||
@@ -499,8 +499,8 @@ render_color_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int
|
||||
ans->rows = bitmap->rows;
|
||||
ans->pixel_mode = bitmap->pixel_mode;
|
||||
if (ans->width > num_cells * cell_width + 2) downsample_bitmap(ans, num_cells * cell_width, cell_height);
|
||||
ans->bitmap_top = (float)self->face->glyph->bitmap_top / ans->factor;
|
||||
ans->bitmap_left = (float)self->face->glyph->bitmap_left / ans->factor;
|
||||
ans->bitmap_top = (int)((float)self->face->glyph->bitmap_top / ans->factor);
|
||||
ans->bitmap_left = (int)((float)self->face->glyph->bitmap_left / ans->factor);
|
||||
detect_right_edge(ans);
|
||||
return true;
|
||||
}
|
||||
|
||||
38
kitty/gl-wrapper.h
generated
38
kitty/gl-wrapper.h
generated
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Loader generated by glad 2.0.0-beta on Sun Jul 7 20:35:06 2019
|
||||
* Loader generated by glad 2.0.0-beta on Fri Dec 20 14:59:05 2019
|
||||
*
|
||||
* Generator: C/C++
|
||||
* Specification: gl
|
||||
@@ -9,19 +9,19 @@
|
||||
* - gl:core=3.3
|
||||
*
|
||||
* Options:
|
||||
* - ALIAS = False
|
||||
* - DEBUG = True
|
||||
* - HEADER_ONLY = True
|
||||
* - LOADER = False
|
||||
* - MX = False
|
||||
* - MX_GLOBAL = False
|
||||
* - ON_DEMAND = False
|
||||
* - LOADER = False
|
||||
* - ALIAS = False
|
||||
* - HEADER_ONLY = True
|
||||
* - DEBUG = True
|
||||
* - MX = False
|
||||
*
|
||||
* Commandline:
|
||||
* --api='gl:core=3.3' --extensions='GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,GL_ARB_texture_storage,GL_KHR_debug' c --header-only --debug
|
||||
* --api='gl:core=3.3' --extensions='GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,GL_ARB_texture_storage,GL_KHR_debug' c --debug --header-only
|
||||
*
|
||||
* Online:
|
||||
* http://glad.sh/#api=gl%3Acore%3D3.3&extensions=GL_ARB_copy_image%2CGL_ARB_multisample%2CGL_ARB_robustness%2CGL_ARB_texture_storage%2CGL_KHR_debug&generator=c&options=HEADER_ONLY%2CDEBUG
|
||||
* http://glad.sh/#api=gl%3Acore%3D3.3&extensions=GL_ARB_copy_image%2CGL_ARB_multisample%2CGL_ARB_robustness%2CGL_ARB_texture_storage%2CGL_KHR_debug&generator=c&options=DEBUG%2CHEADER_ONLY
|
||||
*
|
||||
*/
|
||||
#ifndef GLAD_GL_H_
|
||||
@@ -31,8 +31,8 @@
|
||||
#endif
|
||||
#define __gl_h_ 1
|
||||
#define GLAD_GL
|
||||
#define GLAD_OPTION_GL_HEADER_ONLY
|
||||
#define GLAD_OPTION_GL_DEBUG
|
||||
#define GLAD_OPTION_GL_HEADER_ONLY
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -1324,13 +1324,13 @@ typedef khronos_uint64_t GLuint64EXT;
|
||||
typedef struct __GLsync *GLsync;
|
||||
struct _cl_context;
|
||||
struct _cl_event;
|
||||
typedef void ( *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
typedef void ( *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
typedef void ( *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
typedef void ( *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);
|
||||
typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);
|
||||
typedef unsigned short GLhalfNV;
|
||||
typedef GLintptr GLvdpauSurfaceNV;
|
||||
typedef void ( *GLVULKANPROCNV)(void);
|
||||
typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void);
|
||||
#define GL_VERSION_1_0 1
|
||||
GLAD_API_CALL int GLAD_GL_VERSION_1_0;
|
||||
#define GL_VERSION_1_1 1
|
||||
@@ -1522,7 +1522,7 @@ typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei buf
|
||||
typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params);
|
||||
typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name);
|
||||
typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index);
|
||||
typedef void (GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei * length, GLint * values);
|
||||
typedef void (GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei * length, GLint * values);
|
||||
typedef void (GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels);
|
||||
typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params);
|
||||
typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params);
|
||||
@@ -4037,10 +4037,10 @@ static const GLubyte * GLAD_API_PTR glad_debug_impl_glGetStringi(GLenum name, GL
|
||||
}
|
||||
PFNGLGETSTRINGIPROC glad_debug_glGetStringi = glad_debug_impl_glGetStringi;
|
||||
PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL;
|
||||
static void GLAD_API_PTR glad_debug_impl_glGetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei * length, GLint * values) {
|
||||
_pre_call_gl_callback("glGetSynciv", (GLADapiproc) glad_glGetSynciv, 5, sync, pname, bufSize, length, values);
|
||||
glad_glGetSynciv(sync, pname, bufSize, length, values);
|
||||
_post_call_gl_callback(NULL, "glGetSynciv", (GLADapiproc) glad_glGetSynciv, 5, sync, pname, bufSize, length, values);
|
||||
static void GLAD_API_PTR glad_debug_impl_glGetSynciv(GLsync sync, GLenum pname, GLsizei count, GLsizei * length, GLint * values) {
|
||||
_pre_call_gl_callback("glGetSynciv", (GLADapiproc) glad_glGetSynciv, 5, sync, pname, count, length, values);
|
||||
glad_glGetSynciv(sync, pname, count, length, values);
|
||||
_post_call_gl_callback(NULL, "glGetSynciv", (GLADapiproc) glad_glGetSynciv, 5, sync, pname, count, length, values);
|
||||
}
|
||||
PFNGLGETSYNCIVPROC glad_debug_glGetSynciv = glad_debug_impl_glGetSynciv;
|
||||
PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL;
|
||||
|
||||
17
kitty/glfw-wrapper.c
generated
17
kitty/glfw-wrapper.c
generated
@@ -245,8 +245,8 @@ load_glfw(const char* path) {
|
||||
*(void **) (&glfwGetKeyName_impl) = dlsym(handle, "glfwGetKeyName");
|
||||
if (glfwGetKeyName_impl == NULL) fail("Failed to load glfw function glfwGetKeyName with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwGetKeyScancode_impl) = dlsym(handle, "glfwGetKeyScancode");
|
||||
if (glfwGetKeyScancode_impl == NULL) fail("Failed to load glfw function glfwGetKeyScancode with error: %s", dlerror());
|
||||
*(void **) (&glfwGetNativeKeyForKey_impl) = dlsym(handle, "glfwGetNativeKeyForKey");
|
||||
if (glfwGetNativeKeyForKey_impl == NULL) fail("Failed to load glfw function glfwGetNativeKeyForKey with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwGetKey_impl) = dlsym(handle, "glfwGetKey");
|
||||
if (glfwGetKey_impl == NULL) fail("Failed to load glfw function glfwGetKey with error: %s", dlerror());
|
||||
@@ -344,15 +344,6 @@ load_glfw(const char* path) {
|
||||
*(void **) (&glfwGetTime_impl) = dlsym(handle, "glfwGetTime");
|
||||
if (glfwGetTime_impl == NULL) fail("Failed to load glfw function glfwGetTime with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetTime_impl) = dlsym(handle, "glfwSetTime");
|
||||
if (glfwSetTime_impl == NULL) fail("Failed to load glfw function glfwSetTime with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwGetTimerValue_impl) = dlsym(handle, "glfwGetTimerValue");
|
||||
if (glfwGetTimerValue_impl == NULL) fail("Failed to load glfw function glfwGetTimerValue with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwGetTimerFrequency_impl) = dlsym(handle, "glfwGetTimerFrequency");
|
||||
if (glfwGetTimerFrequency_impl == NULL) fail("Failed to load glfw function glfwGetTimerFrequency with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwMakeContextCurrent_impl) = dlsym(handle, "glfwMakeContextCurrent");
|
||||
if (glfwMakeContextCurrent_impl == NULL) fail("Failed to load glfw function glfwMakeContextCurrent with error: %s", dlerror());
|
||||
|
||||
@@ -389,6 +380,8 @@ load_glfw(const char* path) {
|
||||
|
||||
*(void **) (&glfwSetApplicationShouldHandleReopen_impl) = dlsym(handle, "glfwSetApplicationShouldHandleReopen");
|
||||
|
||||
*(void **) (&glfwSetApplicationWillFinishLaunching_impl) = dlsym(handle, "glfwSetApplicationWillFinishLaunching");
|
||||
|
||||
*(void **) (&glfwGetCocoaKeyEquivalent_impl) = dlsym(handle, "glfwGetCocoaKeyEquivalent");
|
||||
|
||||
*(void **) (&glfwCocoaRequestRenderFrame_impl) = dlsym(handle, "glfwCocoaRequestRenderFrame");
|
||||
@@ -401,7 +394,7 @@ load_glfw(const char* path) {
|
||||
|
||||
*(void **) (&glfwGetPrimarySelectionString_impl) = dlsym(handle, "glfwGetPrimarySelectionString");
|
||||
|
||||
*(void **) (&glfwGetXKBScancode_impl) = dlsym(handle, "glfwGetXKBScancode");
|
||||
*(void **) (&glfwGetNativeKeyForName_impl) = dlsym(handle, "glfwGetNativeKeyForName");
|
||||
|
||||
*(void **) (&glfwRequestWaylandFrameEvent_impl) = dlsym(handle, "glfwRequestWaylandFrameEvent");
|
||||
|
||||
|
||||
344
kitty/glfw-wrapper.h
generated
344
kitty/glfw-wrapper.h
generated
@@ -1,6 +1,13 @@
|
||||
//
|
||||
// THIS FILE IS GENERATED BY glfw.py
|
||||
//
|
||||
// SAVE YOURSELF SOME TIME, DO NOT MANUALLY EDIT
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "monotonic.h"
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +25,7 @@
|
||||
* backward-compatible.
|
||||
* @ingroup init
|
||||
*/
|
||||
#define GLFW_VERSION_MINOR 3
|
||||
#define GLFW_VERSION_MINOR 4
|
||||
/*! @brief The revision number of the GLFW library.
|
||||
*
|
||||
* This is incremented when a bug fix release is made that does not contain any
|
||||
@@ -54,6 +61,7 @@
|
||||
/*! @} */
|
||||
|
||||
/*! @defgroup hat_state Joystick hat states
|
||||
* @brief Joystick hat states.
|
||||
*
|
||||
* See [joystick hat input](@ref joystick_hat) for how these are used.
|
||||
*
|
||||
@@ -725,74 +733,87 @@
|
||||
#define GLFW_CLIENT_API 0x00022001
|
||||
/*! @brief Context client API major version hint and attribute.
|
||||
*
|
||||
* Context client API major version [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint)
|
||||
* and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib).
|
||||
*/
|
||||
#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002
|
||||
/*! @brief Context client API minor version hint and attribute.
|
||||
*
|
||||
* Context client API minor version [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint)
|
||||
* and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib).
|
||||
*/
|
||||
#define GLFW_CONTEXT_VERSION_MINOR 0x00022003
|
||||
/*! @brief Context client API revision number hint and attribute.
|
||||
*
|
||||
* Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* Context client API revision number
|
||||
* [attribute](@ref GLFW_CONTEXT_REVISION_attrib).
|
||||
*/
|
||||
#define GLFW_CONTEXT_REVISION 0x00022004
|
||||
/*! @brief Context robustness hint and attribute.
|
||||
*
|
||||
* Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint)
|
||||
* and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib).
|
||||
*/
|
||||
#define GLFW_CONTEXT_ROBUSTNESS 0x00022005
|
||||
/*! @brief OpenGL forward-compatibility hint and attribute.
|
||||
*
|
||||
* OpenGL forward-compatibility [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint)
|
||||
* and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib).
|
||||
*/
|
||||
#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006
|
||||
/*! @brief OpenGL debug context hint and attribute.
|
||||
*
|
||||
* OpenGL debug context [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* OpenGL debug context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and
|
||||
* [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib).
|
||||
*/
|
||||
#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007
|
||||
/*! @brief OpenGL profile hint and attribute.
|
||||
*
|
||||
* OpenGL profile [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and
|
||||
* [attribute](@ref GLFW_OPENGL_PROFILE_attrib).
|
||||
*/
|
||||
#define GLFW_OPENGL_PROFILE 0x00022008
|
||||
/*! @brief Context flush-on-release hint and attribute.
|
||||
*
|
||||
* Context flush-on-release [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and
|
||||
* [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib).
|
||||
*/
|
||||
#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009
|
||||
/*! @brief Context error suppression hint and attribute.
|
||||
*
|
||||
* Context error suppression [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and
|
||||
* [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib).
|
||||
*/
|
||||
#define GLFW_CONTEXT_NO_ERROR 0x0002200A
|
||||
/*! @brief Context creation API hint and attribute.
|
||||
*
|
||||
* Context creation API [hint](@ref GLFW_CLIENT_API_hint) and
|
||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||
* Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and
|
||||
* [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib).
|
||||
*/
|
||||
#define GLFW_CONTEXT_CREATION_API 0x0002200B
|
||||
/*! @brief Window content area scaling window
|
||||
* [window hint](@ref GLFW_SCALE_TO_MONITOR).
|
||||
*/
|
||||
#define GLFW_SCALE_TO_MONITOR 0x0002200C
|
||||
|
||||
/*! @brief macOS specific
|
||||
* [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint).
|
||||
*/
|
||||
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
|
||||
/*! @brief macOS specific
|
||||
* [window hint](@ref GLFW_COCOA_FRAME_NAME_hint).
|
||||
*/
|
||||
#define GLFW_COCOA_FRAME_NAME 0x00023002
|
||||
/*! @brief macOS specific
|
||||
* [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint).
|
||||
*/
|
||||
#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003
|
||||
|
||||
/*! @brief X11 specific
|
||||
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
|
||||
*/
|
||||
#define GLFW_X11_CLASS_NAME 0x00024001
|
||||
/*! @brief X11 specific
|
||||
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
|
||||
*/
|
||||
#define GLFW_X11_INSTANCE_NAME 0x00024002
|
||||
|
||||
#define GLFW_WAYLAND_APP_ID 0x00025001
|
||||
@@ -855,11 +876,22 @@ typedef enum {
|
||||
|
||||
/*! @addtogroup init
|
||||
* @{ */
|
||||
/*! @brief Joystick hat buttons init hint.
|
||||
*
|
||||
* Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS).
|
||||
*/
|
||||
#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001
|
||||
#define GLFW_DEBUG_KEYBOARD 0x00050002
|
||||
#define GLFW_ENABLE_JOYSTICKS 0x00050003
|
||||
|
||||
/*! @brief macOS specific init hint.
|
||||
*
|
||||
* macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).
|
||||
*/
|
||||
#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001
|
||||
/*! @brief macOS specific init hint.
|
||||
*
|
||||
* macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint).
|
||||
*/
|
||||
#define GLFW_COCOA_MENUBAR 0x00051002
|
||||
/*! @} */
|
||||
|
||||
@@ -930,17 +962,48 @@ typedef struct GLFWwindow GLFWwindow;
|
||||
*
|
||||
* @since Added in version 3.1.
|
||||
*
|
||||
* @ingroup cursor
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef struct GLFWcursor GLFWcursor;
|
||||
|
||||
/*! @brief The function signature for error callbacks.
|
||||
typedef struct GLFWkeyevent
|
||||
{
|
||||
// The [keyboard key](@ref keys) that was pressed or released.
|
||||
int key;
|
||||
|
||||
// The platform-specific identifier of the key.
|
||||
int native_key;
|
||||
|
||||
// The event action. Either `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
||||
int action;
|
||||
|
||||
// Bit field describing which [modifier keys](@ref mods) were held down.
|
||||
int mods;
|
||||
|
||||
// UTF-8 encoded text generated by this key event or empty string or NULL
|
||||
const char *text;
|
||||
|
||||
// Used for Input Method events. Zero for normal key events.
|
||||
// A value of 1 means the pre-edit text for the input event has been changed.
|
||||
// A value of 2 means the text should be committed.
|
||||
int ime_state;
|
||||
} GLFWkeyevent;
|
||||
|
||||
/*! @brief The function pointer type for error callbacks.
|
||||
*
|
||||
* This is the function signature for error callback functions.
|
||||
* This is the function pointer type for error callbacks. An error callback
|
||||
* function has the following signature:
|
||||
* @code
|
||||
* void callback_name(int error_code, const char* description)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] error An [error code](@ref errors).
|
||||
* @param[in] error_code An [error code](@ref errors). Future releases may add
|
||||
* more error codes.
|
||||
* @param[in] description A UTF-8 encoded string describing the error.
|
||||
*
|
||||
* @pointer_lifetime The error description string is valid until the callback
|
||||
* function returns.
|
||||
*
|
||||
* @sa @ref error_handling
|
||||
* @sa @ref glfwSetErrorCallback
|
||||
*
|
||||
@@ -950,9 +1013,13 @@ typedef struct GLFWcursor GLFWcursor;
|
||||
*/
|
||||
typedef void (* GLFWerrorfun)(int,const char*);
|
||||
|
||||
/*! @brief The function signature for window position callbacks.
|
||||
/*! @brief The function pointer type for window position callbacks.
|
||||
*
|
||||
* This is the function signature for window position callback functions.
|
||||
* This is the function pointer type for window position callbacks. A window
|
||||
* position callback function has the following signature:
|
||||
* @code
|
||||
* void callback_name(GLFWwindow* window, int xpos, int ypos)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that was moved.
|
||||
* @param[in] xpos The new x-coordinate, in screen coordinates, of the
|
||||
@@ -969,9 +1036,13 @@ typedef void (* GLFWerrorfun)(int,const char*);
|
||||
*/
|
||||
typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int);
|
||||
|
||||
/*! @brief The function signature for window resize callbacks.
|
||||
/*! @brief The function pointer type for window size callbacks.
|
||||
*
|
||||
* This is the function signature for window size callback functions.
|
||||
* This is the function pointer type for window size callbacks. A window size
|
||||
* callback function has the following signature:
|
||||
* @code
|
||||
* void callback_name(GLFWwindow* window, int width, int height)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that was resized.
|
||||
* @param[in] width The new width, in screen coordinates, of the window.
|
||||
@@ -987,9 +1058,13 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int);
|
||||
*/
|
||||
typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int);
|
||||
|
||||
/*! @brief The function signature for window close callbacks.
|
||||
/*! @brief The function pointer type for window close callbacks.
|
||||
*
|
||||
* This is the function signature for window close callback functions.
|
||||
* This is the function pointer type for window close callbacks. A window
|
||||
* close callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that the user attempted to close.
|
||||
*
|
||||
@@ -1003,9 +1078,13 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int);
|
||||
*/
|
||||
typedef void (* GLFWwindowclosefun)(GLFWwindow*);
|
||||
|
||||
/*! @brief The function signature for window content refresh callbacks.
|
||||
/*! @brief The function pointer type for window content refresh callbacks.
|
||||
*
|
||||
* This is the function signature for window refresh callback functions.
|
||||
* This is the function pointer type for window content refresh callbacks.
|
||||
* A window content refresh callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window);
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window whose content needs to be refreshed.
|
||||
*
|
||||
@@ -1019,9 +1098,13 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*);
|
||||
*/
|
||||
typedef void (* GLFWwindowrefreshfun)(GLFWwindow*);
|
||||
|
||||
/*! @brief The function signature for window focus/defocus callbacks.
|
||||
/*! @brief The function pointer type for window focus callbacks.
|
||||
*
|
||||
* This is the function signature for window focus callback functions.
|
||||
* This is the function pointer type for window focus callbacks. A window
|
||||
* focus callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int focused)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that gained or lost input focus.
|
||||
* @param[in] focused `true` if the window was given input focus, or
|
||||
@@ -1054,10 +1137,13 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int);
|
||||
typedef void (* GLFWwindowocclusionfun)(GLFWwindow*, bool);
|
||||
|
||||
|
||||
/*! @brief The function signature for window iconify/restore callbacks.
|
||||
/*! @brief The function pointer type for window iconify callbacks.
|
||||
*
|
||||
* This is the function signature for window iconify/restore callback
|
||||
* functions.
|
||||
* This is the function pointer type for window iconify callbacks. A window
|
||||
* iconify callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int iconified)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that was iconified or restored.
|
||||
* @param[in] iconified `true` if the window was iconified, or
|
||||
@@ -1072,10 +1158,13 @@ typedef void (* GLFWwindowocclusionfun)(GLFWwindow*, bool);
|
||||
*/
|
||||
typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int);
|
||||
|
||||
/*! @brief The function signature for window maximize/restore callbacks.
|
||||
/*! @brief The function pointer type for window maximize callbacks.
|
||||
*
|
||||
* This is the function signature for window maximize/restore callback
|
||||
* functions.
|
||||
* This is the function pointer type for window maximize callbacks. A window
|
||||
* maximize callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int maximized)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that was maximized or restored.
|
||||
* @param[in] iconified `true` if the window was maximized, or
|
||||
@@ -1090,10 +1179,13 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int);
|
||||
*/
|
||||
typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int);
|
||||
|
||||
/*! @brief The function signature for framebuffer resize callbacks.
|
||||
/*! @brief The function pointer type for framebuffer size callbacks.
|
||||
*
|
||||
* This is the function signature for framebuffer resize callback
|
||||
* functions.
|
||||
* This is the function pointer type for framebuffer size callbacks.
|
||||
* A framebuffer size callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int width, int height)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window whose framebuffer was resized.
|
||||
* @param[in] width The new width, in pixels, of the framebuffer.
|
||||
@@ -1108,10 +1200,13 @@ typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int);
|
||||
*/
|
||||
typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);
|
||||
|
||||
/*! @brief The function signature for window content scale callbacks.
|
||||
/*! @brief The function pointer type for window content scale callbacks.
|
||||
*
|
||||
* This is the function signature for window content scale callback
|
||||
* functions.
|
||||
* This is the function pointer type for window content scale callbacks.
|
||||
* A window content scale callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, float xscale, float yscale)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window whose content scale changed.
|
||||
* @param[in] xscale The new x-axis content scale of the window.
|
||||
@@ -1126,14 +1221,19 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);
|
||||
*/
|
||||
typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float);
|
||||
|
||||
/*! @brief The function signature for mouse button callbacks.
|
||||
/*! @brief The function pointer type for mouse button callbacks.
|
||||
*
|
||||
* This is the function signature for mouse button callback functions.
|
||||
* This is the function pointer type for mouse button callback functions.
|
||||
* A mouse button callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int button, int action, int mods)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] button The [mouse button](@ref buttons) that was pressed or
|
||||
* released.
|
||||
* @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`.
|
||||
* @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases
|
||||
* may add more actions.
|
||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
||||
* held down.
|
||||
*
|
||||
@@ -1147,9 +1247,13 @@ typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float);
|
||||
*/
|
||||
typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int);
|
||||
|
||||
/*! @brief The function signature for cursor position callbacks.
|
||||
/*! @brief The function pointer type for cursor position callbacks.
|
||||
*
|
||||
* This is the function signature for cursor position callback functions.
|
||||
* This is the function pointer type for cursor position callbacks. A cursor
|
||||
* position callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, double xpos, double ypos);
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] xpos The new cursor x-coordinate, relative to the left edge of
|
||||
@@ -1166,12 +1270,16 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int);
|
||||
*/
|
||||
typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double);
|
||||
|
||||
/*! @brief The function signature for cursor enter/leave callbacks.
|
||||
/*! @brief The function pointer type for cursor enter/leave callbacks.
|
||||
*
|
||||
* This is the function signature for cursor enter/leave callback functions.
|
||||
* This is the function pointer type for cursor enter/leave callbacks.
|
||||
* A cursor enter/leave callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int entered)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] entered `true` if the cursor entered the window's client
|
||||
* @param[in] entered `true` if the cursor entered the window's content
|
||||
* area, or `false` if it left it.
|
||||
*
|
||||
* @sa @ref cursor_enter
|
||||
@@ -1183,16 +1291,20 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double);
|
||||
*/
|
||||
typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
||||
|
||||
/*! @brief The function signature for scroll callbacks.
|
||||
/*! @brief The function pointer type for scroll callbacks.
|
||||
*
|
||||
* This is the function signature for scroll callback functions.
|
||||
* This is the function pointer type for scroll callbacks. A scroll callback
|
||||
* function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, double xoffset, double yoffset)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] xoffset The scroll offset along the x-axis.
|
||||
* @param[in] yoffset The scroll offset along the y-axis.
|
||||
* @param[in] flags A bit-mask providing extra data about the event.
|
||||
* flags & 1 will be true if and only if the offset values are "high-precision".
|
||||
* Typically pixel values. Otherwise the offset values are number of lines.
|
||||
* flags & 1 will be true if and only if the offset values are "high-precision",
|
||||
* typically pixel values. Otherwise the offset values are number of lines.
|
||||
* (flags >> 1) & 7 will have value 1 for the start of momentum scrolling,
|
||||
* value 2 for stationary momentum scrolling, value 3 for momentum scrolling
|
||||
* in progress, value 4 for momentum scrolling ended, value 5 for momentum
|
||||
@@ -1208,9 +1320,13 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
||||
*/
|
||||
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int);
|
||||
|
||||
/*! @brief The function signature for key callbacks.
|
||||
/*! @brief The function pointer type for key callbacks.
|
||||
*
|
||||
* This is the function signature for key callback functions.
|
||||
* This is the function pointer type for key callbacks. A keyboard
|
||||
* key callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int key, int native_key, int action, int mods)
|
||||
* @endcode
|
||||
* The semantics of this function are that the key that is interacted with on the
|
||||
* keyboard is reported, and the text, if any generated by the key is reported.
|
||||
* So, for example, if on a US-ASCII keyboard the user presses Shift+= GLFW
|
||||
@@ -1219,15 +1335,8 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int);
|
||||
* the "s" key will generate text "o" and GLFW_KEY_O.
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
||||
* @param[in] scancode The system-specific scancode of the key.
|
||||
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
||||
* held down.
|
||||
* @param[in] text UTF-8 encoded text generated by this key event or empty string.
|
||||
* @param[in] Used for Input Method events. Zero for normal key events.
|
||||
* A value of 1 means the pre-edit text for the input event has been changed.
|
||||
* A value of 2 means the text should be committed.
|
||||
* @param[in] ev The key event, see GLFWkeyevent. The data in this event is only valid for
|
||||
* the lifetime of the callback.
|
||||
*
|
||||
* @note On X11/Wayland if a modifier other than the modifiers GLFW reports
|
||||
* (ctrl/shift/alt/super) is used, GLFW will report the shifted key rather
|
||||
@@ -1241,16 +1350,23 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int);
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, int);
|
||||
typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
||||
|
||||
/*! @brief The function signature for file drop callbacks.
|
||||
/*! @brief The function pointer type for path drop callbacks.
|
||||
*
|
||||
* This is the function signature for file drop callbacks.
|
||||
* This is the function pointer type for path drop callbacks. A path drop
|
||||
* callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int path_count, const char* paths[])
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] count The number of dropped files.
|
||||
* @param[in] path_count The number of dropped paths.
|
||||
* @param[in] paths The UTF-8 encoded file and/or directory path names.
|
||||
*
|
||||
* @pointer_lifetime The path array and its strings are valid until the
|
||||
* callback function returns.
|
||||
*
|
||||
* @sa @ref path_drop
|
||||
* @sa @ref glfwSetDropCallback
|
||||
*
|
||||
@@ -1258,17 +1374,21 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, i
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**);
|
||||
typedef void (* GLFWdropfun)(GLFWwindow*,int,const char*[]);
|
||||
|
||||
typedef void (* GLFWliveresizefun)(GLFWwindow*, bool);
|
||||
|
||||
/*! @brief The function signature for monitor configuration callbacks.
|
||||
/*! @brief The function pointer type for monitor configuration callbacks.
|
||||
*
|
||||
* This is the function signature for monitor configuration callback functions.
|
||||
* This is the function pointer type for monitor configuration callbacks.
|
||||
* A monitor callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWmonitor* monitor, int event)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] monitor The monitor that was connected or disconnected.
|
||||
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining
|
||||
* values reserved for future use.
|
||||
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future
|
||||
* releases may add more events.
|
||||
*
|
||||
* @sa @ref monitor_event
|
||||
* @sa @ref glfwSetMonitorCallback
|
||||
@@ -1279,14 +1399,17 @@ typedef void (* GLFWliveresizefun)(GLFWwindow*, bool);
|
||||
*/
|
||||
typedef void (* GLFWmonitorfun)(GLFWmonitor*,int);
|
||||
|
||||
/*! @brief The function signature for joystick configuration callbacks.
|
||||
/*! @brief The function pointer type for joystick configuration callbacks.
|
||||
*
|
||||
* This is the function signature for joystick configuration callback
|
||||
* functions.
|
||||
* This is the function pointer type for joystick configuration callbacks.
|
||||
* A joystick configuration callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(int jid, int event)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] jid The joystick that was connected or disconnected.
|
||||
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining
|
||||
* values reserved for future use.
|
||||
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future
|
||||
* releases may add more events.
|
||||
*
|
||||
* @sa @ref joystick_event
|
||||
* @sa @ref glfwSetJoystickCallback
|
||||
@@ -1373,6 +1496,8 @@ typedef struct GLFWgammaramp
|
||||
*
|
||||
* @since Added in version 2.1.
|
||||
* @glfw3 Removed format and bytes-per-pixel members.
|
||||
*
|
||||
* @ingroup window
|
||||
*/
|
||||
typedef struct GLFWimage
|
||||
{
|
||||
@@ -1395,6 +1520,8 @@ typedef struct GLFWimage
|
||||
* @sa @ref glfwGetGamepadState
|
||||
*
|
||||
* @since Added in version 3.3.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef struct GLFWgamepadstate
|
||||
{
|
||||
@@ -1448,13 +1575,14 @@ typedef struct GLFWgamepadstate
|
||||
|
||||
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
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)(void);
|
||||
typedef int (*glfwInit_func)(monotonic_t);
|
||||
glfwInit_func glfwInit_impl;
|
||||
#define glfwInit glfwInit_impl
|
||||
|
||||
@@ -1466,11 +1594,11 @@ typedef void (*glfwStopMainLoop_func)(void);
|
||||
glfwStopMainLoop_func glfwStopMainLoop_impl;
|
||||
#define glfwStopMainLoop glfwStopMainLoop_impl
|
||||
|
||||
typedef unsigned long long (*glfwAddTimer_func)(double, bool, GLFWuserdatafun, void *, GLFWuserdatafun);
|
||||
typedef unsigned long long (*glfwAddTimer_func)(monotonic_t, bool, GLFWuserdatafun, void *, GLFWuserdatafun);
|
||||
glfwAddTimer_func glfwAddTimer_impl;
|
||||
#define glfwAddTimer glfwAddTimer_impl
|
||||
|
||||
typedef void (*glfwUpdateTimer_func)(unsigned long long, double, bool);
|
||||
typedef void (*glfwUpdateTimer_func)(unsigned long long, monotonic_t, bool);
|
||||
glfwUpdateTimer_func glfwUpdateTimer_impl;
|
||||
#define glfwUpdateTimer glfwUpdateTimer_impl
|
||||
|
||||
@@ -1638,7 +1766,7 @@ typedef void (*glfwGetWindowContentScale_func)(GLFWwindow*, float*, float*);
|
||||
glfwGetWindowContentScale_func glfwGetWindowContentScale_impl;
|
||||
#define glfwGetWindowContentScale glfwGetWindowContentScale_impl
|
||||
|
||||
typedef double (*glfwGetDoubleClickInterval_func)(GLFWwindow*);
|
||||
typedef monotonic_t (*glfwGetDoubleClickInterval_func)(GLFWwindow*);
|
||||
glfwGetDoubleClickInterval_func glfwGetDoubleClickInterval_impl;
|
||||
#define glfwGetDoubleClickInterval glfwGetDoubleClickInterval_impl
|
||||
|
||||
@@ -1762,9 +1890,9 @@ typedef const char* (*glfwGetKeyName_func)(int, int);
|
||||
glfwGetKeyName_func glfwGetKeyName_impl;
|
||||
#define glfwGetKeyName glfwGetKeyName_impl
|
||||
|
||||
typedef int (*glfwGetKeyScancode_func)(int);
|
||||
glfwGetKeyScancode_func glfwGetKeyScancode_impl;
|
||||
#define glfwGetKeyScancode glfwGetKeyScancode_impl
|
||||
typedef int (*glfwGetNativeKeyForKey_func)(int);
|
||||
glfwGetNativeKeyForKey_func glfwGetNativeKeyForKey_impl;
|
||||
#define glfwGetNativeKeyForKey glfwGetNativeKeyForKey_impl
|
||||
|
||||
typedef int (*glfwGetKey_func)(GLFWwindow*, int);
|
||||
glfwGetKey_func glfwGetKey_impl;
|
||||
@@ -1890,22 +2018,10 @@ typedef const char* (*glfwGetClipboardString_func)(GLFWwindow*);
|
||||
glfwGetClipboardString_func glfwGetClipboardString_impl;
|
||||
#define glfwGetClipboardString glfwGetClipboardString_impl
|
||||
|
||||
typedef double (*glfwGetTime_func)(void);
|
||||
typedef monotonic_t (*glfwGetTime_func)(void);
|
||||
glfwGetTime_func glfwGetTime_impl;
|
||||
#define glfwGetTime glfwGetTime_impl
|
||||
|
||||
typedef void (*glfwSetTime_func)(double);
|
||||
glfwSetTime_func glfwSetTime_impl;
|
||||
#define glfwSetTime glfwSetTime_impl
|
||||
|
||||
typedef uint64_t (*glfwGetTimerValue_func)(void);
|
||||
glfwGetTimerValue_func glfwGetTimerValue_impl;
|
||||
#define glfwGetTimerValue glfwGetTimerValue_impl
|
||||
|
||||
typedef uint64_t (*glfwGetTimerFrequency_func)(void);
|
||||
glfwGetTimerFrequency_func glfwGetTimerFrequency_impl;
|
||||
#define glfwGetTimerFrequency glfwGetTimerFrequency_impl
|
||||
|
||||
typedef void (*glfwMakeContextCurrent_func)(GLFWwindow*);
|
||||
glfwMakeContextCurrent_func glfwMakeContextCurrent_impl;
|
||||
#define glfwMakeContextCurrent glfwMakeContextCurrent_impl
|
||||
@@ -1962,6 +2078,10 @@ typedef GLFWapplicationshouldhandlereopenfun (*glfwSetApplicationShouldHandleReo
|
||||
glfwSetApplicationShouldHandleReopen_func glfwSetApplicationShouldHandleReopen_impl;
|
||||
#define glfwSetApplicationShouldHandleReopen glfwSetApplicationShouldHandleReopen_impl
|
||||
|
||||
typedef GLFWapplicationwillfinishlaunchingfun (*glfwSetApplicationWillFinishLaunching_func)(GLFWapplicationwillfinishlaunchingfun);
|
||||
glfwSetApplicationWillFinishLaunching_func glfwSetApplicationWillFinishLaunching_impl;
|
||||
#define glfwSetApplicationWillFinishLaunching glfwSetApplicationWillFinishLaunching_impl
|
||||
|
||||
typedef void (*glfwGetCocoaKeyEquivalent_func)(int, int, void*, void*);
|
||||
glfwGetCocoaKeyEquivalent_func glfwGetCocoaKeyEquivalent_impl;
|
||||
#define glfwGetCocoaKeyEquivalent glfwGetCocoaKeyEquivalent_impl
|
||||
@@ -1986,9 +2106,9 @@ typedef const char* (*glfwGetPrimarySelectionString_func)(GLFWwindow*);
|
||||
glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl;
|
||||
#define glfwGetPrimarySelectionString glfwGetPrimarySelectionString_impl
|
||||
|
||||
typedef int (*glfwGetXKBScancode_func)(const char*, int);
|
||||
glfwGetXKBScancode_func glfwGetXKBScancode_impl;
|
||||
#define glfwGetXKBScancode glfwGetXKBScancode_impl
|
||||
typedef int (*glfwGetNativeKeyForName_func)(const char*, int);
|
||||
glfwGetNativeKeyForName_func glfwGetNativeKeyForName_impl;
|
||||
#define glfwGetNativeKeyForName glfwGetNativeKeyForName_impl
|
||||
|
||||
typedef void (*glfwRequestWaylandFrameEvent_func)(GLFWwindow*, unsigned long long, GLFWwaylandframecallbackfunc);
|
||||
glfwRequestWaylandFrameEvent_func glfwRequestWaylandFrameEvent_impl;
|
||||
|
||||
83
kitty/glfw.c
83
kitty/glfw.c
@@ -7,6 +7,7 @@
|
||||
#include "state.h"
|
||||
#include "glfw_tests.h"
|
||||
#include "fonts.h"
|
||||
#include "monotonic.h"
|
||||
#include <structmember.h>
|
||||
#include "glfw-wrapper.h"
|
||||
extern bool cocoa_make_window_resizable(void *w, bool);
|
||||
@@ -17,7 +18,7 @@ extern void cocoa_set_activation_policy(bool);
|
||||
extern void cocoa_set_titlebar_color(void *w, color_type color);
|
||||
extern bool cocoa_alt_option_key_pressed(unsigned long);
|
||||
extern size_t cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz);
|
||||
extern double cocoa_cursor_blink_interval(void);
|
||||
extern monotonic_t cocoa_cursor_blink_interval(void);
|
||||
|
||||
|
||||
#if GLFW_KEY_LAST >= MAX_KEY_COUNT
|
||||
@@ -83,7 +84,7 @@ log_event(const char *format, ...) {
|
||||
{
|
||||
va_list vl;
|
||||
|
||||
fprintf(stderr, "[%.4f] ", glfwGetTime());
|
||||
fprintf(stderr, "[%.4f] ", monotonic_t_to_s_double(glfwGetTime()));
|
||||
va_start(vl, format);
|
||||
vfprintf(stderr, format, vl);
|
||||
va_end(vl);
|
||||
@@ -231,14 +232,14 @@ refresh_callback(GLFWwindow *w) {
|
||||
static int mods_at_last_key_or_button_event = 0;
|
||||
|
||||
static void
|
||||
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods, const char* text, int state) {
|
||||
key_callback(GLFWwindow *w, GLFWkeyevent *ev) {
|
||||
if (!set_callback_window(w)) return;
|
||||
mods_at_last_key_or_button_event = mods;
|
||||
mods_at_last_key_or_button_event = ev->mods;
|
||||
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
|
||||
if (key >= 0 && key <= GLFW_KEY_LAST) {
|
||||
global_state.callback_os_window->is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
|
||||
if (ev->key >= 0 && ev->key <= GLFW_KEY_LAST) {
|
||||
global_state.callback_os_window->is_key_pressed[ev->key] = ev->action == GLFW_RELEASE ? false : true;
|
||||
}
|
||||
if (is_window_ready_for_callbacks()) on_key_input(key, scancode, action, mods, text, state);
|
||||
if (is_window_ready_for_callbacks()) on_key_input(ev);
|
||||
global_state.callback_os_window = NULL;
|
||||
request_tick_callback();
|
||||
}
|
||||
@@ -248,7 +249,7 @@ cursor_enter_callback(GLFWwindow *w, int entered) {
|
||||
if (!set_callback_window(w)) return;
|
||||
if (entered) {
|
||||
show_mouse_cursor(w);
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
global_state.callback_os_window->last_mouse_activity_at = now;
|
||||
if (is_window_ready_for_callbacks()) enter_event();
|
||||
request_tick_callback();
|
||||
@@ -261,7 +262,7 @@ mouse_button_callback(GLFWwindow *w, int button, int action, int mods) {
|
||||
if (!set_callback_window(w)) return;
|
||||
show_mouse_cursor(w);
|
||||
mods_at_last_key_or_button_event = mods;
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
global_state.callback_os_window->last_mouse_activity_at = now;
|
||||
if (button >= 0 && (unsigned int)button < arraysz(global_state.callback_os_window->mouse_button_pressed)) {
|
||||
global_state.callback_os_window->mouse_button_pressed[button] = action == GLFW_PRESS ? true : false;
|
||||
@@ -275,7 +276,7 @@ static void
|
||||
cursor_pos_callback(GLFWwindow *w, double x, double y) {
|
||||
if (!set_callback_window(w)) return;
|
||||
show_mouse_cursor(w);
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
global_state.callback_os_window->last_mouse_activity_at = now;
|
||||
global_state.callback_os_window->cursor_blink_zero_time = now;
|
||||
global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
|
||||
@@ -289,7 +290,7 @@ static void
|
||||
scroll_callback(GLFWwindow *w, double xoffset, double yoffset, int flags) {
|
||||
if (!set_callback_window(w)) return;
|
||||
show_mouse_cursor(w);
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
global_state.callback_os_window->last_mouse_activity_at = now;
|
||||
if (is_window_ready_for_callbacks()) scroll_event(xoffset, yoffset, flags);
|
||||
request_tick_callback();
|
||||
@@ -308,7 +309,7 @@ window_focus_callback(GLFWwindow *w, int focused) {
|
||||
focus_in_event();
|
||||
global_state.callback_os_window->last_focused_counter = ++focus_counter;
|
||||
}
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
global_state.callback_os_window->last_mouse_activity_at = now;
|
||||
global_state.callback_os_window->cursor_blink_zero_time = now;
|
||||
if (is_window_ready_for_callbacks()) {
|
||||
@@ -450,7 +451,7 @@ toggle_maximized_for_os_window(OSWindow *w) {
|
||||
|
||||
#ifdef __APPLE__
|
||||
static int
|
||||
filter_option(int key UNUSED, int mods, unsigned int scancode UNUSED, unsigned long flags) {
|
||||
filter_option(int key UNUSED, int mods, unsigned int native_key UNUSED, unsigned long flags) {
|
||||
if ((mods == GLFW_MOD_ALT) || (mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))) {
|
||||
if (OPT(macos_option_as_alt) == 3) return 1;
|
||||
if (cocoa_alt_option_key_pressed(flags)) return 1;
|
||||
@@ -460,20 +461,19 @@ filter_option(int key UNUSED, int mods, unsigned int scancode UNUSED, unsigned l
|
||||
|
||||
static GLFWwindow *application_quit_canary = NULL;
|
||||
|
||||
static int
|
||||
static bool
|
||||
on_application_reopen(int has_visible_windows) {
|
||||
if (has_visible_windows) return true;
|
||||
set_cocoa_pending_action(NEW_OS_WINDOW, NULL);
|
||||
// Without unjam wait_for_events() blocks until the next event
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
static bool
|
||||
intercept_cocoa_fullscreen(GLFWwindow *w) {
|
||||
if (!OPT(macos_traditional_fullscreen) || !set_callback_window(w)) return 0;
|
||||
if (!OPT(macos_traditional_fullscreen) || !set_callback_window(w)) return false;
|
||||
toggle_fullscreen_for_os_window(global_state.callback_os_window);
|
||||
global_state.callback_os_window = NULL;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -517,6 +517,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
|
||||
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
|
||||
glfwSetApplicationShouldHandleReopen(on_application_reopen);
|
||||
glfwSetApplicationWillFinishLaunching(cocoa_create_global_menu);
|
||||
if (OPT(hide_window_decorations)) glfwWindowHint(GLFW_DECORATED, false);
|
||||
#endif
|
||||
|
||||
@@ -548,8 +549,13 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
if (!common_context) common_context = application_quit_canary;
|
||||
#endif
|
||||
|
||||
GLFWwindow *temp_window = glfwCreateWindow(640, 480, "temp", NULL, common_context);
|
||||
if (temp_window == NULL) { fatal("Failed to create GLFW temp window! This usually happens because of old/broken OpenGL drivers. kitty requires working OpenGL 3.3 drivers."); }
|
||||
GLFWwindow *temp_window = NULL;
|
||||
if (!global_state.is_wayland) {
|
||||
// On Wayland windows dont get a content scale until they receive an enterEvent anyway
|
||||
// which wont happen until the event loop ticks, so using a temp window is useless.
|
||||
temp_window = glfwCreateWindow(640, 480, "temp", NULL, common_context);
|
||||
if (temp_window == NULL) { fatal("Failed to create GLFW temp window! This usually happens because of old/broken OpenGL drivers. kitty requires working OpenGL 3.3 drivers."); }
|
||||
}
|
||||
float xscale, yscale;
|
||||
double xdpi, ydpi;
|
||||
get_window_content_scale(temp_window, &xscale, &yscale, &xdpi, &ydpi);
|
||||
@@ -564,8 +570,8 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
// is no startup notification in Wayland anyway. It amazes me that anyone
|
||||
// uses Wayland as anything other than a butt for jokes.
|
||||
if (global_state.is_wayland) glfwWindowHint(GLFW_VISIBLE, true);
|
||||
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, temp_window);
|
||||
glfwDestroyWindow(temp_window); temp_window = NULL;
|
||||
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, temp_window ? temp_window : common_context);
|
||||
if (temp_window) { glfwDestroyWindow(temp_window); temp_window = NULL; }
|
||||
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
|
||||
glfwMakeContextCurrent(glfw_window);
|
||||
if (is_first_window) {
|
||||
@@ -590,9 +596,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
PyObject *ret = PyObject_CallFunction(load_programs, "O", is_semi_transparent ? Py_True : Py_False);
|
||||
if (ret == NULL) return NULL;
|
||||
Py_DECREF(ret);
|
||||
#ifdef __APPLE__
|
||||
cocoa_create_global_menu();
|
||||
#endif
|
||||
#define CC(dest, shape) {\
|
||||
if (!dest##_cursor) { \
|
||||
dest##_cursor = glfwCreateStandardCursor(GLFW_##shape##_CURSOR); \
|
||||
@@ -602,10 +605,10 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
#undef CC
|
||||
if (OPT(click_interval) < 0) OPT(click_interval) = glfwGetDoubleClickInterval(glfw_window);
|
||||
if (OPT(cursor_blink_interval) < 0) {
|
||||
OPT(cursor_blink_interval) = 0.5;
|
||||
OPT(cursor_blink_interval) = ms_to_monotonic_t(500ll);
|
||||
#ifdef __APPLE__
|
||||
double cbi = cocoa_cursor_blink_interval();
|
||||
if (cbi >= 0) OPT(cursor_blink_interval) = cbi / 2000.0;
|
||||
monotonic_t cbi = cocoa_cursor_blink_interval();
|
||||
if (cbi >= 0) OPT(cursor_blink_interval) = cbi / 2;
|
||||
#endif
|
||||
}
|
||||
is_first_window = false;
|
||||
@@ -655,7 +658,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window), OPT(macos_window_resizable));
|
||||
} else log_error("Failed to load glfwGetCocoaWindow");
|
||||
#endif
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
w->is_focused = true;
|
||||
w->cursor_blink_zero_time = now;
|
||||
w->last_mouse_activity_at = now;
|
||||
@@ -791,7 +794,7 @@ glfw_init(PyObject UNUSED *self, PyObject *args) {
|
||||
glfwDBusSetUserNotificationHandler(dbus_user_notification_activated);
|
||||
}
|
||||
#endif
|
||||
PyObject *ans = glfwInit() ? Py_True: Py_False;
|
||||
PyObject *ans = glfwInit(monotonic_start_time) ? Py_True: Py_False;
|
||||
if (ans == Py_True) {
|
||||
OSWindow w = {0};
|
||||
set_os_window_dpi(&w);
|
||||
@@ -815,8 +818,8 @@ get_physical_dpi(GLFWmonitor *m) {
|
||||
if (width == 0 || height == 0) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor size"); return NULL; }
|
||||
const GLFWvidmode *vm = glfwGetVideoMode(m);
|
||||
if (vm == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get video mode for monitor"); return NULL; }
|
||||
float dpix = vm->width / (width / 25.4);
|
||||
float dpiy = vm->height / (height / 25.4);
|
||||
float dpix = (float)(vm->width / (width / 25.4));
|
||||
float dpiy = (float)(vm->height / (height / 25.4));
|
||||
return Py_BuildValue("ff", dpix, dpiy);
|
||||
}
|
||||
|
||||
@@ -829,9 +832,9 @@ glfw_get_physical_dpi(PYNOARG) {
|
||||
|
||||
static PyObject*
|
||||
glfw_get_key_name(PyObject UNUSED *self, PyObject *args) {
|
||||
int key, scancode;
|
||||
if (!PyArg_ParseTuple(args, "ii", &key, &scancode)) return NULL;
|
||||
return Py_BuildValue("s", glfwGetKeyName(key, scancode));
|
||||
int key, native_key;
|
||||
if (!PyArg_ParseTuple(args, "ii", &key, &native_key)) return NULL;
|
||||
return Py_BuildValue("s", glfwGetKeyName(key, native_key));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
@@ -854,9 +857,9 @@ get_clipboard_string(PYNOARG) {
|
||||
|
||||
void
|
||||
ring_audio_bell(OSWindow *w UNUSED) {
|
||||
static double last_bell_at = -1;
|
||||
double now = monotonic();
|
||||
if (now - last_bell_at <= 0.1) return;
|
||||
static monotonic_t last_bell_at = -1;
|
||||
monotonic_t now = monotonic();
|
||||
if (now - last_bell_at <= ms_to_monotonic_t(100ll)) return;
|
||||
last_bell_at = now;
|
||||
#ifdef __APPLE__
|
||||
if (w->handle) {
|
||||
@@ -1139,12 +1142,12 @@ dbus_send_notification(PyObject *self UNUSED, PyObject *args) {
|
||||
#endif
|
||||
|
||||
id_type
|
||||
add_main_loop_timer(double interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback) {
|
||||
add_main_loop_timer(monotonic_t interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback) {
|
||||
return glfwAddTimer(interval, repeats, callback, callback_data, free_callback);
|
||||
}
|
||||
|
||||
void
|
||||
update_main_loop_timer(id_type timer_id, double interval, bool enabled) {
|
||||
update_main_loop_timer(id_type timer_id, monotonic_t interval, bool enabled) {
|
||||
glfwUpdateTimer(timer_id, interval, enabled);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ static void* empty_thread_main(void* data UNUSED)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void key_callback(GLFWwindow *w UNUSED, int key, int scancode UNUSED, int action, int mods UNUSED, const char* text UNUSED, int state UNUSED)
|
||||
static void key_callback(GLFWwindow *w UNUSED, GLFWkeyevent *ev)
|
||||
{
|
||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
|
||||
if (ev->key == GLFW_KEY_ESCAPE && ev->action == GLFW_PRESS) {
|
||||
glfwSetWindowShouldClose(w, true);
|
||||
wakeup_main_loop();
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ trim_predicate(Image *img) {
|
||||
|
||||
static int
|
||||
oldest_last(const void* a, const void *b) {
|
||||
double ans = ((Image*)(b))->atime - ((Image*)(a))->atime;
|
||||
monotonic_t ans = ((Image*)(b))->atime - ((Image*)(a))->atime;
|
||||
return ans < 0 ? -1 : (ans == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "data-types.h"
|
||||
#include "monotonic.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned char action, transmission_type, compressed, delete_action;
|
||||
@@ -50,7 +51,7 @@ typedef struct {
|
||||
|
||||
ImageRef *refs;
|
||||
size_t refcnt, refcap;
|
||||
double atime;
|
||||
monotonic_t atime;
|
||||
size_t used_storage;
|
||||
} Image;
|
||||
|
||||
|
||||
37
kitty/keys.c
37
kitty/keys.c
@@ -26,7 +26,7 @@ key_to_bytes(int glfw_key, bool smkx, bool extended, int mods, int action) {
|
||||
#define SPECIAL_INDEX(key) ((key & 0x7f) | ( (mods & 0xF) << 7))
|
||||
#define IS_ALT_MODS(mods) (mods == GLFW_MOD_ALT || mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))
|
||||
|
||||
typedef struct { int mods, scancode; } NativeKey;
|
||||
typedef struct { int mods, native_key; } NativeKey;
|
||||
static NativeKey *native_special_keys = NULL;
|
||||
static size_t native_special_keys_capacity = 0, native_special_keys_count = 0;
|
||||
|
||||
@@ -39,7 +39,7 @@ set_special_key_combo(int glfw_key, int mods, bool is_native) {
|
||||
if (native_special_keys == NULL) fatal("Out of memory");
|
||||
}
|
||||
native_special_keys[native_special_keys_count].mods = mods;
|
||||
native_special_keys[native_special_keys_count++].scancode = glfw_key;
|
||||
native_special_keys[native_special_keys_count++].native_key = glfw_key;
|
||||
} else {
|
||||
uint16_t key = key_map[glfw_key];
|
||||
if (key != UINT8_MAX) {
|
||||
@@ -68,6 +68,7 @@ is_modifier_key(int key) {
|
||||
case GLFW_KEY_RIGHT_CONTROL:
|
||||
case GLFW_KEY_LEFT_SUPER:
|
||||
case GLFW_KEY_RIGHT_SUPER:
|
||||
case GLFW_KEY_CAPS_LOCK:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -96,7 +97,7 @@ is_ascii_control_char(char c) {
|
||||
}
|
||||
|
||||
static inline bool
|
||||
check_if_special(int key, int mods, int scancode) {
|
||||
check_if_special(int key, int mods, int native_key) {
|
||||
uint16_t qkey = (0 <= key && key < (ssize_t)arraysz(key_map)) ? key_map[key] : UINT8_MAX;
|
||||
bool special = false;
|
||||
if (qkey != UINT8_MAX) {
|
||||
@@ -104,7 +105,8 @@ check_if_special(int key, int mods, int scancode) {
|
||||
special = needs_special_handling[qkey];
|
||||
}
|
||||
for (size_t i = 0; !special && i < native_special_keys_count; i++) {
|
||||
if (scancode == native_special_keys[i].scancode && mods == native_special_keys[i].mods) special = true;
|
||||
if (native_key == native_special_keys[i].native_key && mods == native_special_keys[i].mods)
|
||||
special = true;
|
||||
}
|
||||
return special;
|
||||
}
|
||||
@@ -121,23 +123,26 @@ update_ime_position(OSWindow *os_window, Window* w, Screen *screen) {
|
||||
#define debug(...) if (OPT(debug_keyboard)) printf(__VA_ARGS__);
|
||||
|
||||
void
|
||||
on_key_input(int key, int scancode, int action, int mods, const char* text, int state) {
|
||||
on_key_input(GLFWkeyevent *ev) {
|
||||
Window *w = active_window();
|
||||
int action = ev->action, native_key = ev->native_key, key = ev->key, mods = ev->mods;
|
||||
const char *text = ev->text ? ev->text : "";
|
||||
|
||||
debug("on_key_input: glfw key: %d native_code: 0x%x action: %s mods: 0x%x text: '%s' state: %d ",
|
||||
key, scancode,
|
||||
key, native_key,
|
||||
(action == GLFW_RELEASE ? "RELEASE" : (action == GLFW_PRESS ? "PRESS" : "REPEAT")),
|
||||
mods, text, state);
|
||||
mods, text, ev->ime_state);
|
||||
if (!w) { debug("no active window, ignoring\n"); return; }
|
||||
if (OPT(mouse_hide_wait) < 0 && !is_modifier_key(key)) hide_mouse(global_state.callback_os_window);
|
||||
Screen *screen = w->render_data.screen;
|
||||
switch(state) {
|
||||
switch(ev->ime_state) {
|
||||
case 1: // update pre-edit text
|
||||
update_ime_position(global_state.callback_os_window, w, screen);
|
||||
screen_draw_overlay_text(screen, text);
|
||||
debug("updated pre-edit text: '%s'\n", text);
|
||||
return;
|
||||
case 2: // commit text
|
||||
if (text && *text) {
|
||||
if (*text) {
|
||||
schedule_write_to_child(w->id, 1, text, strlen(text));
|
||||
debug("committed pre-edit text: %s\n", text);
|
||||
} else debug("committed pre-edit text: (null)\n");
|
||||
@@ -158,13 +163,13 @@ on_key_input(int key, int scancode, int action, int mods, const char* text, int
|
||||
if (
|
||||
action != GLFW_RELEASE &&
|
||||
key != GLFW_KEY_LEFT_SHIFT && key != GLFW_KEY_RIGHT_SHIFT && key != GLFW_KEY_LEFT_ALT && key != GLFW_KEY_RIGHT_ALT && key != GLFW_KEY_LEFT_CONTROL && key != GLFW_KEY_RIGHT_CONTROL
|
||||
) call_boss(process_sequence, "iiii", key, scancode, action, mods);
|
||||
) call_boss(process_sequence, "iiii", key, native_key, action, mods);
|
||||
return;
|
||||
}
|
||||
bool has_text = text && !is_ascii_control_char(text[0]);
|
||||
bool has_text = text[0] && !is_ascii_control_char(text[0]);
|
||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||
if (check_if_special(key, mods, scancode)) {
|
||||
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", key, scancode, action, mods);
|
||||
if (check_if_special(key, mods, native_key)) {
|
||||
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", key, native_key, action, mods);
|
||||
if (ret == NULL) { PyErr_Print(); }
|
||||
else {
|
||||
bool consumed = ret == Py_True;
|
||||
@@ -225,9 +230,9 @@ PYWRAP1(key_for_native_key_name) {
|
||||
int case_sensitive = 0;
|
||||
PA("s|p", &name, &case_sensitive);
|
||||
#ifndef __APPLE__
|
||||
if (glfwGetXKBScancode) { // if this function is called before GLFW is initialized glfwGetXKBScancode will be NULL
|
||||
int scancode = glfwGetXKBScancode(name, case_sensitive);
|
||||
if (scancode) return Py_BuildValue("i", scancode);
|
||||
if (glfwGetNativeKeyForName) { // if this function is called before GLFW is initialized glfwGetNativeKeyForName will be NULL
|
||||
int native_key = glfwGetNativeKeyForName(name, case_sensitive);
|
||||
if (native_key) return Py_BuildValue("i", native_key);
|
||||
}
|
||||
#endif
|
||||
Py_RETURN_NONE;
|
||||
|
||||
@@ -264,7 +264,7 @@ def key_to_bytes(key, smkx, extended, mods, action):
|
||||
return bytes(data)
|
||||
|
||||
|
||||
def interpret_key_event(key, scancode, mods, window, action):
|
||||
def interpret_key_event(key, native_key, mods, window, action):
|
||||
screen = window.screen
|
||||
if (
|
||||
action == defines.GLFW_PRESS or
|
||||
@@ -275,17 +275,17 @@ def interpret_key_event(key, scancode, mods, window, action):
|
||||
return b''
|
||||
|
||||
|
||||
def get_shortcut(keymap, mods, key, scancode):
|
||||
def get_shortcut(keymap, mods, key, native_key):
|
||||
mods &= 0b1111
|
||||
ans = keymap.get((mods, False, key))
|
||||
if ans is None:
|
||||
ans = keymap.get((mods, True, scancode))
|
||||
ans = keymap.get((mods, True, native_key))
|
||||
return ans
|
||||
|
||||
|
||||
def shortcut_matches(s, mods, key, scancode):
|
||||
def shortcut_matches(s, mods, key, native_key):
|
||||
mods &= 0b1111
|
||||
q = scancode if s[1] else key
|
||||
q = native_key if s[1] else key
|
||||
return s[0] & 0b1111 == mods & 0b1111 and s[2] == q
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "data-types.h"
|
||||
#include "monotonic.h"
|
||||
|
||||
#define CMD_BUF_SZ 2048
|
||||
|
||||
@@ -34,13 +35,13 @@ add_char(char buf[CMD_BUF_SZ], size_t *pos, char ch, PyObject *ans) {
|
||||
}
|
||||
|
||||
static inline bool
|
||||
read_response(int fd, double timeout, PyObject *ans) {
|
||||
read_response(int fd, monotonic_t timeout, PyObject *ans) {
|
||||
static char buf[CMD_BUF_SZ];
|
||||
size_t pos = 0;
|
||||
enum ReadState {START, STARTING_ESC, P, AT, K, I, T, T2, Y, HYPHEN, C, M, BODY, TRAILING_ESC};
|
||||
enum ReadState state = START;
|
||||
char ch;
|
||||
double end_time = monotonic() + timeout;
|
||||
monotonic_t end_time = monotonic() + timeout;
|
||||
while(monotonic() <= end_time) {
|
||||
ssize_t len = read(fd, &ch, 1);
|
||||
if (len == 0) continue;
|
||||
@@ -94,7 +95,7 @@ read_command_response(PyObject *self UNUSED, PyObject *args) {
|
||||
int fd;
|
||||
PyObject *ans;
|
||||
if (!PyArg_ParseTuple(args, "idO!", &fd, &timeout, &PyList_Type, &ans)) return NULL;
|
||||
if (!read_response(fd, timeout, ans)) return NULL;
|
||||
if (!read_response(fd, s_double_to_monotonic_t(timeout), ans)) return NULL;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
216
kitty/launch.py
Normal file
216
kitty/launch.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.fast_data_types import set_clipboard_string
|
||||
from kitty.utils import set_primary_selection
|
||||
|
||||
|
||||
def options_spec():
|
||||
if not hasattr(options_spec, 'ans'):
|
||||
OPTIONS = '''
|
||||
--window-title --title
|
||||
The title to set for the new window. By default, title is controlled by the
|
||||
child process.
|
||||
|
||||
|
||||
--tab-title
|
||||
The title for the new tab if launching in a new tab. By default, the title
|
||||
of the actie window in the tab is used as the tab title.
|
||||
|
||||
|
||||
--type
|
||||
type=choices
|
||||
default=window
|
||||
choices=window,tab,os-window,overlay,background,clipboard,primary
|
||||
Where to launch the child process, in a new kitty window in the current tab,
|
||||
a new tab, or a new OS window or an overlay over the current window.
|
||||
Note that if the current window already has an overlay, then it will
|
||||
open a new window. The value of none means the process will be
|
||||
run in the background. The values clipboard and primary are meant
|
||||
to work with :option:`launch --stdin-source` to copy data to the system
|
||||
clipboard or primary selection.
|
||||
|
||||
|
||||
--keep-focus
|
||||
type=bool-set
|
||||
Keep the focus on the currently active window instead of switching
|
||||
to the newly opened window.
|
||||
|
||||
|
||||
--cwd
|
||||
The working directory for the newly launched child. Use the special value
|
||||
:code:`current` to use the working directory of the currently active window.
|
||||
|
||||
|
||||
--env
|
||||
type=list
|
||||
Environment variables to set in the child process. Can be specified multiple
|
||||
times to set different environment variables.
|
||||
Syntax: :italic:`name=value`.
|
||||
|
||||
|
||||
--copy-colors
|
||||
type=bool-set
|
||||
Set the colors of the newly created window to be the same as the colors in the
|
||||
currently active window.
|
||||
|
||||
|
||||
--copy-cmdline
|
||||
type=bool-set
|
||||
Ignore any specified command line and instead use the command line from the
|
||||
currently active window.
|
||||
|
||||
|
||||
--copy-env
|
||||
type=bool-set
|
||||
Copy the environment variables from the currently active window into the
|
||||
newly launched child process.
|
||||
|
||||
|
||||
--location
|
||||
type=choices
|
||||
default=last
|
||||
choices=first,neighbor,last
|
||||
Where to place the newly created window when it is added to a tab which
|
||||
already has existing windows in it. Also applies to creating a new tab,
|
||||
where the value of neighbor will cause the new tab to be placed next to
|
||||
the current tab instead of at the end.
|
||||
|
||||
|
||||
--allow-remote-control
|
||||
type=bool-set
|
||||
Programs running in this window can control kitty (if remote control is
|
||||
enabled). Note that any program with the right level of permissions can 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.
|
||||
|
||||
|
||||
--stdin-source
|
||||
type=choices
|
||||
default=none
|
||||
choices=none,@selection,@screen,@screen_scrollback,@alternate,@alternate_scrollback
|
||||
Pass the screen contents as :code:`STDIN` to the child process. @selection is
|
||||
the currently selected text. @screen is the contents of the currently active
|
||||
window. @screen_scrollback is the same as @screen, but includes the scrollback
|
||||
buffer as well. @alternate is the secondary screen of the current active
|
||||
window. For example if you run a full screen terminal application, the
|
||||
secondary screen will be the screen you return to when quitting the
|
||||
application.
|
||||
|
||||
|
||||
--stdin-add-formatting
|
||||
type=bool-set
|
||||
When using :option:`launch --stdin-source` add formatting escape codes, without this
|
||||
only plain text will be sent.
|
||||
|
||||
|
||||
--stdin-add-line-wrap-markers
|
||||
type=bool-set
|
||||
When using :option:`launch --stdin-source` add a carriage return at every line wrap
|
||||
location (where long lines are wrapped at screen edges). This is useful if you
|
||||
want to pipe to program that wants to duplicate the screen layout of the
|
||||
screen.
|
||||
|
||||
|
||||
'''
|
||||
options_spec.ans = OPTIONS
|
||||
return options_spec.ans
|
||||
|
||||
|
||||
def parse_launch_args(args=None):
|
||||
args = list(args or ())
|
||||
try:
|
||||
opts, args = parse_args(args=args, ospec=options_spec)
|
||||
except SystemExit as e:
|
||||
raise ValueError from e
|
||||
return opts, args
|
||||
|
||||
|
||||
def get_env(opts, active_child):
|
||||
env = {}
|
||||
if opts.copy_env and active_child:
|
||||
env.update(active_child.foreground_environ)
|
||||
for x in opts.env:
|
||||
parts = x.split('=', 1)
|
||||
if len(parts) == 2:
|
||||
env[parts[0]] = parts[1]
|
||||
return env
|
||||
|
||||
|
||||
def tab_for_window(boss, opts, target_tab=None):
|
||||
if opts.type == 'tab':
|
||||
tm = boss.active_tab_manager
|
||||
tab = tm.new_tab(empty_tab=True, as_neighbor=opts.location == 'neighbor')
|
||||
if opts.tab_title:
|
||||
tab.set_title(opts.tab_title)
|
||||
elif opts.type == 'os-window':
|
||||
oswid = boss.add_os_window()
|
||||
tm = boss.os_window_map[oswid]
|
||||
tab = tm.new_tab(empty_tab=True)
|
||||
if opts.tab_title:
|
||||
tab.set_title(opts.tab_title)
|
||||
else:
|
||||
tab = target_tab or boss.active_tab
|
||||
|
||||
return tab
|
||||
|
||||
|
||||
def launch(boss, opts, args, target_tab=None):
|
||||
active = boss.active_window_for_cwd
|
||||
active_child = getattr(active, 'child', None)
|
||||
env = get_env(opts, active_child)
|
||||
kw = {
|
||||
'allow_remote_control': opts.allow_remote_control
|
||||
}
|
||||
if opts.cwd:
|
||||
if opts.cwd == 'current':
|
||||
if active_child:
|
||||
kw['cwd_from'] = active_child.pid_for_cwd
|
||||
else:
|
||||
kw['cwd'] = opts.cwd
|
||||
if opts.location != 'last':
|
||||
kw['location'] = opts.location
|
||||
if opts.window_title:
|
||||
kw['override_title'] = opts.window_title
|
||||
if opts.copy_colors and active:
|
||||
kw['copy_colors_from'] = active
|
||||
cmd = args or None
|
||||
if opts.copy_cmdline and active_child:
|
||||
cmd = active_child.foreground_cmdline
|
||||
if cmd:
|
||||
final_cmd = []
|
||||
for x in cmd:
|
||||
if x == '@selection' and active and not opts.copy_cmdline:
|
||||
s = boss.data_for_at(active, x)
|
||||
if s:
|
||||
x = s
|
||||
final_cmd.append(x)
|
||||
kw['cmd'] = final_cmd
|
||||
if opts.type == 'overlay' and active and not active.overlay_window_id:
|
||||
kw['overlay_for'] = active.id
|
||||
if opts.stdin_source and opts.stdin_source != 'none':
|
||||
q = opts.stdin_source
|
||||
if opts.stdin_add_line_wrap_markers:
|
||||
q += '_wrap'
|
||||
penv, stdin = boss.process_stdin_source(window=active, stdin=q)
|
||||
if stdin:
|
||||
kw['stdin'] = stdin
|
||||
if penv:
|
||||
env.update(penv)
|
||||
|
||||
if opts.type == 'background':
|
||||
boss.run_background_process(kw['cmd'], cwd=kw.get('cwd'), cwd_from=kw.get('cwd_from'), env=env or None, stdin=kw.get('stdin'))
|
||||
elif opts.type in ('clipboard', 'primary'):
|
||||
if 'stdin' in kw:
|
||||
func = set_clipboard_string if opts.type == 'clipboard' else set_primary_selection
|
||||
func(kw['stdin'])
|
||||
else:
|
||||
tab = tab_for_window(boss, opts, target_tab)
|
||||
new_window = tab.new_window(env=env or None, **kw)
|
||||
if opts.keep_focus and active:
|
||||
boss.set_active_window(active, switch_os_window_if_needed=True)
|
||||
return new_window
|
||||
@@ -279,8 +279,8 @@ class Layout: # {{{
|
||||
all_windows[i] = window
|
||||
active_window_idx = i
|
||||
elif location is not None:
|
||||
if location == 'neighbor' and current_active_window_idx is not None:
|
||||
active_window_idx = current_active_window_idx + 1
|
||||
if location == 'neighbor' and current_active_window_idx is not None and len(all_windows) > 1:
|
||||
active_window_idx = min(current_active_window_idx + 1, len(all_windows))
|
||||
elif location == 'first':
|
||||
active_window_idx = 0
|
||||
if active_window_idx is not None:
|
||||
|
||||
@@ -18,7 +18,7 @@ from .constants import (
|
||||
is_wayland, kitty_exe, logo_data_file
|
||||
)
|
||||
from .fast_data_types import (
|
||||
GLFW_IBEAM_CURSOR, GLFW_MOD_SUPER, create_os_window, free_font_data,
|
||||
GLFW_IBEAM_CURSOR, create_os_window, free_font_data,
|
||||
glfw_init, glfw_terminate, load_png_data, set_custom_cursor,
|
||||
set_default_window_icon, set_options
|
||||
)
|
||||
@@ -95,10 +95,6 @@ def init_glfw(opts, debug_keyboard=False):
|
||||
return glfw_module
|
||||
|
||||
|
||||
def prefer_cmd_shortcuts(x):
|
||||
return x[0] == GLFW_MOD_SUPER
|
||||
|
||||
|
||||
def get_new_os_window_trigger(opts):
|
||||
new_os_window_trigger = None
|
||||
if is_macos:
|
||||
@@ -108,8 +104,8 @@ def get_new_os_window_trigger(opts):
|
||||
new_os_window_shortcuts.append(k)
|
||||
if new_os_window_shortcuts:
|
||||
from .fast_data_types import cocoa_set_new_window_trigger
|
||||
new_os_window_shortcuts.sort(key=prefer_cmd_shortcuts, reverse=True)
|
||||
for candidate in new_os_window_shortcuts:
|
||||
# Reverse list so that later defined keyboard shortcuts take priority over earlier defined ones
|
||||
for candidate in reversed(new_os_window_shortcuts):
|
||||
if cocoa_set_new_window_trigger(candidate[0], candidate[2]):
|
||||
new_os_window_trigger = candidate
|
||||
break
|
||||
@@ -214,7 +210,9 @@ def read_shell_environment(opts=None):
|
||||
with os.fdopen(master, 'rb') as stdout, os.fdopen(slave, 'wb'):
|
||||
raw = b''
|
||||
from subprocess import TimeoutExpired
|
||||
while True:
|
||||
from time import monotonic
|
||||
start_time = monotonic()
|
||||
while monotonic() - start_time < 1.5:
|
||||
try:
|
||||
ret = p.wait(0.01)
|
||||
except TimeoutExpired:
|
||||
@@ -223,7 +221,10 @@ def read_shell_environment(opts=None):
|
||||
raw += stdout.read()
|
||||
if ret is not None:
|
||||
break
|
||||
if p.returncode == 0:
|
||||
if p.returncode is None:
|
||||
log_error('Timed out waiting for shell to quit while reading shell environment')
|
||||
p.kill()
|
||||
elif p.returncode == 0:
|
||||
while True:
|
||||
try:
|
||||
x = stdout.read()
|
||||
@@ -244,24 +245,32 @@ def read_shell_environment(opts=None):
|
||||
return read_shell_environment.ans
|
||||
|
||||
|
||||
def get_editor_from_env(shell_env):
|
||||
for var in ('VISUAL', 'EDITOR'):
|
||||
editor = shell_env.get(var)
|
||||
if editor:
|
||||
if 'PATH' in shell_env:
|
||||
import shlex
|
||||
editor_cmd = shlex.split(editor)
|
||||
if not os.path.isabs(editor_cmd[0]):
|
||||
editor_cmd[0] = shutil.which(editor_cmd[0], path=shell_env['PATH'])
|
||||
if editor_cmd[0]:
|
||||
editor = ' '.join(map(shlex.quote, editor_cmd))
|
||||
else:
|
||||
editor = None
|
||||
if editor:
|
||||
return editor
|
||||
|
||||
|
||||
def setup_environment(opts, args):
|
||||
extra_env = opts.env.copy()
|
||||
if opts.editor == '.':
|
||||
if 'EDITOR' not in os.environ:
|
||||
editor = get_editor_from_env(os.environ)
|
||||
if not editor:
|
||||
shell_env = read_shell_environment(opts)
|
||||
if 'EDITOR' in shell_env:
|
||||
editor = shell_env['EDITOR']
|
||||
if 'PATH' in shell_env:
|
||||
import shlex
|
||||
editor_cmd = shlex.split(editor)
|
||||
if not os.path.isabs(editor_cmd[0]):
|
||||
editor_cmd[0] = shutil.which(editor_cmd[0], path=shell_env['PATH'])
|
||||
if editor_cmd[0]:
|
||||
editor = ' '.join(map(shlex.quote, editor_cmd))
|
||||
else:
|
||||
editor = None
|
||||
if editor:
|
||||
os.environ['EDITOR'] = editor
|
||||
editor = get_editor_from_env(shell_env)
|
||||
if editor:
|
||||
os.environ['EDITOR'] = editor
|
||||
else:
|
||||
os.environ['EDITOR'] = opts.editor
|
||||
if args.listen_on:
|
||||
|
||||
10
kitty/monotonic.c
Normal file
10
kitty/monotonic.c
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* monotonic.c
|
||||
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#define MONOTONIC_IMPLEMENTATION
|
||||
#include "monotonic.h"
|
||||
84
kitty/monotonic.h
Normal file
84
kitty/monotonic.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* monotonic.h
|
||||
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MONOTONIC_T_MAX INT64_MAX
|
||||
#define MONOTONIC_T_MIN INT64_MIN
|
||||
#define MONOTONIC_T_1e6 1000000ll
|
||||
#define MONOTONIC_T_1e9 1000000000ll
|
||||
|
||||
typedef int64_t monotonic_t;
|
||||
|
||||
static inline monotonic_t
|
||||
s_double_to_monotonic_t(double time) {
|
||||
return (monotonic_t)(time * 1e9);
|
||||
}
|
||||
|
||||
static inline monotonic_t
|
||||
ms_double_to_monotonic_t(double time) {
|
||||
return (monotonic_t)(time * 1e6);
|
||||
}
|
||||
|
||||
static inline monotonic_t
|
||||
s_to_monotonic_t(monotonic_t time) {
|
||||
return time * MONOTONIC_T_1e9;
|
||||
}
|
||||
|
||||
static inline monotonic_t
|
||||
ms_to_monotonic_t(monotonic_t time) {
|
||||
return time * MONOTONIC_T_1e6;
|
||||
}
|
||||
|
||||
static inline int
|
||||
monotonic_t_to_ms(monotonic_t time) {
|
||||
return (int)(time / MONOTONIC_T_1e6);
|
||||
}
|
||||
|
||||
static inline double
|
||||
monotonic_t_to_s_double(monotonic_t time) {
|
||||
return ((double)time) / 1e9;
|
||||
}
|
||||
|
||||
extern monotonic_t monotonic_start_time;
|
||||
extern monotonic_t monotonic_(void);
|
||||
|
||||
static inline monotonic_t
|
||||
monotonic(void) {
|
||||
return monotonic_() - monotonic_start_time;
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_monotonic(void) {
|
||||
monotonic_start_time = monotonic_();
|
||||
}
|
||||
|
||||
#ifdef MONOTONIC_IMPLEMENTATION
|
||||
#include <time.h>
|
||||
monotonic_t monotonic_start_time = 0;
|
||||
|
||||
static inline monotonic_t
|
||||
calc_nano_time(struct timespec time) {
|
||||
return ((monotonic_t)time.tv_sec * MONOTONIC_T_1e9) + (monotonic_t)time.tv_nsec;
|
||||
}
|
||||
|
||||
monotonic_t
|
||||
monotonic_(void) {
|
||||
struct timespec ts = {0};
|
||||
#ifdef CLOCK_HIGHRES
|
||||
clock_gettime(CLOCK_HIGHRES, &ts);
|
||||
#elif CLOCK_MONOTONIC_RAW
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
#else
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
#endif
|
||||
return calc_nano_time(ts);
|
||||
}
|
||||
#endif
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <math.h>
|
||||
#include "glfw-wrapper.h"
|
||||
#include "control-codes.h"
|
||||
#include "monotonic.h"
|
||||
|
||||
static MouseShape mouse_cursor_shape = BEAM;
|
||||
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
||||
@@ -292,8 +293,8 @@ HANDLER(handle_move_event) {
|
||||
bool handle_in_kitty = !in_tracking_mode || has_terminal_select_modifiers;
|
||||
if (handle_in_kitty) {
|
||||
if (screen->selection.in_progress && button == GLFW_MOUSE_BUTTON_LEFT) {
|
||||
double now = monotonic();
|
||||
if ((now - w->last_drag_scroll_at) >= 0.02 || mouse_cell_changed) {
|
||||
monotonic_t now = monotonic();
|
||||
if ((now - w->last_drag_scroll_at) >= ms_to_monotonic_t(20ll) || mouse_cell_changed) {
|
||||
update_drag(false, w, false, 0);
|
||||
w->last_drag_scroll_at = now;
|
||||
}
|
||||
@@ -338,7 +339,7 @@ distance(double x1, double y1, double x2, double y2) {
|
||||
HANDLER(add_click) {
|
||||
ClickQueue *q = &w->click_queue;
|
||||
if (q->length == CLICK_QUEUE_SZ) { memmove(q->clicks, q->clicks + 1, sizeof(Click) * (CLICK_QUEUE_SZ - 1)); q->length--; }
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
#define N(n) (q->clicks[q->length - n])
|
||||
N(0).at = now; N(0).button = button; N(0).modifiers = modifiers; N(0).x = w->mouse_pos.x; N(0).y = w->mouse_pos.y;
|
||||
q->length++;
|
||||
@@ -608,16 +609,16 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
|
||||
|
||||
if (is_high_resolution) {
|
||||
yoffset *= OPT(touch_scroll_multiplier);
|
||||
if (yoffset * global_state.callback_os_window->pending_scroll_pixels < 0) {
|
||||
global_state.callback_os_window->pending_scroll_pixels = 0; // change of direction
|
||||
if (yoffset * screen->pending_scroll_pixels < 0) {
|
||||
screen->pending_scroll_pixels = 0; // change of direction
|
||||
}
|
||||
double pixels = global_state.callback_os_window->pending_scroll_pixels + yoffset;
|
||||
double pixels = screen->pending_scroll_pixels + yoffset;
|
||||
if (fabs(pixels) < global_state.callback_os_window->fonts_data->cell_height) {
|
||||
global_state.callback_os_window->pending_scroll_pixels = pixels;
|
||||
screen->pending_scroll_pixels = pixels;
|
||||
return;
|
||||
}
|
||||
s = (int)round(pixels) / (int)global_state.callback_os_window->fonts_data->cell_height;
|
||||
global_state.callback_os_window->pending_scroll_pixels = pixels - s * (int) global_state.callback_os_window->fonts_data->cell_height;
|
||||
screen->pending_scroll_pixels = pixels - s * (int) global_state.callback_os_window->fonts_data->cell_height;
|
||||
} else {
|
||||
if (screen->linebuf == screen->main_linebuf || !screen->modes.mouse_tracking_mode) {
|
||||
// Only use wheel_scroll_multiplier if we are scrolling kitty scrollback or in mouse
|
||||
@@ -632,7 +633,7 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
|
||||
// apparently on cocoa some mice generate really small yoffset values
|
||||
// when scrolling slowly https://github.com/kovidgoyal/kitty/issues/1238
|
||||
if (s == 0 && yoffset != 0) s = yoffset > 0 ? 1 : -1;
|
||||
global_state.callback_os_window->pending_scroll_pixels = 0;
|
||||
screen->pending_scroll_pixels = 0;
|
||||
}
|
||||
if (s == 0) return;
|
||||
bool upwards = s > 0;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "screen.h"
|
||||
#include "graphics.h"
|
||||
#include "charsets.h"
|
||||
#include "monotonic.h"
|
||||
#include <time.h>
|
||||
|
||||
extern PyTypeObject Screen_Type;
|
||||
@@ -1196,7 +1197,7 @@ end:
|
||||
}
|
||||
|
||||
static inline void
|
||||
do_parse_bytes(Screen *screen, const uint8_t *read_buf, const size_t read_buf_sz, double now, PyObject *dump_callback DUMP_UNUSED) {
|
||||
do_parse_bytes(Screen *screen, const uint8_t *read_buf, const size_t read_buf_sz, monotonic_t now, PyObject *dump_callback DUMP_UNUSED) {
|
||||
enum STATE {START, PARSE_PENDING, PARSE_READ_BUF, QUEUE_PENDING};
|
||||
enum STATE state = START;
|
||||
size_t read_buf_pos = 0;
|
||||
@@ -1272,7 +1273,7 @@ FNAME(parse_bytes)(PyObject UNUSED *self, PyObject *args) {
|
||||
|
||||
|
||||
void
|
||||
FNAME(parse_worker)(Screen *screen, PyObject *dump_callback, double now) {
|
||||
FNAME(parse_worker)(Screen *screen, PyObject *dump_callback, monotonic_t now) {
|
||||
#ifdef DUMP_COMMANDS
|
||||
if (screen->read_buf_sz) {
|
||||
Py_XDECREF(PyObject_CallFunction(dump_callback, "sy#", "bytes", screen->read_buf, screen->read_buf_sz)); PyErr_Clear();
|
||||
|
||||
@@ -1538,7 +1538,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
|
||||
|
||||
static inline bool
|
||||
is_selection_empty(Screen *self, unsigned int start_x, unsigned int start_y, unsigned int end_x, unsigned int end_y) {
|
||||
return (start_x >= self->columns || start_y >= self->lines || end_x >= self->columns || end_y >= self->lines || (start_x == end_x && start_y == end_y)) ? true : false;
|
||||
return start_x >= self->columns || start_y >= self->lines || end_x >= self->columns || end_y >= self->lines || (start_x == end_x && start_y == end_y);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -1730,7 +1730,7 @@ static PyObject*
|
||||
set_pending_timeout(Screen *self, PyObject *val) {
|
||||
if (!PyFloat_Check(val)) { PyErr_SetString(PyExc_TypeError, "timeout must be a float"); return NULL; }
|
||||
PyObject *ans = PyFloat_FromDouble(self->pending_mode.wait_time);
|
||||
self->pending_mode.wait_time = PyFloat_AS_DOUBLE(val);
|
||||
self->pending_mode.wait_time = s_double_to_monotonic_t(PyFloat_AS_DOUBLE(val));
|
||||
return ans;
|
||||
}
|
||||
|
||||
@@ -2267,6 +2267,13 @@ wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {
|
||||
return PyLong_FromLong(wcwidth_std(PyLong_AsLong(chr)));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
screen_is_emoji_presentation_base(PyObject UNUSED *self, PyObject *code_) {
|
||||
unsigned long code = PyLong_AsUnsignedLong(code_);
|
||||
if (is_emoji_presentation_base(code)) Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
|
||||
#define MND(name, args) {#name, (PyCFunction)name, args, #name},
|
||||
#define MODEFUNC(name) MND(name, METH_NOARGS) MND(set_##name, METH_O)
|
||||
@@ -2380,6 +2387,7 @@ PyTypeObject Screen_Type = {
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
||||
{"wcswidth", (PyCFunction)screen_wcswidth, METH_O, ""},
|
||||
{"is_emoji_presentation_base", (PyCFunction)screen_is_emoji_presentation_base, METH_O, ""},
|
||||
{"truncate_point_for_length", (PyCFunction)screen_truncate_point_for_length, METH_VARARGS, ""},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user