Compare commits
331 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b6240a067 | ||
|
|
f5c713b118 | ||
|
|
ac7596b7c5 | ||
|
|
f69ed984a4 | ||
|
|
83850b28ae | ||
|
|
5efa26a98d | ||
|
|
51d2c01bc1 | ||
|
|
be9d876997 | ||
|
|
866b53f384 | ||
|
|
de1a2f7682 | ||
|
|
86686bfac7 | ||
|
|
3f316c39d1 | ||
|
|
14459a7d18 | ||
|
|
c062b84bc6 | ||
|
|
890a68fa53 | ||
|
|
59017a6e6f | ||
|
|
06cb2cafe6 | ||
|
|
e4ed170748 | ||
|
|
f011a6c86c | ||
|
|
45fc1ace0c | ||
|
|
8d51edec17 | ||
|
|
f2afba2ef4 | ||
|
|
efcd3a5df7 | ||
|
|
2a52acdef4 | ||
|
|
eadfeeec9b | ||
|
|
f8a581129f | ||
|
|
93ff651d9e | ||
|
|
bec0f02b11 | ||
|
|
0721951c79 | ||
|
|
b37d07d31a | ||
|
|
9cacfc0c26 | ||
|
|
0b93b85cf2 | ||
|
|
f7001ea068 | ||
|
|
aa93c3fb66 | ||
|
|
166c7bf0e6 | ||
|
|
bb1374f64e | ||
|
|
053835afbe | ||
|
|
4760580d1c | ||
|
|
8fa492e0ee | ||
|
|
040ae75c77 | ||
|
|
759aaf294a | ||
|
|
9ea0de868c | ||
|
|
78002cf3ba | ||
|
|
f68f8bf9c9 | ||
|
|
54438cae3f | ||
|
|
2e693c31df | ||
|
|
0f06bf1bdb | ||
|
|
b7f4f2e898 | ||
|
|
7148ee8a0f | ||
|
|
238508fc0a | ||
|
|
84ab0f5062 | ||
|
|
cf04bdb36a | ||
|
|
d09a30d16c | ||
|
|
d54b58d2f3 | ||
|
|
0be0963dc7 | ||
|
|
61a2360df5 | ||
|
|
b3ed4e3bc2 | ||
|
|
316e7cb9f5 | ||
|
|
23851e31bf | ||
|
|
b7b64ec513 | ||
|
|
2b372373ae | ||
|
|
7a543fac86 | ||
|
|
5c4b14468c | ||
|
|
7d4d978d97 | ||
|
|
fe5b8f3aec | ||
|
|
f5abe7cbf7 | ||
|
|
bd0382830a | ||
|
|
0b99bb534f | ||
|
|
9d67198ff9 | ||
|
|
fda57a81e1 | ||
|
|
9962ac2703 | ||
|
|
edc4b91d15 | ||
|
|
92ca8ab9cc | ||
|
|
07e2d9175c | ||
|
|
c90dbb25b9 | ||
|
|
b5da109e7f | ||
|
|
aa18429a8c | ||
|
|
e578d41adb | ||
|
|
f3579e78af | ||
|
|
9c43707f40 | ||
|
|
99ff92a5a7 | ||
|
|
d866c0d202 | ||
|
|
20c42ab519 | ||
|
|
8bbc2b82d4 | ||
|
|
6929358976 | ||
|
|
82acd6b511 | ||
|
|
bb939ed4f8 | ||
|
|
cd25abc49f | ||
|
|
2e90e443ed | ||
|
|
92bb6e293a | ||
|
|
ea9b431386 | ||
|
|
23077fd6a6 | ||
|
|
5eee7ce338 | ||
|
|
784894cac3 | ||
|
|
757983b729 | ||
|
|
b6660353cd | ||
|
|
215a7689d9 | ||
|
|
dd18a1dfd8 | ||
|
|
32f16ee5f5 | ||
|
|
d4f426d2ce | ||
|
|
a6d96c9a14 | ||
|
|
bf3bfeaf69 | ||
|
|
6ce82177ea | ||
|
|
dc6efb5b37 | ||
|
|
b26d1f306d | ||
|
|
cf678d8179 | ||
|
|
8cecb22481 | ||
|
|
2504266806 | ||
|
|
e3795eb07e | ||
|
|
796ead5d75 | ||
|
|
d5907a1bf9 | ||
|
|
ddd4774169 | ||
|
|
1dc83a47cf | ||
|
|
0a8dc4be0a | ||
|
|
3d99361790 | ||
|
|
1e278938cc | ||
|
|
ba177c61bc | ||
|
|
458f10be10 | ||
|
|
c1667d9fc1 | ||
|
|
52f4e81d59 | ||
|
|
1056ab1b81 | ||
|
|
3f9d04b616 | ||
|
|
d9cc065c99 | ||
|
|
612fb3b596 | ||
|
|
532d51c411 | ||
|
|
5630c0e434 | ||
|
|
5ba8ca2745 | ||
|
|
e9ffb8d185 | ||
|
|
339a2e0800 | ||
|
|
98c04fb7da | ||
|
|
c95d1393cc | ||
|
|
1779cdd353 | ||
|
|
6a090ac740 | ||
|
|
3c7236557c | ||
|
|
83a3d99824 | ||
|
|
808750a76a | ||
|
|
32caea4197 | ||
|
|
038248f55d | ||
|
|
21b7bf6009 | ||
|
|
9dc571d8a9 | ||
|
|
805baad9cd | ||
|
|
cc910f6b79 | ||
|
|
56dd442e2c | ||
|
|
3192b61385 | ||
|
|
eef3a215a1 | ||
|
|
cb7fff6986 | ||
|
|
75b5130d96 | ||
|
|
2cf8c6aea7 | ||
|
|
5755ba72b1 | ||
|
|
785937ecfc | ||
|
|
955ead05be | ||
|
|
3830261384 | ||
|
|
662ab0df45 | ||
|
|
1ce50fb6f0 | ||
|
|
74dcf8800d | ||
|
|
e90df0ca95 | ||
|
|
49c375ae0f | ||
|
|
6c4d1a4951 | ||
|
|
f99e368831 | ||
|
|
8468ad60d5 | ||
|
|
a1ce29aab9 | ||
|
|
20b5084904 | ||
|
|
2fde0ea86b | ||
|
|
b64bceac7f | ||
|
|
a97174a350 | ||
|
|
6141c9fdda | ||
|
|
a32e010f89 | ||
|
|
4847fb52cc | ||
|
|
58e33e2512 | ||
|
|
584b59c7a1 | ||
|
|
2e657f0020 | ||
|
|
ae112294c8 | ||
|
|
8b4c7ea23f | ||
|
|
83e225a50f | ||
|
|
6256b9027a | ||
|
|
1fd84612a8 | ||
|
|
2efa83bc4d | ||
|
|
fa064bf9cf | ||
|
|
325d2bc14e | ||
|
|
90a4b4f859 | ||
|
|
ce0db16479 | ||
|
|
43ce3ce4b0 | ||
|
|
910cebeeb5 | ||
|
|
1c78633d1a | ||
|
|
530fd61125 | ||
|
|
9edfafcac2 | ||
|
|
677c47b9dd | ||
|
|
b59d7dda11 | ||
|
|
7a3534baf8 | ||
|
|
3bfcadcd81 | ||
|
|
529bb5c6a0 | ||
|
|
caf9a12b34 | ||
|
|
da8e2d3d4a | ||
|
|
582500d3e9 | ||
|
|
7926a140b8 | ||
|
|
023487a1d7 | ||
|
|
71f84b1023 | ||
|
|
ce85382c68 | ||
|
|
94d248d812 | ||
|
|
62db7639a8 | ||
|
|
80f3fe6e9f | ||
|
|
9ba2c4b397 | ||
|
|
bfa53d4b45 | ||
|
|
69f3ceb903 | ||
|
|
d0f839bbd8 | ||
|
|
48538c0e25 | ||
|
|
02ab22f08c | ||
|
|
b3b216a6dc | ||
|
|
431564c8bc | ||
|
|
57f1bbb71a | ||
|
|
faac8649e4 | ||
|
|
1e6a41186f | ||
|
|
eddee67130 | ||
|
|
9b834014e9 | ||
|
|
a467e79144 | ||
|
|
90cc785b55 | ||
|
|
09710425ab | ||
|
|
dd36727adf | ||
|
|
47b665a624 | ||
|
|
7011329761 | ||
|
|
ea24c23fde | ||
|
|
be8f2b8106 | ||
|
|
71db024a3b | ||
|
|
3790af6897 | ||
|
|
dc6ef63ddd | ||
|
|
ef762f7a2b | ||
|
|
4ed646e062 | ||
|
|
afbd843388 | ||
|
|
18c3598ef2 | ||
|
|
1c2623e609 | ||
|
|
e232863028 | ||
|
|
a89309153d | ||
|
|
fee7c8e573 | ||
|
|
1a52127c55 | ||
|
|
4920431c3a | ||
|
|
773f26b61d | ||
|
|
16e77d7329 | ||
|
|
2cec0908a5 | ||
|
|
b3ef62c188 | ||
|
|
3385f6fe67 | ||
|
|
0170bf636b | ||
|
|
b2561fd61e | ||
|
|
64b48945d4 | ||
|
|
bbfd463357 | ||
|
|
2179dab8ed | ||
|
|
801ead7f7f | ||
|
|
9e4834d58b | ||
|
|
f38e089de0 | ||
|
|
b252504306 | ||
|
|
81bcdaf48a | ||
|
|
44f654ff86 | ||
|
|
84cfa9382f | ||
|
|
c6da374aa9 | ||
|
|
d9fe0b2540 | ||
|
|
5a905dfe7f | ||
|
|
09b6d6e62b | ||
|
|
6aa26cf75f | ||
|
|
bae8915881 | ||
|
|
9c46de319e | ||
|
|
994eecadb7 | ||
|
|
f7f9ceec34 | ||
|
|
527255e3a1 | ||
|
|
0333da991d | ||
|
|
2bf8e1b1a3 | ||
|
|
ffc717717c | ||
|
|
2c8414db5d | ||
|
|
32473d7dc7 | ||
|
|
079f10eebd | ||
|
|
ae1f305a81 | ||
|
|
0b1f5209d5 | ||
|
|
fc9980903d | ||
|
|
ad87131926 | ||
|
|
4dfe1b664f | ||
|
|
d7b2ac97b5 | ||
|
|
fd9dac4ca4 | ||
|
|
8ca9348b07 | ||
|
|
5cc18fdb4b | ||
|
|
51b6b325b9 | ||
|
|
b3cc6f3802 | ||
|
|
1295094715 | ||
|
|
af85f58e3d | ||
|
|
3b34821c86 | ||
|
|
eaea0b16b5 | ||
|
|
964ffadc8e | ||
|
|
1b84064e5f | ||
|
|
dd128a99ac | ||
|
|
bc829a9712 | ||
|
|
3459c55ebf | ||
|
|
a631d950e1 | ||
|
|
f35e2d76ad | ||
|
|
8db838da9f | ||
|
|
7254a880dc | ||
|
|
a0df5b970b | ||
|
|
54ed08c24e | ||
|
|
97e1ecabdc | ||
|
|
e716f6d35e | ||
|
|
c7506496da | ||
|
|
1e3d3df9b8 | ||
|
|
ed75ad0d41 | ||
|
|
fd6159c0b4 | ||
|
|
976c322b83 | ||
|
|
28c1935800 | ||
|
|
1edf1524f8 | ||
|
|
0dfe101c0f | ||
|
|
a571821f73 | ||
|
|
f8e6f87984 | ||
|
|
c7338bca6f | ||
|
|
15f07f57bf | ||
|
|
ec989a45b5 | ||
|
|
e8286c50b2 | ||
|
|
bfbbe3db14 | ||
|
|
6214a87522 | ||
|
|
743ff719c8 | ||
|
|
b491eacb9c | ||
|
|
5bfc89e13f | ||
|
|
52d2b7b09e | ||
|
|
19e59d9575 | ||
|
|
6fe4baf1de | ||
|
|
bc21ec7551 | ||
|
|
aad4a1fd6a | ||
|
|
721595ef04 | ||
|
|
139838c717 | ||
|
|
c93238d8c9 | ||
|
|
5ee1fbe07d | ||
|
|
ee1287211d | ||
|
|
94e8be6b1c | ||
|
|
053ba7f082 | ||
|
|
e8f3f00792 | ||
|
|
740a89b77f | ||
|
|
1fe936dbbc | ||
|
|
11e85ac703 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
|||||||
tags
|
tags
|
||||||
build
|
build
|
||||||
linux-package
|
linux-package
|
||||||
|
kitty.app
|
||||||
logo/*.iconset
|
logo/*.iconset
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
glad/out
|
glad/out
|
||||||
|
|||||||
16
.travis.yml
16
.travis.yml
@@ -6,7 +6,7 @@ matrix:
|
|||||||
group: beta
|
group: beta
|
||||||
sudo: false
|
sudo: false
|
||||||
env:
|
env:
|
||||||
- CC=gcc SANITIZE_ARG=--sanitize
|
- CC=gcc CFLAGS=-funsigned-char SANITIZE_ARG=--sanitize
|
||||||
language: python
|
language: python
|
||||||
python: "3.5"
|
python: "3.5"
|
||||||
addons:
|
addons:
|
||||||
@@ -20,8 +20,6 @@ matrix:
|
|||||||
- libxcursor-dev
|
- libxcursor-dev
|
||||||
- libunistring-dev
|
- libunistring-dev
|
||||||
- libxcb-xkb-dev
|
- libxcb-xkb-dev
|
||||||
- libxkbcommon-dev
|
|
||||||
- libxkbcommon-x11-dev
|
|
||||||
- libpng-dev
|
- libpng-dev
|
||||||
- python3-pil
|
- python3-pil
|
||||||
|
|
||||||
@@ -44,8 +42,6 @@ matrix:
|
|||||||
- libxcursor-dev
|
- libxcursor-dev
|
||||||
- libunistring-dev
|
- libunistring-dev
|
||||||
- libxcb-xkb-dev
|
- libxcb-xkb-dev
|
||||||
- libxkbcommon-dev
|
|
||||||
- libxkbcommon-x11-dev
|
|
||||||
- libpng-dev
|
- libpng-dev
|
||||||
- python3-pil
|
- python3-pil
|
||||||
|
|
||||||
@@ -68,8 +64,6 @@ matrix:
|
|||||||
- libxcursor-dev
|
- libxcursor-dev
|
||||||
- libunistring-dev
|
- libunistring-dev
|
||||||
- libxcb-xkb-dev
|
- libxcb-xkb-dev
|
||||||
- libxkbcommon-dev
|
|
||||||
- libxkbcommon-x11-dev
|
|
||||||
- libpng-dev
|
- libpng-dev
|
||||||
- python3-pil
|
- python3-pil
|
||||||
- clang
|
- clang
|
||||||
@@ -94,7 +88,7 @@ install: |
|
|||||||
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
|
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
|
||||||
if [[ "$USE_BREW" == "1" ]]; then
|
if [[ "$USE_BREW" == "1" ]]; then
|
||||||
brew update;
|
brew update;
|
||||||
brew install python3;
|
brew upgrade python;
|
||||||
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
|
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
|
||||||
else
|
else
|
||||||
mkdir -p $SW;
|
mkdir -p $SW;
|
||||||
@@ -102,6 +96,12 @@ install: |
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
pushd /tmp
|
pushd /tmp
|
||||||
|
wget --no-check-certificate http://xkbcommon.org/download/libxkbcommon-0.6.1.tar.xz
|
||||||
|
tar xf libxkbcommon-0.6.1.tar.xz
|
||||||
|
cd libxkbcommon-0.6.1
|
||||||
|
./configure --prefix=$HOME/harfbuzz --disable-dependency-tracking --disable-static
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
wget https://github.com/behdad/harfbuzz/releases/download/1.6.3/harfbuzz-1.6.3.tar.bz2
|
wget https://github.com/behdad/harfbuzz/releases/download/1.6.3/harfbuzz-1.6.3.tar.bz2
|
||||||
tar xf harfbuzz-1.6.3.tar.bz2
|
tar xf harfbuzz-1.6.3.tar.bz2
|
||||||
cd harfbuzz-1.6.3
|
cd harfbuzz-1.6.3
|
||||||
|
|||||||
201
CHANGELOG.rst
201
CHANGELOG.rst
@@ -3,6 +3,207 @@ Changelog
|
|||||||
|
|
||||||
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||||
|
|
||||||
|
version 0.9.1 [2018-05-05]
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
- Show a bell symbol on the tab if a bell occurs in one of the windows in the tab and
|
||||||
|
the window is not the currently focused window
|
||||||
|
|
||||||
|
- Change the window border color if a bell occurs in an unfocused window. Can
|
||||||
|
be disabled by setting the bell_border_color to be the same as the
|
||||||
|
inactive_border_color.
|
||||||
|
|
||||||
|
- macOS: Add support for dead keys
|
||||||
|
|
||||||
|
- Unicode input: When searching by name search for prefix matches as well as
|
||||||
|
whole word matches
|
||||||
|
|
||||||
|
- Dynamically allocate the memory used for the scrollback history buffer.
|
||||||
|
Reduces startup memory consumption when using very large scrollback
|
||||||
|
buffer sizes.
|
||||||
|
|
||||||
|
- Add an option to not request window attention on bell.
|
||||||
|
|
||||||
|
- Remote control: Allow matching windows by number (visible position).
|
||||||
|
|
||||||
|
- macOS: Fix changing tab title and kitty shell not working
|
||||||
|
|
||||||
|
- When triple-clicking select all wrapped lines belonging to a single logical line.
|
||||||
|
|
||||||
|
- hints kitten: Detect bracketed URLs and dont include the closing bracket in the URL.
|
||||||
|
|
||||||
|
- When calling pass_selection_to_program use the current directory of the child
|
||||||
|
process as the cwd of the program.
|
||||||
|
|
||||||
|
- Add macos_hide_from_tasks option to hide kitty from the macOS task switcher
|
||||||
|
|
||||||
|
- macOS: When the macos_titlebar_color is set to background change the titlebar
|
||||||
|
colors to match the current background color of the active kitty window
|
||||||
|
|
||||||
|
- Add a setting to clear all shortcuts defined up to that point in the config
|
||||||
|
file(s)
|
||||||
|
|
||||||
|
- Add a setting (kitty_mod) to change the modifier used by all the default
|
||||||
|
kitty shortcuts, globally
|
||||||
|
|
||||||
|
- Fix Shift+function key not working
|
||||||
|
|
||||||
|
- Support the F13 to F25 function keys
|
||||||
|
|
||||||
|
- Don't fail to start if the user deletes the hintstyle key from their
|
||||||
|
fontconfig configuration.
|
||||||
|
|
||||||
|
- When rendering a private use unicode codepoint and a space as a two cell
|
||||||
|
ligature, set the foreground colors of the space cell to match the colors of
|
||||||
|
the first cell. Works around applications like powerline that use different
|
||||||
|
colors for the two cells.
|
||||||
|
|
||||||
|
- Fix passing @text to other programs such as when viewing the scrollback
|
||||||
|
buffer not working correctly if kitty is itself scrolled up.
|
||||||
|
|
||||||
|
- Fix window focus gained/lost events not being reported to child programs when
|
||||||
|
switching windows/tabs using the various keyboard shortcuts.
|
||||||
|
|
||||||
|
- Fix tab title not changing to reflect the window title when switching between different windows in a tab
|
||||||
|
|
||||||
|
- Ignore -e if it is specified on the command line. This is for compatibility
|
||||||
|
with broken software that assumes terminals should run with an -e option to
|
||||||
|
execute commands instead of just passing the commands as arguments.
|
||||||
|
|
||||||
|
|
||||||
|
version 0.9.0 [2018-04-15]
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
- A new kitty command shell to allow controlling kitty via commands. Press
|
||||||
|
`ctrl+shift+escape` to run the shell.
|
||||||
|
|
||||||
|
- The hints kitten has become much more powerful. Now in addition to URLs you
|
||||||
|
can use it to select word, paths, filenames, lines, etc. from the screen.
|
||||||
|
These can be inserted into the terminal, copied to clipboard or sent to
|
||||||
|
external programs.
|
||||||
|
|
||||||
|
- Linux: Switch to libxkbcommon for keyboard handling. It allows kitty to
|
||||||
|
support XCompose and dead keys and also react to keyboard remapping/layout
|
||||||
|
change without needing a restart.
|
||||||
|
|
||||||
|
- Add support for multiple-key-sequence shortcuts
|
||||||
|
|
||||||
|
- A new remote control command `set-colors` to change the current and/or
|
||||||
|
configured colors.
|
||||||
|
|
||||||
|
- When double-clicking to select a word, select words that continue onto the
|
||||||
|
next/prev line as well.
|
||||||
|
|
||||||
|
- Add an `include` directive for the config files to read multiple config files
|
||||||
|
|
||||||
|
- Improve mouse selection for windows with padding. Moving the mouse into the
|
||||||
|
padding area now acts as if the mouse is over the nearest cell.
|
||||||
|
|
||||||
|
- Allow setting all 256 terminal colors in the config file
|
||||||
|
|
||||||
|
- Fix using `kitty --single-instance` to open a new window in a running kitty
|
||||||
|
instance, not respecting the `--directory` flag
|
||||||
|
|
||||||
|
- URL hints: Exclude trailing punctuation from URLs
|
||||||
|
|
||||||
|
- URL hints: Launch the browser from the kitty parent process rather than the
|
||||||
|
hints kitten. Fixes launching on some systems where xdg-open doesn't like
|
||||||
|
being run from a kitten.
|
||||||
|
|
||||||
|
- Allow using rectangle select mode by pressing shift in addition to the
|
||||||
|
rectangle select modifiers even when the terminal program has grabbed the
|
||||||
|
mouse.
|
||||||
|
|
||||||
|
|
||||||
|
version 0.8.4 [2018-03-31]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Fix presence of XDG_CONFIG_DIRS and absence of XDG_CONFIG_HOME preventing
|
||||||
|
kitty from starting
|
||||||
|
|
||||||
|
- Revert change in last release to cell width calculation. Instead just clip
|
||||||
|
the right edges of characters that overflow the cell by at most two pixels
|
||||||
|
|
||||||
|
|
||||||
|
version 0.8.3 [2018-03-29]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Fix a regression that broke the visual bell and invert screen colors escape
|
||||||
|
code
|
||||||
|
|
||||||
|
- Allow double-click and triple-click + drag to extend selections word at a
|
||||||
|
time or line at a time
|
||||||
|
|
||||||
|
- Add a keyboard shortcut to set the tab title
|
||||||
|
|
||||||
|
- Fix setting window title to empty via OSC escape code not working correctly
|
||||||
|
|
||||||
|
- Linux: Fix cell width calculation incorrect for some fonts (cell widths are
|
||||||
|
now calculated by actually rendering bitmaps, which is slower but more
|
||||||
|
accurate)
|
||||||
|
|
||||||
|
- Allow specifying a system wide kitty config file, for all users
|
||||||
|
|
||||||
|
- Add a --debug-config command line flag to output data about the system and
|
||||||
|
kitty configuration.
|
||||||
|
|
||||||
|
- Wayland: Fix auto-repeat of keys not working
|
||||||
|
|
||||||
|
|
||||||
|
version 0.8.2 [2018-03-17]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Allow extending existing selections by right clicking
|
||||||
|
|
||||||
|
- Add a configurable keyboard shortcut and remote command to set the font size to a specific value
|
||||||
|
|
||||||
|
- Add an option to have kitty close the window when the main processes running in it exits, even if there are still background processes writing to that terminal
|
||||||
|
|
||||||
|
- Add configurable keyboard shortcuts to switch to a specific layout
|
||||||
|
|
||||||
|
- Add a keyboard shortcut to edit the kitty config file easily
|
||||||
|
|
||||||
|
- macOS: Fix restoring of window size not correct on Retina screens
|
||||||
|
|
||||||
|
- macOS: Add a facility to specify command line arguments when running kitty from the GUI
|
||||||
|
|
||||||
|
- Add a focus-tab remote command
|
||||||
|
|
||||||
|
- Fix screen not being refreshed immediately after moving a window.
|
||||||
|
|
||||||
|
- Fix a crash when getting the contents of the scrollback buffer as text
|
||||||
|
|
||||||
|
version 0.8.1 [2018-03-09]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Extend kitty's remote control feature to work over both UNIX and TCP sockets,
|
||||||
|
so now you can control kitty from across the internet, if you want to.
|
||||||
|
|
||||||
|
- Render private use unicode characters that are followed by a space as a two
|
||||||
|
character ligature. This fixes rendering for applications that misuse
|
||||||
|
private-use characters to display square symbols.
|
||||||
|
|
||||||
|
- Fix Unicode emoji presentation variant selector causing new a fallback font
|
||||||
|
instance to be created
|
||||||
|
|
||||||
|
- Fix a rare error that prevented the Unicode input kitten from working
|
||||||
|
sometimes
|
||||||
|
|
||||||
|
- Allow using Ctrl+Alt+letter in legacy keyboard modes by outputting them as Ctrl+letter and Alt+letter.
|
||||||
|
This matches other terminals' behavior.
|
||||||
|
|
||||||
|
- Fix cursor position off-by-one on horizontal axis when resizing the terminal
|
||||||
|
|
||||||
|
- Wayland: Fix auto-repeat of keys not working
|
||||||
|
|
||||||
|
- Wayland: Add support for window decorations provided by the Wayland shell
|
||||||
|
|
||||||
|
- macOS: Fix URL hints not working
|
||||||
|
|
||||||
|
- macOS: Fix shell not starting in login mode on some computers
|
||||||
|
|
||||||
|
- macOS: Output errors into console.app when running as a bundle
|
||||||
|
|
||||||
|
|
||||||
version 0.8.0 [2018-02-24]
|
version 0.8.0 [2018-02-24]
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|||||||
25
CONTRIBUTING.md
Normal file
25
CONTRIBUTING.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
### Reporting bugs
|
||||||
|
|
||||||
|
Please first search existing bug reports (especially closed ones) for a report
|
||||||
|
that matches your issue.
|
||||||
|
|
||||||
|
When reporting a bug, provide full details of your environment, that means, at
|
||||||
|
a minimum, kitty version, OS and OS version, kitty config (ideally a minimal
|
||||||
|
config to reproduce the issue with).
|
||||||
|
|
||||||
|
|
||||||
|
### Contributing code
|
||||||
|
|
||||||
|
Install [the dependencies](https://github.com/kovidgoyal/kitty#dependencies)
|
||||||
|
using your favorite package manager. Build and run kitty [from
|
||||||
|
source](https://github.com/kovidgoyal/kitty#install-and-run-from-source).
|
||||||
|
|
||||||
|
Make a fork, submit your Pull Request. If it's a large/controversial change, open an issue
|
||||||
|
beforehand to discuss it, so that you don't waste your time making a pull
|
||||||
|
request that gets rejected.
|
||||||
|
|
||||||
|
If the code you are submitting is reasonably easily testable, please contribute
|
||||||
|
tests as well (see the `kitty_tests/` sub-directory for existing tests, which
|
||||||
|
can be run with `./test.py`).
|
||||||
|
|
||||||
|
That's it.
|
||||||
2
Makefile
2
Makefile
@@ -31,4 +31,4 @@ logo/kitty.iconset/icon_256x256.png: logo/kitty.svg logo/make.py
|
|||||||
rendered_logo: logo/kitty.iconset/icon_256x256.png
|
rendered_logo: logo/kitty.iconset/icon_256x256.png
|
||||||
|
|
||||||
app: rendered_logo
|
app: rendered_logo
|
||||||
python3 setup.py app $(VVAL)
|
python3 setup.py kitty.app $(VVAL)
|
||||||
|
|||||||
163
README.asciidoc
163
README.asciidoc
@@ -6,12 +6,14 @@
|
|||||||
:sc_close_window: pass:quotes[`ctrl+shift+w`]
|
:sc_close_window: pass:quotes[`ctrl+shift+w`]
|
||||||
:sc_copy_to_clipboard: pass:quotes[`ctrl+shift+c`]
|
:sc_copy_to_clipboard: pass:quotes[`ctrl+shift+c`]
|
||||||
:sc_decrease_font_size: pass:quotes[`ctrl+shift+minus`]
|
:sc_decrease_font_size: pass:quotes[`ctrl+shift+minus`]
|
||||||
|
:sc_edit_config_file: pass:quotes[`ctrl+shift+f2`]
|
||||||
:sc_eighth_window: pass:quotes[`ctrl+shift+8`]
|
:sc_eighth_window: pass:quotes[`ctrl+shift+8`]
|
||||||
:sc_fifth_window: pass:quotes[`ctrl+shift+5`]
|
:sc_fifth_window: pass:quotes[`ctrl+shift+5`]
|
||||||
:sc_first_window: pass:quotes[`ctrl+shift+1`]
|
:sc_first_window: pass:quotes[`ctrl+shift+1`]
|
||||||
:sc_fourth_window: pass:quotes[`ctrl+shift+4`]
|
:sc_fourth_window: pass:quotes[`ctrl+shift+4`]
|
||||||
:sc_increase_font_size: pass:quotes[`ctrl+shift+equal`]
|
:sc_increase_font_size: pass:quotes[`ctrl+shift+equal`]
|
||||||
:sc_input_unicode_character: pass:quotes[`ctrl+shift+u`]
|
:sc_input_unicode_character: pass:quotes[`ctrl+shift+u`]
|
||||||
|
:sc_kitty_shell_window: pass:quotes[`ctrl+shift+escape`]
|
||||||
:sc_move_tab_backward: pass:quotes[`ctrl+shift+,`]
|
:sc_move_tab_backward: pass:quotes[`ctrl+shift+,`]
|
||||||
:sc_move_tab_forward: pass:quotes[`ctrl+shift+.`]
|
:sc_move_tab_forward: pass:quotes[`ctrl+shift+.`]
|
||||||
:sc_move_window_backward: pass:quotes[`ctrl+shift+b`]
|
:sc_move_window_backward: pass:quotes[`ctrl+shift+b`]
|
||||||
@@ -30,7 +32,11 @@
|
|||||||
:sc_previous_tab: pass:quotes[`ctrl+shift+left`]
|
:sc_previous_tab: pass:quotes[`ctrl+shift+left`]
|
||||||
:sc_previous_window: pass:quotes[`ctrl+shift+[`]
|
:sc_previous_window: pass:quotes[`ctrl+shift+[`]
|
||||||
:sc_restore_font_size: pass:quotes[`ctrl+shift+backspace`]
|
:sc_restore_font_size: pass:quotes[`ctrl+shift+backspace`]
|
||||||
:sc_run_simple_kitten_text_url_hints: pass:quotes[`ctrl+shift+e`]
|
:sc_run_kitten_text_hints: pass:quotes[`ctrl+shift+e`]
|
||||||
|
:sc_run_kitten_text_hints_type_line_program: pass:quotes[`ctrl+shift+p → l`]
|
||||||
|
:sc_run_kitten_text_hints_type_path: pass:quotes[`ctrl+shift+p → shift+f`]
|
||||||
|
:sc_run_kitten_text_hints_type_path_program: pass:quotes[`ctrl+shift+p → f`]
|
||||||
|
:sc_run_kitten_text_hints_type_word_program: pass:quotes[`ctrl+shift+p → w`]
|
||||||
:sc_scroll_end: pass:quotes[`ctrl+shift+end`]
|
:sc_scroll_end: pass:quotes[`ctrl+shift+end`]
|
||||||
:sc_scroll_home: pass:quotes[`ctrl+shift+home`]
|
:sc_scroll_home: pass:quotes[`ctrl+shift+home`]
|
||||||
:sc_scroll_line_down: pass:quotes[`ctrl+shift+down` or `ctrl+shift+j`]
|
:sc_scroll_line_down: pass:quotes[`ctrl+shift+down` or `ctrl+shift+j`]
|
||||||
@@ -38,6 +44,7 @@
|
|||||||
:sc_scroll_page_down: pass:quotes[`ctrl+shift+page_down`]
|
:sc_scroll_page_down: pass:quotes[`ctrl+shift+page_down`]
|
||||||
:sc_scroll_page_up: pass:quotes[`ctrl+shift+page_up`]
|
:sc_scroll_page_up: pass:quotes[`ctrl+shift+page_up`]
|
||||||
:sc_second_window: pass:quotes[`ctrl+shift+2`]
|
:sc_second_window: pass:quotes[`ctrl+shift+2`]
|
||||||
|
:sc_set_tab_title: pass:quotes[`ctrl+shift+alt+t`]
|
||||||
:sc_seventh_window: pass:quotes[`ctrl+shift+7`]
|
:sc_seventh_window: pass:quotes[`ctrl+shift+7`]
|
||||||
:sc_show_scrollback: pass:quotes[`ctrl+shift+h`]
|
:sc_show_scrollback: pass:quotes[`ctrl+shift+h`]
|
||||||
:sc_sixth_window: pass:quotes[`ctrl+shift+6`]
|
:sc_sixth_window: pass:quotes[`ctrl+shift+6`]
|
||||||
@@ -66,7 +73,7 @@ link:#layouts[layouts] without needing to use an extra program like tmux
|
|||||||
* Can be link:remote-control.asciidoc[controlled from scripts or the shell prompt], even over SSH.
|
* Can be link:remote-control.asciidoc[controlled from scripts or the shell prompt], even over SSH.
|
||||||
|
|
||||||
* Has a framework for _kittens_, small terminal programs that can be used to extend kitty's functionality.
|
* Has a framework for _kittens_, small terminal programs that can be used to extend kitty's functionality.
|
||||||
For example, they are used for link:#unicode-input[Unicode input] and link:#url-hints[URL hints].
|
For example, they are used for link:#unicode-input[Unicode input] and link:#url-hints[Hints].
|
||||||
|
|
||||||
* Supports link:#startup-sessions[startup sessions] which allow you to specify the window/tab layout,
|
* Supports link:#startup-sessions[startup sessions] which allow you to specify the window/tab layout,
|
||||||
working directories and programs to run on startup.
|
working directories and programs to run on startup.
|
||||||
@@ -101,7 +108,7 @@ the following dependencies are installed first.
|
|||||||
* ImageMagick (optional, needed to use the `kitty icat` tool to display images in the terminal)
|
* ImageMagick (optional, needed to use the `kitty icat` tool to display images in the terminal)
|
||||||
* gcc or clang (required only for building)
|
* gcc or clang (required only for building)
|
||||||
* pkg-config (required only for building)
|
* pkg-config (required only for building)
|
||||||
* For building on Linux in addition to the above dependencies you might also need to install the `-dev` packages for `xcursor`, `xrandr`, `xinerama` and `xkbcommon-x11`, if they are not already installed by your distro.
|
* For building on Linux in addition to the above dependencies you might also need to install the `-dev` packages for `xcursor`, `xrandr`, `libxi`, `xinerama`, `libgl1-mesa` and `xkbcommon-x11`, if they are not already installed by your distro.
|
||||||
|
|
||||||
=== Install and run from source
|
=== Install and run from source
|
||||||
|
|
||||||
@@ -109,7 +116,7 @@ the following dependencies are installed first.
|
|||||||
git clone https://github.com/kovidgoyal/kitty && cd kitty
|
git clone https://github.com/kovidgoyal/kitty && cd kitty
|
||||||
....
|
....
|
||||||
|
|
||||||
Now build the C parts of kitty with the following command:
|
Now build the native code parts of kitty with the following command:
|
||||||
|
|
||||||
....
|
....
|
||||||
make
|
make
|
||||||
@@ -121,16 +128,32 @@ You can run kitty, as:
|
|||||||
python3 .
|
python3 .
|
||||||
....
|
....
|
||||||
|
|
||||||
|
If that works, you can create a script to launch kitty:
|
||||||
|
|
||||||
|
....
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import runpy
|
||||||
|
runpy.run_path('/path/to/kitty/dir', run_name='__main__')
|
||||||
|
....
|
||||||
|
|
||||||
|
And place it in `~/bin` or `/usr/bin` so that you can run kitty using
|
||||||
|
just `kitty`.
|
||||||
|
|
||||||
=== Linux packages
|
=== Linux packages
|
||||||
|
|
||||||
* Arch Linux: AUR git package https://aur.archlinux.org/packages/kitty-git/
|
* Arch Linux: https://www.archlinux.org/packages/community/x86_64/kitty/
|
||||||
|
|
||||||
* Arch Linux: AUR stable package https://aur.archlinux.org/packages/kitty/
|
* Arch Linux: AUR git package https://aur.archlinux.org/packages/kitty-git/
|
||||||
|
|
||||||
* NixOS / nixpkgs: https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/kitty/default.nix
|
* NixOS / nixpkgs: https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/kitty/default.nix
|
||||||
|
|
||||||
* Gentoo: https://packages.gentoo.org/packages/x11-terms/kitty
|
* Gentoo: https://packages.gentoo.org/packages/x11-terms/kitty
|
||||||
|
|
||||||
|
* openSUSE: https://build.opensuse.org/package/show/X11:terminals/kitty
|
||||||
|
|
||||||
|
* Debian: https://packages.qa.debian.org/k/kitty/news/20180419T070013Z.html
|
||||||
|
|
||||||
=== macOS packages
|
=== macOS packages
|
||||||
|
|
||||||
kitty is available as a macOS `dmg` file for easy installation from the
|
kitty is available as a macOS `dmg` file for easy installation from the
|
||||||
@@ -199,6 +222,7 @@ windows are:
|
|||||||
|Next layout | {sc_next_layout}
|
|Next layout | {sc_next_layout}
|
||||||
|Move tab forward | {sc_move_tab_forward}
|
|Move tab forward | {sc_move_tab_forward}
|
||||||
|Move tab backward | {sc_move_tab_backward}
|
|Move tab backward | {sc_move_tab_backward}
|
||||||
|
|Set tab title | {sc_set_tab_title}
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|
|
||||||
@@ -234,6 +258,8 @@ windows are:
|
|||||||
|Input unicode character | {sc_input_unicode_character}
|
|Input unicode character | {sc_input_unicode_character}
|
||||||
|Click URL using the keyboard | {sc_run_simple_kitten_text_url_hints}
|
|Click URL using the keyboard | {sc_run_simple_kitten_text_url_hints}
|
||||||
|Pass current selection to program | {sc_pass_selection_to_program}
|
|Pass current selection to program | {sc_pass_selection_to_program}
|
||||||
|
|Edit kitty config file | {sc_edit_config_file}
|
||||||
|
|Open a kitty shell | {sc_kitty_shell}
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@@ -241,10 +267,11 @@ windows are:
|
|||||||
|
|
||||||
You can control kitty from the command line/scripts by sending it messages.
|
You can control kitty from the command line/scripts by sending it messages.
|
||||||
You can tell kitty to open/close/rename tabs and windows. You can even send
|
You can tell kitty to open/close/rename tabs and windows. You can even send
|
||||||
arbitrary input/text to any specified window. Messages can be sent using
|
arbitrary input/text to any specified window. Messages can be sent using `kitty
|
||||||
`kitty @`. Note that you must set `allow_remote_control yes` in your
|
@`. Note that you must set `allow_remote_control yes` in your
|
||||||
kitty.conf to use this feature. It even works over SSH connections. This
|
link:kitty/kitty.conf[kitty.conf] to use this feature. It even works over SSH
|
||||||
feature is best illustrated with a link:remote-control.asciidoc[tutorial].
|
connections. This feature is best illustrated with a
|
||||||
|
link:remote-control.asciidoc[tutorial].
|
||||||
|
|
||||||
|
|
||||||
== The scrollback buffer
|
== The scrollback buffer
|
||||||
@@ -267,17 +294,38 @@ Press {sc_input_unicode_character} to start the unicode input widget, shown belo
|
|||||||
|
|
||||||
image::unicode.png?raw=true[Unicode input widget, showing selection of unicode characters by name]
|
image::unicode.png?raw=true[Unicode input widget, showing selection of unicode characters by name]
|
||||||
|
|
||||||
== URL hints
|
In Code mode, you enter a unicode character by typing in the hex code for the
|
||||||
|
character and pressing enter, for example, type in 2716 and press enter to get
|
||||||
|
✖. You can also choose a character from the list of recently used characters by
|
||||||
|
typing a leading period and then the two character index and pressing Enter.
|
||||||
|
|
||||||
kitty has a _hints mode_ to click URLs visible on the screen by using only the keyboard.
|
In Name mode you instead type words from the character name and use the arrow
|
||||||
Press {sc_run_simple_kitten_text_url_hints} to activate hints mode, as shown below.
|
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 dont like to use arrow
|
||||||
|
keys.
|
||||||
|
|
||||||
|
== Hints
|
||||||
|
|
||||||
|
kitty has a _hints mode_ to select and act on arbitrary text snippets currently
|
||||||
|
visible on the screen. For example, you can press {sc_run_kitten_text_hints}
|
||||||
|
to choose any URL visible on the screen and then open it using your system
|
||||||
|
browser.
|
||||||
|
|
||||||
image::hints_mode.png?raw=true[URL hints mode]
|
image::hints_mode.png?raw=true[URL hints mode]
|
||||||
|
|
||||||
|
Similarly, you can press {sc_run_kitten_text_hints_type_path_program} to
|
||||||
|
select anything that looks like a path or filename and then insert it into the
|
||||||
|
terminal, very useful for picking files from the output of a git or ls command and
|
||||||
|
adding them to the command line for the next command.
|
||||||
|
|
||||||
|
The hints kitten is very powerful to see more detailed help on its various
|
||||||
|
options and modes of operation, use: `kitty +kitten hints --help`.
|
||||||
|
|
||||||
== Miscellaneous features
|
== Miscellaneous features
|
||||||
|
|
||||||
You can double click to select a word and triple click to select a line. You can also
|
* You can also hold down `ctrl+shift` and click on a URL to open it in a browser.
|
||||||
hold down `ctrl+shift` and click on a URL to open it in a browser.
|
* You can double click to select a word and triple click to select a line.
|
||||||
|
* You can right click to extend a previous selection
|
||||||
|
|
||||||
|
|
||||||
== Layouts
|
== Layouts
|
||||||
@@ -290,7 +338,9 @@ Currently, there are five layouts available,
|
|||||||
* Horizontal -- All windows are shown side-by-side
|
* Horizontal -- All windows are shown side-by-side
|
||||||
* Vertical -- All windows are shown one below the other
|
* Vertical -- All windows are shown one below the other
|
||||||
|
|
||||||
You can switch between layouts using the {sc_next_layout} key combination.
|
You can switch between layouts using the {sc_next_layout} key combination. You can
|
||||||
|
also create shortcuts to select particular layouts, and choose which layouts
|
||||||
|
you want to enable/disable, see link:kitty/kitty.conf[kitty.conf] for examples.
|
||||||
|
|
||||||
Writing a new layout only requires about fifty lines of code, so if there is
|
Writing a new layout only requires about fifty lines of code, so if there is
|
||||||
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
|
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
|
||||||
@@ -299,13 +349,18 @@ a pull request!
|
|||||||
|
|
||||||
== Configuration
|
== Configuration
|
||||||
|
|
||||||
kitty is highly customizable, everything from keyboard shortcuts, to
|
kitty is highly customizable, everything from keyboard shortcuts, to painting
|
||||||
painting frames-per-second. See the heavily commented
|
frames-per-second. See the heavily commented link:kitty/kitty.conf[default
|
||||||
link:kitty/kitty.conf[default config file]. By default kitty looks for a
|
config file] for an overview of all customization possibilities.
|
||||||
config file in the OS config directory (usually
|
|
||||||
`~/.config/kitty/kitty.conf` on Linux and
|
By default kitty looks for a config file in the OS config directories (usually
|
||||||
|
`~/.config/kitty/kitty.conf` and additionally
|
||||||
`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a specific
|
`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a specific
|
||||||
path via the `--config` option.
|
path via the `--config` option or use the `KITTY_CONFIG_DIRECTORY` environment
|
||||||
|
variable. See the help for the `--config` option in `kitty --help` for full
|
||||||
|
details. You can also dump the current configuration using the `--debug-config`
|
||||||
|
option.
|
||||||
|
|
||||||
|
|
||||||
== Startup Sessions
|
== Startup Sessions
|
||||||
|
|
||||||
@@ -422,6 +477,8 @@ installs the terminfo file and `kitty` that installs the main program.
|
|||||||
This allows users to install the terminfo file on servers into which they ssh,
|
This allows users to install the terminfo file on servers into which they ssh,
|
||||||
without needing to install all of kitty.
|
without needing to install all of kitty.
|
||||||
|
|
||||||
|
You also need `tic` to compile the terminfo files, it is usually found in the development package of `ncurses`
|
||||||
|
|
||||||
This applies to creating packages for kitty for macOS package managers such as
|
This applies to creating packages for kitty for macOS package managers such as
|
||||||
brew or MacPorts as well.
|
brew or MacPorts as well.
|
||||||
|
|
||||||
@@ -439,18 +496,16 @@ let the character overflow into neighboring cells, which is fine if the
|
|||||||
neighboring cell is empty, but looks terrible if it is not.
|
neighboring cell is empty, but looks terrible if it is not.
|
||||||
|
|
||||||
Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc.
|
Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc.
|
||||||
use unicode characters from the private use area to represent symbols. Often
|
misuse unicode characters from the private use area to represent symbols. Often
|
||||||
these symbols are square and should be rendered in two cells. However, since
|
these symbols are square and should be rendered in two cells. However, since
|
||||||
private use area symbols all have their width set to one in the unicode
|
private use area symbols all have their width set to one in the unicode
|
||||||
standard, kitty renders them either smaller or truncated. The correct solution
|
standard, kitty renders them either smaller or truncated. The exception is if
|
||||||
for this is to use either use different symbols that are not square, or to use
|
these characters are followed by a space or empty cell in which case kitty
|
||||||
a font that defines ligatures with the space character for these symbols. See
|
makes use of the extra cell to render them in two cells.
|
||||||
link:https://github.com/kovidgoyal/kitty/issues/182[#182] for a discussion of
|
|
||||||
the approach using ligatures.
|
|
||||||
|
|
||||||
=== How do I build kitty.app on macOS?
|
=== How do I build kitty.app on macOS?
|
||||||
|
|
||||||
Install `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
|
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
|
||||||
|
|
||||||
And run:
|
And run:
|
||||||
```
|
```
|
||||||
@@ -481,19 +536,53 @@ let &t_ut=''
|
|||||||
See link:https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc#setting-text-styles-colors-in-arbitrary-regions-of-the-screen[here]
|
See link:https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc#setting-text-styles-colors-in-arbitrary-regions-of-the-screen[here]
|
||||||
for why kitty does not support background color erase.
|
for why kitty does not support background color erase.
|
||||||
|
|
||||||
== I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
|
=== I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
|
||||||
|
|
||||||
This happens because the kitty terminfo files are not available on the server. Either install kitty on the server,
|
This happens because the kitty terminfo files are not available on the server.
|
||||||
or simply copy over the terminfo files, using:
|
Either install kitty on the server, or simply copy over the terminfo files,
|
||||||
|
using:
|
||||||
|
|
||||||
....
|
....
|
||||||
ssh myserver mkdir -p \~/.terminfo
|
infocmp xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
|
||||||
infocmp xterm-kitty | ssh myserver "cat > /tmp/kitty.terminfo"
|
|
||||||
ssh myserver tic -x -o \~/.terminfo /tmp/kitty.terminfo
|
|
||||||
....
|
....
|
||||||
|
|
||||||
Really, the correct solution for this is to convince the OpenSSH maintainers to have ssh do this
|
Really, the correct solution for this is to convince the OpenSSH maintainers to
|
||||||
automatically when connecting to a server.
|
have ssh do this automatically when connecting to a server, so that all
|
||||||
|
terminals work transparently.
|
||||||
|
|
||||||
|
In the meantime, you can automate it by using a simple script called, for example,
|
||||||
|
`~/bin/myssh`
|
||||||
|
|
||||||
|
....
|
||||||
|
#!/bin/sh
|
||||||
|
infocmp xterm-kitty | ssh $1 tic -x -o \~/.terminfo /dev/stdin && ssh $1
|
||||||
|
....
|
||||||
|
|
||||||
|
Then you can use `myssh server` to log in to `server` with the terminfo file
|
||||||
|
being automatically available.
|
||||||
|
|
||||||
|
|
||||||
|
=== How do I change the colors in a running kitty instance?
|
||||||
|
|
||||||
|
You can either use the
|
||||||
|
link:http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands[OSC
|
||||||
|
terminal escape codes] to set colors or you can enable link:remote-control.asciidoc[remote control]
|
||||||
|
for kitty and use `kitty @ set-colors --help`.
|
||||||
|
|
||||||
|
|
||||||
|
=== How do I specify command line options for kitty on macOS?
|
||||||
|
|
||||||
|
Apple does not want you to use command line options with GUI applications. To
|
||||||
|
workaround that limitation, kitty will read command line options from the file
|
||||||
|
`<kitty config dir>/macos-launch-services-cmdline` when it is launched
|
||||||
|
from the GUI, i.e. by clicking the kitty application icon or using `open -a kitty`.
|
||||||
|
Note that this file is *only read* when running via the GUI.
|
||||||
|
|
||||||
|
You can, of course, also run kitty from a terminal with command line options, using:
|
||||||
|
`/Applications/kitty.app/Contents/MacOS/kitty`.
|
||||||
|
|
||||||
|
And within kitty itself, you can always run kitty using just `kitty` as it
|
||||||
|
cleverly adds itself to the PATH.
|
||||||
|
|
||||||
== Resources on terminal behavior
|
== Resources on terminal behavior
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ def launch(args):
|
|||||||
runpy.run_path(args[1], run_name='__main__')
|
runpy.run_path(args[1], run_name='__main__')
|
||||||
|
|
||||||
|
|
||||||
|
def run_kitten(args):
|
||||||
|
kitten = args[1]
|
||||||
|
sys.argv = args[1:]
|
||||||
|
from kittens.runner import run_kitten
|
||||||
|
run_kitten(kitten)
|
||||||
|
|
||||||
|
|
||||||
def namespaced(args):
|
def namespaced(args):
|
||||||
func = namespaced_entry_points[args[1]]
|
func = namespaced_entry_points[args[1]]
|
||||||
func(args[1:])
|
func(args[1:])
|
||||||
@@ -42,6 +49,7 @@ entry_points = {
|
|||||||
'list-fonts': list_fonts,
|
'list-fonts': list_fonts,
|
||||||
'runpy': runpy,
|
'runpy': runpy,
|
||||||
'launch': launch,
|
'launch': launch,
|
||||||
|
'kitten': run_kitten,
|
||||||
|
|
||||||
'@': remote_control,
|
'@': remote_control,
|
||||||
'+': namespaced,
|
'+': namespaced,
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/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') kitty
|
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
|
||||||
|
|||||||
128
gen-wcwidth.py
128
gen-wcwidth.py
@@ -44,7 +44,8 @@ def parse_ucd():
|
|||||||
def add_word(w, c):
|
def add_word(w, c):
|
||||||
if c <= 32 or c == 127 or 128 <= c <= 159:
|
if c <= 32 or c == 127 or 128 <= c <= 159:
|
||||||
return
|
return
|
||||||
word_search_map[w.lower()].add(c)
|
if len(w) > 1:
|
||||||
|
word_search_map[w.lower()].add(c)
|
||||||
|
|
||||||
first = None
|
first = None
|
||||||
for word, c in html5.items():
|
for word, c in html5.items():
|
||||||
@@ -54,7 +55,9 @@ def parse_ucd():
|
|||||||
for line in get_data('ucd/UnicodeData.txt'):
|
for line in get_data('ucd/UnicodeData.txt'):
|
||||||
parts = [x.strip() for x in line.split(';')]
|
parts = [x.strip() for x in line.split(';')]
|
||||||
codepoint = int(parts[0], 16)
|
codepoint = int(parts[0], 16)
|
||||||
name = parts[1]
|
name = parts[1] or parts[10]
|
||||||
|
if name == '<control>':
|
||||||
|
name = parts[10]
|
||||||
if name:
|
if name:
|
||||||
name_map[codepoint] = name
|
name_map[codepoint] = name
|
||||||
for word in name.lower().split():
|
for word in name.lower().split():
|
||||||
@@ -207,7 +210,8 @@ def codepoint_to_mark_map(p, mark_map):
|
|||||||
for spec in get_ranges(mark_map):
|
for spec in get_ranges(mark_map):
|
||||||
if isinstance(spec, tuple):
|
if isinstance(spec, tuple):
|
||||||
s = rmap[spec[0]]
|
s = rmap[spec[0]]
|
||||||
p(f'\t\tcase {spec[0]} ... {spec[1]}: return {s} + c - {spec[0]};')
|
cases = ' '.join(f'case {i}:' for i in range(spec[0], spec[1]+1))
|
||||||
|
p(f'\t\t{cases} return {s} + c - {spec[0]};')
|
||||||
else:
|
else:
|
||||||
p(f'\t\tcase {spec}: return {rmap[spec]};')
|
p(f'\t\tcase {spec}: return {rmap[spec]};')
|
||||||
p('default: return 0;')
|
p('default: return 0;')
|
||||||
@@ -256,53 +260,111 @@ def gen_ucd():
|
|||||||
raise ValueError('The mark for 0xfe0e has changed, you have to update VS15 to {} and VS16 to {} in unicode-data.h'.format(
|
raise ValueError('The mark for 0xfe0e has changed, you have to update VS15 to {} and VS16 to {} in unicode-data.h'.format(
|
||||||
rmap[0xfe0e], rmap[0xfe0f]
|
rmap[0xfe0e], rmap[0xfe0f]
|
||||||
))
|
))
|
||||||
with open('kittens/url_hints/url_regex.py', 'w') as f:
|
with open('kittens/hints/url_regex.py', 'w') as f:
|
||||||
f.write("url_delimiters = '{}' # noqa".format(''.join(classes_to_regex(cz))))
|
f.write("url_delimiters = '{}' # noqa".format(''.join(classes_to_regex(cz))))
|
||||||
|
|
||||||
|
|
||||||
def gen_names():
|
def gen_names():
|
||||||
words = tuple(sorted(word_search_map))
|
|
||||||
|
|
||||||
with create_header('kittens/unicode_input/names.h') as p:
|
with create_header('kittens/unicode_input/names.h') as p:
|
||||||
cp_map = list(sorted(name_map))
|
mark_to_cp = list(sorted(name_map))
|
||||||
p(f'static const char* name_map[{len(cp_map)}] = {{' ' // {{{')
|
cp_to_mark = {cp: m for m, cp in enumerate(mark_to_cp)}
|
||||||
for cp in cp_map:
|
# Mapping of mark to codepoint name
|
||||||
|
p(f'static const char* name_map[{len(mark_to_cp)}] = {{' ' // {{{')
|
||||||
|
for cp in mark_to_cp:
|
||||||
w = name_map[cp].replace('"', '\\"')
|
w = name_map[cp].replace('"', '\\"')
|
||||||
p(f'\t"{w}",')
|
p(f'\t"{w}",')
|
||||||
p("}; // }}}\n")
|
p("}; // }}}\n")
|
||||||
|
|
||||||
p(f'static const char* idx_to_word[{len(words)}] = ' '{ // {{{')
|
# Mapping of mark to codepoint
|
||||||
for s in words:
|
p(f'static const char_type mark_to_cp[{len(mark_to_cp)}] = {{' ' // {{{')
|
||||||
s = s.replace('"', '\\"')
|
p(', '.join(map(str, mark_to_cp)))
|
||||||
p(f'\t"{s}",')
|
p('}; // }}}\n')
|
||||||
p("}; // }}}\n")
|
|
||||||
|
|
||||||
first_letters = {ord(w[0]) for w in words if ord(w[0]) < 256}
|
|
||||||
wmap = {w: i for i, w in enumerate(words)}
|
|
||||||
p(f'static const unsigned short* words_for_first_letter[256] = ' '{ // {{{')
|
|
||||||
for fl in range(0, 256):
|
|
||||||
if fl in first_letters:
|
|
||||||
winds = [str(wmap[w]) for w in words if w.startswith(chr(fl))]
|
|
||||||
p(f'\t(const unsigned short[{len(winds) + 1}]){{{len(winds)}, ', ', '.join(winds), '},')
|
|
||||||
else:
|
|
||||||
p('NULL,')
|
|
||||||
p("}; // }}}\n")
|
|
||||||
|
|
||||||
p(f'static const char_type* codepoints_for_word_idx[{len(words)}] = ' '{ // {{{')
|
|
||||||
for s in words:
|
|
||||||
cps = word_search_map[s]
|
|
||||||
a = ', '.join(map(str, cps))
|
|
||||||
p(f'\t(const char_type[{len(cps) + 1}]){{{len(cps)}, ', a, '},')
|
|
||||||
p("}; // }}}\n")
|
|
||||||
|
|
||||||
|
# Function to get mark number for codepoint
|
||||||
p('static char_type mark_for_codepoint(char_type c) {')
|
p('static char_type mark_for_codepoint(char_type c) {')
|
||||||
codepoint_to_mark_map(p, cp_map)
|
codepoint_to_mark_map(p, mark_to_cp)
|
||||||
p('}\n')
|
p('}\n')
|
||||||
p('static inline const char* name_for_codepoint(char_type cp) {')
|
p('static inline const char* name_for_codepoint(char_type cp) {')
|
||||||
p('\tchar_type m = mark_for_codepoint(cp); if (m == 0) return NULL;')
|
p('\tchar_type m = mark_for_codepoint(cp); if (m == 0) return NULL;')
|
||||||
p('\treturn name_map[m];')
|
p('\treturn name_map[m];')
|
||||||
p('}\n')
|
p('}\n')
|
||||||
|
|
||||||
|
# Array of all words
|
||||||
|
word_map = tuple(sorted(word_search_map))
|
||||||
|
word_rmap = {w: i for i, w in enumerate(word_map)}
|
||||||
|
p(f'static const char* all_words_map[{len(word_map)}] = {{' ' // {{{')
|
||||||
|
cwords = (w.replace('"', '\\"') for w in word_map)
|
||||||
|
p(', '.join(f'"{w}"' for w in cwords))
|
||||||
|
p('}; // }}}\n')
|
||||||
|
|
||||||
|
# Array of sets of marks for each word
|
||||||
|
word_to_marks = {word_rmap[w]: frozenset(map(cp_to_mark.__getitem__, cps)) for w, cps in word_search_map.items()}
|
||||||
|
all_mark_groups = frozenset(word_to_marks.values())
|
||||||
|
array = [0]
|
||||||
|
mg_to_offset = {}
|
||||||
|
for mg in all_mark_groups:
|
||||||
|
mg_to_offset[mg] = len(array)
|
||||||
|
array.append(len(mg))
|
||||||
|
array.extend(sorted(mg))
|
||||||
|
p(f'static const char_type mark_groups[{len(array)}] = {{' ' // {{{')
|
||||||
|
p(', '.join(map(str, array)))
|
||||||
|
p('}; // }}}\n')
|
||||||
|
offsets_array = []
|
||||||
|
for wi, w in enumerate(word_map):
|
||||||
|
mg = word_to_marks[wi]
|
||||||
|
offsets_array.append(mg_to_offset[mg])
|
||||||
|
p(f'static const char_type mark_to_offset[{len(offsets_array)}] = {{' ' // {{{')
|
||||||
|
p(', '.join(map(str, offsets_array)))
|
||||||
|
p('}; // }}}\n')
|
||||||
|
|
||||||
|
# The trie
|
||||||
|
p(f'typedef struct {{ uint32_t children_offset; uint32_t match_offset; }} word_trie;\n')
|
||||||
|
all_trie_nodes = []
|
||||||
|
|
||||||
|
class TrieNode:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.match_offset = 0
|
||||||
|
self.children_offset = 0
|
||||||
|
self.children = {}
|
||||||
|
|
||||||
|
def add_letter(self, letter):
|
||||||
|
if letter not in self.children:
|
||||||
|
self.children[letter] = len(all_trie_nodes)
|
||||||
|
all_trie_nodes.append(TrieNode())
|
||||||
|
return self.children[letter]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{{ .children_offset={self.children_offset}, .match_offset={self.match_offset} }}'
|
||||||
|
|
||||||
|
root = TrieNode()
|
||||||
|
all_trie_nodes.append(root)
|
||||||
|
|
||||||
|
def add_word(word_idx):
|
||||||
|
word = word_map[word_idx]
|
||||||
|
parent = root
|
||||||
|
for letter in map(ord, word):
|
||||||
|
idx = parent.add_letter(letter)
|
||||||
|
parent = all_trie_nodes[idx]
|
||||||
|
parent.match_offset = offsets_array[word_idx]
|
||||||
|
|
||||||
|
for i in range(len(word_map)):
|
||||||
|
add_word(i)
|
||||||
|
children_array = [0]
|
||||||
|
for node in all_trie_nodes:
|
||||||
|
if node.children:
|
||||||
|
node.children_offset = len(children_array)
|
||||||
|
children_array.append(len(node.children))
|
||||||
|
for letter, child_offset in node.children.items():
|
||||||
|
children_array.append((child_offset << 8) | (letter & 0xff))
|
||||||
|
|
||||||
|
p(f'static const word_trie all_trie_nodes[{len(all_trie_nodes)}] = {{' ' // {{{')
|
||||||
|
p(',\n'.join(map(str, all_trie_nodes)))
|
||||||
|
p('\n}; // }}}\n')
|
||||||
|
p(f'static const uint32_t children_array[{len(children_array)}] = {{' ' // {{{')
|
||||||
|
p(', '.join(map(str, children_array)))
|
||||||
|
p('}; // }}}\n')
|
||||||
|
|
||||||
|
|
||||||
def gen_wcwidth():
|
def gen_wcwidth():
|
||||||
seen = set()
|
seen = set()
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ def regenerate():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def strip_trailing_whitespace(c):
|
||||||
|
return re.sub(r'\s+$', '', c, flags=re.MULTILINE) + '\n'
|
||||||
|
|
||||||
|
|
||||||
def export():
|
def export():
|
||||||
c = open('out/src/glad.c', 'rb').read().decode('utf-8')
|
c = open('out/src/glad.c', 'rb').read().decode('utf-8')
|
||||||
functions = []
|
functions = []
|
||||||
@@ -48,12 +52,13 @@ init_glad(GLADloadproc load, int debug) {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}'''.replace('SUB', ';\n '.join(switch), 1)
|
}'''.replace('SUB', ';\n '.join(switch), 1)
|
||||||
open('../kitty/gl-wrapper.c', 'w').write(c)
|
open('../kitty/gl-wrapper.c', 'w').write(strip_trailing_whitespace(c))
|
||||||
raw = open('out/include/glad/glad.h').read()
|
raw = open('out/include/glad/glad.h').read()
|
||||||
raw = raw.replace('<KHR/khrplatform.h>', '"khrplatform.h"')
|
raw = raw.replace('<KHR/khrplatform.h>', '"khrplatform.h"')
|
||||||
raw += '\nint init_glad(GLADloadproc, int);\n'
|
raw += '\nint init_glad(GLADloadproc, int);\n'
|
||||||
open('../kitty/gl-wrapper.h', 'w').write(raw)
|
open('../kitty/gl-wrapper.h', 'w').write(strip_trailing_whitespace(raw))
|
||||||
shutil.copy2('out/include/KHR/khrplatform.h', '../kitty')
|
raw = open('out/include/KHR/khrplatform.h', 'rb').read().decode('utf-8')
|
||||||
|
open('../kitty/khrplatform.h', 'w').write(strip_trailing_whitespace(raw))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ static void changeToResourcesDirectory(void)
|
|||||||
static void createKeyTables(void)
|
static void createKeyTables(void)
|
||||||
{
|
{
|
||||||
int scancode;
|
int scancode;
|
||||||
|
_glfw.ns.debug_keyboard = getenv("GLFW_DEBUG_KEYBOARD") != NULL;
|
||||||
|
|
||||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||||
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
||||||
@@ -207,6 +208,9 @@ static GLFWbool updateUnicodeDataNS(void)
|
|||||||
_glfw.ns.unicodeData = nil;
|
_glfw.ns.unicodeData = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (_GLFWwindow *window = _glfw.windowListHead; window; window = window->next)
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
|
|
||||||
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
||||||
if (!_glfw.ns.inputSource)
|
if (!_glfw.ns.inputSource)
|
||||||
{
|
{
|
||||||
@@ -371,4 +375,3 @@ const char* _glfwPlatformGetVersionString(void)
|
|||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,10 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
|
const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
|
||||||
|
|||||||
8
glfw/cocoa_platform.h
vendored
8
glfw/cocoa_platform.h
vendored
@@ -37,6 +37,7 @@ typedef void* id;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||||
|
typedef int (* GLFWcocoatextinputfilterfun)(int,int,int);
|
||||||
|
|
||||||
typedef struct VkMacOSSurfaceCreateInfoMVK
|
typedef struct VkMacOSSurfaceCreateInfoMVK
|
||||||
{
|
{
|
||||||
@@ -98,6 +99,10 @@ typedef struct _GLFWwindowNS
|
|||||||
// This is kept to counteract Cocoa doing the same internally
|
// This is kept to counteract Cocoa doing the same internally
|
||||||
double cursorWarpDeltaX, cursorWarpDeltaY;
|
double cursorWarpDeltaX, cursorWarpDeltaY;
|
||||||
|
|
||||||
|
// The text input filter callback
|
||||||
|
GLFWcocoatextinputfilterfun textInputFilterCallback;
|
||||||
|
// Dead key state
|
||||||
|
UInt32 deadKeyState;
|
||||||
} _GLFWwindowNS;
|
} _GLFWwindowNS;
|
||||||
|
|
||||||
// Cocoa-specific global data
|
// Cocoa-specific global data
|
||||||
@@ -114,6 +119,8 @@ typedef struct _GLFWlibraryNS
|
|||||||
id listener;
|
id listener;
|
||||||
|
|
||||||
char keyName[64];
|
char keyName[64];
|
||||||
|
char text[256];
|
||||||
|
GLFWbool debug_keyboard;
|
||||||
short int keycodes[256];
|
short int keycodes[256];
|
||||||
short int scancodes[GLFW_KEY_LAST + 1];
|
short int scancodes[GLFW_KEY_LAST + 1];
|
||||||
char* clipboardString;
|
char* clipboardString;
|
||||||
@@ -166,4 +173,3 @@ void _glfwInitTimerNS(void);
|
|||||||
void _glfwPollMonitorsNS(void);
|
void _glfwPollMonitorsNS(void);
|
||||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,32 @@ static void updateCursorImage(_GLFWwindow* window)
|
|||||||
hideCursor(window);
|
hideCursor(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply chosen cursor mode to a focused window
|
||||||
|
//
|
||||||
|
static void updateCursorMode(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
|
{
|
||||||
|
_glfw.ns.disabledCursorWindow = window;
|
||||||
|
_glfwPlatformGetCursorPos(window,
|
||||||
|
&_glfw.ns.restoreCursorPosX,
|
||||||
|
&_glfw.ns.restoreCursorPosY);
|
||||||
|
centerCursor(window);
|
||||||
|
CGAssociateMouseAndMouseCursorPosition(false);
|
||||||
|
}
|
||||||
|
else if (_glfw.ns.disabledCursorWindow == window)
|
||||||
|
{
|
||||||
|
_glfw.ns.disabledCursorWindow = NULL;
|
||||||
|
CGAssociateMouseAndMouseCursorPosition(true);
|
||||||
|
_glfwPlatformSetCursorPos(window,
|
||||||
|
_glfw.ns.restoreCursorPosX,
|
||||||
|
_glfw.ns.restoreCursorPosY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursorInClientArea(window))
|
||||||
|
updateCursorImage(window);
|
||||||
|
}
|
||||||
|
|
||||||
// Transforms the specified y-coordinate between the CG display and NS screen
|
// Transforms the specified y-coordinate between the CG display and NS screen
|
||||||
// coordinate systems
|
// coordinate systems
|
||||||
//
|
//
|
||||||
@@ -183,10 +209,112 @@ static int translateFlags(NSUInteger flags)
|
|||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define debug_key(...) if (_glfw.ns.debug_keyboard) NSLog(__VA_ARGS__)
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
format_mods(int mods) {
|
||||||
|
static char buf[128];
|
||||||
|
char *p = buf, *s;
|
||||||
|
#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, x)
|
||||||
|
pr("mods: ");
|
||||||
|
s = p;
|
||||||
|
if (mods & GLFW_MOD_CONTROL) pr("ctrl+");
|
||||||
|
if (mods & GLFW_MOD_ALT) pr("alt+");
|
||||||
|
if (mods & GLFW_MOD_SHIFT) pr("shift+");
|
||||||
|
if (mods & GLFW_MOD_SUPER) pr("super+");
|
||||||
|
if (mods & GLFW_MOD_CAPS_LOCK) pr("capslock+");
|
||||||
|
if (mods & GLFW_MOD_NUM_LOCK) pr("numlock+");
|
||||||
|
if (p == s) pr("none");
|
||||||
|
else p--;
|
||||||
|
pr(" ");
|
||||||
|
#undef pr
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
format_text(const char *src) {
|
||||||
|
static char buf[256];
|
||||||
|
char *p = buf;
|
||||||
|
if (!src[0]) return "<none>";
|
||||||
|
while (*src) {
|
||||||
|
p += snprintf(p, sizeof(buf) - (p - buf), "%x ", (unsigned char)*(src++));
|
||||||
|
}
|
||||||
|
if (p != buf) *(--p) = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
safe_name_for_scancode(unsigned int scancode) {
|
||||||
|
const char *ans = _glfwPlatformGetScancodeName(scancode);
|
||||||
|
if (!ans) return "<noname>";
|
||||||
|
if ((1 <= ans[0] && ans[0] <= 31) || ans[0] == 127) ans = "<cc>";
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Translates a macOS keycode to a GLFW keycode
|
// Translates a macOS keycode to a GLFW keycode
|
||||||
//
|
//
|
||||||
static int translateKey(unsigned int key)
|
static int translateKey(unsigned int key, GLFWbool apply_keymap)
|
||||||
{
|
{
|
||||||
|
if (apply_keymap) {
|
||||||
|
// Look for the effective key name after applying any keyboard layouts/mappings
|
||||||
|
const char *name = _glfwPlatformGetScancodeName(key);
|
||||||
|
if (name && name[1] == 0) {
|
||||||
|
// Single letter key name
|
||||||
|
switch(name[0]) {
|
||||||
|
#define K(ch, name) case ch: return GLFW_KEY_##name
|
||||||
|
K('A', A); K('a', A);
|
||||||
|
K('B', B); K('b', B);
|
||||||
|
K('C', C); K('c', C);
|
||||||
|
K('D', D); K('d', D);
|
||||||
|
K('E', E); K('e', E);
|
||||||
|
K('F', F); K('f', F);
|
||||||
|
K('G', G); K('g', G);
|
||||||
|
K('H', H); K('h', H);
|
||||||
|
K('I', I); K('i', I);
|
||||||
|
K('J', J); K('j', J);
|
||||||
|
K('K', K); K('k', K);
|
||||||
|
K('L', L); K('l', L);
|
||||||
|
K('M', M); K('m', M);
|
||||||
|
K('N', N); K('n', N);
|
||||||
|
K('O', O); K('o', O);
|
||||||
|
K('P', P); K('p', P);
|
||||||
|
K('Q', Q); K('q', Q);
|
||||||
|
K('R', R); K('r', R);
|
||||||
|
K('S', S); K('s', S);
|
||||||
|
K('T', T); K('t', T);
|
||||||
|
K('U', U); K('u', U);
|
||||||
|
K('V', V); K('v', V);
|
||||||
|
K('W', W); K('w', W);
|
||||||
|
K('X', X); K('x', X);
|
||||||
|
K('Y', Y); K('y', Y);
|
||||||
|
K('Z', Z); K('z', Z);
|
||||||
|
K('0', 0);
|
||||||
|
K('1', 1);
|
||||||
|
K('2', 2);
|
||||||
|
K('3', 3);
|
||||||
|
K('5', 5);
|
||||||
|
K('6', 6);
|
||||||
|
K('7', 7);
|
||||||
|
K('8', 8);
|
||||||
|
K('9', 9);
|
||||||
|
K('\'', APOSTROPHE);
|
||||||
|
K(',', COMMA);
|
||||||
|
K('.', PERIOD);
|
||||||
|
K('/', SLASH);
|
||||||
|
K('-', MINUS);
|
||||||
|
K('=', EQUAL);
|
||||||
|
K(';', SEMICOLON);
|
||||||
|
K('[', LEFT_BRACKET);
|
||||||
|
K(']', RIGHT_BRACKET);
|
||||||
|
K('`', GRAVE_ACCENT);
|
||||||
|
K('\\', BACKSLASH);
|
||||||
|
#undef K
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0]))
|
if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0]))
|
||||||
return GLFW_KEY_UNKNOWN;
|
return GLFW_KEY_UNKNOWN;
|
||||||
|
|
||||||
@@ -321,7 +449,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
centerCursor(window);
|
centerCursor(window);
|
||||||
|
|
||||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||||
_glfwPlatformSetCursorMode(window, window->cursorMode);
|
updateCursorMode(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidResignKey:(NSNotification *)notification
|
- (void)windowDidResignKey:(NSNotification *)notification
|
||||||
@@ -582,6 +710,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
window->ns.xscale = xscale;
|
window->ns.xscale = xscale;
|
||||||
window->ns.yscale = yscale;
|
window->ns.yscale = yscale;
|
||||||
_glfwInputWindowContentScale(window, xscale, yscale);
|
_glfwInputWindowContentScale(window, xscale, yscale);
|
||||||
|
|
||||||
|
if (window->ns.layer)
|
||||||
|
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,14 +745,99 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
[super updateTrackingAreas];
|
[super updateTrackingAreas];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline UInt32
|
||||||
|
convert_cocoa_to_carbon_modifiers(NSUInteger flags) {
|
||||||
|
UInt32 mods = 0;
|
||||||
|
if (flags & NSEventModifierFlagShift)
|
||||||
|
mods |= shiftKey;
|
||||||
|
if (flags & NSEventModifierFlagControl)
|
||||||
|
mods |= controlKey;
|
||||||
|
if (flags & NSEventModifierFlagOption)
|
||||||
|
mods |= optionKey;
|
||||||
|
if (flags & NSEventModifierFlagCommand)
|
||||||
|
mods |= cmdKey;
|
||||||
|
if (flags & NSEventModifierFlagCapsLock)
|
||||||
|
mods |= alphaLock;
|
||||||
|
|
||||||
|
return (mods >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
convert_utf16_to_utf8(UniChar *src, UniCharCount src_length, char *dest, size_t dest_sz) {
|
||||||
|
CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
|
||||||
|
src,
|
||||||
|
src_length,
|
||||||
|
kCFAllocatorNull);
|
||||||
|
CFStringGetCString(string,
|
||||||
|
dest,
|
||||||
|
dest_sz,
|
||||||
|
kCFStringEncodingUTF8);
|
||||||
|
CFRelease(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline GLFWbool
|
||||||
|
is_ascii_control_char(char x) {
|
||||||
|
return x == 0 || (1 <= x && x <= 31) || x == 127;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent *)event
|
- (void)keyDown:(NSEvent *)event
|
||||||
{
|
{
|
||||||
const int key = translateKey([event keyCode]);
|
const unsigned int scancode = [event keyCode];
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
const NSUInteger flags = [event modifierFlags];
|
||||||
|
const int mods = translateFlags(flags);
|
||||||
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
|
const int key = translateKey(scancode, GLFW_TRUE);
|
||||||
|
const GLFWbool process_text = !window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, scancode) != 1;
|
||||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
_glfw.ns.text[0] = 0;
|
||||||
|
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
|
||||||
|
// keyboard unicode data is not available.
|
||||||
|
if (process_text) {
|
||||||
|
// this will call insertText with the text for this event, if any
|
||||||
|
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static UniChar text[256];
|
||||||
|
UniCharCount char_count = 0;
|
||||||
|
if (UCKeyTranslate(
|
||||||
|
[(NSData*) _glfw.ns.unicodeData bytes],
|
||||||
|
scancode,
|
||||||
|
kUCKeyActionDown,
|
||||||
|
convert_cocoa_to_carbon_modifiers(flags),
|
||||||
|
LMGetKbdType(),
|
||||||
|
(process_text ? 0 : kUCKeyTranslateNoDeadKeysMask),
|
||||||
|
&(window->ns.deadKeyState),
|
||||||
|
sizeof(text)/sizeof(text[0]),
|
||||||
|
&char_count,
|
||||||
|
text
|
||||||
|
) != noErr) {
|
||||||
|
debug_key(@"UCKeyTranslate failed for scancode: 0x%x (%s) %s\n", scancode, safe_name_for_scancode(scancode), format_mods(mods));
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (process_text) {
|
||||||
|
// We check if cocoa wants to insert text, as UCKeyTranslate
|
||||||
|
// inserts text even when the cmd key is pressed. For instance,
|
||||||
|
// cmd+a will result in the text a.
|
||||||
|
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
|
debug_key(@"char_count: %lu cocoa text: %s\n", char_count, format_text(_glfw.ns.text));
|
||||||
|
GLFWbool cocoa_wants_to_insert_text = !is_ascii_control_char(_glfw.ns.text[0]);
|
||||||
|
_glfw.ns.text[0] = 0;
|
||||||
|
if (char_count && cocoa_wants_to_insert_text) convert_utf16_to_utf8(text, char_count, _glfw.ns.text, sizeof(_glfw.ns.text));
|
||||||
|
} else {
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
|
}
|
||||||
|
if (window->ns.deadKeyState && char_count == 0) {
|
||||||
|
debug_key(@"Ignoring dead key. deadKeyState: 0x%x scancode: 0x%x (%s) %s\n",
|
||||||
|
window->ns.deadKeyState, scancode, safe_name_for_scancode(scancode), format_mods(mods));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_key(@"scancode: 0x%x (%s) %stext: %s glfw_key: %s\n",
|
||||||
|
scancode, safe_name_for_scancode(scancode), format_mods(mods),
|
||||||
|
format_text(_glfw.ns.text), _glfwGetKeyName(key));
|
||||||
|
if (is_ascii_control_char(_glfw.ns.text[0])) _glfw.ns.text[0] = 0; // dont send text for ascii control codes
|
||||||
|
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods, _glfw.ns.text, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)flagsChanged:(NSEvent *)event
|
- (void)flagsChanged:(NSEvent *)event
|
||||||
@@ -629,7 +845,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
int action;
|
int action;
|
||||||
const unsigned int modifierFlags =
|
const unsigned int modifierFlags =
|
||||||
[event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
[event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||||
const int key = translateKey([event keyCode]);
|
const int key = translateKey([event keyCode], GLFW_FALSE);
|
||||||
const int mods = translateFlags(modifierFlags);
|
const int mods = translateFlags(modifierFlags);
|
||||||
const NSUInteger keyFlag = translateKeyToModifierFlag(key);
|
const NSUInteger keyFlag = translateKeyToModifierFlag(key);
|
||||||
|
|
||||||
@@ -643,14 +859,14 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
else
|
else
|
||||||
action = GLFW_RELEASE;
|
action = GLFW_RELEASE;
|
||||||
|
|
||||||
_glfwInputKey(window, key, [event keyCode], action, mods);
|
_glfwInputKeyboard(window, key, [event keyCode], action, mods, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)keyUp:(NSEvent *)event
|
- (void)keyUp:(NSEvent *)event
|
||||||
{
|
{
|
||||||
const int key = translateKey([event keyCode]);
|
const int key = translateKey([event keyCode], GLFW_TRUE);
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
const int mods = translateFlags([event modifierFlags]);
|
||||||
_glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);
|
_glfwInputKeyboard(window, key, [event keyCode], GLFW_RELEASE, mods, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollWheel:(NSEvent *)event
|
- (void)scrollWheel:(NSEvent *)event
|
||||||
@@ -785,25 +1001,12 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||||
{
|
{
|
||||||
NSString* characters;
|
NSString* characters;
|
||||||
NSEvent* event = [NSApp currentEvent];
|
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
|
||||||
const int plain = !(mods & GLFW_MOD_SUPER);
|
|
||||||
|
|
||||||
if ([string isKindOfClass:[NSAttributedString class]])
|
if ([string isKindOfClass:[NSAttributedString class]])
|
||||||
characters = [string string];
|
characters = [string string];
|
||||||
else
|
else
|
||||||
characters = (NSString*) string;
|
characters = (NSString*) string;
|
||||||
|
snprintf(_glfw.ns.text, sizeof(_glfw.ns.text), "%s", [characters UTF8String]);
|
||||||
NSUInteger i, length = [characters length];
|
_glfw.ns.text[sizeof(_glfw.ns.text) - 1] = 0;
|
||||||
|
|
||||||
for (i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
const unichar codepoint = [characters characterAtIndex:i];
|
|
||||||
if ((codepoint & 0xff00) == 0xf700)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_glfwInputChar(window, codepoint, mods, plain);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)doCommandBySelector:(SEL)selector
|
- (void)doCommandBySelector:(SEL)selector
|
||||||
@@ -1136,6 +1339,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
|||||||
const _GLFWctxconfig* ctxconfig,
|
const _GLFWctxconfig* ctxconfig,
|
||||||
const _GLFWfbconfig* fbconfig)
|
const _GLFWfbconfig* fbconfig)
|
||||||
{
|
{
|
||||||
|
window->ns.deadKeyState = 0;
|
||||||
if (!initializeAppKit())
|
if (!initializeAppKit())
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
|
|
||||||
@@ -1358,7 +1562,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
[NSApp requestUserAttention:NSInformationalRequest];
|
[NSApp requestUserAttention:NSInformationalRequest];
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
NSBeep();
|
NSBeep();
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
@@ -1534,6 +1738,9 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
|||||||
|
|
||||||
void _glfwPlatformPollEvents(void)
|
void _glfwPlatformPollEvents(void)
|
||||||
{
|
{
|
||||||
|
if (!initializeAppKit())
|
||||||
|
return;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
||||||
@@ -1632,26 +1839,8 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
|
|||||||
|
|
||||||
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
||||||
{
|
{
|
||||||
if (mode == GLFW_CURSOR_DISABLED)
|
if (_glfwPlatformWindowFocused(window))
|
||||||
{
|
updateCursorMode(window);
|
||||||
_glfw.ns.disabledCursorWindow = window;
|
|
||||||
_glfwPlatformGetCursorPos(window,
|
|
||||||
&_glfw.ns.restoreCursorPosX,
|
|
||||||
&_glfw.ns.restoreCursorPosY);
|
|
||||||
centerCursor(window);
|
|
||||||
CGAssociateMouseAndMouseCursorPosition(false);
|
|
||||||
}
|
|
||||||
else if (_glfw.ns.disabledCursorWindow == window)
|
|
||||||
{
|
|
||||||
_glfw.ns.disabledCursorWindow = NULL;
|
|
||||||
CGAssociateMouseAndMouseCursorPosition(true);
|
|
||||||
_glfwPlatformSetCursorPos(window,
|
|
||||||
_glfw.ns.restoreCursorPosX,
|
|
||||||
_glfw.ns.restoreCursorPosY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursorInClientArea(window))
|
|
||||||
updateCursorImage(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||||
@@ -1677,16 +1866,7 @@ const char* _glfwPlatformGetScancodeName(int scancode)
|
|||||||
if (!characterCount)
|
if (!characterCount)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
|
convert_utf16_to_utf8(characters, characterCount, _glfw.ns.keyName, sizeof(_glfw.ns.keyName));
|
||||||
characters,
|
|
||||||
characterCount,
|
|
||||||
kCFAllocatorNull);
|
|
||||||
CFStringGetCString(string,
|
|
||||||
_glfw.ns.keyName,
|
|
||||||
sizeof(_glfw.ns.keyName),
|
|
||||||
kCFStringEncodingUTF8);
|
|
||||||
CFRelease(string);
|
|
||||||
|
|
||||||
return _glfw.ns.keyName;
|
return _glfw.ns.keyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1868,6 +2048,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
|
||||||
[window->ns.view setWantsLayer:YES];
|
[window->ns.view setWantsLayer:YES];
|
||||||
|
|
||||||
memset(&sci, 0, sizeof(sci));
|
memset(&sci, 0, sizeof(sci));
|
||||||
@@ -1900,3 +2081,10 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
|
|||||||
return window->ns.object;
|
return window->ns.object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLFWAPI GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow *handle, GLFWcocoatextinputfilterfun callback) {
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
|
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
|
||||||
|
GLFWcocoatextinputfilterfun previous = window->ns.textInputFilterCallback;
|
||||||
|
window->ns.textInputFilterCallback = callback;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|||||||
22
glfw/glfw.py
22
glfw/glfw.py
@@ -10,6 +10,10 @@ import sys
|
|||||||
|
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
is_macos = 'darwin' in _plat
|
is_macos = 'darwin' in _plat
|
||||||
|
is_freebsd = 'freebsd' in _plat
|
||||||
|
is_netbsd = 'netbsd' in _plat
|
||||||
|
is_dragonflybsd = 'dragonfly' in _plat
|
||||||
|
is_bsd = is_freebsd or is_netbsd or is_dragonflybsd
|
||||||
base = os.path.dirname(os.path.abspath(__file__))
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
@@ -42,8 +46,11 @@ def init_env(env, pkg_config, at_least_version, module='x11'):
|
|||||||
ans.sources = sinfo['common']['sources'] + sinfo[module]['sources']
|
ans.sources = sinfo['common']['sources'] + sinfo[module]['sources']
|
||||||
ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]
|
ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]
|
||||||
|
|
||||||
|
if module in ('x11', 'wayland'):
|
||||||
|
at_least_version('xkbcommon', 0, 5)
|
||||||
|
|
||||||
if module == 'x11':
|
if module == 'x11':
|
||||||
for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11'.split():
|
for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11 x11-xcb'.split():
|
||||||
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
|
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
|
||||||
ans.ldpaths.extend(pkg_config(dep, '--libs'))
|
ans.ldpaths.extend(pkg_config(dep, '--libs'))
|
||||||
|
|
||||||
@@ -94,19 +101,20 @@ def collect_source_information():
|
|||||||
).group(1).strip().split()
|
).group(1).strip().split()
|
||||||
)
|
)
|
||||||
|
|
||||||
wayland_protocols = re.search('WaylandProtocols\s+(\S+)\s+', mraw).group(1)
|
wayland_protocols = re.search(r'WaylandProtocols\s+(\S+)\s+', mraw).group(1)
|
||||||
wayland_protocols = list(map(int, wayland_protocols.split('.')))
|
wayland_protocols = list(map(int, wayland_protocols.split('.')))
|
||||||
ans = {
|
ans = {
|
||||||
'common': dict(extract_sources('common')),
|
'common': dict(extract_sources('common')),
|
||||||
'wayland_protocols': wayland_protocols,
|
'wayland_protocols': wayland_protocols,
|
||||||
}
|
}
|
||||||
|
joystick = 'null' if is_bsd else 'linux'
|
||||||
for group in 'cocoa win32 x11 wayland osmesa'.split():
|
for group in 'cocoa win32 x11 wayland osmesa'.split():
|
||||||
m = re.search('_GLFW_' + group.upper(), raw)
|
m = re.search('_GLFW_' + group.upper(), raw)
|
||||||
ans[group] = dict(extract_sources('glfw', m.start()))
|
ans[group] = dict(extract_sources('glfw', m.start()))
|
||||||
if group == 'x11':
|
if group in ('x11', 'wayland'):
|
||||||
ans[group]['headers'].append('linux_joystick.h')
|
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
|
||||||
ans[group]['sources'].append('linux_joystick.c')
|
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
|
||||||
elif group == 'wayland':
|
if group == 'wayland':
|
||||||
ans[group]['protocols'] = p = []
|
ans[group]['protocols'] = p = []
|
||||||
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
|
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
|
||||||
p.append(m.group(1))
|
p.append(m.group(1))
|
||||||
@@ -188,6 +196,7 @@ def generate_wrappers(glfw_header, glfw_native_header):
|
|||||||
for line in '''\
|
for line in '''\
|
||||||
void* glfwGetCocoaWindow(GLFWwindow* window)
|
void* glfwGetCocoaWindow(GLFWwindow* window)
|
||||||
uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor)
|
uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor)
|
||||||
|
GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback)
|
||||||
void* glfwGetX11Display(void)
|
void* glfwGetX11Display(void)
|
||||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||||
void glfwSetX11SelectionString(const char* string)
|
void glfwSetX11SelectionString(const char* string)
|
||||||
@@ -204,6 +213,7 @@ def generate_wrappers(glfw_header, glfw_native_header):
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int);
|
||||||
{}
|
{}
|
||||||
|
|
||||||
{}
|
{}
|
||||||
|
|||||||
206
glfw/glfw3.h
vendored
206
glfw/glfw3.h
vendored
@@ -1375,9 +1375,15 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
|||||||
*/
|
*/
|
||||||
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
||||||
|
|
||||||
/*! @brief The function signature for keyboard key callbacks.
|
/*! @brief The function signature for key callbacks.
|
||||||
*
|
*
|
||||||
* This is the function signature for keyboard key callback functions.
|
* This is the function signature for key callback functions.
|
||||||
|
* 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
|
||||||
|
* will report the text "+" and the key as GLFW_KEY_EQUAL. The reported key takes into
|
||||||
|
* account any current keyboard maps defined in the OS. So with a dvorak mapping, pressing
|
||||||
|
* the "s" key will generate text "o" and GLFW_KEY_O.
|
||||||
*
|
*
|
||||||
* @param[in] window The window that received the event.
|
* @param[in] window The window that received the event.
|
||||||
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
||||||
@@ -1385,56 +1391,22 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
|||||||
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
||||||
* held down.
|
* held down.
|
||||||
|
* @param[in] text UTF-8 encoded text generated by this key event or empty string.
|
||||||
|
* @param[in] reserved Reserved for future use.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* than the unshifted key. So for example, if ISO_Shift_Level_5 is used to
|
||||||
|
* convert the key A into UP GLFW will report the key as UP with no modifiers.
|
||||||
*
|
*
|
||||||
* @sa @ref input_key
|
* @sa @ref input_key
|
||||||
* @sa @ref glfwSetKeyCallback
|
* @sa @ref glfwSetKeyboardCallback
|
||||||
*
|
*
|
||||||
* @since Added in version 1.0.
|
* @since Added in version 4.0.
|
||||||
* @glfw3 Added window handle, scancode and modifier mask parameters.
|
|
||||||
*
|
*
|
||||||
* @ingroup input
|
* @ingroup input
|
||||||
*/
|
*/
|
||||||
typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int);
|
typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, int);
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character callback functions.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharCallback
|
|
||||||
*
|
|
||||||
* @since Added in version 2.4.
|
|
||||||
* @glfw3 Added window handle parameter.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character with modifiers
|
|
||||||
* callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character with modifiers callback
|
|
||||||
* functions. It is called for each input character, regardless of what
|
|
||||||
* modifier keys are held down.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
|
||||||
* held down.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharModsCallback
|
|
||||||
*
|
|
||||||
* @deprecated Scheduled for removal in version 4.0.
|
|
||||||
*
|
|
||||||
* @since Added in version 3.1.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for file drop callbacks.
|
/*! @brief The function signature for file drop callbacks.
|
||||||
*
|
*
|
||||||
@@ -2453,10 +2425,11 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
|
|||||||
* GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override
|
* GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override
|
||||||
* this.
|
* this.
|
||||||
*
|
*
|
||||||
* @remark @wayland The window frame is currently unimplemented, as if
|
* @remark @wayland The window frame is currently very simple, only allowing
|
||||||
* [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`.
|
* window resize or move. A compositor can still emit close, maximize or
|
||||||
* A compositor can still emit close, resize or maximize events, using for
|
* fullscreen events, using for example a keybind mechanism. Additionally,
|
||||||
* example a keybind mechanism.
|
* the wp_viewporter protocol is required for this feature, otherwise the
|
||||||
|
* window will not be decorated.
|
||||||
*
|
*
|
||||||
* @remark @wayland A full screen window will not attempt to change the mode,
|
* @remark @wayland A full screen window will not attempt to change the mode,
|
||||||
* no matter what the requested size or refresh rate.
|
* no matter what the requested size or refresh rate.
|
||||||
@@ -2898,10 +2871,6 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height)
|
|||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||||
* GLFW_PLATFORM_ERROR.
|
* GLFW_PLATFORM_ERROR.
|
||||||
*
|
*
|
||||||
* @remark @wayland The window frame is currently unimplemented, as if
|
|
||||||
* [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`,
|
|
||||||
* so the returned values will always be zero.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
* @thread_safety This function must only be called from the main thread.
|
||||||
*
|
*
|
||||||
* @sa @ref window_size
|
* @sa @ref window_size
|
||||||
@@ -3191,13 +3160,9 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
|
|||||||
/*! @brief Sounds an audible bell associated with the window
|
/*! @brief Sounds an audible bell associated with the window
|
||||||
*
|
*
|
||||||
* This function sounds an audible bell, on platforms where it is
|
* This function sounds an audible bell, on platforms where it is
|
||||||
* supported. Currently (macOS, Windows and X11).
|
* supported. Currently (macOS, Windows, X11 and Wayland).
|
||||||
*
|
*
|
||||||
* @param[in] window The window with which the bell is associated.
|
* @param[in] window The window with which the bell is associated.
|
||||||
* @param[in] param The meaning of this parameter is platform dependent. On
|
|
||||||
* X11 it corresponds to the percentage controlling bell volume (see man
|
|
||||||
* XBell). On Windows it is the type of sound to make, see the MSDN docs for
|
|
||||||
* MessageBeep. On macOS, it is ignored.
|
|
||||||
* @return GLFW_TRUE if the bell succeeded otherwise GLFW_FALSE
|
* @return GLFW_TRUE if the bell succeeded otherwise GLFW_FALSE
|
||||||
*
|
*
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||||
@@ -3212,7 +3177,7 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
|
|||||||
*
|
*
|
||||||
* @ingroup window
|
* @ingroup window
|
||||||
*/
|
*/
|
||||||
GLFWAPI int glfwWindowBell(GLFWwindow* window, int64_t param);
|
GLFWAPI int glfwWindowBell(GLFWwindow* window);
|
||||||
|
|
||||||
|
|
||||||
/*! @brief Returns the monitor that the window uses for full screen mode.
|
/*! @brief Returns the monitor that the window uses for full screen mode.
|
||||||
@@ -4239,121 +4204,11 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
|
|||||||
*/
|
*/
|
||||||
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
||||||
|
|
||||||
/*! @brief Sets the key callback.
|
/*! @brief Sets the callback for handling keyboard events.
|
||||||
*
|
|
||||||
* This function sets the key callback of the specified window, which is called
|
|
||||||
* when a key is pressed, repeated or released.
|
|
||||||
*
|
|
||||||
* The key functions deal with physical keys, with layout independent
|
|
||||||
* [key tokens](@ref keys) named after their values in the standard US keyboard
|
|
||||||
* layout. If you want to input text, use the
|
|
||||||
* [character callback](@ref glfwSetCharCallback) instead.
|
|
||||||
*
|
|
||||||
* When a window loses input focus, it will generate synthetic key release
|
|
||||||
* events for all pressed keys. You can tell these events from user-generated
|
|
||||||
* events by the fact that the synthetic ones are generated after the focus
|
|
||||||
* loss event has been processed, i.e. after the
|
|
||||||
* [window focus callback](@ref glfwSetWindowFocusCallback) has been called.
|
|
||||||
*
|
|
||||||
* The scancode of a key is specific to that platform or sometimes even to that
|
|
||||||
* machine. Scancodes are intended to allow users to bind keys that don't have
|
|
||||||
* a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their
|
|
||||||
* state is not saved and so it cannot be queried with @ref glfwGetKey.
|
|
||||||
*
|
|
||||||
* Sometimes GLFW needs to generate synthetic key events, in which case the
|
|
||||||
* scancode may be zero.
|
|
||||||
*
|
|
||||||
* @param[in] window The window whose callback to set.
|
|
||||||
* @param[in] cbfun The new key callback, or `NULL` to remove the currently
|
|
||||||
* set callback.
|
|
||||||
* @return The previously set callback, or `NULL` if no callback was set or the
|
|
||||||
* library had not been [initialized](@ref intro_init).
|
|
||||||
*
|
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
|
||||||
*
|
|
||||||
* @sa @ref input_key
|
|
||||||
*
|
|
||||||
* @since Added in version 1.0.
|
|
||||||
* @glfw3 Added window handle parameter and return value.
|
|
||||||
*
|
*
|
||||||
* @ingroup input
|
* @ingroup input
|
||||||
*/
|
*/
|
||||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun);
|
GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboardfun cbfun);
|
||||||
|
|
||||||
/*! @brief Sets the Unicode character callback.
|
|
||||||
*
|
|
||||||
* This function sets the character callback of the specified window, which is
|
|
||||||
* called when a Unicode character is input.
|
|
||||||
*
|
|
||||||
* The character callback is intended for Unicode text input. As it deals with
|
|
||||||
* characters, it is keyboard layout dependent, whereas the
|
|
||||||
* [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1
|
|
||||||
* to physical keys, as a key may produce zero, one or more characters. If you
|
|
||||||
* want to know whether a specific physical key was pressed or released, see
|
|
||||||
* the key callback instead.
|
|
||||||
*
|
|
||||||
* The character callback behaves as system text input normally does and will
|
|
||||||
* not be called if modifier keys are held down that would prevent normal text
|
|
||||||
* input on that platform, for example a Super (Command) key on macOS or Alt key
|
|
||||||
* on Windows. There is a
|
|
||||||
* [character with modifiers callback](@ref glfwSetCharModsCallback) that
|
|
||||||
* receives these events.
|
|
||||||
*
|
|
||||||
* @param[in] window The window whose callback to set.
|
|
||||||
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
|
|
||||||
* callback.
|
|
||||||
* @return The previously set callback, or `NULL` if no callback was set or the
|
|
||||||
* library had not been [initialized](@ref intro_init).
|
|
||||||
*
|
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
*
|
|
||||||
* @since Added in version 2.4.
|
|
||||||
* @glfw3 Added window handle parameter and return value.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun);
|
|
||||||
|
|
||||||
/*! @brief Sets the Unicode character with modifiers callback.
|
|
||||||
*
|
|
||||||
* This function sets the character with modifiers callback of the specified
|
|
||||||
* window, which is called when a Unicode character is input regardless of what
|
|
||||||
* modifier keys are used.
|
|
||||||
*
|
|
||||||
* The character with modifiers callback is intended for implementing custom
|
|
||||||
* Unicode character input. For regular Unicode text input, see the
|
|
||||||
* [character callback](@ref glfwSetCharCallback). Like the character
|
|
||||||
* callback, the character with modifiers callback deals with characters and is
|
|
||||||
* keyboard layout dependent. Characters do not map 1:1 to physical keys, as
|
|
||||||
* a key may produce zero, one or more characters. If you want to know whether
|
|
||||||
* a specific physical key was pressed or released, see the
|
|
||||||
* [key callback](@ref glfwSetKeyCallback) instead.
|
|
||||||
*
|
|
||||||
* @param[in] window The window whose callback to set.
|
|
||||||
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
|
|
||||||
* callback.
|
|
||||||
* @return The previously set callback, or `NULL` if no callback was set or an
|
|
||||||
* [error](@ref error_handling) occurred.
|
|
||||||
*
|
|
||||||
* @deprecated Scheduled for removal in version 4.0.
|
|
||||||
*
|
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
*
|
|
||||||
* @since Added in version 3.1.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun);
|
|
||||||
|
|
||||||
/*! @brief Sets the mouse button callback.
|
/*! @brief Sets the mouse button callback.
|
||||||
*
|
*
|
||||||
@@ -4727,7 +4582,7 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid);
|
|||||||
* This function may be called from the joystick callback, even for a joystick
|
* This function may be called from the joystick callback, even for a joystick
|
||||||
* that is being disconnected.
|
* that is being disconnected.
|
||||||
*
|
*
|
||||||
* @param[in] joystick The joystick whose pointer to set.
|
* @param[in] jid The joystick whose pointer to set.
|
||||||
* @param[in] pointer The new value.
|
* @param[in] pointer The new value.
|
||||||
*
|
*
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||||
@@ -4752,7 +4607,7 @@ GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer);
|
|||||||
* This function may be called from the joystick callback, even for a joystick
|
* This function may be called from the joystick callback, even for a joystick
|
||||||
* that is being disconnected.
|
* that is being disconnected.
|
||||||
*
|
*
|
||||||
* @param[in] joystick The joystick whose pointer to return.
|
* @param[in] jid The joystick whose pointer to return.
|
||||||
*
|
*
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||||
*
|
*
|
||||||
@@ -4904,7 +4759,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
|
|||||||
*
|
*
|
||||||
* Not all devices have all the buttons or axes provided by @ref
|
* Not all devices have all the buttons or axes provided by @ref
|
||||||
* GLFWgamepadstate. Unavailable buttons and axes will always report
|
* GLFWgamepadstate. Unavailable buttons and axes will always report
|
||||||
* `GLFW_RELEASE` and 1.0 respectively.
|
* `GLFW_RELEASE` and 0.0 respectively.
|
||||||
*
|
*
|
||||||
* @param[in] jid The [joystick](@ref joysticks) to query.
|
* @param[in] jid The [joystick](@ref joysticks) to query.
|
||||||
* @param[out] state The gamepad input state of the joystick.
|
* @param[out] state The gamepad input state of the joystick.
|
||||||
@@ -5549,4 +5404,3 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _glfw3_h_ */
|
#endif /* _glfw3_h_ */
|
||||||
|
|
||||||
|
|||||||
56
glfw/init.c
vendored
56
glfw/init.c
vendored
@@ -57,37 +57,6 @@ static _GLFWinitconfig _glfwInitHints =
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns a generic string representation of the specified error
|
|
||||||
//
|
|
||||||
static const char* getErrorString(int code)
|
|
||||||
{
|
|
||||||
switch (code)
|
|
||||||
{
|
|
||||||
case GLFW_NOT_INITIALIZED:
|
|
||||||
return "The GLFW library is not initialized";
|
|
||||||
case GLFW_NO_CURRENT_CONTEXT:
|
|
||||||
return "There is no current context";
|
|
||||||
case GLFW_INVALID_ENUM:
|
|
||||||
return "Invalid argument for enum parameter";
|
|
||||||
case GLFW_INVALID_VALUE:
|
|
||||||
return "Invalid value for parameter";
|
|
||||||
case GLFW_OUT_OF_MEMORY:
|
|
||||||
return "Out of memory";
|
|
||||||
case GLFW_API_UNAVAILABLE:
|
|
||||||
return "The requested API is unavailable";
|
|
||||||
case GLFW_VERSION_UNAVAILABLE:
|
|
||||||
return "The requested API version is unavailable";
|
|
||||||
case GLFW_PLATFORM_ERROR:
|
|
||||||
return "An undocumented platform-specific error occurred";
|
|
||||||
case GLFW_FORMAT_UNAVAILABLE:
|
|
||||||
return "The requested format is unavailable";
|
|
||||||
case GLFW_NO_WINDOW_CONTEXT:
|
|
||||||
return "The specified window has no context";
|
|
||||||
default:
|
|
||||||
return "ERROR: UNKNOWN GLFW ERROR";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Terminate the library
|
// Terminate the library
|
||||||
//
|
//
|
||||||
static void terminate(void)
|
static void terminate(void)
|
||||||
@@ -173,7 +142,30 @@ void _glfwInputError(int code, const char* format, ...)
|
|||||||
description[sizeof(description) - 1] = '\0';
|
description[sizeof(description) - 1] = '\0';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
strcpy(description, getErrorString(code));
|
{
|
||||||
|
if (code == GLFW_NOT_INITIALIZED)
|
||||||
|
strcpy(description, "The GLFW library is not initialized");
|
||||||
|
else if (code == GLFW_NO_CURRENT_CONTEXT)
|
||||||
|
strcpy(description, "There is no current context");
|
||||||
|
else if (code == GLFW_INVALID_ENUM)
|
||||||
|
strcpy(description, "Invalid argument for enum parameter");
|
||||||
|
else if (code == GLFW_INVALID_VALUE)
|
||||||
|
strcpy(description, "Invalid value for parameter");
|
||||||
|
else if (code == GLFW_OUT_OF_MEMORY)
|
||||||
|
strcpy(description, "Out of memory");
|
||||||
|
else if (code == GLFW_API_UNAVAILABLE)
|
||||||
|
strcpy(description, "The requested API is unavailable");
|
||||||
|
else if (code == GLFW_VERSION_UNAVAILABLE)
|
||||||
|
strcpy(description, "The requested API version is unavailable");
|
||||||
|
else if (code == GLFW_PLATFORM_ERROR)
|
||||||
|
strcpy(description, "A platform-specific error occurred");
|
||||||
|
else if (code == GLFW_FORMAT_UNAVAILABLE)
|
||||||
|
strcpy(description, "The requested format is unavailable");
|
||||||
|
else if (code == GLFW_NO_WINDOW_CONTEXT)
|
||||||
|
strcpy(description, "The specified window has no context");
|
||||||
|
else
|
||||||
|
strcpy(description, "ERROR: UNKNOWN GLFW ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
if (_glfw.initialized)
|
if (_glfw.initialized)
|
||||||
{
|
{
|
||||||
|
|||||||
274
glfw/input.c
vendored
274
glfw/input.c
vendored
@@ -62,11 +62,11 @@ static _GLFWmapping* findMapping(const char* guid)
|
|||||||
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
|
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
|
||||||
const _GLFWjoystick* js)
|
const _GLFWjoystick* js)
|
||||||
{
|
{
|
||||||
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->value >> 4) >= js->hatCount)
|
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount)
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->value >= js->buttonCount)
|
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount)
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
else if (e->type == _GLFW_JOYSTICK_AXIS && e->value >= js->axisCount)
|
else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount)
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
@@ -167,6 +167,10 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
|||||||
|
|
||||||
while (*c)
|
while (*c)
|
||||||
{
|
{
|
||||||
|
// TODO: Implement output modifiers
|
||||||
|
if (*c == '+' || *c == '-')
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
|
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
|
||||||
{
|
{
|
||||||
length = strlen(fields[i].name);
|
length = strlen(fields[i].name);
|
||||||
@@ -177,23 +181,50 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
|||||||
|
|
||||||
if (fields[i].element)
|
if (fields[i].element)
|
||||||
{
|
{
|
||||||
|
_GLFWmapelement* e = fields[i].element;
|
||||||
|
int8_t minimum = -1;
|
||||||
|
int8_t maximum = 1;
|
||||||
|
|
||||||
|
if (*c == '+')
|
||||||
|
{
|
||||||
|
minimum = 0;
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
else if (*c == '-')
|
||||||
|
{
|
||||||
|
maximum = 0;
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (*c == 'a')
|
if (*c == 'a')
|
||||||
fields[i].element->type = _GLFW_JOYSTICK_AXIS;
|
e->type = _GLFW_JOYSTICK_AXIS;
|
||||||
else if (*c == 'b')
|
else if (*c == 'b')
|
||||||
fields[i].element->type = _GLFW_JOYSTICK_BUTTON;
|
e->type = _GLFW_JOYSTICK_BUTTON;
|
||||||
else if (*c == 'h')
|
else if (*c == 'h')
|
||||||
fields[i].element->type = _GLFW_JOYSTICK_HATBIT;
|
e->type = _GLFW_JOYSTICK_HATBIT;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (fields[i].element->type == _GLFW_JOYSTICK_HATBIT)
|
if (e->type == _GLFW_JOYSTICK_HATBIT)
|
||||||
{
|
{
|
||||||
const unsigned long hat = strtoul(c + 1, (char**) &c, 10);
|
const unsigned long hat = strtoul(c + 1, (char**) &c, 10);
|
||||||
const unsigned long bit = strtoul(c + 1, (char**) &c, 10);
|
const unsigned long bit = strtoul(c + 1, (char**) &c, 10);
|
||||||
fields[i].element->value = (uint8_t) ((hat << 4) | bit);
|
e->index = (uint8_t) ((hat << 4) | bit);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fields[i].element->value = (uint8_t) strtoul(c + 1, (char**) &c, 10);
|
e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10);
|
||||||
|
|
||||||
|
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||||
|
{
|
||||||
|
e->axisScale = 2 / (maximum - minimum);
|
||||||
|
e->axisOffset = -(maximum + minimum);
|
||||||
|
|
||||||
|
if (*c == '~')
|
||||||
|
{
|
||||||
|
e->axisScale = -e->axisScale;
|
||||||
|
e->axisOffset = -e->axisOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -224,9 +255,9 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
|||||||
////// GLFW event API //////
|
////// GLFW event API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Notifies shared code of a physical key event
|
// Notifies shared code of a key event
|
||||||
//
|
//
|
||||||
void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods)
|
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state)
|
||||||
{
|
{
|
||||||
if (key >= 0 && key <= GLFW_KEY_LAST)
|
if (key >= 0 && key <= GLFW_KEY_LAST)
|
||||||
{
|
{
|
||||||
@@ -247,31 +278,10 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m
|
|||||||
action = GLFW_REPEAT;
|
action = GLFW_REPEAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!window->lockKeyMods)
|
|
||||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
|
||||||
|
|
||||||
if (window->callbacks.key)
|
if (window->callbacks.keyboard) {
|
||||||
window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods);
|
if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||||
}
|
window->callbacks.keyboard((GLFWwindow*) window, key, scancode, action, mods, text, state);
|
||||||
|
|
||||||
// Notifies shared code of a Unicode codepoint input event
|
|
||||||
// The 'plain' parameter determines whether to emit a regular character event
|
|
||||||
//
|
|
||||||
void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain)
|
|
||||||
{
|
|
||||||
if (codepoint < 32 || (codepoint > 126 && codepoint < 160))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!window->lockKeyMods)
|
|
||||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
|
||||||
|
|
||||||
if (window->callbacks.charmods)
|
|
||||||
window->callbacks.charmods((GLFWwindow*) window, codepoint, mods);
|
|
||||||
|
|
||||||
if (plain)
|
|
||||||
{
|
|
||||||
if (window->callbacks.character)
|
|
||||||
window->callbacks.character((GLFWwindow*) window, codepoint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,6 +433,137 @@ void _glfwFreeJoystick(_GLFWjoystick* js)
|
|||||||
memset(js, 0, sizeof(_GLFWjoystick));
|
memset(js, 0, sizeof(_GLFWjoystick));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* _glfwGetKeyName(int key)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
// Printable keys
|
||||||
|
case GLFW_KEY_A: return "A";
|
||||||
|
case GLFW_KEY_B: return "B";
|
||||||
|
case GLFW_KEY_C: return "C";
|
||||||
|
case GLFW_KEY_D: return "D";
|
||||||
|
case GLFW_KEY_E: return "E";
|
||||||
|
case GLFW_KEY_F: return "F";
|
||||||
|
case GLFW_KEY_G: return "G";
|
||||||
|
case GLFW_KEY_H: return "H";
|
||||||
|
case GLFW_KEY_I: return "I";
|
||||||
|
case GLFW_KEY_J: return "J";
|
||||||
|
case GLFW_KEY_K: return "K";
|
||||||
|
case GLFW_KEY_L: return "L";
|
||||||
|
case GLFW_KEY_M: return "M";
|
||||||
|
case GLFW_KEY_N: return "N";
|
||||||
|
case GLFW_KEY_O: return "O";
|
||||||
|
case GLFW_KEY_P: return "P";
|
||||||
|
case GLFW_KEY_Q: return "Q";
|
||||||
|
case GLFW_KEY_R: return "R";
|
||||||
|
case GLFW_KEY_S: return "S";
|
||||||
|
case GLFW_KEY_T: return "T";
|
||||||
|
case GLFW_KEY_U: return "U";
|
||||||
|
case GLFW_KEY_V: return "V";
|
||||||
|
case GLFW_KEY_W: return "W";
|
||||||
|
case GLFW_KEY_X: return "X";
|
||||||
|
case GLFW_KEY_Y: return "Y";
|
||||||
|
case GLFW_KEY_Z: return "Z";
|
||||||
|
case GLFW_KEY_1: return "1";
|
||||||
|
case GLFW_KEY_2: return "2";
|
||||||
|
case GLFW_KEY_3: return "3";
|
||||||
|
case GLFW_KEY_4: return "4";
|
||||||
|
case GLFW_KEY_5: return "5";
|
||||||
|
case GLFW_KEY_6: return "6";
|
||||||
|
case GLFW_KEY_7: return "7";
|
||||||
|
case GLFW_KEY_8: return "8";
|
||||||
|
case GLFW_KEY_9: return "9";
|
||||||
|
case GLFW_KEY_0: return "0";
|
||||||
|
case GLFW_KEY_SPACE: return "SPACE";
|
||||||
|
case GLFW_KEY_MINUS: return "MINUS";
|
||||||
|
case GLFW_KEY_EQUAL: return "EQUAL";
|
||||||
|
case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET";
|
||||||
|
case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET";
|
||||||
|
case GLFW_KEY_BACKSLASH: return "BACKSLASH";
|
||||||
|
case GLFW_KEY_SEMICOLON: return "SEMICOLON";
|
||||||
|
case GLFW_KEY_APOSTROPHE: return "APOSTROPHE";
|
||||||
|
case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT";
|
||||||
|
case GLFW_KEY_COMMA: return "COMMA";
|
||||||
|
case GLFW_KEY_PERIOD: return "PERIOD";
|
||||||
|
case GLFW_KEY_SLASH: return "SLASH";
|
||||||
|
case GLFW_KEY_WORLD_1: return "WORLD 1";
|
||||||
|
case GLFW_KEY_WORLD_2: return "WORLD 2";
|
||||||
|
|
||||||
|
// Function keys
|
||||||
|
case GLFW_KEY_ESCAPE: return "ESCAPE";
|
||||||
|
case GLFW_KEY_F1: return "F1";
|
||||||
|
case GLFW_KEY_F2: return "F2";
|
||||||
|
case GLFW_KEY_F3: return "F3";
|
||||||
|
case GLFW_KEY_F4: return "F4";
|
||||||
|
case GLFW_KEY_F5: return "F5";
|
||||||
|
case GLFW_KEY_F6: return "F6";
|
||||||
|
case GLFW_KEY_F7: return "F7";
|
||||||
|
case GLFW_KEY_F8: return "F8";
|
||||||
|
case GLFW_KEY_F9: return "F9";
|
||||||
|
case GLFW_KEY_F10: return "F10";
|
||||||
|
case GLFW_KEY_F11: return "F11";
|
||||||
|
case GLFW_KEY_F12: return "F12";
|
||||||
|
case GLFW_KEY_F13: return "F13";
|
||||||
|
case GLFW_KEY_F14: return "F14";
|
||||||
|
case GLFW_KEY_F15: return "F15";
|
||||||
|
case GLFW_KEY_F16: return "F16";
|
||||||
|
case GLFW_KEY_F17: return "F17";
|
||||||
|
case GLFW_KEY_F18: return "F18";
|
||||||
|
case GLFW_KEY_F19: return "F19";
|
||||||
|
case GLFW_KEY_F20: return "F20";
|
||||||
|
case GLFW_KEY_F21: return "F21";
|
||||||
|
case GLFW_KEY_F22: return "F22";
|
||||||
|
case GLFW_KEY_F23: return "F23";
|
||||||
|
case GLFW_KEY_F24: return "F24";
|
||||||
|
case GLFW_KEY_F25: return "F25";
|
||||||
|
case GLFW_KEY_UP: return "UP";
|
||||||
|
case GLFW_KEY_DOWN: return "DOWN";
|
||||||
|
case GLFW_KEY_LEFT: return "LEFT";
|
||||||
|
case GLFW_KEY_RIGHT: return "RIGHT";
|
||||||
|
case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT";
|
||||||
|
case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT";
|
||||||
|
case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL";
|
||||||
|
case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL";
|
||||||
|
case GLFW_KEY_LEFT_ALT: return "LEFT ALT";
|
||||||
|
case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT";
|
||||||
|
case GLFW_KEY_TAB: return "TAB";
|
||||||
|
case GLFW_KEY_ENTER: return "ENTER";
|
||||||
|
case GLFW_KEY_BACKSPACE: return "BACKSPACE";
|
||||||
|
case GLFW_KEY_INSERT: return "INSERT";
|
||||||
|
case GLFW_KEY_DELETE: return "DELETE";
|
||||||
|
case GLFW_KEY_PAGE_UP: return "PAGE UP";
|
||||||
|
case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN";
|
||||||
|
case GLFW_KEY_HOME: return "HOME";
|
||||||
|
case GLFW_KEY_END: return "END";
|
||||||
|
case GLFW_KEY_KP_0: return "KEYPAD 0";
|
||||||
|
case GLFW_KEY_KP_1: return "KEYPAD 1";
|
||||||
|
case GLFW_KEY_KP_2: return "KEYPAD 2";
|
||||||
|
case GLFW_KEY_KP_3: return "KEYPAD 3";
|
||||||
|
case GLFW_KEY_KP_4: return "KEYPAD 4";
|
||||||
|
case GLFW_KEY_KP_5: return "KEYPAD 5";
|
||||||
|
case GLFW_KEY_KP_6: return "KEYPAD 6";
|
||||||
|
case GLFW_KEY_KP_7: return "KEYPAD 7";
|
||||||
|
case GLFW_KEY_KP_8: return "KEYPAD 8";
|
||||||
|
case GLFW_KEY_KP_9: return "KEYPAD 9";
|
||||||
|
case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE";
|
||||||
|
case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTPLY";
|
||||||
|
case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT";
|
||||||
|
case GLFW_KEY_KP_ADD: return "KEYPAD ADD";
|
||||||
|
case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL";
|
||||||
|
case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL";
|
||||||
|
case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER";
|
||||||
|
case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN";
|
||||||
|
case GLFW_KEY_NUM_LOCK: return "NUM LOCK";
|
||||||
|
case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK";
|
||||||
|
case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK";
|
||||||
|
case GLFW_KEY_PAUSE: return "PAUSE";
|
||||||
|
case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER";
|
||||||
|
case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER";
|
||||||
|
case GLFW_KEY_MENU: return "MENU";
|
||||||
|
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW public API //////
|
////// GLFW public API //////
|
||||||
@@ -478,9 +619,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
|||||||
_glfwPlatformGetCursorPos(window,
|
_glfwPlatformGetCursorPos(window,
|
||||||
&window->virtualCursorPosX,
|
&window->virtualCursorPosX,
|
||||||
&window->virtualCursorPosY);
|
&window->virtualCursorPosY);
|
||||||
|
_glfwPlatformSetCursorMode(window, value);
|
||||||
if (_glfwPlatformWindowFocused(window))
|
|
||||||
_glfwPlatformSetCursorMode(window, value);
|
|
||||||
}
|
}
|
||||||
else if (mode == GLFW_STICKY_KEYS)
|
else if (mode == GLFW_STICKY_KEYS)
|
||||||
{
|
{
|
||||||
@@ -760,33 +899,13 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
|
|||||||
_glfwPlatformSetCursor(window, cursor);
|
_glfwPlatformSetCursor(window, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboardfun cbfun)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
assert(window != NULL);
|
assert(window != NULL);
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
_GLFW_SWAP_POINTERS(window->callbacks.key, cbfun);
|
_GLFW_SWAP_POINTERS(window->callbacks.keyboard, cbfun);
|
||||||
return cbfun;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun)
|
|
||||||
{
|
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
|
||||||
assert(window != NULL);
|
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
||||||
_GLFW_SWAP_POINTERS(window->callbacks.character, cbfun);
|
|
||||||
return cbfun;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun)
|
|
||||||
{
|
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
|
||||||
assert(window != NULL);
|
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
||||||
_GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun);
|
|
||||||
return cbfun;
|
return cbfun;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,35 +1307,41 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
|||||||
|
|
||||||
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
|
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
|
||||||
{
|
{
|
||||||
if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_AXIS)
|
const _GLFWmapelement* e = js->mapping->buttons + i;
|
||||||
|
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||||
{
|
{
|
||||||
if (fabsf(js->axes[js->mapping->buttons[i].value]) > 0.5f)
|
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
|
||||||
|
if (value > 0.f)
|
||||||
state->buttons[i] = GLFW_PRESS;
|
state->buttons[i] = GLFW_PRESS;
|
||||||
}
|
}
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
|
else if (e->type == _GLFW_JOYSTICK_HATBIT)
|
||||||
{
|
{
|
||||||
const unsigned int hat = js->mapping->buttons[i].value >> 4;
|
const unsigned int hat = e->index >> 4;
|
||||||
const unsigned int bit = js->mapping->buttons[i].value & 0xf;
|
const unsigned int bit = e->index & 0xf;
|
||||||
if (js->hats[hat] & bit)
|
if (js->hats[hat] & bit)
|
||||||
state->buttons[i] = GLFW_PRESS;
|
state->buttons[i] = GLFW_PRESS;
|
||||||
}
|
}
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
|
else if (e->type == _GLFW_JOYSTICK_BUTTON)
|
||||||
state->buttons[i] = js->buttons[js->mapping->buttons[i].value];
|
state->buttons[i] = js->buttons[e->index];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
|
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
|
||||||
{
|
{
|
||||||
if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS)
|
const _GLFWmapelement* e = js->mapping->axes + i;
|
||||||
state->axes[i] = js->axes[js->mapping->axes[i].value];
|
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
|
|
||||||
{
|
{
|
||||||
const unsigned int hat = js->mapping->axes[i].value >> 4;
|
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
|
||||||
const unsigned int bit = js->mapping->axes[i].value & 0xf;
|
state->axes[i] = fminf(fmaxf(value, -1.f), 1.f);
|
||||||
|
}
|
||||||
|
else if (e->type == _GLFW_JOYSTICK_HATBIT)
|
||||||
|
{
|
||||||
|
const unsigned int hat = e->index >> 4;
|
||||||
|
const unsigned int bit = e->index & 0xf;
|
||||||
if (js->hats[hat] & bit)
|
if (js->hats[hat] & bit)
|
||||||
state->axes[i] = 1.f;
|
state->axes[i] = 1.f;
|
||||||
}
|
}
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
|
else if (e->type == _GLFW_JOYSTICK_BUTTON)
|
||||||
state->axes[i] = (float) js->buttons[js->mapping->axes[i].value];
|
state->axes[i] = (float) js->buttons[e->index];
|
||||||
}
|
}
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
@@ -1268,4 +1393,3 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||||
return _glfwPlatformGetTimerFrequency();
|
return _glfwPlatformGetTimerFrequency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
glfw/internal.h
vendored
20
glfw/internal.h
vendored
@@ -128,7 +128,7 @@ typedef enum VkStructureType
|
|||||||
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
|
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
|
||||||
VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
|
VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
|
||||||
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
|
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
|
||||||
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000053000,
|
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
|
||||||
VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
} VkStructureType;
|
} VkStructureType;
|
||||||
|
|
||||||
@@ -407,9 +407,7 @@ struct _GLFWwindow
|
|||||||
GLFWcursorposfun cursorPos;
|
GLFWcursorposfun cursorPos;
|
||||||
GLFWcursorenterfun cursorEnter;
|
GLFWcursorenterfun cursorEnter;
|
||||||
GLFWscrollfun scroll;
|
GLFWscrollfun scroll;
|
||||||
GLFWkeyfun key;
|
GLFWkeyboardfun keyboard;
|
||||||
GLFWcharfun character;
|
|
||||||
GLFWcharmodsfun charmods;
|
|
||||||
GLFWdropfun drop;
|
GLFWdropfun drop;
|
||||||
} callbacks;
|
} callbacks;
|
||||||
|
|
||||||
@@ -456,7 +454,9 @@ struct _GLFWcursor
|
|||||||
struct _GLFWmapelement
|
struct _GLFWmapelement
|
||||||
{
|
{
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t value;
|
uint8_t index;
|
||||||
|
int8_t axisScale;
|
||||||
|
int8_t axisOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gamepad mapping structure
|
// Gamepad mapping structure
|
||||||
@@ -605,6 +605,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
|||||||
const char* _glfwPlatformGetScancodeName(int scancode);
|
const char* _glfwPlatformGetScancodeName(int scancode);
|
||||||
int _glfwPlatformGetKeyScancode(int key);
|
int _glfwPlatformGetKeyScancode(int key);
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||||
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||||
float* xscale, float* yscale);
|
float* xscale, float* yscale);
|
||||||
@@ -650,7 +651,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
|
|||||||
void _glfwPlatformShowWindow(_GLFWwindow* window);
|
void _glfwPlatformShowWindow(_GLFWwindow* window);
|
||||||
void _glfwPlatformHideWindow(_GLFWwindow* window);
|
void _glfwPlatformHideWindow(_GLFWwindow* window);
|
||||||
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
|
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t);
|
int _glfwPlatformWindowBell(_GLFWwindow* window);
|
||||||
void _glfwPlatformFocusWindow(_GLFWwindow* window);
|
void _glfwPlatformFocusWindow(_GLFWwindow* window);
|
||||||
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
|
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
|
||||||
int xpos, int ypos, int width, int height,
|
int xpos, int ypos, int width, int height,
|
||||||
@@ -708,10 +709,7 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
|
|||||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||||
|
|
||||||
void _glfwInputKey(_GLFWwindow* window,
|
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state);
|
||||||
int key, int scancode, int action, int mods);
|
|
||||||
void _glfwInputChar(_GLFWwindow* window,
|
|
||||||
unsigned int codepoint, int mods, GLFWbool plain);
|
|
||||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
||||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||||
@@ -760,10 +758,10 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name,
|
|||||||
int buttonCount,
|
int buttonCount,
|
||||||
int hatCount);
|
int hatCount);
|
||||||
void _glfwFreeJoystick(_GLFWjoystick* js);
|
void _glfwFreeJoystick(_GLFWjoystick* js);
|
||||||
|
const char* _glfwGetKeyName(int key);
|
||||||
|
|
||||||
GLFWbool _glfwInitVulkan(int mode);
|
GLFWbool _glfwInitVulkan(int mode);
|
||||||
void _glfwTerminateVulkan(void);
|
void _glfwTerminateVulkan(void);
|
||||||
const char* _glfwGetVulkanResultString(VkResult result);
|
const char* _glfwGetVulkanResultString(VkResult result);
|
||||||
|
|
||||||
char* _glfw_strdup(const char* source);
|
char* _glfw_strdup(const char* source);
|
||||||
|
|
||||||
|
|||||||
624
glfw/mappings.h
vendored
624
glfw/mappings.h
vendored
@@ -60,243 +60,411 @@
|
|||||||
|
|
||||||
const char* _glfwDefaultMappings[] =
|
const char* _glfwDefaultMappings[] =
|
||||||
{
|
{
|
||||||
"02200090000000000000504944564944,8Bitdo NES30 PRO USB,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,",
|
||||||
"20380900000000000000504944564944,8Bitdo NES30 PRO Wireless,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
|
||||||
"10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,y:b3,x:b4,start:b11,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,platform:Windows,",
|
"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
|
||||||
"8f0e1200000000000000504944564944,Acme,platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
|
||||||
"341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
|
||||||
"c0111352000000000000504944564944,Battalife Joystick,platform:Windows,x:b4,a:b6,b:b7,y:b5,back:b2,start:b3,leftshoulder:b0,rightshoulder:b1,leftx:a0,lefty:a1,",
|
"030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"d81d0b00000000000000504944564944,BUFFALO BSGP1601 Series ,platform:Windows,x:b4,a:b5,b:b3,y:b2,back:b12,start:b13,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b6,rightshoulder:b9,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"5e048e02000000000000504944564944,Controller (XBOX 360 For Windows),platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,righttrigger:a2,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,",
|
||||||
"4f0423b3000000000000504944564944,Dual Trigger 3-in-1,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"341a0108000000000000504944564944,EXEQ RF USB Gamepad 8206,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,leftstick:b8,rightstick:b7,back:b8,start:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
|
"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f8500000000000000504944564944,Fighting Commander 2016 PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,",
|
||||||
"0d0f5f00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f5e00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:a3,platform:Windows,",
|
"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f8400000000000000504944564944,Fighting Commander 5,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f8700000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f8800000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b9,guide:b12,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f2700000000000000504944564944,FIGHTING STICK V3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79000600000000000000504944564944,G-Shark GS-GP702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"28040140000000000000504944564944,GamePad Pro USB,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
|
"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"45130010000000000000504944564944,Generic USB Joystick,a:b0,b:b1,x:b2,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f4900000000000000504944564944,Hatsune Miku Sho Controller,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d8140862000000000000504944564944,HitBox Edition Cthulhu+,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
|
"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f4000000000000000504944564944,Hori Fighting Stick Mini 3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
|
"03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f6e00000000000000504944564944,HORIPAD 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f4d00000000000000504944564944,HORIPAD3 A,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"25090017000000000000504944564944,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b5,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b6,platform:Windows,",
|
"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d81d0f00000000000000504944564944,iBUFFALO BSGP1204 Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"d81d1000000000000000504944564944,iBUFFALO BSGP1204P Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"83056020000000000000504944564944,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6f0e2401000000000000504944564944,INJUSTICE FightStick for PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Windows",
|
"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"49190204000000000000504944564944,Ipega PG-9023,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,",
|
"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0418c2000000000000504944564944,Logitech RumblePad 2 USB,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38075032000000000000504944564944,Mad Catz FightPad PRO PS3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38075082000000000000504944564944,Mad Catz FightPad PRO PS4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b13,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078433000000000000504944564944,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078483000000000000504944564944,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:b6,platform:Windows,",
|
"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078134000000000000504944564944,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b7,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b4,platform:Windows,",
|
"030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078184000000000000504944564944,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:b7,platform:Windows,",
|
"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,",
|
||||||
"38078034000000000000504944564944,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"38078084000000000000504944564944,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"38078532000000000000504944564944,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"38073888000000000000504944564944,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,",
|
||||||
"38071888000000000000504944564944,MadCatz SFIV FightStick PS3,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b6,platform:Windows,",
|
"03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"25090128000000000000504944564944,Mayflash Arcade Stick,a:b1,b:b2,x:b5,y:b6,back:b8,start:b9,leftshoulder:b0,rightshoulder:b3,leftx:a0,lefty:a1,rightx:h0.4,righty:h0.0,lefttrigger:b4,righttrigger:b7,platform:Windows,",
|
"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
|
||||||
"79004318000000000000504944564944,Mayflash GameCube Controller Adapter,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b0,start:b9,guide:b0,leftshoulder:b4,rightshoulder:b7,leftstick:b0,rightstick:b0,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,",
|
||||||
"8f0e1030000000000000504944564944,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,x:b3,y:b4,start:b9,leftshoulder:b6,rightshoulder:b2,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"8f0e0d31000000000000504944564944,Multilaser JS071 USB,platform:Windows,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"bd1215d0000000000000504944564944,Nintendo Retrolink USB Super SNES Classic Controller,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,start:b9,back:b8,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftstick:a0,rightstick:a2,leftshoulder:a3,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:h0.6,lefty:h0.12,rightx:h0.9,righty:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"36280100000000000000504944564944,OUYA Controller,platform:Windows,a:b0,b:b3,y:b2,x:b1,start:b14,guide:b15,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b8,dpleft:b10,dpdown:b9,dpright:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b12,righttrigger:b13,",
|
"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"120cf60e000000000000504944564944,P4 Wired Gamepad,a:b1,b:b2,x:b0,y:b3,back:b12,guide:b8,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.0,lefttrigger:b7,righttrigger:b6,platform:Windows,",
|
"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"8f0e0300000000000000504944564944,Piranha xtreme,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d6206dca000000000000504944564944,PowerA Pro Ex,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"10080100000000000000504944564944,PS1 USB,platform:Windows,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"10080300000000000000504944564944,PS2 USB,platform:Windows,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a4,righty:a2,lefttrigger:b4,righttrigger:b5,",
|
"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
|
"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
|
"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
|
"030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"10008200000000000000504944564944,PS360+ v1.66,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,",
|
||||||
"4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"300f0011000000000000504944564944,QanBa Arcade JoyStick 1008,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b10,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
|
"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"300f1611000000000000504944564944,QanBa Arcade JoyStick 4018,a:b1,b:b2,x:b0,y:b3,back:b10,guide:b9,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,",
|
||||||
"222c0020000000000000504944564944,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
|
||||||
"300f1210000000000000504944564944,QanBa Joystick Plus,a:b0,b:b1,x:b2,y:b3,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,platform:Windows,",
|
"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"341a0104000000000000504944564944,QanBa Joystick Q4RAF,a:b5,b:b6,x:b1,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b0,rightshoulder:b3,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b7,platform:Windows,",
|
"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"222c0223000000000000504944564944,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"222c0023000000000000504944564944,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,x:b0,y:b3,back:b13,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f1100000000000000504944564944,REAL ARCADE PRO.3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f8b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,",
|
||||||
"0d0f8a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f6b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f6a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f7000000000000000504944564944,REAL ARCADE PRO.4 VLX,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f2200000000000000504944564944,REAL ARCADE Pro.V3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
|
||||||
"00f00300000000000000504944564944,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"00f0f100000000000000504944564944,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"6f0e1e01000000000000504944564944,Rock Candy Gamepad for PS3,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
|
||||||
"300f1201000000000000504944564944,Saitek Dual Analog Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
|
||||||
"a3060cff000000000000504944564944,Saitek P2500,a:b2,b:b3,y:b1,x:b0,start:b4,guide:b10,back:b5,leftstick:b8,rightstick:b9,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
|
"03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"300f1001000000000000504944564944,Saitek P480 Rumble Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"9b280500000000000000504944564944,Saturn_Adapter_2.0,a:b1,b:b2,x:b0,y:b3,start:b9,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,platform:Windows,",
|
"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79001100000000000000504944564944,Sega Saturn Gamepad,a:b1,b:b2,x:b4,y:b5,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a4,lefttrigger:b3,righttrigger:b0,platform:Windows,",
|
"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05cc09000000000000504944564944,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05a00b000000000000504944564944,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,",
|
||||||
"ff113133000000000000504944564944,SVEN X-PAD,platform:Windows,a:b2,b:b3,y:b1,x:b0,start:b5,back:b4,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b8,righttrigger:b9,",
|
"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"4f0415b3000000000000504944564944,Thrustmaster Dual Analog 3.2,platform:Windows,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4f0400b3000000000000504944564944,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"66660488000000000000504944564944,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpdown:b14,dpleft:b15,dpright:b13,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,platform:Windows,",
|
"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,",
|
||||||
"38076652000000000000504944564944,UnKnown,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"63252305000000000000504944564944,USB Vibration Joystick (BM),platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79001b18000000000000504944564944,Venom Arcade Joystick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"10280000000000000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79000000000000000600000000000000,G-Shark GP-702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
|
"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"AD1B00000000000001F9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
|
"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f0000000000004d00000000000000,HORI Gem Pad 3,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f0000000000006600000000000000,HORIPAD FPS PLUS 4,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:a4,",
|
"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"83050000000000006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
|
||||||
"2509000000000000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
|
"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
|
||||||
"79000000000000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,x:b0,y:b12,back:b32,start:b36,leftstick:b40,rightstick:b44,leftshoulder:b16,rightshoulder:b20,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a4,rightx:a8,righty:a12,lefttrigger:b24,righttrigger:b28,platform:Mac OS X,",
|
"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,rightx:,righty:,lefttrigger:b6,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,righttrigger:b7,",
|
"030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"8f0e0000000000000300000000000000,Piranha xtreme,platform:Mac OS X,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
|
"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"891600000000000000fd000000000000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b8,guide:b10,back:b9,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b11,dpleft:b13,dpdown:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X,",
|
"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"79000000000000001100000000000000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a3,lefty:a4,platform:Mac OS X,",
|
"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,",
|
||||||
"81170000000000007e05000000000000,Sega Saturn,x:b0,a:b2,b:b4,y:b6,start:b13,dpleft:b15,dpdown:b16,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,lefttrigger:b10,rightshoulder:b9,righttrigger:a4,righttrigger:b11,leftx:a0,lefty:a2,platform:Mac OS X,",
|
"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"b4040000000000000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,x:b3,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05000000000000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
|
"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05000000000000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
|
"03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"4f0400000000000015b3000000000000,Thrustmaster Dual Analog 3.2,platform:Mac OS X,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4f0400000000000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Mac OS X,",
|
"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"10080000000000000100000000000000,Twin USB Joystick,a:b4,b:b2,x:b6,y:b0,back:b16,start:b18,leftstick:b20,rightstick:b22,leftshoulder:b12,rightshoulder:b14,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a2,rightx:a6,righty:a4,lefttrigger:b8,righttrigger:b10,platform:Mac OS X,",
|
"030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,y:b9,x:b10,start:b6,guide:b8,back:b7,dpup:b2,dpleft:b0,dpdown:b3,dpright:b1,leftx:a0,lefty:a1,lefttrigger:b12,righttrigger:,leftshoulder:b11,platform:Mac OS X,",
|
"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,",
|
||||||
"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,x:b18,y:b17,back:b7,guide:b8,start:b6,leftstick:b23,rightstick:b24,leftshoulder:b19,rightshoulder:b20,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b21,righttrigger:b22,platform:Mac OS X,",
|
"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"5e04000000000000dd02000000000000,Xbox One Wired Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
|
||||||
"5e04000000000000ea02000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,",
|
||||||
"5e04000000000000e002000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b10,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
|
||||||
"05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
|
||||||
"05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
|
"03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux,",
|
"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,",
|
"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
|
||||||
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
"030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
"03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
|
"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
|
||||||
"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,",
|
||||||
"03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"06000000adde0000efbe000002010000,Hidromancer Game Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1,",
|
"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
"03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux,",
|
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
|
||||||
"030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5,",
|
"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7",
|
"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,",
|
"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
|
"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,",
|
"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,",
|
||||||
"030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
|
||||||
"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
|
||||||
"030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux,",
|
"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,",
|
||||||
|
"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,",
|
||||||
|
"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
|
||||||
|
"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
|
||||||
|
"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,",
|
||||||
|
"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,",
|
||||||
|
"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,",
|
||||||
|
"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,",
|
||||||
|
"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,",
|
||||||
|
"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
|
||||||
|
"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,",
|
||||||
|
"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,",
|
||||||
|
"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,",
|
||||||
|
"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,",
|
||||||
|
"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
|
||||||
|
"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,",
|
||||||
|
"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,",
|
||||||
|
"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
|
||||||
|
"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,",
|
||||||
"030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux,",
|
"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,",
|
||||||
"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5,",
|
"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,",
|
"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1,",
|
"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
|
"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,",
|
"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux,",
|
"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,",
|
"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
|
"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
|
"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
|
||||||
|
"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
|
||||||
|
"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
|
||||||
|
"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
|
||||||
|
"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
|
||||||
|
"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
|
"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
|
"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
|
"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
"050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
|
"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
|
"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,platform:Linux,x:b1,a:b0,b:b4,y:b5,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
|
"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
|
"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
|
"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,",
|
||||||
"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,",
|
||||||
"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,platform:Linux,a:b12,b:b10,x:b13,y:b11,back:b4,start:b5,leftstick:b14,rightstick:b15,leftshoulder:b9,rightshoulder:b8,dpup:b0,dpdown:b2,dpleft:b3,dpright:b1,leftx:a1,lefty:a0,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
|
"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
"050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
|
"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
|
||||||
"030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
|
"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
|
||||||
"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,",
|
"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13,",
|
"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
|
"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
|
||||||
"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,",
|
"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
|
||||||
"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
|
"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,",
|
"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,",
|
||||||
"03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
|
"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
|
"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
|
||||||
"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
|
"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
|
"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
|
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,",
|
||||||
|
"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e040000a102000000010000,X360 Wireless Controller,platform:Linux,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
|
"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,",
|
||||||
|
"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,platform:Linux,y:b0,x:b1,b:b3,a:b4,leftshoulder:b2,rightshoulder:b5,back:b6,start:b7,guide:b9,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftx:a0,lefty:a1,",
|
"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,",
|
||||||
|
"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,",
|
||||||
|
"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
|
||||||
|
"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
|
||||||
|
"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
|
||||||
|
"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,",
|
||||||
|
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,",
|
||||||
|
"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,",
|
||||||
|
"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,",
|
||||||
|
"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,",
|
||||||
|
"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,",
|
||||||
|
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,",
|
||||||
|
|
||||||
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
||||||
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
||||||
|
|||||||
2
glfw/monitor.c
vendored
2
glfw/monitor.c
vendored
@@ -177,6 +177,8 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor)
|
|||||||
if (monitor == NULL)
|
if (monitor == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_glfwPlatformFreeMonitor(monitor);
|
||||||
|
|
||||||
_glfwFreeGammaArrays(&monitor->originalRamp);
|
_glfwFreeGammaArrays(&monitor->originalRamp);
|
||||||
_glfwFreeGammaArrays(&monitor->currentRamp);
|
_glfwFreeGammaArrays(&monitor->currentRamp);
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
|||||||
// Info.plist for unbundled applications
|
// Info.plist for unbundled applications
|
||||||
// HACK: This assumes that NSOpenGLPixelFormat will remain
|
// HACK: This assumes that NSOpenGLPixelFormat will remain
|
||||||
// a straightforward wrapper of its CGL counterpart
|
// a straightforward wrapper of its CGL counterpart
|
||||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
|
||||||
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
|
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
|
||||||
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
||||||
}
|
}
|
||||||
|
|||||||
4
glfw/null_monitor.c
vendored
4
glfw/null_monitor.c
vendored
@@ -32,6 +32,10 @@
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
3
glfw/null_window.c
vendored
3
glfw/null_window.c
vendored
@@ -205,7 +205,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
@@ -323,4 +323,3 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
// This seems like the most appropriate error to return here
|
// This seems like the most appropriate error to return here
|
||||||
return VK_ERROR_INITIALIZATION_FAILED;
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,15 +55,16 @@
|
|||||||
"wayland": {
|
"wayland": {
|
||||||
"headers": [
|
"headers": [
|
||||||
"wl_platform.h",
|
"wl_platform.h",
|
||||||
"linux_joystick.h",
|
|
||||||
"posix_time.h",
|
"posix_time.h",
|
||||||
"posix_thread.h",
|
"posix_thread.h",
|
||||||
"xkb_unicode.h",
|
"xkb_glfw.h",
|
||||||
"egl_context.h",
|
"egl_context.h",
|
||||||
"osmesa_context.h"
|
"osmesa_context.h",
|
||||||
|
"linux_joystick.h"
|
||||||
],
|
],
|
||||||
"protocols": [
|
"protocols": [
|
||||||
"stable/xdg-shell/xdg-shell.xml",
|
"stable/xdg-shell/xdg-shell.xml",
|
||||||
|
"stable/viewporter/viewporter.xml",
|
||||||
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
||||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||||
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||||
@@ -72,12 +73,12 @@
|
|||||||
"wl_init.c",
|
"wl_init.c",
|
||||||
"wl_monitor.c",
|
"wl_monitor.c",
|
||||||
"wl_window.c",
|
"wl_window.c",
|
||||||
"linux_joystick.c",
|
|
||||||
"posix_time.c",
|
"posix_time.c",
|
||||||
"posix_thread.c",
|
"posix_thread.c",
|
||||||
"xkb_unicode.c",
|
"xkb_glfw.c",
|
||||||
"egl_context.c",
|
"egl_context.c",
|
||||||
"osmesa_context.c"
|
"osmesa_context.c",
|
||||||
|
"linux_joystick.c"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"wayland_protocols": [
|
"wayland_protocols": [
|
||||||
@@ -107,7 +108,7 @@
|
|||||||
"x11": {
|
"x11": {
|
||||||
"headers": [
|
"headers": [
|
||||||
"x11_platform.h",
|
"x11_platform.h",
|
||||||
"xkb_unicode.h",
|
"xkb_glfw.h",
|
||||||
"posix_time.h",
|
"posix_time.h",
|
||||||
"posix_thread.h",
|
"posix_thread.h",
|
||||||
"glx_context.h",
|
"glx_context.h",
|
||||||
@@ -119,7 +120,7 @@
|
|||||||
"x11_init.c",
|
"x11_init.c",
|
||||||
"x11_monitor.c",
|
"x11_monitor.c",
|
||||||
"x11_window.c",
|
"x11_window.c",
|
||||||
"xkb_unicode.c",
|
"xkb_glfw.c",
|
||||||
"posix_time.c",
|
"posix_time.c",
|
||||||
"posix_thread.c",
|
"posix_thread.c",
|
||||||
"glx_context.c",
|
"glx_context.c",
|
||||||
|
|||||||
2
glfw/vulkan.c
vendored
2
glfw/vulkan.c
vendored
@@ -54,7 +54,7 @@ GLFWbool _glfwInitVulkan(int mode)
|
|||||||
#elif defined(_GLFW_WIN32)
|
#elif defined(_GLFW_WIN32)
|
||||||
_glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
|
_glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
|
||||||
#elif defined(_GLFW_COCOA)
|
#elif defined(_GLFW_COCOA)
|
||||||
_glfw.vk.handle = _glfw_dlopen("libMoltenVK.dylib");
|
_glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib");
|
||||||
#else
|
#else
|
||||||
_glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
|
_glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
4
glfw/win32_monitor.c
vendored
4
glfw/win32_monitor.c
vendored
@@ -334,6 +334,10 @@ void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* ysc
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
DEVMODEW dm;
|
DEVMODEW dm;
|
||||||
|
|||||||
2
glfw/win32_platform.h
vendored
2
glfw/win32_platform.h
vendored
@@ -120,6 +120,8 @@ typedef struct
|
|||||||
HRGN hRgnBlur;
|
HRGN hRgnBlur;
|
||||||
BOOL fTransitionOnMaximized;
|
BOOL fTransitionOnMaximized;
|
||||||
} DWM_BLURBEHIND;
|
} DWM_BLURBEHIND;
|
||||||
|
#else
|
||||||
|
#include <dwmapi.h>
|
||||||
#endif /*Windows Vista*/
|
#endif /*Windows Vista*/
|
||||||
|
|
||||||
#ifndef DPI_ENUMS_DECLARED
|
#ifndef DPI_ENUMS_DECLARED
|
||||||
|
|||||||
135
glfw/win32_window.c
vendored
135
glfw/win32_window.c
vendored
@@ -235,26 +235,6 @@ static void centerCursor(_GLFWwindow* window)
|
|||||||
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
|
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether the cursor is in the client area of the specified window
|
|
||||||
//
|
|
||||||
static GLFWbool cursorInClientArea(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
RECT area;
|
|
||||||
POINT pos;
|
|
||||||
|
|
||||||
if (!GetCursorPos(&pos))
|
|
||||||
return GLFW_FALSE;
|
|
||||||
|
|
||||||
if (WindowFromPoint(pos) != window->win32.handle)
|
|
||||||
return GLFW_FALSE;
|
|
||||||
|
|
||||||
GetClientRect(window->win32.handle, &area);
|
|
||||||
ClientToScreen(window->win32.handle, (POINT*) &area.left);
|
|
||||||
ClientToScreen(window->win32.handle, (POINT*) &area.right);
|
|
||||||
|
|
||||||
return PtInRect(&area, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the cursor image according to its cursor mode
|
// Updates the cursor image according to its cursor mode
|
||||||
//
|
//
|
||||||
static void updateCursorImage(_GLFWwindow* window)
|
static void updateCursorImage(_GLFWwindow* window)
|
||||||
@@ -286,6 +266,67 @@ static void updateClipRect(_GLFWwindow* window)
|
|||||||
ClipCursor(NULL);
|
ClipCursor(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply disabled cursor mode to a focused window
|
||||||
|
//
|
||||||
|
static void disableCursor(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
|
||||||
|
|
||||||
|
_glfw.win32.disabledCursorWindow = window;
|
||||||
|
_glfwPlatformGetCursorPos(window,
|
||||||
|
&_glfw.win32.restoreCursorPosX,
|
||||||
|
&_glfw.win32.restoreCursorPosY);
|
||||||
|
updateCursorImage(window);
|
||||||
|
centerCursor(window);
|
||||||
|
updateClipRect(window);
|
||||||
|
|
||||||
|
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
|
||||||
|
{
|
||||||
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
||||||
|
"Win32: Failed to register raw input device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit disabled cursor mode for the specified window
|
||||||
|
//
|
||||||
|
static void enableCursor(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
|
||||||
|
|
||||||
|
_glfw.win32.disabledCursorWindow = NULL;
|
||||||
|
updateClipRect(NULL);
|
||||||
|
_glfwPlatformSetCursorPos(window,
|
||||||
|
_glfw.win32.restoreCursorPosX,
|
||||||
|
_glfw.win32.restoreCursorPosY);
|
||||||
|
updateCursorImage(window);
|
||||||
|
|
||||||
|
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
|
||||||
|
{
|
||||||
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
||||||
|
"Win32: Failed to remove raw input device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether the cursor is in the client area of the specified window
|
||||||
|
//
|
||||||
|
static GLFWbool cursorInClientArea(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
RECT area;
|
||||||
|
POINT pos;
|
||||||
|
|
||||||
|
if (!GetCursorPos(&pos))
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
|
if (WindowFromPoint(pos) != window->win32.handle)
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
|
GetClientRect(window->win32.handle, &area);
|
||||||
|
ClientToScreen(window->win32.handle, (POINT*) &area.left);
|
||||||
|
ClientToScreen(window->win32.handle, (POINT*) &area.right);
|
||||||
|
|
||||||
|
return PtInRect(&area, pos);
|
||||||
|
}
|
||||||
|
|
||||||
// Update native window styles to match attributes
|
// Update native window styles to match attributes
|
||||||
//
|
//
|
||||||
static void updateWindowStyles(const _GLFWwindow* window)
|
static void updateWindowStyles(const _GLFWwindow* window)
|
||||||
@@ -575,7 +616,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
if (lParam == 0 && window->win32.frameAction)
|
if (lParam == 0 && window->win32.frameAction)
|
||||||
{
|
{
|
||||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
disableCursor(window);
|
||||||
|
|
||||||
window->win32.frameAction = GLFW_FALSE;
|
window->win32.frameAction = GLFW_FALSE;
|
||||||
}
|
}
|
||||||
@@ -593,7 +634,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
disableCursor(window);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -601,7 +642,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
case WM_KILLFOCUS:
|
case WM_KILLFOCUS:
|
||||||
{
|
{
|
||||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
|
enableCursor(window);
|
||||||
|
|
||||||
if (window->monitor && window->autoIconify)
|
if (window->monitor && window->autoIconify)
|
||||||
_glfwPlatformIconifyWindow(window);
|
_glfwPlatformIconifyWindow(window);
|
||||||
@@ -857,10 +898,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
case WM_ENTERSIZEMOVE:
|
case WM_ENTERSIZEMOVE:
|
||||||
case WM_ENTERMENULOOP:
|
case WM_ENTERMENULOOP:
|
||||||
{
|
{
|
||||||
// HACK: Postpone cursor disabling while the user is moving or
|
// HACK: Enable the cursor while the user is moving or
|
||||||
// resizing the window or using the menu
|
// resizing the window or using the window menu
|
||||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
|
enableCursor(window);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -871,7 +912,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
// HACK: Disable the cursor once the user is done moving or
|
// HACK: Disable the cursor once the user is done moving or
|
||||||
// resizing the window or using the menu
|
// resizing the window or using the menu
|
||||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
disableCursor(window);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1474,9 +1515,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
FlashWindow(window->win32.handle, TRUE);
|
FlashWindow(window->win32.handle, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return MessageBeep(0xFFFFFFFF & param) ? GLFW_TRUE : GLFW_FALSE;
|
return MessageBeep(0xFFFFFFFF) ? GLFW_TRUE : GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
||||||
@@ -1777,39 +1818,12 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
|||||||
{
|
{
|
||||||
if (mode == GLFW_CURSOR_DISABLED)
|
if (mode == GLFW_CURSOR_DISABLED)
|
||||||
{
|
{
|
||||||
const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
|
if (_glfwPlatformWindowFocused(window))
|
||||||
|
disableCursor(window);
|
||||||
_glfw.win32.disabledCursorWindow = window;
|
|
||||||
_glfwPlatformGetCursorPos(window,
|
|
||||||
&_glfw.win32.restoreCursorPosX,
|
|
||||||
&_glfw.win32.restoreCursorPosY);
|
|
||||||
centerCursor(window);
|
|
||||||
updateClipRect(window);
|
|
||||||
|
|
||||||
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
|
|
||||||
{
|
|
||||||
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
||||||
"Win32: Failed to register raw input device");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (_glfw.win32.disabledCursorWindow == window)
|
else if (_glfw.win32.disabledCursorWindow == window)
|
||||||
{
|
enableCursor(window);
|
||||||
const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
|
else if (cursorInClientArea(window))
|
||||||
|
|
||||||
_glfw.win32.disabledCursorWindow = NULL;
|
|
||||||
updateClipRect(NULL);
|
|
||||||
_glfwPlatformSetCursorPos(window,
|
|
||||||
_glfw.win32.restoreCursorPosX,
|
|
||||||
_glfw.win32.restoreCursorPosY);
|
|
||||||
|
|
||||||
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
|
|
||||||
{
|
|
||||||
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
||||||
"Win32: Failed to remove raw input device");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursorInClientArea(window))
|
|
||||||
updateCursorImage(window);
|
updateCursorImage(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2014,4 +2028,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
return window->win32.handle;
|
return window->win32.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
glfw/window.c
vendored
7
glfw/window.c
vendored
@@ -54,7 +54,7 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused)
|
|||||||
if (window->keys[key] == GLFW_PRESS)
|
if (window->keys[key] == GLFW_PRESS)
|
||||||
{
|
{
|
||||||
const int scancode = _glfwPlatformGetKeyScancode(key);
|
const int scancode = _glfwPlatformGetKeyScancode(key);
|
||||||
_glfwInputKey(window, key, scancode, GLFW_RELEASE, 0);
|
_glfwInputKeyboard(window, key, scancode, GLFW_RELEASE, 0, "", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,14 +768,14 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
|
|||||||
_glfwPlatformRequestWindowAttention(window);
|
_glfwPlatformRequestWindowAttention(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWAPI int glfwWindowBell(GLFWwindow* handle, int64_t param)
|
GLFWAPI int glfwWindowBell(GLFWwindow* handle)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
assert(window != NULL);
|
assert(window != NULL);
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||||
|
|
||||||
return _glfwPlatformWindowBell(window, param);
|
return _glfwPlatformWindowBell(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWAPI void glfwHideWindow(GLFWwindow* handle)
|
GLFWAPI void glfwHideWindow(GLFWwindow* handle)
|
||||||
@@ -1106,4 +1106,3 @@ GLFWAPI void glfwPostEmptyEvent(void)
|
|||||||
|
|
||||||
_glfwPlatformPostEmptyEvent();
|
_glfwPlatformPostEmptyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
640
glfw/wl_init.c
vendored
640
glfw/wl_init.c
vendored
@@ -41,6 +41,39 @@ static inline int min(int n1, int n2)
|
|||||||
return n1 < n2 ? n1 : n2;
|
return n1 < n2 ? n1 : n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, int* which)
|
||||||
|
{
|
||||||
|
int focus;
|
||||||
|
_GLFWwindow* window = _glfw.windowListHead;
|
||||||
|
if (!which)
|
||||||
|
which = &focus;
|
||||||
|
while (window)
|
||||||
|
{
|
||||||
|
if (surface == window->wl.decorations.top.surface)
|
||||||
|
{
|
||||||
|
*which = topDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (surface == window->wl.decorations.left.surface)
|
||||||
|
{
|
||||||
|
*which = leftDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (surface == window->wl.decorations.right.surface)
|
||||||
|
{
|
||||||
|
*which = rightDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (surface == window->wl.decorations.bottom.surface)
|
||||||
|
{
|
||||||
|
*which = bottomDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
window = window->next;
|
||||||
|
}
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
static void pointerHandleEnter(void* data,
|
static void pointerHandleEnter(void* data,
|
||||||
struct wl_pointer* pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t serial,
|
uint32_t serial,
|
||||||
@@ -52,8 +85,16 @@ static void pointerHandleEnter(void* data,
|
|||||||
if (!surface)
|
if (!surface)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int focus = 0;
|
||||||
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
window = findWindowFromDecorationSurface(surface, &focus);
|
||||||
|
if (!window)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window->wl.decorations.focus = focus;
|
||||||
_glfw.wl.pointerSerial = serial;
|
_glfw.wl.pointerSerial = serial;
|
||||||
_glfw.wl.pointerFocus = window;
|
_glfw.wl.pointerFocus = window;
|
||||||
|
|
||||||
@@ -80,6 +121,39 @@ static void pointerHandleLeave(void* data,
|
|||||||
_glfwInputCursorEnter(window, GLFW_FALSE);
|
_glfwInputCursorEnter(window, GLFW_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setCursor(const char* name)
|
||||||
|
{
|
||||||
|
struct wl_buffer* buffer;
|
||||||
|
struct wl_cursor* cursor;
|
||||||
|
struct wl_cursor_image* image;
|
||||||
|
struct wl_surface* surface = _glfw.wl.cursorSurface;
|
||||||
|
|
||||||
|
cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
|
||||||
|
name);
|
||||||
|
if (!cursor)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Standard cursor not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
image = cursor->images[0];
|
||||||
|
|
||||||
|
if (!image)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buffer = wl_cursor_image_get_buffer(image);
|
||||||
|
if (!buffer)
|
||||||
|
return;
|
||||||
|
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
|
||||||
|
surface,
|
||||||
|
image->hotspot_x,
|
||||||
|
image->hotspot_y);
|
||||||
|
wl_surface_attach(surface, buffer, 0, 0);
|
||||||
|
wl_surface_damage(surface, 0, 0,
|
||||||
|
image->width, image->height);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
}
|
||||||
|
|
||||||
static void pointerHandleMotion(void* data,
|
static void pointerHandleMotion(void* data,
|
||||||
struct wl_pointer* pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
@@ -87,6 +161,7 @@ static void pointerHandleMotion(void* data,
|
|||||||
wl_fixed_t sy)
|
wl_fixed_t sy)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||||
|
const char* cursorName;
|
||||||
|
|
||||||
if (!window)
|
if (!window)
|
||||||
return;
|
return;
|
||||||
@@ -99,13 +174,47 @@ static void pointerHandleMotion(void* data,
|
|||||||
window->wl.cursorPosY = wl_fixed_to_double(sy);
|
window->wl.cursorPosY = wl_fixed_to_double(sy);
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfwInputCursorPos(window,
|
switch (window->wl.decorations.focus)
|
||||||
wl_fixed_to_double(sx),
|
{
|
||||||
wl_fixed_to_double(sy));
|
case mainWindow:
|
||||||
|
_glfwInputCursorPos(window,
|
||||||
|
wl_fixed_to_double(sx),
|
||||||
|
wl_fixed_to_double(sy));
|
||||||
|
return;
|
||||||
|
case topDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "n-resize";
|
||||||
|
else
|
||||||
|
cursorName = "left_ptr";
|
||||||
|
break;
|
||||||
|
case leftDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "nw-resize";
|
||||||
|
else
|
||||||
|
cursorName = "w-resize";
|
||||||
|
break;
|
||||||
|
case rightDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "ne-resize";
|
||||||
|
else
|
||||||
|
cursorName = "e-resize";
|
||||||
|
break;
|
||||||
|
case bottomDecoration:
|
||||||
|
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "sw-resize";
|
||||||
|
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "se-resize";
|
||||||
|
else
|
||||||
|
cursorName = "s-resize";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
setCursor(cursorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointerHandleButton(void* data,
|
static void pointerHandleButton(void* data,
|
||||||
struct wl_pointer* wl_pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t serial,
|
uint32_t serial,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
uint32_t button,
|
uint32_t button,
|
||||||
@@ -114,8 +223,76 @@ static void pointerHandleButton(void* data,
|
|||||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||||
int glfwButton;
|
int glfwButton;
|
||||||
|
|
||||||
|
// Both xdg-shell and wl_shell use the same values.
|
||||||
|
uint32_t edges = WL_SHELL_SURFACE_RESIZE_NONE;
|
||||||
|
|
||||||
if (!window)
|
if (!window)
|
||||||
return;
|
return;
|
||||||
|
if (button == BTN_LEFT)
|
||||||
|
{
|
||||||
|
switch (window->wl.decorations.focus)
|
||||||
|
{
|
||||||
|
case mainWindow:
|
||||||
|
break;
|
||||||
|
case topDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_TOP;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (window->wl.xdg.toplevel)
|
||||||
|
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
|
||||||
|
else
|
||||||
|
wl_shell_surface_move(window->wl.shellSurface, _glfw.wl.seat, serial);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case leftDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_TOP_LEFT;
|
||||||
|
else
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_LEFT;
|
||||||
|
break;
|
||||||
|
case rightDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT;
|
||||||
|
else
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_RIGHT;
|
||||||
|
break;
|
||||||
|
case bottomDecoration:
|
||||||
|
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT;
|
||||||
|
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT;
|
||||||
|
else
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
if (edges != WL_SHELL_SURFACE_RESIZE_NONE)
|
||||||
|
{
|
||||||
|
if (window->wl.xdg.toplevel)
|
||||||
|
xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
|
||||||
|
serial, edges);
|
||||||
|
else
|
||||||
|
wl_shell_surface_resize(window->wl.shellSurface, _glfw.wl.seat,
|
||||||
|
serial, edges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (button == BTN_RIGHT)
|
||||||
|
{
|
||||||
|
if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel)
|
||||||
|
{
|
||||||
|
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
|
||||||
|
_glfw.wl.seat, serial,
|
||||||
|
window->wl.cursorPosX,
|
||||||
|
window->wl.cursorPosY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don’t pass the button to the user if it was related to a decoration.
|
||||||
|
if (window->wl.decorations.focus != mainWindow)
|
||||||
|
return;
|
||||||
|
|
||||||
_glfw.wl.pointerSerial = serial;
|
_glfw.wl.pointerSerial = serial;
|
||||||
|
|
||||||
@@ -132,7 +309,7 @@ static void pointerHandleButton(void* data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void pointerHandleAxis(void* data,
|
static void pointerHandleAxis(void* data,
|
||||||
struct wl_pointer* wl_pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
uint32_t axis,
|
uint32_t axis,
|
||||||
wl_fixed_t value)
|
wl_fixed_t value)
|
||||||
@@ -172,16 +349,7 @@ static void keyboardHandleKeymap(void* data,
|
|||||||
int fd,
|
int fd,
|
||||||
uint32_t size)
|
uint32_t size)
|
||||||
{
|
{
|
||||||
struct xkb_keymap* keymap;
|
|
||||||
struct xkb_state* state;
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
struct xkb_compose_table* composeTable;
|
|
||||||
struct xkb_compose_state* composeState;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char* mapStr;
|
char* mapStr;
|
||||||
const char* locale;
|
|
||||||
|
|
||||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
||||||
{
|
{
|
||||||
@@ -194,78 +362,10 @@ static void keyboardHandleKeymap(void* data,
|
|||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
glfw_xkb_compile_keymap(&_glfw.wl.xkb, mapStr);
|
||||||
keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
|
|
||||||
mapStr,
|
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
|
||||||
0);
|
|
||||||
munmap(mapStr, size);
|
munmap(mapStr, size);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (!keymap)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to compile keymap");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = xkb_state_new(keymap);
|
|
||||||
if (!state)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to create XKB state");
|
|
||||||
xkb_keymap_unref(keymap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up the preferred locale, falling back to "C" as default.
|
|
||||||
locale = getenv("LC_ALL");
|
|
||||||
if (!locale)
|
|
||||||
locale = getenv("LC_CTYPE");
|
|
||||||
if (!locale)
|
|
||||||
locale = getenv("LANG");
|
|
||||||
if (!locale)
|
|
||||||
locale = "C";
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
composeTable =
|
|
||||||
xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
|
|
||||||
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
||||||
if (composeTable)
|
|
||||||
{
|
|
||||||
composeState =
|
|
||||||
xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
|
|
||||||
xkb_compose_table_unref(composeTable);
|
|
||||||
if (composeState)
|
|
||||||
_glfw.wl.xkb.composeState = composeState;
|
|
||||||
else
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to create XKB compose state");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to create XKB compose table");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
xkb_keymap_unref(_glfw.wl.xkb.keymap);
|
|
||||||
xkb_state_unref(_glfw.wl.xkb.state);
|
|
||||||
_glfw.wl.xkb.keymap = keymap;
|
|
||||||
_glfw.wl.xkb.state = state;
|
|
||||||
|
|
||||||
_glfw.wl.xkb.controlMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
|
|
||||||
_glfw.wl.xkb.altMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
|
|
||||||
_glfw.wl.xkb.shiftMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
|
|
||||||
_glfw.wl.xkb.superMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
|
|
||||||
_glfw.wl.xkb.capsLockMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
|
|
||||||
_glfw.wl.xkb.numLockMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboardHandleEnter(void* data,
|
static void keyboardHandleEnter(void* data,
|
||||||
@@ -279,6 +379,12 @@ static void keyboardHandleEnter(void* data,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
window = findWindowFromDecorationSurface(surface, NULL);
|
||||||
|
if (!window)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_glfw.wl.keyboardFocus = window;
|
_glfw.wl.keyboardFocus = window;
|
||||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||||
@@ -298,63 +404,6 @@ static void keyboardHandleLeave(void* data,
|
|||||||
_glfwInputWindowFocus(window, GLFW_FALSE);
|
_glfwInputWindowFocus(window, GLFW_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int toGLFWKeyCode(uint32_t key)
|
|
||||||
{
|
|
||||||
if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0]))
|
|
||||||
return _glfw.wl.keycodes[key];
|
|
||||||
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
|
|
||||||
{
|
|
||||||
if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
|
|
||||||
return sym;
|
|
||||||
if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym)
|
|
||||||
!= XKB_COMPOSE_FEED_ACCEPTED)
|
|
||||||
return sym;
|
|
||||||
switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState))
|
|
||||||
{
|
|
||||||
case XKB_COMPOSE_COMPOSED:
|
|
||||||
return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState);
|
|
||||||
case XKB_COMPOSE_COMPOSING:
|
|
||||||
case XKB_COMPOSE_CANCELLED:
|
|
||||||
return XKB_KEY_NoSymbol;
|
|
||||||
case XKB_COMPOSE_NOTHING:
|
|
||||||
default:
|
|
||||||
return sym;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void inputChar(_GLFWwindow* window, uint32_t key)
|
|
||||||
{
|
|
||||||
uint32_t code, numSyms;
|
|
||||||
long cp;
|
|
||||||
const xkb_keysym_t *syms;
|
|
||||||
xkb_keysym_t sym;
|
|
||||||
|
|
||||||
code = key + 8;
|
|
||||||
numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms);
|
|
||||||
|
|
||||||
if (numSyms == 1)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
sym = composeSymbol(syms[0]);
|
|
||||||
#else
|
|
||||||
sym = syms[0];
|
|
||||||
#endif
|
|
||||||
cp = _glfwKeySym2Unicode(sym);
|
|
||||||
if (cp != -1)
|
|
||||||
{
|
|
||||||
const int mods = _glfw.wl.xkb.modifiers;
|
|
||||||
const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
|
|
||||||
_glfwInputChar(window, cp, mods, plain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboardHandleKey(void* data,
|
static void keyboardHandleKey(void* data,
|
||||||
struct wl_keyboard* keyboard,
|
struct wl_keyboard* keyboard,
|
||||||
uint32_t serial,
|
uint32_t serial,
|
||||||
@@ -362,22 +411,19 @@ static void keyboardHandleKey(void* data,
|
|||||||
uint32_t key,
|
uint32_t key,
|
||||||
uint32_t state)
|
uint32_t state)
|
||||||
{
|
{
|
||||||
int keyCode;
|
|
||||||
int action;
|
|
||||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||||
|
|
||||||
if (!window)
|
if (!window)
|
||||||
return;
|
return;
|
||||||
|
int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
|
||||||
|
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);
|
||||||
|
_glfw.wl.keyRepeatInfo.nextRepeatAt = 0;
|
||||||
|
|
||||||
keyCode = toGLFWKeyCode(key);
|
if (action == GLFW_PRESS && _glfw.wl.keyboardRepeatRate > 0 && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))
|
||||||
action = state == WL_KEYBOARD_KEY_STATE_PRESSED
|
{
|
||||||
? GLFW_PRESS : GLFW_RELEASE;
|
_glfw.wl.keyRepeatInfo.key = key;
|
||||||
|
_glfw.wl.keyRepeatInfo.nextRepeatAt = glfwGetTime() + (double)(_glfw.wl.keyboardRepeatDelay) / 1000.0;
|
||||||
_glfwInputKey(window, keyCode, key, action,
|
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
|
||||||
_glfw.wl.xkb.modifiers);
|
}
|
||||||
|
|
||||||
if (action == GLFW_PRESS)
|
|
||||||
inputChar(window, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboardHandleModifiers(void* data,
|
static void keyboardHandleModifiers(void* data,
|
||||||
@@ -388,38 +434,19 @@ static void keyboardHandleModifiers(void* data,
|
|||||||
uint32_t modsLocked,
|
uint32_t modsLocked,
|
||||||
uint32_t group)
|
uint32_t group)
|
||||||
{
|
{
|
||||||
xkb_mod_mask_t mask;
|
glfw_xkb_update_modifiers(&_glfw.wl.xkb, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
||||||
unsigned int modifiers = 0;
|
}
|
||||||
|
|
||||||
if (!_glfw.wl.xkb.keymap)
|
static void keyboardHandleRepeatInfo(void* data,
|
||||||
|
struct wl_keyboard* keyboard,
|
||||||
|
int32_t rate,
|
||||||
|
int32_t delay)
|
||||||
|
{
|
||||||
|
if (keyboard != _glfw.wl.keyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
xkb_state_update_mask(_glfw.wl.xkb.state,
|
_glfw.wl.keyboardRepeatRate = rate;
|
||||||
modsDepressed,
|
_glfw.wl.keyboardRepeatDelay = delay;
|
||||||
modsLatched,
|
|
||||||
modsLocked,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
group);
|
|
||||||
|
|
||||||
mask = xkb_state_serialize_mods(_glfw.wl.xkb.state,
|
|
||||||
XKB_STATE_MODS_DEPRESSED |
|
|
||||||
XKB_STATE_LAYOUT_DEPRESSED |
|
|
||||||
XKB_STATE_MODS_LATCHED |
|
|
||||||
XKB_STATE_LAYOUT_LATCHED);
|
|
||||||
if (mask & _glfw.wl.xkb.controlMask)
|
|
||||||
modifiers |= GLFW_MOD_CONTROL;
|
|
||||||
if (mask & _glfw.wl.xkb.altMask)
|
|
||||||
modifiers |= GLFW_MOD_ALT;
|
|
||||||
if (mask & _glfw.wl.xkb.shiftMask)
|
|
||||||
modifiers |= GLFW_MOD_SHIFT;
|
|
||||||
if (mask & _glfw.wl.xkb.superMask)
|
|
||||||
modifiers |= GLFW_MOD_SUPER;
|
|
||||||
if (mask & _glfw.wl.xkb.capsLockMask)
|
|
||||||
modifiers |= GLFW_MOD_CAPS_LOCK;
|
|
||||||
if (mask & _glfw.wl.xkb.numLockMask)
|
|
||||||
modifiers |= GLFW_MOD_NUM_LOCK;
|
|
||||||
_glfw.wl.xkb.modifiers = modifiers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_keyboard_listener keyboardListener = {
|
static const struct wl_keyboard_listener keyboardListener = {
|
||||||
@@ -428,6 +455,7 @@ static const struct wl_keyboard_listener keyboardListener = {
|
|||||||
keyboardHandleLeave,
|
keyboardHandleLeave,
|
||||||
keyboardHandleKey,
|
keyboardHandleKey,
|
||||||
keyboardHandleModifiers,
|
keyboardHandleModifiers,
|
||||||
|
keyboardHandleRepeatInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void seatHandleCapabilities(void* data,
|
static void seatHandleCapabilities(void* data,
|
||||||
@@ -457,8 +485,15 @@ static void seatHandleCapabilities(void* data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void seatHandleName(void* data,
|
||||||
|
struct wl_seat* seat,
|
||||||
|
const char* name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static const struct wl_seat_listener seatListener = {
|
static const struct wl_seat_listener seatListener = {
|
||||||
seatHandleCapabilities
|
seatHandleCapabilities,
|
||||||
|
seatHandleName,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void wmBaseHandlePing(void* data,
|
static void wmBaseHandlePing(void* data,
|
||||||
@@ -485,6 +520,11 @@ static void registryHandleGlobal(void* data,
|
|||||||
wl_registry_bind(registry, name, &wl_compositor_interface,
|
wl_registry_bind(registry, name, &wl_compositor_interface,
|
||||||
_glfw.wl.compositorVersion);
|
_glfw.wl.compositorVersion);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(interface, "wl_subcompositor") == 0)
|
||||||
|
{
|
||||||
|
_glfw.wl.subcompositor =
|
||||||
|
wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
|
||||||
|
}
|
||||||
else if (strcmp(interface, "wl_shm") == 0)
|
else if (strcmp(interface, "wl_shm") == 0)
|
||||||
{
|
{
|
||||||
_glfw.wl.shm =
|
_glfw.wl.shm =
|
||||||
@@ -503,8 +543,10 @@ static void registryHandleGlobal(void* data,
|
|||||||
{
|
{
|
||||||
if (!_glfw.wl.seat)
|
if (!_glfw.wl.seat)
|
||||||
{
|
{
|
||||||
|
_glfw.wl.seatVersion = min(4, version);
|
||||||
_glfw.wl.seat =
|
_glfw.wl.seat =
|
||||||
wl_registry_bind(registry, name, &wl_seat_interface, 1);
|
wl_registry_bind(registry, name, &wl_seat_interface,
|
||||||
|
_glfw.wl.seatVersion);
|
||||||
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,6 +556,11 @@ static void registryHandleGlobal(void* data,
|
|||||||
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
||||||
xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
|
xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(interface, "wp_viewporter") == 0)
|
||||||
|
{
|
||||||
|
_glfw.wl.viewporter =
|
||||||
|
wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
|
||||||
|
}
|
||||||
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
|
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
|
||||||
{
|
{
|
||||||
_glfw.wl.relativePointerManager =
|
_glfw.wl.relativePointerManager =
|
||||||
@@ -541,6 +588,18 @@ static void registryHandleGlobalRemove(void *data,
|
|||||||
struct wl_registry *registry,
|
struct wl_registry *registry,
|
||||||
uint32_t name)
|
uint32_t name)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
_GLFWmonitor* monitor;
|
||||||
|
|
||||||
|
for (i = 0; i < _glfw.monitorCount; ++i)
|
||||||
|
{
|
||||||
|
monitor = _glfw.monitors[i];
|
||||||
|
if (monitor->wl.name == name)
|
||||||
|
{
|
||||||
|
_glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -549,140 +608,6 @@ static const struct wl_registry_listener registryListener = {
|
|||||||
registryHandleGlobalRemove
|
registryHandleGlobalRemove
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create key code translation tables
|
|
||||||
//
|
|
||||||
static void createKeyTables(void)
|
|
||||||
{
|
|
||||||
int scancode;
|
|
||||||
|
|
||||||
memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes));
|
|
||||||
memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes));
|
|
||||||
|
|
||||||
_glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT;
|
|
||||||
_glfw.wl.keycodes[KEY_1] = GLFW_KEY_1;
|
|
||||||
_glfw.wl.keycodes[KEY_2] = GLFW_KEY_2;
|
|
||||||
_glfw.wl.keycodes[KEY_3] = GLFW_KEY_3;
|
|
||||||
_glfw.wl.keycodes[KEY_4] = GLFW_KEY_4;
|
|
||||||
_glfw.wl.keycodes[KEY_5] = GLFW_KEY_5;
|
|
||||||
_glfw.wl.keycodes[KEY_6] = GLFW_KEY_6;
|
|
||||||
_glfw.wl.keycodes[KEY_7] = GLFW_KEY_7;
|
|
||||||
_glfw.wl.keycodes[KEY_8] = GLFW_KEY_8;
|
|
||||||
_glfw.wl.keycodes[KEY_9] = GLFW_KEY_9;
|
|
||||||
_glfw.wl.keycodes[KEY_0] = GLFW_KEY_0;
|
|
||||||
_glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE;
|
|
||||||
_glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS;
|
|
||||||
_glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL;
|
|
||||||
_glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q;
|
|
||||||
_glfw.wl.keycodes[KEY_W] = GLFW_KEY_W;
|
|
||||||
_glfw.wl.keycodes[KEY_E] = GLFW_KEY_E;
|
|
||||||
_glfw.wl.keycodes[KEY_R] = GLFW_KEY_R;
|
|
||||||
_glfw.wl.keycodes[KEY_T] = GLFW_KEY_T;
|
|
||||||
_glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y;
|
|
||||||
_glfw.wl.keycodes[KEY_U] = GLFW_KEY_U;
|
|
||||||
_glfw.wl.keycodes[KEY_I] = GLFW_KEY_I;
|
|
||||||
_glfw.wl.keycodes[KEY_O] = GLFW_KEY_O;
|
|
||||||
_glfw.wl.keycodes[KEY_P] = GLFW_KEY_P;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET;
|
|
||||||
_glfw.wl.keycodes[KEY_A] = GLFW_KEY_A;
|
|
||||||
_glfw.wl.keycodes[KEY_S] = GLFW_KEY_S;
|
|
||||||
_glfw.wl.keycodes[KEY_D] = GLFW_KEY_D;
|
|
||||||
_glfw.wl.keycodes[KEY_F] = GLFW_KEY_F;
|
|
||||||
_glfw.wl.keycodes[KEY_G] = GLFW_KEY_G;
|
|
||||||
_glfw.wl.keycodes[KEY_H] = GLFW_KEY_H;
|
|
||||||
_glfw.wl.keycodes[KEY_J] = GLFW_KEY_J;
|
|
||||||
_glfw.wl.keycodes[KEY_K] = GLFW_KEY_K;
|
|
||||||
_glfw.wl.keycodes[KEY_L] = GLFW_KEY_L;
|
|
||||||
_glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON;
|
|
||||||
_glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE;
|
|
||||||
_glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z;
|
|
||||||
_glfw.wl.keycodes[KEY_X] = GLFW_KEY_X;
|
|
||||||
_glfw.wl.keycodes[KEY_C] = GLFW_KEY_C;
|
|
||||||
_glfw.wl.keycodes[KEY_V] = GLFW_KEY_V;
|
|
||||||
_glfw.wl.keycodes[KEY_B] = GLFW_KEY_B;
|
|
||||||
_glfw.wl.keycodes[KEY_N] = GLFW_KEY_N;
|
|
||||||
_glfw.wl.keycodes[KEY_M] = GLFW_KEY_M;
|
|
||||||
_glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA;
|
|
||||||
_glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD;
|
|
||||||
_glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH;
|
|
||||||
_glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH;
|
|
||||||
_glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE;
|
|
||||||
_glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER;
|
|
||||||
_glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU;
|
|
||||||
_glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK;
|
|
||||||
_glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK;
|
|
||||||
_glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN;
|
|
||||||
_glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK;
|
|
||||||
_glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE;
|
|
||||||
_glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE;
|
|
||||||
_glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE;
|
|
||||||
_glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER;
|
|
||||||
_glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME;
|
|
||||||
_glfw.wl.keycodes[KEY_END] = GLFW_KEY_END;
|
|
||||||
_glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP;
|
|
||||||
_glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN;
|
|
||||||
_glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT;
|
|
||||||
_glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN;
|
|
||||||
_glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP;
|
|
||||||
_glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1;
|
|
||||||
_glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2;
|
|
||||||
_glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3;
|
|
||||||
_glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4;
|
|
||||||
_glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5;
|
|
||||||
_glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6;
|
|
||||||
_glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7;
|
|
||||||
_glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8;
|
|
||||||
_glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9;
|
|
||||||
_glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10;
|
|
||||||
_glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11;
|
|
||||||
_glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12;
|
|
||||||
_glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13;
|
|
||||||
_glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14;
|
|
||||||
_glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15;
|
|
||||||
_glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16;
|
|
||||||
_glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17;
|
|
||||||
_glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18;
|
|
||||||
_glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19;
|
|
||||||
_glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20;
|
|
||||||
_glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21;
|
|
||||||
_glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22;
|
|
||||||
_glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23;
|
|
||||||
_glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24;
|
|
||||||
_glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE;
|
|
||||||
_glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY;
|
|
||||||
_glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT;
|
|
||||||
_glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD;
|
|
||||||
_glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0;
|
|
||||||
_glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1;
|
|
||||||
_glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2;
|
|
||||||
_glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3;
|
|
||||||
_glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4;
|
|
||||||
_glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5;
|
|
||||||
_glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6;
|
|
||||||
_glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7;
|
|
||||||
_glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8;
|
|
||||||
_glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9;
|
|
||||||
_glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL;
|
|
||||||
_glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL;
|
|
||||||
_glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER;
|
|
||||||
|
|
||||||
for (scancode = 0; scancode < 256; scancode++)
|
|
||||||
{
|
|
||||||
if (_glfw.wl.keycodes[scancode] > 0)
|
|
||||||
_glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
@@ -722,52 +647,6 @@ int _glfwPlatformInit(void)
|
|||||||
_glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize)
|
_glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize)
|
||||||
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
|
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
|
||||||
|
|
||||||
_glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0");
|
|
||||||
if (!_glfw.wl.xkb.handle)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to open libxkbcommon");
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfw.wl.xkb.context_new = (PFN_xkb_context_new)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new");
|
|
||||||
_glfw.wl.xkb.context_unref = (PFN_xkb_context_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref");
|
|
||||||
_glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string");
|
|
||||||
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref");
|
|
||||||
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
|
|
||||||
_glfw.wl.xkb.state_new = (PFN_xkb_state_new)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new");
|
|
||||||
_glfw.wl.xkb.state_unref = (PFN_xkb_state_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref");
|
|
||||||
_glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms");
|
|
||||||
_glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
|
|
||||||
_glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
_glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale");
|
|
||||||
_glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref");
|
|
||||||
_glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new");
|
|
||||||
_glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref");
|
|
||||||
_glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed");
|
|
||||||
_glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status");
|
|
||||||
_glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_glfw.wl.display = wl_display_connect(NULL);
|
_glfw.wl.display = wl_display_connect(NULL);
|
||||||
if (!_glfw.wl.display)
|
if (!_glfw.wl.display)
|
||||||
{
|
{
|
||||||
@@ -779,15 +658,7 @@ int _glfwPlatformInit(void)
|
|||||||
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
|
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
|
||||||
wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL);
|
wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL);
|
||||||
|
|
||||||
createKeyTables();
|
if (!glfw_xkb_create_context(&_glfw.wl.xkb)) return GLFW_FALSE;
|
||||||
|
|
||||||
_glfw.wl.xkb.context = xkb_context_new(0);
|
|
||||||
if (!_glfw.wl.xkb.context)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to initialize xkb context");
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync so we got all registry objects
|
// Sync so we got all registry objects
|
||||||
wl_display_roundtrip(_glfw.wl.display);
|
wl_display_roundtrip(_glfw.wl.display);
|
||||||
@@ -795,8 +666,10 @@ int _glfwPlatformInit(void)
|
|||||||
// Sync so we got all initial output events
|
// Sync so we got all initial output events
|
||||||
wl_display_roundtrip(_glfw.wl.display);
|
wl_display_roundtrip(_glfw.wl.display);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
if (!_glfwInitJoysticksLinux())
|
if (!_glfwInitJoysticksLinux())
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
_glfwInitTimerPOSIX();
|
_glfwInitTimerPOSIX();
|
||||||
|
|
||||||
@@ -818,7 +691,9 @@ int _glfwPlatformInit(void)
|
|||||||
|
|
||||||
void _glfwPlatformTerminate(void)
|
void _glfwPlatformTerminate(void)
|
||||||
{
|
{
|
||||||
|
#ifdef __linux__
|
||||||
_glfwTerminateJoysticksLinux();
|
_glfwTerminateJoysticksLinux();
|
||||||
|
#endif
|
||||||
_glfwTerminateEGL();
|
_glfwTerminateEGL();
|
||||||
if (_glfw.wl.egl.handle)
|
if (_glfw.wl.egl.handle)
|
||||||
{
|
{
|
||||||
@@ -826,21 +701,7 @@ void _glfwPlatformTerminate(void)
|
|||||||
_glfw.wl.egl.handle = NULL;
|
_glfw.wl.egl.handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
glfw_xkb_release(&_glfw.wl.xkb);
|
||||||
if (_glfw.wl.xkb.composeState)
|
|
||||||
xkb_compose_state_unref(_glfw.wl.xkb.composeState);
|
|
||||||
#endif
|
|
||||||
if (_glfw.wl.xkb.keymap)
|
|
||||||
xkb_keymap_unref(_glfw.wl.xkb.keymap);
|
|
||||||
if (_glfw.wl.xkb.state)
|
|
||||||
xkb_state_unref(_glfw.wl.xkb.state);
|
|
||||||
if (_glfw.wl.xkb.context)
|
|
||||||
xkb_context_unref(_glfw.wl.xkb.context);
|
|
||||||
if (_glfw.wl.xkb.handle)
|
|
||||||
{
|
|
||||||
_glfw_dlclose(_glfw.wl.xkb.handle);
|
|
||||||
_glfw.wl.xkb.handle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_glfw.wl.cursorTheme)
|
if (_glfw.wl.cursorTheme)
|
||||||
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
|
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
|
||||||
@@ -852,12 +713,16 @@ void _glfwPlatformTerminate(void)
|
|||||||
|
|
||||||
if (_glfw.wl.cursorSurface)
|
if (_glfw.wl.cursorSurface)
|
||||||
wl_surface_destroy(_glfw.wl.cursorSurface);
|
wl_surface_destroy(_glfw.wl.cursorSurface);
|
||||||
|
if (_glfw.wl.subcompositor)
|
||||||
|
wl_subcompositor_destroy(_glfw.wl.subcompositor);
|
||||||
if (_glfw.wl.compositor)
|
if (_glfw.wl.compositor)
|
||||||
wl_compositor_destroy(_glfw.wl.compositor);
|
wl_compositor_destroy(_glfw.wl.compositor);
|
||||||
if (_glfw.wl.shm)
|
if (_glfw.wl.shm)
|
||||||
wl_shm_destroy(_glfw.wl.shm);
|
wl_shm_destroy(_glfw.wl.shm);
|
||||||
if (_glfw.wl.shell)
|
if (_glfw.wl.shell)
|
||||||
wl_shell_destroy(_glfw.wl.shell);
|
wl_shell_destroy(_glfw.wl.shell);
|
||||||
|
if (_glfw.wl.viewporter)
|
||||||
|
wp_viewporter_destroy(_glfw.wl.viewporter);
|
||||||
if (_glfw.wl.wmBase)
|
if (_glfw.wl.wmBase)
|
||||||
xdg_wm_base_destroy(_glfw.wl.wmBase);
|
xdg_wm_base_destroy(_glfw.wl.wmBase);
|
||||||
if (_glfw.wl.pointer)
|
if (_glfw.wl.pointer)
|
||||||
@@ -895,4 +760,3 @@ const char* _glfwPlatformGetVersionString(void)
|
|||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
glfw/wl_monitor.c
vendored
7
glfw/wl_monitor.c
vendored
@@ -136,6 +136,7 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
|
|||||||
|
|
||||||
monitor->wl.scale = 1;
|
monitor->wl.scale = 1;
|
||||||
monitor->wl.output = output;
|
monitor->wl.output = output;
|
||||||
|
monitor->wl.name = name;
|
||||||
|
|
||||||
wl_output_add_listener(output, &outputListener, monitor);
|
wl_output_add_listener(output, &outputListener, monitor);
|
||||||
}
|
}
|
||||||
@@ -145,6 +146,12 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
if (monitor->wl.output)
|
||||||
|
wl_output_destroy(monitor->wl.output);
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
if (xpos)
|
if (xpos)
|
||||||
|
|||||||
126
glfw/wl_platform.h
vendored
126
glfw/wl_platform.h
vendored
@@ -25,10 +25,6 @@
|
|||||||
//========================================================================
|
//========================================================================
|
||||||
|
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
#include <xkbcommon/xkbcommon-compose.h>
|
|
||||||
#endif
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
||||||
@@ -47,12 +43,17 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
|||||||
|
|
||||||
#include "posix_thread.h"
|
#include "posix_thread.h"
|
||||||
#include "posix_time.h"
|
#include "posix_time.h"
|
||||||
|
#ifdef __linux__
|
||||||
#include "linux_joystick.h"
|
#include "linux_joystick.h"
|
||||||
#include "xkb_unicode.h"
|
#else
|
||||||
|
#include "null_joystick.h"
|
||||||
|
#endif
|
||||||
|
#include "xkb_glfw.h"
|
||||||
#include "egl_context.h"
|
#include "egl_context.h"
|
||||||
#include "osmesa_context.h"
|
#include "osmesa_context.h"
|
||||||
|
|
||||||
#include "wayland-xdg-shell-client-protocol.h"
|
#include "wayland-xdg-shell-client-protocol.h"
|
||||||
|
#include "wayland-viewporter-client-protocol.h"
|
||||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
@@ -100,44 +101,28 @@ typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int,
|
|||||||
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
|
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
|
||||||
#define wl_egl_window_resize _glfw.wl.egl.window_resize
|
#define wl_egl_window_resize _glfw.wl.egl.window_resize
|
||||||
|
|
||||||
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
|
#define _GLFW_DECORATION_WIDTH 4
|
||||||
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
|
#define _GLFW_DECORATION_TOP 24
|
||||||
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
|
#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH)
|
||||||
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
|
#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH)
|
||||||
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
|
|
||||||
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
|
|
||||||
typedef void (* PFN_xkb_state_unref)(struct xkb_state*);
|
|
||||||
typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
|
|
||||||
typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
|
|
||||||
typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component);
|
|
||||||
#define xkb_context_new _glfw.wl.xkb.context_new
|
|
||||||
#define xkb_context_unref _glfw.wl.xkb.context_unref
|
|
||||||
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
|
|
||||||
#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref
|
|
||||||
#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index
|
|
||||||
#define xkb_state_new _glfw.wl.xkb.state_new
|
|
||||||
#define xkb_state_unref _glfw.wl.xkb.state_unref
|
|
||||||
#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms
|
|
||||||
#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask
|
|
||||||
#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
typedef enum _GLFWdecorationSideWayland
|
||||||
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
|
{
|
||||||
typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
|
mainWindow,
|
||||||
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
|
topDecoration,
|
||||||
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
|
leftDecoration,
|
||||||
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
|
rightDecoration,
|
||||||
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
|
bottomDecoration,
|
||||||
typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*);
|
|
||||||
#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale
|
|
||||||
#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref
|
|
||||||
#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new
|
|
||||||
#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref
|
|
||||||
#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed
|
|
||||||
#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status
|
|
||||||
#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
} _GLFWdecorationSideWayland;
|
||||||
|
|
||||||
|
typedef struct _GLFWdecorationWayland
|
||||||
|
{
|
||||||
|
struct wl_surface* surface;
|
||||||
|
struct wl_subsurface* subsurface;
|
||||||
|
struct wp_viewport* viewport;
|
||||||
|
|
||||||
|
} _GLFWdecorationWayland;
|
||||||
|
|
||||||
// Wayland-specific per-window data
|
// Wayland-specific per-window data
|
||||||
//
|
//
|
||||||
@@ -180,6 +165,12 @@ typedef struct _GLFWwindowWayland
|
|||||||
// This is a hack to prevent auto-iconification on creation.
|
// This is a hack to prevent auto-iconification on creation.
|
||||||
GLFWbool justCreated;
|
GLFWbool justCreated;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct wl_buffer* buffer;
|
||||||
|
_GLFWdecorationWayland top, left, right, bottom;
|
||||||
|
int focus;
|
||||||
|
} decorations;
|
||||||
|
|
||||||
} _GLFWwindowWayland;
|
} _GLFWwindowWayland;
|
||||||
|
|
||||||
// Wayland-specific global data
|
// Wayland-specific global data
|
||||||
@@ -189,64 +180,33 @@ typedef struct _GLFWlibraryWayland
|
|||||||
struct wl_display* display;
|
struct wl_display* display;
|
||||||
struct wl_registry* registry;
|
struct wl_registry* registry;
|
||||||
struct wl_compositor* compositor;
|
struct wl_compositor* compositor;
|
||||||
|
struct wl_subcompositor* subcompositor;
|
||||||
struct wl_shell* shell;
|
struct wl_shell* shell;
|
||||||
struct wl_shm* shm;
|
struct wl_shm* shm;
|
||||||
struct wl_seat* seat;
|
struct wl_seat* seat;
|
||||||
struct wl_pointer* pointer;
|
struct wl_pointer* pointer;
|
||||||
struct wl_keyboard* keyboard;
|
struct wl_keyboard* keyboard;
|
||||||
struct xdg_wm_base* wmBase;
|
struct xdg_wm_base* wmBase;
|
||||||
|
struct wp_viewporter* viewporter;
|
||||||
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
||||||
struct zwp_pointer_constraints_v1* pointerConstraints;
|
struct zwp_pointer_constraints_v1* pointerConstraints;
|
||||||
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
||||||
|
|
||||||
int compositorVersion;
|
int compositorVersion;
|
||||||
|
int seatVersion;
|
||||||
|
|
||||||
struct wl_cursor_theme* cursorTheme;
|
struct wl_cursor_theme* cursorTheme;
|
||||||
struct wl_surface* cursorSurface;
|
struct wl_surface* cursorSurface;
|
||||||
uint32_t pointerSerial;
|
uint32_t pointerSerial;
|
||||||
|
|
||||||
short int keycodes[256];
|
int32_t keyboardRepeatRate;
|
||||||
short int scancodes[GLFW_KEY_LAST + 1];
|
int32_t keyboardRepeatDelay;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
void* handle;
|
uint32_t key;
|
||||||
struct xkb_context* context;
|
double nextRepeatAt;
|
||||||
struct xkb_keymap* keymap;
|
_GLFWwindow* keyboardFocus;
|
||||||
struct xkb_state* state;
|
} keyRepeatInfo;
|
||||||
|
_GLFWXKBData xkb;
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
struct xkb_compose_state* composeState;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
xkb_mod_mask_t controlMask;
|
|
||||||
xkb_mod_mask_t altMask;
|
|
||||||
xkb_mod_mask_t shiftMask;
|
|
||||||
xkb_mod_mask_t superMask;
|
|
||||||
xkb_mod_mask_t capsLockMask;
|
|
||||||
xkb_mod_mask_t numLockMask;
|
|
||||||
unsigned int modifiers;
|
|
||||||
|
|
||||||
PFN_xkb_context_new context_new;
|
|
||||||
PFN_xkb_context_unref context_unref;
|
|
||||||
PFN_xkb_keymap_new_from_string keymap_new_from_string;
|
|
||||||
PFN_xkb_keymap_unref keymap_unref;
|
|
||||||
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
|
|
||||||
PFN_xkb_state_new state_new;
|
|
||||||
PFN_xkb_state_unref state_unref;
|
|
||||||
PFN_xkb_state_key_get_syms state_key_get_syms;
|
|
||||||
PFN_xkb_state_update_mask state_update_mask;
|
|
||||||
PFN_xkb_state_serialize_mods state_serialize_mods;
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
|
|
||||||
PFN_xkb_compose_table_unref compose_table_unref;
|
|
||||||
PFN_xkb_compose_state_new compose_state_new;
|
|
||||||
PFN_xkb_compose_state_unref compose_state_unref;
|
|
||||||
PFN_xkb_compose_state_feed compose_state_feed;
|
|
||||||
PFN_xkb_compose_state_get_status compose_state_get_status;
|
|
||||||
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
|
|
||||||
#endif
|
|
||||||
} xkb;
|
|
||||||
|
|
||||||
_GLFWwindow* pointerFocus;
|
_GLFWwindow* pointerFocus;
|
||||||
_GLFWwindow* keyboardFocus;
|
_GLFWwindow* keyboardFocus;
|
||||||
@@ -275,6 +235,7 @@ typedef struct _GLFWlibraryWayland
|
|||||||
typedef struct _GLFWmonitorWayland
|
typedef struct _GLFWmonitorWayland
|
||||||
{
|
{
|
||||||
struct wl_output* output;
|
struct wl_output* output;
|
||||||
|
int name;
|
||||||
int currentMode;
|
int currentMode;
|
||||||
|
|
||||||
int x;
|
int x;
|
||||||
@@ -295,4 +256,3 @@ typedef struct _GLFWcursorWayland
|
|||||||
|
|
||||||
|
|
||||||
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||||
|
|
||||||
|
|||||||
559
glfw/wl_window.c
vendored
559
glfw/wl_window.c
vendored
@@ -57,6 +57,16 @@ static void handleConfigure(void* data,
|
|||||||
|
|
||||||
if (!window->monitor)
|
if (!window->monitor)
|
||||||
{
|
{
|
||||||
|
if (_glfw.wl.viewporter && window->decorated)
|
||||||
|
{
|
||||||
|
width -= _GLFW_DECORATION_HORIZONTAL;
|
||||||
|
height -= _GLFW_DECORATION_VERTICAL;
|
||||||
|
}
|
||||||
|
if (width < 1)
|
||||||
|
width = 1;
|
||||||
|
if (height < 1)
|
||||||
|
height = 1;
|
||||||
|
|
||||||
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
|
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
|
||||||
{
|
{
|
||||||
aspectRatio = (float)width / (float)height;
|
aspectRatio = (float)width / (float)height;
|
||||||
@@ -94,9 +104,265 @@ static const struct wl_shell_surface_listener shellSurfaceListener = {
|
|||||||
handlePopupDone
|
handlePopupDone
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
createTmpfileCloexec(char* tmpname)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = mkostemp(tmpname, O_CLOEXEC);
|
||||||
|
if (fd >= 0)
|
||||||
|
unlink(tmpname);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new, unique, anonymous file of the given size, and
|
||||||
|
* return the file descriptor for it. The file descriptor is set
|
||||||
|
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
||||||
|
* the given size at offset zero.
|
||||||
|
*
|
||||||
|
* The file should not have a permanent backing store like a disk,
|
||||||
|
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
||||||
|
*
|
||||||
|
* The file name is deleted from the file system.
|
||||||
|
*
|
||||||
|
* The file is suitable for buffer sharing between processes by
|
||||||
|
* transmitting the file descriptor over Unix sockets using the
|
||||||
|
* SCM_RIGHTS methods.
|
||||||
|
*
|
||||||
|
* posix_fallocate() is used to guarantee that disk space is available
|
||||||
|
* for the file at the given size. If disk space is insufficent, errno
|
||||||
|
* is set to ENOSPC. If posix_fallocate() is not supported, program may
|
||||||
|
* receive SIGBUS on accessing mmap()'ed file contents instead.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
createAnonymousFile(off_t size)
|
||||||
|
{
|
||||||
|
static const char template[] = "/glfw-shared-XXXXXX";
|
||||||
|
const char* path;
|
||||||
|
char* name;
|
||||||
|
int fd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
path = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = calloc(strlen(path) + sizeof(template), 1);
|
||||||
|
strcpy(name, path);
|
||||||
|
strcat(name, template);
|
||||||
|
|
||||||
|
fd = createTmpfileCloexec(name);
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
ret = posix_fallocate(fd, 0, size);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
errno = ret;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wl_buffer* createShmBuffer(const GLFWimage* image)
|
||||||
|
{
|
||||||
|
struct wl_shm_pool* pool;
|
||||||
|
struct wl_buffer* buffer;
|
||||||
|
int stride = image->width * 4;
|
||||||
|
int length = image->width * image->height * 4;
|
||||||
|
void* data;
|
||||||
|
int fd, i;
|
||||||
|
|
||||||
|
fd = createAnonymousFile(length);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Creating a buffer file for %d B failed: %m",
|
||||||
|
length);
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (data == MAP_FAILED)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: mmap failed: %m");
|
||||||
|
close(fd);
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
unsigned char* source = (unsigned char*) image->pixels;
|
||||||
|
unsigned char* target = data;
|
||||||
|
for (i = 0; i < image->width * image->height; i++, source += 4)
|
||||||
|
{
|
||||||
|
unsigned int alpha = source[3];
|
||||||
|
|
||||||
|
*target++ = (unsigned char) ((source[2] * alpha) / 255);
|
||||||
|
*target++ = (unsigned char) ((source[1] * alpha) / 255);
|
||||||
|
*target++ = (unsigned char) ((source[0] * alpha) / 255);
|
||||||
|
*target++ = (unsigned char) alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer =
|
||||||
|
wl_shm_pool_create_buffer(pool, 0,
|
||||||
|
image->width,
|
||||||
|
image->height,
|
||||||
|
stride, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
munmap(data, length);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createDecoration(_GLFWdecorationWayland* decoration,
|
||||||
|
struct wl_surface* parent,
|
||||||
|
struct wl_buffer* buffer, GLFWbool opaque,
|
||||||
|
int x, int y,
|
||||||
|
int width, int height)
|
||||||
|
{
|
||||||
|
struct wl_region* region;
|
||||||
|
|
||||||
|
decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||||
|
decoration->subsurface =
|
||||||
|
wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
|
||||||
|
decoration->surface, parent);
|
||||||
|
wl_subsurface_set_position(decoration->subsurface, x, y);
|
||||||
|
decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
|
||||||
|
decoration->surface);
|
||||||
|
wp_viewport_set_destination(decoration->viewport, width, height);
|
||||||
|
wl_surface_attach(decoration->surface, buffer, 0, 0);
|
||||||
|
|
||||||
|
if (opaque)
|
||||||
|
{
|
||||||
|
region = wl_compositor_create_region(_glfw.wl.compositor);
|
||||||
|
wl_region_add(region, 0, 0, width, height);
|
||||||
|
wl_surface_set_opaque_region(decoration->surface, region);
|
||||||
|
wl_surface_commit(decoration->surface);
|
||||||
|
wl_region_destroy(region);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
wl_surface_commit(decoration->surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createDecorations(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
unsigned char data[] = { 224, 224, 224, 255 };
|
||||||
|
const GLFWimage image = { 1, 1, data };
|
||||||
|
GLFWbool opaque = (data[3] == 255);
|
||||||
|
|
||||||
|
if (!_glfw.wl.viewporter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!window->wl.decorations.buffer)
|
||||||
|
window->wl.decorations.buffer = createShmBuffer(&image);
|
||||||
|
|
||||||
|
createDecoration(&window->wl.decorations.top, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
0, -_GLFW_DECORATION_TOP,
|
||||||
|
window->wl.width, _GLFW_DECORATION_TOP);
|
||||||
|
createDecoration(&window->wl.decorations.left, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
-_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
createDecoration(&window->wl.decorations.right, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
window->wl.width, -_GLFW_DECORATION_TOP,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
createDecoration(&window->wl.decorations.bottom, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
-_GLFW_DECORATION_WIDTH, window->wl.height,
|
||||||
|
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroyDecoration(_GLFWdecorationWayland* decoration)
|
||||||
|
{
|
||||||
|
if (decoration->surface)
|
||||||
|
wl_surface_destroy(decoration->surface);
|
||||||
|
if (decoration->subsurface)
|
||||||
|
wl_subsurface_destroy(decoration->subsurface);
|
||||||
|
if (decoration->viewport)
|
||||||
|
wp_viewport_destroy(decoration->viewport);
|
||||||
|
decoration->surface = NULL;
|
||||||
|
decoration->subsurface = NULL;
|
||||||
|
decoration->viewport = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroyDecorations(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
destroyDecoration(&window->wl.decorations.top);
|
||||||
|
destroyDecoration(&window->wl.decorations.left);
|
||||||
|
destroyDecoration(&window->wl.decorations.right);
|
||||||
|
destroyDecoration(&window->wl.decorations.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes the surface considered as XRGB instead of ARGB.
|
||||||
|
static void setOpaqueRegion(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
struct wl_region* region;
|
||||||
|
|
||||||
|
region = wl_compositor_create_region(_glfw.wl.compositor);
|
||||||
|
if (!region)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
|
||||||
|
wl_surface_set_opaque_region(window->wl.surface, region);
|
||||||
|
wl_surface_commit(window->wl.surface);
|
||||||
|
wl_region_destroy(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void resizeWindow(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
int scale = window->wl.scale;
|
||||||
|
int scaledWidth = window->wl.width * scale;
|
||||||
|
int scaledHeight = window->wl.height * scale;
|
||||||
|
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
||||||
|
if (!window->wl.transparent)
|
||||||
|
setOpaqueRegion(window);
|
||||||
|
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
||||||
|
_glfwInputWindowContentScale(window, scale, scale);
|
||||||
|
|
||||||
|
if (!window->wl.decorations.top.surface)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Top decoration.
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.top.viewport,
|
||||||
|
window->wl.width, _GLFW_DECORATION_TOP);
|
||||||
|
wl_surface_commit(window->wl.decorations.top.surface);
|
||||||
|
|
||||||
|
// Left decoration.
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.left.viewport,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
wl_surface_commit(window->wl.decorations.left.surface);
|
||||||
|
|
||||||
|
// Right decoration.
|
||||||
|
wl_subsurface_set_position(window->wl.decorations.right.subsurface,
|
||||||
|
window->wl.width, -_GLFW_DECORATION_TOP);
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.right.viewport,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
wl_surface_commit(window->wl.decorations.right.surface);
|
||||||
|
|
||||||
|
// Bottom decoration.
|
||||||
|
wl_subsurface_set_position(window->wl.decorations.bottom.subsurface,
|
||||||
|
-_GLFW_DECORATION_WIDTH, window->wl.height);
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.bottom.viewport,
|
||||||
|
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
|
||||||
|
wl_surface_commit(window->wl.decorations.bottom.surface);
|
||||||
|
}
|
||||||
|
|
||||||
static void checkScaleChange(_GLFWwindow* window)
|
static void checkScaleChange(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
int scaledWidth, scaledHeight;
|
|
||||||
int scale = 1;
|
int scale = 1;
|
||||||
int i;
|
int i;
|
||||||
int monitorScale;
|
int monitorScale;
|
||||||
@@ -117,12 +383,8 @@ static void checkScaleChange(_GLFWwindow* window)
|
|||||||
if (scale != window->wl.scale)
|
if (scale != window->wl.scale)
|
||||||
{
|
{
|
||||||
window->wl.scale = scale;
|
window->wl.scale = scale;
|
||||||
scaledWidth = window->wl.width * scale;
|
|
||||||
scaledHeight = window->wl.height * scale;
|
|
||||||
wl_surface_set_buffer_scale(window->wl.surface, scale);
|
wl_surface_set_buffer_scale(window->wl.surface, scale);
|
||||||
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
resizeWindow(window);
|
||||||
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
|
||||||
_glfwInputWindowContentScale(window, scale, scale);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,21 +434,6 @@ static const struct wl_surface_listener surfaceListener = {
|
|||||||
handleLeave
|
handleLeave
|
||||||
};
|
};
|
||||||
|
|
||||||
// Makes the surface considered as XRGB instead of ARGB.
|
|
||||||
static void setOpaqueRegion(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
struct wl_region* region;
|
|
||||||
|
|
||||||
region = wl_compositor_create_region(_glfw.wl.compositor);
|
|
||||||
if (!region)
|
|
||||||
return;
|
|
||||||
|
|
||||||
wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
|
|
||||||
wl_surface_set_opaque_region(window->wl.surface, region);
|
|
||||||
wl_surface_commit(window->wl.surface);
|
|
||||||
wl_region_destroy(region);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
|
static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
|
||||||
{
|
{
|
||||||
if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
|
if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
|
||||||
@@ -231,9 +478,32 @@ static GLFWbool createSurface(_GLFWwindow* window,
|
|||||||
if (!window->wl.transparent)
|
if (!window->wl.transparent)
|
||||||
setOpaqueRegion(window);
|
setOpaqueRegion(window);
|
||||||
|
|
||||||
|
if (window->decorated && !window->monitor)
|
||||||
|
createDecorations(window);
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refreshRate)
|
||||||
|
{
|
||||||
|
if (window->wl.xdg.toplevel)
|
||||||
|
{
|
||||||
|
xdg_toplevel_set_fullscreen(
|
||||||
|
window->wl.xdg.toplevel,
|
||||||
|
monitor->wl.output);
|
||||||
|
}
|
||||||
|
else if (window->wl.shellSurface)
|
||||||
|
{
|
||||||
|
wl_shell_surface_set_fullscreen(
|
||||||
|
window->wl.shellSurface,
|
||||||
|
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
|
||||||
|
refreshRate * 1000, // Convert Hz to mHz.
|
||||||
|
monitor->wl.output);
|
||||||
|
}
|
||||||
|
setIdleInhibitor(window, GLFW_TRUE);
|
||||||
|
destroyDecorations(window);
|
||||||
|
}
|
||||||
|
|
||||||
static GLFWbool createShellSurface(_GLFWwindow* window)
|
static GLFWbool createShellSurface(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
if (!_glfw.wl.shell)
|
if (!_glfw.wl.shell)
|
||||||
@@ -261,12 +531,7 @@ static GLFWbool createShellSurface(_GLFWwindow* window)
|
|||||||
|
|
||||||
if (window->monitor)
|
if (window->monitor)
|
||||||
{
|
{
|
||||||
wl_shell_surface_set_fullscreen(
|
setFullscreen(window, window->monitor, 0);
|
||||||
window->wl.shellSurface,
|
|
||||||
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
|
|
||||||
0,
|
|
||||||
window->monitor->wl.output);
|
|
||||||
setIdleInhibitor(window, GLFW_TRUE);
|
|
||||||
}
|
}
|
||||||
else if (window->wl.maximized)
|
else if (window->wl.maximized)
|
||||||
{
|
{
|
||||||
@@ -424,25 +689,32 @@ static GLFWbool createXdgSurface(_GLFWwindow* window)
|
|||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dispatchPendingKeyRepeats() {
|
||||||
|
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||||
|
double now = glfwGetTime();
|
||||||
|
while (_glfw.wl.keyRepeatInfo.nextRepeatAt <= now) {
|
||||||
|
glfw_xkb_handle_key_event(_glfw.wl.keyRepeatInfo.keyboardFocus, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
|
||||||
|
_glfw.wl.keyRepeatInfo.nextRepeatAt += 1.0 / _glfw.wl.keyboardRepeatRate;
|
||||||
|
now = glfwGetTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
createTmpfileCloexec(char* tmpname)
|
adjustTimeoutForKeyRepeat(int timeout) {
|
||||||
{
|
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return timeout;
|
||||||
int fd;
|
double now = glfwGetTime();
|
||||||
|
if (timeout < 0 || now + timeout / 1000. > _glfw.wl.keyRepeatInfo.nextRepeatAt) {
|
||||||
fd = mkostemp(tmpname, O_CLOEXEC);
|
timeout = _glfw.wl.keyRepeatInfo.nextRepeatAt <= now ? 0 : ( (_glfw.wl.keyRepeatInfo.nextRepeatAt - now) * 1000 + 1 );
|
||||||
if (fd >= 0)
|
}
|
||||||
unlink(tmpname);
|
return timeout;
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handleEvents(int timeout)
|
handleEvents(int timeout)
|
||||||
{
|
{
|
||||||
struct wl_display* display = _glfw.wl.display;
|
struct wl_display* display = _glfw.wl.display;
|
||||||
struct pollfd fds[] = {
|
struct pollfd pfd = { wl_display_get_fd(display), POLLIN };
|
||||||
{ wl_display_get_fd(display), POLLIN },
|
|
||||||
};
|
|
||||||
|
|
||||||
while (wl_display_prepare_read(display) != 0)
|
while (wl_display_prepare_read(display) != 0)
|
||||||
wl_display_dispatch_pending(display);
|
wl_display_dispatch_pending(display);
|
||||||
@@ -462,71 +734,27 @@ handleEvents(int timeout)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (poll(fds, 1, timeout) > 0)
|
dispatchPendingKeyRepeats();
|
||||||
|
timeout = adjustTimeoutForKeyRepeat(timeout);
|
||||||
|
|
||||||
|
if (poll(&pfd, 1, timeout) > 0)
|
||||||
{
|
{
|
||||||
wl_display_read_events(display);
|
if (pfd.revents & POLLIN)
|
||||||
wl_display_dispatch_pending(display);
|
{
|
||||||
|
wl_display_read_events(display);
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wl_display_cancel_read(display);
|
wl_display_cancel_read(display);
|
||||||
}
|
}
|
||||||
}
|
dispatchPendingKeyRepeats();
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a new, unique, anonymous file of the given size, and
|
|
||||||
* return the file descriptor for it. The file descriptor is set
|
|
||||||
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
|
||||||
* the given size at offset zero.
|
|
||||||
*
|
|
||||||
* The file should not have a permanent backing store like a disk,
|
|
||||||
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
|
||||||
*
|
|
||||||
* The file name is deleted from the file system.
|
|
||||||
*
|
|
||||||
* The file is suitable for buffer sharing between processes by
|
|
||||||
* transmitting the file descriptor over Unix sockets using the
|
|
||||||
* SCM_RIGHTS methods.
|
|
||||||
*
|
|
||||||
* posix_fallocate() is used to guarantee that disk space is available
|
|
||||||
* for the file at the given size. If disk space is insufficent, errno
|
|
||||||
* is set to ENOSPC. If posix_fallocate() is not supported, program may
|
|
||||||
* receive SIGBUS on accessing mmap()'ed file contents instead.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
createAnonymousFile(off_t size)
|
|
||||||
{
|
|
||||||
static const char template[] = "/glfw-shared-XXXXXX";
|
|
||||||
const char* path;
|
|
||||||
char* name;
|
|
||||||
int fd;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
path = getenv("XDG_RUNTIME_DIR");
|
|
||||||
if (!path)
|
|
||||||
{
|
|
||||||
errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = calloc(strlen(path) + sizeof(template), 1);
|
|
||||||
strcpy(name, path);
|
|
||||||
strcat(name, template);
|
|
||||||
|
|
||||||
fd = createTmpfileCloexec(name);
|
|
||||||
|
|
||||||
free(name);
|
|
||||||
|
|
||||||
if (fd < 0)
|
|
||||||
return -1;
|
|
||||||
ret = posix_fallocate(fd, 0, size);
|
|
||||||
if (ret != 0)
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
errno = ret;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translates a GLFW standard cursor to a theme cursor name
|
// Translates a GLFW standard cursor to a theme cursor name
|
||||||
@@ -639,6 +867,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
|||||||
if (window->context.destroy)
|
if (window->context.destroy)
|
||||||
window->context.destroy(window);
|
window->context.destroy(window);
|
||||||
|
|
||||||
|
destroyDecorations(window);
|
||||||
|
if (window->wl.decorations.buffer)
|
||||||
|
wl_buffer_destroy(window->wl.decorations.buffer);
|
||||||
|
|
||||||
if (window->wl.native)
|
if (window->wl.native)
|
||||||
wl_egl_window_destroy(window->wl.native);
|
wl_egl_window_destroy(window->wl.native);
|
||||||
|
|
||||||
@@ -703,14 +935,9 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
|
|||||||
|
|
||||||
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
||||||
{
|
{
|
||||||
int scaledWidth = width * window->wl.scale;
|
|
||||||
int scaledHeight = height * window->wl.scale;
|
|
||||||
window->wl.width = width;
|
window->wl.width = width;
|
||||||
window->wl.height = height;
|
window->wl.height = height;
|
||||||
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
resizeWindow(window);
|
||||||
if (!window->wl.transparent)
|
|
||||||
setOpaqueRegion(window);
|
|
||||||
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
||||||
@@ -754,8 +981,17 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
|||||||
int* left, int* top,
|
int* left, int* top,
|
||||||
int* right, int* bottom)
|
int* right, int* bottom)
|
||||||
{
|
{
|
||||||
// TODO: will need a proper implementation once decorations are
|
if (window->decorated && !window->monitor)
|
||||||
// implemented, but for now just leave everything as 0.
|
{
|
||||||
|
if (top)
|
||||||
|
*top = _GLFW_DECORATION_TOP;
|
||||||
|
if (left)
|
||||||
|
*left = _GLFW_DECORATION_WIDTH;
|
||||||
|
if (right)
|
||||||
|
*right = _GLFW_DECORATION_WIDTH;
|
||||||
|
if (bottom)
|
||||||
|
*bottom = _GLFW_DECORATION_WIDTH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||||
@@ -851,11 +1087,15 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
"Wayland: Window attention request not implemented yet");
|
"Wayland: Window attention request not implemented yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO: Use an actual Wayland API to implement this when one becomes available
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
int fd = open("/dev/tty", O_WRONLY | O_CLOEXEC);
|
||||||
"Wayland: Window bell request not implemented yet");
|
if (fd > -1) {
|
||||||
|
int ret = write(fd, "\x07", 1) == 1 ? GLFW_TRUE : GLFW_FALSE;
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,35 +1111,20 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
|||||||
int width, int height,
|
int width, int height,
|
||||||
int refreshRate)
|
int refreshRate)
|
||||||
{
|
{
|
||||||
if (window->wl.xdg.toplevel)
|
if (monitor)
|
||||||
{
|
{
|
||||||
if (monitor)
|
setFullscreen(window, monitor, refreshRate);
|
||||||
{
|
}
|
||||||
xdg_toplevel_set_fullscreen(
|
else
|
||||||
window->wl.xdg.toplevel,
|
{
|
||||||
monitor->wl.output);
|
if (window->wl.xdg.toplevel)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
|
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
|
||||||
}
|
else if (window->wl.shellSurface)
|
||||||
}
|
|
||||||
else if (window->wl.shellSurface)
|
|
||||||
{
|
|
||||||
if (monitor)
|
|
||||||
{
|
|
||||||
wl_shell_surface_set_fullscreen(
|
|
||||||
window->wl.shellSurface,
|
|
||||||
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
|
|
||||||
refreshRate * 1000, // Convert Hz to mHz.
|
|
||||||
monitor->wl.output);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wl_shell_surface_set_toplevel(window->wl.shellSurface);
|
wl_shell_surface_set_toplevel(window->wl.shellSurface);
|
||||||
}
|
setIdleInhibitor(window, GLFW_FALSE);
|
||||||
|
if (window->decorated)
|
||||||
|
createDecorations(window);
|
||||||
}
|
}
|
||||||
setIdleInhibitor(window, monitor ? GLFW_TRUE : GLFW_FALSE);
|
|
||||||
_glfwInputWindowMonitor(window, monitor);
|
_glfwInputWindowMonitor(window, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,9 +1169,13 @@ void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
|
|||||||
|
|
||||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
|
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
|
||||||
{
|
{
|
||||||
// TODO
|
if (!window->monitor)
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
{
|
||||||
"Wayland: Window attribute setting not implemented yet");
|
if (enabled)
|
||||||
|
createDecorations(window);
|
||||||
|
else
|
||||||
|
destroyDecorations(window);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
|
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
|
||||||
@@ -1013,66 +1242,19 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
|||||||
|
|
||||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||||
{
|
{
|
||||||
// TODO
|
return glfw_xkb_keysym_name(scancode);
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformGetKeyScancode(int key)
|
int _glfwPlatformGetKeyScancode(int key)
|
||||||
{
|
{
|
||||||
return _glfw.wl.scancodes[key];
|
return glfw_xkb_sym_for_key(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||||
const GLFWimage* image,
|
const GLFWimage* image,
|
||||||
int xhot, int yhot)
|
int xhot, int yhot)
|
||||||
{
|
{
|
||||||
struct wl_shm_pool* pool;
|
cursor->wl.buffer = createShmBuffer(image);
|
||||||
int stride = image->width * 4;
|
|
||||||
int length = image->width * image->height * 4;
|
|
||||||
void* data;
|
|
||||||
int fd, i;
|
|
||||||
|
|
||||||
fd = createAnonymousFile(length);
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Creating a buffer file for %d B failed: %m",
|
|
||||||
length);
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (data == MAP_FAILED)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Cursor mmap failed: %m");
|
|
||||||
close(fd);
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
unsigned char* source = (unsigned char*) image->pixels;
|
|
||||||
unsigned char* target = data;
|
|
||||||
for (i = 0; i < image->width * image->height; i++, source += 4)
|
|
||||||
{
|
|
||||||
unsigned int alpha = source[3];
|
|
||||||
|
|
||||||
*target++ = (unsigned char) ((source[2] * alpha) / 255);
|
|
||||||
*target++ = (unsigned char) ((source[1] * alpha) / 255);
|
|
||||||
*target++ = (unsigned char) ((source[0] * alpha) / 255);
|
|
||||||
*target++ = (unsigned char) alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor->wl.buffer =
|
|
||||||
wl_shm_pool_create_buffer(pool, 0,
|
|
||||||
image->width,
|
|
||||||
image->height,
|
|
||||||
stride, WL_SHM_FORMAT_ARGB8888);
|
|
||||||
munmap(data, length);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
|
|
||||||
cursor->wl.width = image->width;
|
cursor->wl.width = image->width;
|
||||||
cursor->wl.height = image->height;
|
cursor->wl.height = image->height;
|
||||||
cursor->wl.xhot = xhot;
|
cursor->wl.xhot = xhot;
|
||||||
@@ -1376,4 +1558,3 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
return window->wl.surface;
|
return window->wl.surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
447
glfw/x11_init.c
vendored
447
glfw/x11_init.c
vendored
@@ -36,319 +36,6 @@
|
|||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
|
||||||
|
|
||||||
// Translate an X11 key code to a GLFW key code.
|
|
||||||
//
|
|
||||||
static int translateKeyCode(int scancode)
|
|
||||||
{
|
|
||||||
int keySym;
|
|
||||||
|
|
||||||
// Valid key code range is [8,255], according to the Xlib manual
|
|
||||||
if (scancode < 8 || scancode > 255)
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
|
|
||||||
if (_glfw.x11.xkb.available)
|
|
||||||
{
|
|
||||||
// Try secondary keysym, for numeric keypad keys
|
|
||||||
// Note: This way we always force "NumLock = ON", which is intentional
|
|
||||||
// since the returned key code should correspond to a physical
|
|
||||||
// location.
|
|
||||||
keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 1);
|
|
||||||
switch (keySym)
|
|
||||||
{
|
|
||||||
case XK_KP_0: return GLFW_KEY_KP_0;
|
|
||||||
case XK_KP_1: return GLFW_KEY_KP_1;
|
|
||||||
case XK_KP_2: return GLFW_KEY_KP_2;
|
|
||||||
case XK_KP_3: return GLFW_KEY_KP_3;
|
|
||||||
case XK_KP_4: return GLFW_KEY_KP_4;
|
|
||||||
case XK_KP_5: return GLFW_KEY_KP_5;
|
|
||||||
case XK_KP_6: return GLFW_KEY_KP_6;
|
|
||||||
case XK_KP_7: return GLFW_KEY_KP_7;
|
|
||||||
case XK_KP_8: return GLFW_KEY_KP_8;
|
|
||||||
case XK_KP_9: return GLFW_KEY_KP_9;
|
|
||||||
case XK_KP_Separator:
|
|
||||||
case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL;
|
|
||||||
case XK_KP_Equal: return GLFW_KEY_KP_EQUAL;
|
|
||||||
case XK_KP_Enter: return GLFW_KEY_KP_ENTER;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now try primary keysym for function keys (non-printable keys)
|
|
||||||
// These should not depend on the current keyboard layout
|
|
||||||
keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int dummy;
|
|
||||||
KeySym* keySyms;
|
|
||||||
|
|
||||||
keySyms = XGetKeyboardMapping(_glfw.x11.display, scancode, 1, &dummy);
|
|
||||||
keySym = keySyms[0];
|
|
||||||
XFree(keySyms);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (keySym)
|
|
||||||
{
|
|
||||||
case XK_Escape: return GLFW_KEY_ESCAPE;
|
|
||||||
case XK_Tab: return GLFW_KEY_TAB;
|
|
||||||
case XK_Shift_L: return GLFW_KEY_LEFT_SHIFT;
|
|
||||||
case XK_Shift_R: return GLFW_KEY_RIGHT_SHIFT;
|
|
||||||
case XK_Control_L: return GLFW_KEY_LEFT_CONTROL;
|
|
||||||
case XK_Control_R: return GLFW_KEY_RIGHT_CONTROL;
|
|
||||||
case XK_Meta_L:
|
|
||||||
case XK_Alt_L: return GLFW_KEY_LEFT_ALT;
|
|
||||||
case XK_Mode_switch: // Mapped to Alt_R on many keyboards
|
|
||||||
case XK_ISO_Level3_Shift: // AltGr on at least some machines
|
|
||||||
case XK_Meta_R:
|
|
||||||
case XK_Alt_R: return GLFW_KEY_RIGHT_ALT;
|
|
||||||
case XK_Super_L: return GLFW_KEY_LEFT_SUPER;
|
|
||||||
case XK_Super_R: return GLFW_KEY_RIGHT_SUPER;
|
|
||||||
case XK_Menu: return GLFW_KEY_MENU;
|
|
||||||
case XK_Num_Lock: return GLFW_KEY_NUM_LOCK;
|
|
||||||
case XK_Caps_Lock: return GLFW_KEY_CAPS_LOCK;
|
|
||||||
case XK_Print: return GLFW_KEY_PRINT_SCREEN;
|
|
||||||
case XK_Scroll_Lock: return GLFW_KEY_SCROLL_LOCK;
|
|
||||||
case XK_Pause: return GLFW_KEY_PAUSE;
|
|
||||||
case XK_Delete: return GLFW_KEY_DELETE;
|
|
||||||
case XK_BackSpace: return GLFW_KEY_BACKSPACE;
|
|
||||||
case XK_Return: return GLFW_KEY_ENTER;
|
|
||||||
case XK_Home: return GLFW_KEY_HOME;
|
|
||||||
case XK_End: return GLFW_KEY_END;
|
|
||||||
case XK_Page_Up: return GLFW_KEY_PAGE_UP;
|
|
||||||
case XK_Page_Down: return GLFW_KEY_PAGE_DOWN;
|
|
||||||
case XK_Insert: return GLFW_KEY_INSERT;
|
|
||||||
case XK_Left: return GLFW_KEY_LEFT;
|
|
||||||
case XK_Right: return GLFW_KEY_RIGHT;
|
|
||||||
case XK_Down: return GLFW_KEY_DOWN;
|
|
||||||
case XK_Up: return GLFW_KEY_UP;
|
|
||||||
case XK_F1: return GLFW_KEY_F1;
|
|
||||||
case XK_F2: return GLFW_KEY_F2;
|
|
||||||
case XK_F3: return GLFW_KEY_F3;
|
|
||||||
case XK_F4: return GLFW_KEY_F4;
|
|
||||||
case XK_F5: return GLFW_KEY_F5;
|
|
||||||
case XK_F6: return GLFW_KEY_F6;
|
|
||||||
case XK_F7: return GLFW_KEY_F7;
|
|
||||||
case XK_F8: return GLFW_KEY_F8;
|
|
||||||
case XK_F9: return GLFW_KEY_F9;
|
|
||||||
case XK_F10: return GLFW_KEY_F10;
|
|
||||||
case XK_F11: return GLFW_KEY_F11;
|
|
||||||
case XK_F12: return GLFW_KEY_F12;
|
|
||||||
case XK_F13: return GLFW_KEY_F13;
|
|
||||||
case XK_F14: return GLFW_KEY_F14;
|
|
||||||
case XK_F15: return GLFW_KEY_F15;
|
|
||||||
case XK_F16: return GLFW_KEY_F16;
|
|
||||||
case XK_F17: return GLFW_KEY_F17;
|
|
||||||
case XK_F18: return GLFW_KEY_F18;
|
|
||||||
case XK_F19: return GLFW_KEY_F19;
|
|
||||||
case XK_F20: return GLFW_KEY_F20;
|
|
||||||
case XK_F21: return GLFW_KEY_F21;
|
|
||||||
case XK_F22: return GLFW_KEY_F22;
|
|
||||||
case XK_F23: return GLFW_KEY_F23;
|
|
||||||
case XK_F24: return GLFW_KEY_F24;
|
|
||||||
case XK_F25: return GLFW_KEY_F25;
|
|
||||||
|
|
||||||
// Numeric keypad
|
|
||||||
case XK_KP_Divide: return GLFW_KEY_KP_DIVIDE;
|
|
||||||
case XK_KP_Multiply: return GLFW_KEY_KP_MULTIPLY;
|
|
||||||
case XK_KP_Subtract: return GLFW_KEY_KP_SUBTRACT;
|
|
||||||
case XK_KP_Add: return GLFW_KEY_KP_ADD;
|
|
||||||
|
|
||||||
// These should have been detected in secondary keysym test above!
|
|
||||||
case XK_KP_Insert: return GLFW_KEY_KP_0;
|
|
||||||
case XK_KP_End: return GLFW_KEY_KP_1;
|
|
||||||
case XK_KP_Down: return GLFW_KEY_KP_2;
|
|
||||||
case XK_KP_Page_Down: return GLFW_KEY_KP_3;
|
|
||||||
case XK_KP_Left: return GLFW_KEY_KP_4;
|
|
||||||
case XK_KP_Right: return GLFW_KEY_KP_6;
|
|
||||||
case XK_KP_Home: return GLFW_KEY_KP_7;
|
|
||||||
case XK_KP_Up: return GLFW_KEY_KP_8;
|
|
||||||
case XK_KP_Page_Up: return GLFW_KEY_KP_9;
|
|
||||||
case XK_KP_Delete: return GLFW_KEY_KP_DECIMAL;
|
|
||||||
case XK_KP_Equal: return GLFW_KEY_KP_EQUAL;
|
|
||||||
case XK_KP_Enter: return GLFW_KEY_KP_ENTER;
|
|
||||||
|
|
||||||
// Last resort: Check for printable keys (should not happen if the XKB
|
|
||||||
// extension is available). This will give a layout dependent mapping
|
|
||||||
// (which is wrong, and we may miss some keys, especially on non-US
|
|
||||||
// keyboards), but it's better than nothing...
|
|
||||||
case XK_a: return GLFW_KEY_A;
|
|
||||||
case XK_b: return GLFW_KEY_B;
|
|
||||||
case XK_c: return GLFW_KEY_C;
|
|
||||||
case XK_d: return GLFW_KEY_D;
|
|
||||||
case XK_e: return GLFW_KEY_E;
|
|
||||||
case XK_f: return GLFW_KEY_F;
|
|
||||||
case XK_g: return GLFW_KEY_G;
|
|
||||||
case XK_h: return GLFW_KEY_H;
|
|
||||||
case XK_i: return GLFW_KEY_I;
|
|
||||||
case XK_j: return GLFW_KEY_J;
|
|
||||||
case XK_k: return GLFW_KEY_K;
|
|
||||||
case XK_l: return GLFW_KEY_L;
|
|
||||||
case XK_m: return GLFW_KEY_M;
|
|
||||||
case XK_n: return GLFW_KEY_N;
|
|
||||||
case XK_o: return GLFW_KEY_O;
|
|
||||||
case XK_p: return GLFW_KEY_P;
|
|
||||||
case XK_q: return GLFW_KEY_Q;
|
|
||||||
case XK_r: return GLFW_KEY_R;
|
|
||||||
case XK_s: return GLFW_KEY_S;
|
|
||||||
case XK_t: return GLFW_KEY_T;
|
|
||||||
case XK_u: return GLFW_KEY_U;
|
|
||||||
case XK_v: return GLFW_KEY_V;
|
|
||||||
case XK_w: return GLFW_KEY_W;
|
|
||||||
case XK_x: return GLFW_KEY_X;
|
|
||||||
case XK_y: return GLFW_KEY_Y;
|
|
||||||
case XK_z: return GLFW_KEY_Z;
|
|
||||||
case XK_1: return GLFW_KEY_1;
|
|
||||||
case XK_2: return GLFW_KEY_2;
|
|
||||||
case XK_3: return GLFW_KEY_3;
|
|
||||||
case XK_4: return GLFW_KEY_4;
|
|
||||||
case XK_5: return GLFW_KEY_5;
|
|
||||||
case XK_6: return GLFW_KEY_6;
|
|
||||||
case XK_7: return GLFW_KEY_7;
|
|
||||||
case XK_8: return GLFW_KEY_8;
|
|
||||||
case XK_9: return GLFW_KEY_9;
|
|
||||||
case XK_0: return GLFW_KEY_0;
|
|
||||||
case XK_space: return GLFW_KEY_SPACE;
|
|
||||||
case XK_minus: return GLFW_KEY_MINUS;
|
|
||||||
case XK_equal: return GLFW_KEY_EQUAL;
|
|
||||||
case XK_bracketleft: return GLFW_KEY_LEFT_BRACKET;
|
|
||||||
case XK_bracketright: return GLFW_KEY_RIGHT_BRACKET;
|
|
||||||
case XK_backslash: return GLFW_KEY_BACKSLASH;
|
|
||||||
case XK_semicolon: return GLFW_KEY_SEMICOLON;
|
|
||||||
case XK_apostrophe: return GLFW_KEY_APOSTROPHE;
|
|
||||||
case XK_grave: return GLFW_KEY_GRAVE_ACCENT;
|
|
||||||
case XK_comma: return GLFW_KEY_COMMA;
|
|
||||||
case XK_period: return GLFW_KEY_PERIOD;
|
|
||||||
case XK_slash: return GLFW_KEY_SLASH;
|
|
||||||
case XK_less: return GLFW_KEY_WORLD_1; // At least in some layouts...
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No matching translation was found
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create key code translation tables
|
|
||||||
//
|
|
||||||
static void createKeyTables(void)
|
|
||||||
{
|
|
||||||
int scancode, key;
|
|
||||||
|
|
||||||
memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes));
|
|
||||||
memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes));
|
|
||||||
|
|
||||||
if (_glfw.x11.xkb.available)
|
|
||||||
{
|
|
||||||
// Use XKB to determine physical key locations independently of the
|
|
||||||
// current keyboard layout
|
|
||||||
|
|
||||||
char name[XkbKeyNameLength + 1];
|
|
||||||
XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd);
|
|
||||||
XkbGetNames(_glfw.x11.display, XkbKeyNamesMask, desc);
|
|
||||||
|
|
||||||
// Find the X11 key code -> GLFW key code mapping
|
|
||||||
for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++)
|
|
||||||
{
|
|
||||||
memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength);
|
|
||||||
name[XkbKeyNameLength] = '\0';
|
|
||||||
|
|
||||||
// Map the key name to a GLFW key code. Note: We only map printable
|
|
||||||
// keys here, and we use the US keyboard layout. The rest of the
|
|
||||||
// keys (function keys) are mapped using traditional KeySym
|
|
||||||
// translations.
|
|
||||||
if (strcmp(name, "TLDE") == 0) key = GLFW_KEY_GRAVE_ACCENT;
|
|
||||||
else if (strcmp(name, "AE01") == 0) key = GLFW_KEY_1;
|
|
||||||
else if (strcmp(name, "AE02") == 0) key = GLFW_KEY_2;
|
|
||||||
else if (strcmp(name, "AE03") == 0) key = GLFW_KEY_3;
|
|
||||||
else if (strcmp(name, "AE04") == 0) key = GLFW_KEY_4;
|
|
||||||
else if (strcmp(name, "AE05") == 0) key = GLFW_KEY_5;
|
|
||||||
else if (strcmp(name, "AE06") == 0) key = GLFW_KEY_6;
|
|
||||||
else if (strcmp(name, "AE07") == 0) key = GLFW_KEY_7;
|
|
||||||
else if (strcmp(name, "AE08") == 0) key = GLFW_KEY_8;
|
|
||||||
else if (strcmp(name, "AE09") == 0) key = GLFW_KEY_9;
|
|
||||||
else if (strcmp(name, "AE10") == 0) key = GLFW_KEY_0;
|
|
||||||
else if (strcmp(name, "AE11") == 0) key = GLFW_KEY_MINUS;
|
|
||||||
else if (strcmp(name, "AE12") == 0) key = GLFW_KEY_EQUAL;
|
|
||||||
else if (strcmp(name, "AD01") == 0) key = GLFW_KEY_Q;
|
|
||||||
else if (strcmp(name, "AD02") == 0) key = GLFW_KEY_W;
|
|
||||||
else if (strcmp(name, "AD03") == 0) key = GLFW_KEY_E;
|
|
||||||
else if (strcmp(name, "AD04") == 0) key = GLFW_KEY_R;
|
|
||||||
else if (strcmp(name, "AD05") == 0) key = GLFW_KEY_T;
|
|
||||||
else if (strcmp(name, "AD06") == 0) key = GLFW_KEY_Y;
|
|
||||||
else if (strcmp(name, "AD07") == 0) key = GLFW_KEY_U;
|
|
||||||
else if (strcmp(name, "AD08") == 0) key = GLFW_KEY_I;
|
|
||||||
else if (strcmp(name, "AD09") == 0) key = GLFW_KEY_O;
|
|
||||||
else if (strcmp(name, "AD10") == 0) key = GLFW_KEY_P;
|
|
||||||
else if (strcmp(name, "AD11") == 0) key = GLFW_KEY_LEFT_BRACKET;
|
|
||||||
else if (strcmp(name, "AD12") == 0) key = GLFW_KEY_RIGHT_BRACKET;
|
|
||||||
else if (strcmp(name, "AC01") == 0) key = GLFW_KEY_A;
|
|
||||||
else if (strcmp(name, "AC02") == 0) key = GLFW_KEY_S;
|
|
||||||
else if (strcmp(name, "AC03") == 0) key = GLFW_KEY_D;
|
|
||||||
else if (strcmp(name, "AC04") == 0) key = GLFW_KEY_F;
|
|
||||||
else if (strcmp(name, "AC05") == 0) key = GLFW_KEY_G;
|
|
||||||
else if (strcmp(name, "AC06") == 0) key = GLFW_KEY_H;
|
|
||||||
else if (strcmp(name, "AC07") == 0) key = GLFW_KEY_J;
|
|
||||||
else if (strcmp(name, "AC08") == 0) key = GLFW_KEY_K;
|
|
||||||
else if (strcmp(name, "AC09") == 0) key = GLFW_KEY_L;
|
|
||||||
else if (strcmp(name, "AC10") == 0) key = GLFW_KEY_SEMICOLON;
|
|
||||||
else if (strcmp(name, "AC11") == 0) key = GLFW_KEY_APOSTROPHE;
|
|
||||||
else if (strcmp(name, "AB01") == 0) key = GLFW_KEY_Z;
|
|
||||||
else if (strcmp(name, "AB02") == 0) key = GLFW_KEY_X;
|
|
||||||
else if (strcmp(name, "AB03") == 0) key = GLFW_KEY_C;
|
|
||||||
else if (strcmp(name, "AB04") == 0) key = GLFW_KEY_V;
|
|
||||||
else if (strcmp(name, "AB05") == 0) key = GLFW_KEY_B;
|
|
||||||
else if (strcmp(name, "AB06") == 0) key = GLFW_KEY_N;
|
|
||||||
else if (strcmp(name, "AB07") == 0) key = GLFW_KEY_M;
|
|
||||||
else if (strcmp(name, "AB08") == 0) key = GLFW_KEY_COMMA;
|
|
||||||
else if (strcmp(name, "AB09") == 0) key = GLFW_KEY_PERIOD;
|
|
||||||
else if (strcmp(name, "AB10") == 0) key = GLFW_KEY_SLASH;
|
|
||||||
else if (strcmp(name, "BKSL") == 0) key = GLFW_KEY_BACKSLASH;
|
|
||||||
else if (strcmp(name, "LSGT") == 0) key = GLFW_KEY_WORLD_1;
|
|
||||||
else key = GLFW_KEY_UNKNOWN;
|
|
||||||
|
|
||||||
if ((scancode >= 0) && (scancode < 256))
|
|
||||||
_glfw.x11.keycodes[scancode] = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
XkbFreeNames(desc, XkbKeyNamesMask, True);
|
|
||||||
XkbFreeKeyboard(desc, 0, True);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (scancode = 0; scancode < 256; scancode++)
|
|
||||||
{
|
|
||||||
// Translate the un-translated key codes using traditional X11 KeySym
|
|
||||||
// lookups
|
|
||||||
if (_glfw.x11.keycodes[scancode] < 0)
|
|
||||||
_glfw.x11.keycodes[scancode] = translateKeyCode(scancode);
|
|
||||||
|
|
||||||
// Store the reverse translation for faster key name lookup
|
|
||||||
if (_glfw.x11.keycodes[scancode] > 0)
|
|
||||||
_glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the IM has a usable style
|
|
||||||
//
|
|
||||||
static GLFWbool hasUsableInputMethodStyle(void)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
GLFWbool found = GLFW_FALSE;
|
|
||||||
XIMStyles* styles = NULL;
|
|
||||||
|
|
||||||
if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL)
|
|
||||||
return GLFW_FALSE;
|
|
||||||
|
|
||||||
for (i = 0; i < styles->count_styles; i++)
|
|
||||||
{
|
|
||||||
if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing))
|
|
||||||
{
|
|
||||||
found = GLFW_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XFree(styles);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the specified atom is supported
|
// Check whether the specified atom is supported
|
||||||
//
|
//
|
||||||
static Atom getSupportedAtom(Atom* supportedAtoms,
|
static Atom getSupportedAtom(Atom* supportedAtoms,
|
||||||
@@ -479,7 +166,11 @@ static GLFWbool initExtensions(void)
|
|||||||
&_glfw.x11.vidmode.errorBase);
|
&_glfw.x11.vidmode.errorBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__CYGWIN__)
|
||||||
|
_glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so");
|
||||||
|
#else
|
||||||
_glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6");
|
_glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6");
|
||||||
|
#endif
|
||||||
if (_glfw.x11.xi.handle)
|
if (_glfw.x11.xi.handle)
|
||||||
{
|
{
|
||||||
_glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion)
|
_glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion)
|
||||||
@@ -505,7 +196,11 @@ static GLFWbool initExtensions(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__CYGWIN__)
|
||||||
|
_glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so");
|
||||||
|
#else
|
||||||
_glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2");
|
_glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2");
|
||||||
|
#endif
|
||||||
if (_glfw.x11.randr.handle)
|
if (_glfw.x11.randr.handle)
|
||||||
{
|
{
|
||||||
_glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma)
|
_glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma)
|
||||||
@@ -593,7 +288,11 @@ static GLFWbool initExtensions(void)
|
|||||||
RROutputChangeNotifyMask);
|
RROutputChangeNotifyMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__CYGWIN__)
|
||||||
|
_glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so");
|
||||||
|
#else
|
||||||
_glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1");
|
_glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1");
|
||||||
|
#endif
|
||||||
if (_glfw.x11.xcursor.handle)
|
if (_glfw.x11.xcursor.handle)
|
||||||
{
|
{
|
||||||
_glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate)
|
_glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate)
|
||||||
@@ -604,7 +303,11 @@ static GLFWbool initExtensions(void)
|
|||||||
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
|
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__CYGWIN__)
|
||||||
|
_glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so");
|
||||||
|
#else
|
||||||
_glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1");
|
_glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1");
|
||||||
|
#endif
|
||||||
if (_glfw.x11.xinerama.handle)
|
if (_glfw.x11.xinerama.handle)
|
||||||
{
|
{
|
||||||
_glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive)
|
_glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive)
|
||||||
@@ -623,35 +326,11 @@ static GLFWbool initExtensions(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfw.x11.xkb.major = 1;
|
#if defined(__CYGWIN__)
|
||||||
_glfw.x11.xkb.minor = 0;
|
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so");
|
||||||
_glfw.x11.xkb.available =
|
#else
|
||||||
XkbQueryExtension(_glfw.x11.display,
|
|
||||||
&_glfw.x11.xkb.majorOpcode,
|
|
||||||
&_glfw.x11.xkb.eventBase,
|
|
||||||
&_glfw.x11.xkb.errorBase,
|
|
||||||
&_glfw.x11.xkb.major,
|
|
||||||
&_glfw.x11.xkb.minor);
|
|
||||||
|
|
||||||
if (_glfw.x11.xkb.available)
|
|
||||||
{
|
|
||||||
Bool supported;
|
|
||||||
|
|
||||||
if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))
|
|
||||||
{
|
|
||||||
if (supported)
|
|
||||||
_glfw.x11.xkb.detectable = GLFW_TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1");
|
|
||||||
if (_glfw.x11.x11xcb.handle)
|
|
||||||
{
|
|
||||||
_glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection)
|
|
||||||
_glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
|
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
|
||||||
|
#endif
|
||||||
if (_glfw.x11.xrender.handle)
|
if (_glfw.x11.xrender.handle)
|
||||||
{
|
{
|
||||||
_glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension)
|
_glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension)
|
||||||
@@ -674,10 +353,31 @@ static GLFWbool initExtensions(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the key code LUT
|
_glfw.x11.xkb.major = 1;
|
||||||
// FIXME: We should listen to XkbMapNotify events to track changes to
|
_glfw.x11.xkb.minor = 0;
|
||||||
// the keyboard mapping.
|
_glfw.x11.xkb.available = XkbQueryExtension(_glfw.x11.display,
|
||||||
createKeyTables();
|
&_glfw.x11.xkb.majorOpcode,
|
||||||
|
&_glfw.x11.xkb.eventBase,
|
||||||
|
&_glfw.x11.xkb.errorBase,
|
||||||
|
&_glfw.x11.xkb.major,
|
||||||
|
&_glfw.x11.xkb.minor);
|
||||||
|
|
||||||
|
if (!_glfw.x11.xkb.available)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load Xkb extension");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
Bool supported;
|
||||||
|
if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))
|
||||||
|
{
|
||||||
|
if (supported)
|
||||||
|
_glfw.x11.xkb.detectable = GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!glfw_xkb_set_x11_events_mask()) return GLFW_FALSE;
|
||||||
|
if (!glfw_xkb_create_context(&_glfw.x11.xkb)) return GLFW_FALSE;
|
||||||
|
if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return GLFW_FALSE;
|
||||||
|
if (!glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL)) return GLFW_FALSE;
|
||||||
|
|
||||||
// Detect whether an EWMH-conformant window manager is running
|
// Detect whether an EWMH-conformant window manager is running
|
||||||
detectEWMH();
|
detectEWMH();
|
||||||
@@ -896,16 +596,6 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
|
|||||||
|
|
||||||
int _glfwPlatformInit(void)
|
int _glfwPlatformInit(void)
|
||||||
{
|
{
|
||||||
#if !defined(X_HAVE_UTF8_STRING)
|
|
||||||
// HACK: If the current locale is "C" and the Xlib UTF-8 functions are
|
|
||||||
// unavailable, apply the environment's locale in the hope that it's
|
|
||||||
// both available and not "C"
|
|
||||||
// This is done because the "C" locale breaks wide character input,
|
|
||||||
// which is what we fall back on when UTF-8 support is missing
|
|
||||||
if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0)
|
|
||||||
setlocale(LC_CTYPE, "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
XInitThreads();
|
XInitThreads();
|
||||||
XrmInitialize();
|
XrmInitialize();
|
||||||
|
|
||||||
@@ -939,21 +629,6 @@ int _glfwPlatformInit(void)
|
|||||||
_glfw.x11.helperWindowHandle = createHelperWindow();
|
_glfw.x11.helperWindowHandle = createHelperWindow();
|
||||||
_glfw.x11.hiddenCursorHandle = createHiddenCursor();
|
_glfw.x11.hiddenCursorHandle = createHiddenCursor();
|
||||||
|
|
||||||
if (XSupportsLocale())
|
|
||||||
{
|
|
||||||
XSetLocaleModifiers("");
|
|
||||||
|
|
||||||
_glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL);
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
{
|
|
||||||
if (!hasUsableInputMethodStyle())
|
|
||||||
{
|
|
||||||
XCloseIM(_glfw.x11.im);
|
|
||||||
_glfw.x11.im = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
if (!_glfwInitJoysticksLinux())
|
if (!_glfwInitJoysticksLinux())
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
@@ -985,27 +660,16 @@ void _glfwPlatformTerminate(void)
|
|||||||
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glfw_xkb_release(&_glfw.x11.xkb);
|
||||||
free(_glfw.x11.primarySelectionString);
|
free(_glfw.x11.primarySelectionString);
|
||||||
free(_glfw.x11.clipboardString);
|
free(_glfw.x11.clipboardString);
|
||||||
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
{
|
|
||||||
XCloseIM(_glfw.x11.im);
|
|
||||||
_glfw.x11.im = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_glfw.x11.display)
|
if (_glfw.x11.display)
|
||||||
{
|
{
|
||||||
XCloseDisplay(_glfw.x11.display);
|
XCloseDisplay(_glfw.x11.display);
|
||||||
_glfw.x11.display = NULL;
|
_glfw.x11.display = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_glfw.x11.x11xcb.handle)
|
|
||||||
{
|
|
||||||
_glfw_dlclose(_glfw.x11.x11xcb.handle);
|
|
||||||
_glfw.x11.x11xcb.handle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_glfw.x11.xcursor.handle)
|
if (_glfw.x11.xcursor.handle)
|
||||||
{
|
{
|
||||||
_glfw_dlclose(_glfw.x11.xcursor.handle);
|
_glfw_dlclose(_glfw.x11.xcursor.handle);
|
||||||
@@ -1024,6 +688,24 @@ void _glfwPlatformTerminate(void)
|
|||||||
_glfw.x11.xinerama.handle = NULL;
|
_glfw.x11.xinerama.handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_glfw.x11.xrender.handle)
|
||||||
|
{
|
||||||
|
_glfw_dlclose(_glfw.x11.xrender.handle);
|
||||||
|
_glfw.x11.xrender.handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_glfw.x11.vidmode.handle)
|
||||||
|
{
|
||||||
|
_glfw_dlclose(_glfw.x11.vidmode.handle);
|
||||||
|
_glfw.x11.vidmode.handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_glfw.x11.xi.handle)
|
||||||
|
{
|
||||||
|
_glfw_dlclose(_glfw.x11.xi.handle);
|
||||||
|
_glfw.x11.xi.handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: These need to be unloaded after XCloseDisplay, as they register
|
// NOTE: These need to be unloaded after XCloseDisplay, as they register
|
||||||
// cleanup callbacks that get called by that function
|
// cleanup callbacks that get called by that function
|
||||||
_glfwTerminateEGL();
|
_glfwTerminateEGL();
|
||||||
@@ -1050,4 +732,3 @@ const char* _glfwPlatformGetVersionString(void)
|
|||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
glfw/x11_monitor.c
vendored
4
glfw/x11_monitor.c
vendored
@@ -308,6 +308,10 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||||
|
|||||||
36
glfw/x11_platform.h
vendored
36
glfw/x11_platform.h
vendored
@@ -35,6 +35,9 @@
|
|||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/Xcursor/Xcursor.h>
|
#include <X11/Xcursor/Xcursor.h>
|
||||||
|
|
||||||
|
// The xcb library is needed to work with libxkb
|
||||||
|
#include <X11/Xlib-xcb.h>
|
||||||
|
|
||||||
// The XRandR extension provides mode setting and gamma control
|
// The XRandR extension provides mode setting and gamma control
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
|
||||||
@@ -47,6 +50,9 @@
|
|||||||
// The XInput extension provides raw mouse motion input
|
// The XInput extension provides raw mouse motion input
|
||||||
#include <X11/extensions/XInput2.h>
|
#include <X11/extensions/XInput2.h>
|
||||||
|
|
||||||
|
// The libxkb library is used for improved keyboard support
|
||||||
|
#include "xkb_glfw.h"
|
||||||
|
|
||||||
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
|
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
|
||||||
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
|
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
|
||||||
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
|
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
|
||||||
@@ -96,11 +102,7 @@ typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*);
|
|||||||
#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension
|
#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension
|
||||||
#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens
|
#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens
|
||||||
|
|
||||||
typedef XID xcb_window_t;
|
|
||||||
typedef XID xcb_visualid_t;
|
|
||||||
typedef struct xcb_connection_t xcb_connection_t;
|
typedef struct xcb_connection_t xcb_connection_t;
|
||||||
typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*);
|
|
||||||
#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection
|
|
||||||
|
|
||||||
typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*);
|
typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*);
|
||||||
typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*);
|
typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*);
|
||||||
@@ -151,7 +153,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk
|
|||||||
|
|
||||||
#include "posix_thread.h"
|
#include "posix_thread.h"
|
||||||
#include "posix_time.h"
|
#include "posix_time.h"
|
||||||
#include "xkb_unicode.h"
|
|
||||||
#include "glx_context.h"
|
#include "glx_context.h"
|
||||||
#include "egl_context.h"
|
#include "egl_context.h"
|
||||||
#include "osmesa_context.h"
|
#include "osmesa_context.h"
|
||||||
@@ -180,7 +181,6 @@ typedef struct _GLFWwindowX11
|
|||||||
{
|
{
|
||||||
Colormap colormap;
|
Colormap colormap;
|
||||||
Window handle;
|
Window handle;
|
||||||
XIC ic;
|
|
||||||
|
|
||||||
GLFWbool overrideRedirect;
|
GLFWbool overrideRedirect;
|
||||||
GLFWbool iconified;
|
GLFWbool iconified;
|
||||||
@@ -219,20 +219,12 @@ typedef struct _GLFWlibraryX11
|
|||||||
Cursor hiddenCursorHandle;
|
Cursor hiddenCursorHandle;
|
||||||
// Context for mapping window XIDs to _GLFWwindow pointers
|
// Context for mapping window XIDs to _GLFWwindow pointers
|
||||||
XContext context;
|
XContext context;
|
||||||
// XIM input method
|
|
||||||
XIM im;
|
|
||||||
// Most recent error code received by X error handler
|
// Most recent error code received by X error handler
|
||||||
int errorCode;
|
int errorCode;
|
||||||
// Primary selection string (while the primary selection is owned)
|
// Primary selection string (while the primary selection is owned)
|
||||||
char* primarySelectionString;
|
char* primarySelectionString;
|
||||||
// Clipboard string (while the selection is owned)
|
// Clipboard string (while the selection is owned)
|
||||||
char* clipboardString;
|
char* clipboardString;
|
||||||
// Key name string
|
|
||||||
char keyName[5];
|
|
||||||
// X11 keycode to GLFW key LUT
|
|
||||||
short int keycodes[256];
|
|
||||||
// GLFW key to X11 keycode LUT
|
|
||||||
short int scancodes[GLFW_KEY_LAST + 1];
|
|
||||||
// Where to place the cursor when re-enabled
|
// Where to place the cursor when re-enabled
|
||||||
double restoreCursorPosX, restoreCursorPosY;
|
double restoreCursorPosX, restoreCursorPosY;
|
||||||
// The window whose disabled cursor mode is active
|
// The window whose disabled cursor mode is active
|
||||||
@@ -318,15 +310,7 @@ typedef struct _GLFWlibraryX11
|
|||||||
PFN_XRRUpdateConfiguration UpdateConfiguration;
|
PFN_XRRUpdateConfiguration UpdateConfiguration;
|
||||||
} randr;
|
} randr;
|
||||||
|
|
||||||
struct {
|
_GLFWXKBData xkb;
|
||||||
GLFWbool available;
|
|
||||||
GLFWbool detectable;
|
|
||||||
int majorOpcode;
|
|
||||||
int eventBase;
|
|
||||||
int errorBase;
|
|
||||||
int major;
|
|
||||||
int minor;
|
|
||||||
} xkb;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int count;
|
int count;
|
||||||
@@ -359,11 +343,6 @@ typedef struct _GLFWlibraryX11
|
|||||||
PFN_XineramaQueryScreens QueryScreens;
|
PFN_XineramaQueryScreens QueryScreens;
|
||||||
} xinerama;
|
} xinerama;
|
||||||
|
|
||||||
struct {
|
|
||||||
void* handle;
|
|
||||||
PFN_XGetXCBConnection GetXCBConnection;
|
|
||||||
} x11xcb;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
GLFWbool available;
|
GLFWbool available;
|
||||||
void* handle;
|
void* handle;
|
||||||
@@ -441,4 +420,3 @@ void _glfwReleaseErrorHandlerX11(void);
|
|||||||
void _glfwInputErrorX11(int error, const char* message);
|
void _glfwInputErrorX11(int error, const char* message);
|
||||||
|
|
||||||
void _glfwPushSelectionToManagerX11(void);
|
void _glfwPushSelectionToManagerX11(void);
|
||||||
|
|
||||||
|
|||||||
334
glfw/x11_window.c
vendored
334
glfw/x11_window.c
vendored
@@ -220,17 +220,6 @@ static int translateState(int state)
|
|||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translates an X11 key code to a GLFW key token
|
|
||||||
//
|
|
||||||
static int translateKey(int scancode)
|
|
||||||
{
|
|
||||||
// Use the pre-filled LUT (see createKeyTables() in x11_init.c)
|
|
||||||
if (scancode < 0 || scancode > 255)
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
|
|
||||||
return _glfw.x11.keycodes[scancode];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the GLFW window corresponding to the specified X11 window
|
// Return the GLFW window corresponding to the specified X11 window
|
||||||
//
|
//
|
||||||
static _GLFWwindow* findWindowByHandle(Window handle)
|
static _GLFWwindow* findWindowByHandle(Window handle)
|
||||||
@@ -497,31 +486,6 @@ static size_t encodeUTF8(char* s, unsigned int ch)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode a Unicode code point from a UTF-8 stream
|
|
||||||
// Based on cutef8 by Jeff Bezanson (Public Domain)
|
|
||||||
//
|
|
||||||
#if defined(X_HAVE_UTF8_STRING)
|
|
||||||
static unsigned int decodeUTF8(const char** s)
|
|
||||||
{
|
|
||||||
unsigned int ch = 0, count = 0;
|
|
||||||
static const unsigned int offsets[] =
|
|
||||||
{
|
|
||||||
0x00000000u, 0x00003080u, 0x000e2080u,
|
|
||||||
0x03c82080u, 0xfa082080u, 0x82082080u
|
|
||||||
};
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ch = (ch << 6) + (unsigned char) **s;
|
|
||||||
(*s)++;
|
|
||||||
count++;
|
|
||||||
} while ((**s & 0xc0) == 0x80);
|
|
||||||
|
|
||||||
assert(count <= 6);
|
|
||||||
return ch - offsets[count - 1];
|
|
||||||
}
|
|
||||||
#endif /*X_HAVE_UTF8_STRING*/
|
|
||||||
|
|
||||||
// Convert the specified Latin-1 string to UTF-8
|
// Convert the specified Latin-1 string to UTF-8
|
||||||
//
|
//
|
||||||
static char* convertLatin1toUTF8(const char* source)
|
static char* convertLatin1toUTF8(const char* source)
|
||||||
@@ -571,6 +535,61 @@ static void updateCursorImage(_GLFWwindow* window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply disabled cursor mode to a focused window
|
||||||
|
//
|
||||||
|
static void disableCursor(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
if (_glfw.x11.xi.available)
|
||||||
|
{
|
||||||
|
XIEventMask em;
|
||||||
|
unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
|
||||||
|
|
||||||
|
em.deviceid = XIAllMasterDevices;
|
||||||
|
em.mask_len = sizeof(mask);
|
||||||
|
em.mask = mask;
|
||||||
|
XISetMask(mask, XI_RawMotion);
|
||||||
|
|
||||||
|
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_glfw.x11.disabledCursorWindow = window;
|
||||||
|
_glfwPlatformGetCursorPos(window,
|
||||||
|
&_glfw.x11.restoreCursorPosX,
|
||||||
|
&_glfw.x11.restoreCursorPosY);
|
||||||
|
updateCursorImage(window);
|
||||||
|
centerCursor(window);
|
||||||
|
XGrabPointer(_glfw.x11.display, window->x11.handle, True,
|
||||||
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
||||||
|
GrabModeAsync, GrabModeAsync,
|
||||||
|
window->x11.handle,
|
||||||
|
_glfw.x11.hiddenCursorHandle,
|
||||||
|
CurrentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit disabled cursor mode for the specified window
|
||||||
|
//
|
||||||
|
static void enableCursor(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
if (_glfw.x11.xi.available)
|
||||||
|
{
|
||||||
|
XIEventMask em;
|
||||||
|
unsigned char mask[] = { 0 };
|
||||||
|
|
||||||
|
em.deviceid = XIAllMasterDevices;
|
||||||
|
em.mask_len = sizeof(mask);
|
||||||
|
em.mask = mask;
|
||||||
|
|
||||||
|
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_glfw.x11.disabledCursorWindow = NULL;
|
||||||
|
XUngrabPointer(_glfw.x11.display, CurrentTime);
|
||||||
|
_glfwPlatformSetCursorPos(window,
|
||||||
|
_glfw.x11.restoreCursorPosX,
|
||||||
|
_glfw.x11.restoreCursorPosY);
|
||||||
|
updateCursorImage(window);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the X11 window (and its colormap)
|
// Create the X11 window (and its colormap)
|
||||||
//
|
//
|
||||||
static GLFWbool createNativeWindow(_GLFWwindow* window,
|
static GLFWbool createNativeWindow(_GLFWwindow* window,
|
||||||
@@ -746,19 +765,6 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
|||||||
}
|
}
|
||||||
|
|
||||||
_glfwPlatformSetWindowTitle(window, wndconfig->title);
|
_glfwPlatformSetWindowTitle(window, wndconfig->title);
|
||||||
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
{
|
|
||||||
window->x11.ic = XCreateIC(_glfw.x11.im,
|
|
||||||
XNInputStyle,
|
|
||||||
XIMPreeditNothing | XIMStatusNothing,
|
|
||||||
XNClientWindow,
|
|
||||||
window->x11.handle,
|
|
||||||
XNFocusWindow,
|
|
||||||
window->x11.handle,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
|
_glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
|
||||||
_glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
|
_glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
|
||||||
|
|
||||||
@@ -1141,16 +1147,8 @@ static void releaseMonitor(_GLFWwindow* window)
|
|||||||
static void processEvent(XEvent *event)
|
static void processEvent(XEvent *event)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = NULL;
|
_GLFWwindow* window = NULL;
|
||||||
int keycode = 0;
|
|
||||||
Bool filtered = False;
|
Bool filtered = False;
|
||||||
|
|
||||||
// HACK: Save scancode as some IMs clear the field in XFilterEvent
|
|
||||||
if (event->type == KeyPress || event->type == KeyRelease)
|
|
||||||
keycode = event->xkey.keycode;
|
|
||||||
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
filtered = XFilterEvent(event, None);
|
|
||||||
|
|
||||||
if (_glfw.x11.randr.available)
|
if (_glfw.x11.randr.available)
|
||||||
{
|
{
|
||||||
if (event->type == _glfw.x11.randr.eventBase + RRNotify)
|
if (event->type == _glfw.x11.randr.eventBase + RRNotify)
|
||||||
@@ -1208,6 +1206,30 @@ static void processEvent(XEvent *event)
|
|||||||
handleSelectionRequest(event);
|
handleSelectionRequest(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (event->type == _glfw.x11.xkb.eventBase)
|
||||||
|
{
|
||||||
|
XkbEvent *kb_event = (XkbEvent*)event;
|
||||||
|
switch(kb_event->any.xkb_type) {
|
||||||
|
case XkbNewKeyboardNotify:
|
||||||
|
if(!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;
|
||||||
|
/* fallthrough */
|
||||||
|
case XkbMapNotify:
|
||||||
|
{
|
||||||
|
glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case XkbStateNotify:
|
||||||
|
{
|
||||||
|
XkbStateNotifyEvent *state_event = (XkbStateNotifyEvent*)kb_event;
|
||||||
|
glfw_xkb_update_modifiers(
|
||||||
|
&_glfw.x11.xkb, state_event->base_mods, state_event->latched_mods,
|
||||||
|
state_event->locked_mods, state_event->base_group, state_event->latched_group, state_event->locked_group
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
window = findWindowByHandle(event->xany.window);
|
window = findWindowByHandle(event->xany.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
@@ -1220,105 +1242,12 @@ static void processEvent(XEvent *event)
|
|||||||
{
|
{
|
||||||
case KeyPress:
|
case KeyPress:
|
||||||
{
|
{
|
||||||
const int key = translateKey(keycode);
|
glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_PRESS);
|
||||||
const int mods = translateState(event->xkey.state);
|
|
||||||
const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
|
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
{
|
|
||||||
// HACK: Ignore duplicate key press events generated by ibus
|
|
||||||
// These have the same timestamp as the original event
|
|
||||||
// Corresponding release events are filtered out
|
|
||||||
// implicitly by the GLFW key repeat logic
|
|
||||||
if (window->x11.lastKeyTime < event->xkey.time)
|
|
||||||
{
|
|
||||||
if (keycode)
|
|
||||||
_glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
|
|
||||||
|
|
||||||
window->x11.lastKeyTime = event->xkey.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filtered)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
Status status;
|
|
||||||
#if defined(X_HAVE_UTF8_STRING)
|
|
||||||
char buffer[100];
|
|
||||||
char* chars = buffer;
|
|
||||||
|
|
||||||
count = Xutf8LookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
buffer, sizeof(buffer) - 1,
|
|
||||||
NULL, &status);
|
|
||||||
|
|
||||||
if (status == XBufferOverflow)
|
|
||||||
{
|
|
||||||
chars = calloc(count + 1, 1);
|
|
||||||
count = Xutf8LookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
chars, count,
|
|
||||||
NULL, &status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == XLookupChars || status == XLookupBoth)
|
|
||||||
{
|
|
||||||
const char* c = chars;
|
|
||||||
chars[count] = '\0';
|
|
||||||
while (c - chars < count)
|
|
||||||
_glfwInputChar(window, decodeUTF8(&c), mods, plain);
|
|
||||||
}
|
|
||||||
#else /*X_HAVE_UTF8_STRING*/
|
|
||||||
wchar_t buffer[16];
|
|
||||||
wchar_t* chars = buffer;
|
|
||||||
|
|
||||||
count = XwcLookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
buffer,
|
|
||||||
sizeof(buffer) / sizeof(wchar_t),
|
|
||||||
NULL,
|
|
||||||
&status);
|
|
||||||
|
|
||||||
if (status == XBufferOverflow)
|
|
||||||
{
|
|
||||||
chars = calloc(count, sizeof(wchar_t));
|
|
||||||
count = XwcLookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
chars, count,
|
|
||||||
NULL, &status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == XLookupChars || status == XLookupBoth)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
_glfwInputChar(window, chars[i], mods, plain);
|
|
||||||
}
|
|
||||||
#endif /*X_HAVE_UTF8_STRING*/
|
|
||||||
|
|
||||||
if (chars != buffer)
|
|
||||||
free(chars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
KeySym keysym;
|
|
||||||
XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
|
|
||||||
|
|
||||||
_glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
|
|
||||||
|
|
||||||
const long character = _glfwKeySym2Unicode(keysym);
|
|
||||||
if (character != -1)
|
|
||||||
_glfwInputChar(window, character, mods, plain);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case KeyRelease:
|
case KeyRelease:
|
||||||
{
|
{
|
||||||
const int key = translateKey(keycode);
|
|
||||||
const int mods = translateState(event->xkey.state);
|
|
||||||
|
|
||||||
if (!_glfw.x11.xkb.detectable)
|
if (!_glfw.x11.xkb.detectable)
|
||||||
{
|
{
|
||||||
// HACK: Key repeat events will arrive as KeyRelease/KeyPress
|
// HACK: Key repeat events will arrive as KeyRelease/KeyPress
|
||||||
@@ -1332,7 +1261,7 @@ static void processEvent(XEvent *event)
|
|||||||
|
|
||||||
if (next.type == KeyPress &&
|
if (next.type == KeyPress &&
|
||||||
next.xkey.window == event->xkey.window &&
|
next.xkey.window == event->xkey.window &&
|
||||||
next.xkey.keycode == keycode)
|
next.xkey.keycode == event->xkey.keycode)
|
||||||
{
|
{
|
||||||
// HACK: The time of repeat events sometimes doesn't
|
// HACK: The time of repeat events sometimes doesn't
|
||||||
// match that of the press event, so add an
|
// match that of the press event, so add an
|
||||||
@@ -1351,7 +1280,7 @@ static void processEvent(XEvent *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
|
glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_RELEASE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,7 +1361,7 @@ static void processEvent(XEvent *event)
|
|||||||
// HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
|
// HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
|
||||||
// ignore the defined cursor for hidden cursor mode
|
// ignore the defined cursor for hidden cursor mode
|
||||||
if (window->cursorMode == GLFW_CURSOR_HIDDEN)
|
if (window->cursorMode == GLFW_CURSOR_HIDDEN)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN);
|
updateCursorImage(window);
|
||||||
|
|
||||||
_glfwInputCursorEnter(window, GLFW_TRUE);
|
_glfwInputCursorEnter(window, GLFW_TRUE);
|
||||||
return;
|
return;
|
||||||
@@ -1725,7 +1654,7 @@ static void processEvent(XEvent *event)
|
|||||||
case FocusIn:
|
case FocusIn:
|
||||||
{
|
{
|
||||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
disableCursor(window);
|
||||||
|
|
||||||
if (event->xfocus.mode == NotifyGrab ||
|
if (event->xfocus.mode == NotifyGrab ||
|
||||||
event->xfocus.mode == NotifyUngrab)
|
event->xfocus.mode == NotifyUngrab)
|
||||||
@@ -1735,9 +1664,6 @@ static void processEvent(XEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
XSetICFocus(window->x11.ic);
|
|
||||||
|
|
||||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1745,7 +1671,7 @@ static void processEvent(XEvent *event)
|
|||||||
case FocusOut:
|
case FocusOut:
|
||||||
{
|
{
|
||||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
|
enableCursor(window);
|
||||||
|
|
||||||
if (event->xfocus.mode == NotifyGrab ||
|
if (event->xfocus.mode == NotifyGrab ||
|
||||||
event->xfocus.mode == NotifyUngrab)
|
event->xfocus.mode == NotifyUngrab)
|
||||||
@@ -1755,9 +1681,6 @@ static void processEvent(XEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
XUnsetICFocus(window->x11.ic);
|
|
||||||
|
|
||||||
if (window->monitor && window->autoIconify)
|
if (window->monitor && window->autoIconify)
|
||||||
_glfwPlatformIconifyWindow(window);
|
_glfwPlatformIconifyWindow(window);
|
||||||
|
|
||||||
@@ -1989,12 +1912,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
|||||||
if (window->monitor)
|
if (window->monitor)
|
||||||
releaseMonitor(window);
|
releaseMonitor(window);
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
{
|
|
||||||
XDestroyIC(window->x11.ic);
|
|
||||||
window->x11.ic = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window->context.destroy)
|
if (window->context.destroy)
|
||||||
window->context.destroy(window);
|
window->context.destroy(window);
|
||||||
|
|
||||||
@@ -2340,9 +2257,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
0, 1, 0);
|
0, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return XkbBell(_glfw.x11.display, window->x11.handle, (int)param, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
|
return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
||||||
@@ -2713,80 +2630,26 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
|||||||
{
|
{
|
||||||
if (mode == GLFW_CURSOR_DISABLED)
|
if (mode == GLFW_CURSOR_DISABLED)
|
||||||
{
|
{
|
||||||
if (_glfw.x11.xi.available)
|
if (_glfwPlatformWindowFocused(window))
|
||||||
{
|
disableCursor(window);
|
||||||
XIEventMask em;
|
|
||||||
unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
|
|
||||||
|
|
||||||
em.deviceid = XIAllMasterDevices;
|
|
||||||
em.mask_len = sizeof(mask);
|
|
||||||
em.mask = mask;
|
|
||||||
XISetMask(mask, XI_RawMotion);
|
|
||||||
|
|
||||||
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfw.x11.disabledCursorWindow = window;
|
|
||||||
_glfwPlatformGetCursorPos(window,
|
|
||||||
&_glfw.x11.restoreCursorPosX,
|
|
||||||
&_glfw.x11.restoreCursorPosY);
|
|
||||||
centerCursor(window);
|
|
||||||
XGrabPointer(_glfw.x11.display, window->x11.handle, True,
|
|
||||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
|
||||||
GrabModeAsync, GrabModeAsync,
|
|
||||||
window->x11.handle,
|
|
||||||
_glfw.x11.hiddenCursorHandle,
|
|
||||||
CurrentTime);
|
|
||||||
}
|
}
|
||||||
else if (_glfw.x11.disabledCursorWindow == window)
|
else if (_glfw.x11.disabledCursorWindow == window)
|
||||||
{
|
enableCursor(window);
|
||||||
if (_glfw.x11.xi.available)
|
else
|
||||||
{
|
updateCursorImage(window);
|
||||||
XIEventMask em;
|
|
||||||
unsigned char mask[] = { 0 };
|
|
||||||
|
|
||||||
em.deviceid = XIAllMasterDevices;
|
|
||||||
em.mask_len = sizeof(mask);
|
|
||||||
em.mask = mask;
|
|
||||||
|
|
||||||
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfw.x11.disabledCursorWindow = NULL;
|
|
||||||
XUngrabPointer(_glfw.x11.display, CurrentTime);
|
|
||||||
_glfwPlatformSetCursorPos(window,
|
|
||||||
_glfw.x11.restoreCursorPosX,
|
|
||||||
_glfw.x11.restoreCursorPosY);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCursorImage(window);
|
|
||||||
XFlush(_glfw.x11.display);
|
XFlush(_glfw.x11.display);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||||
{
|
{
|
||||||
if (!_glfw.x11.xkb.available)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
|
return glfw_xkb_keysym_name(scancode);
|
||||||
if (keysym == NoSymbol)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
const long ch = _glfwKeySym2Unicode(keysym);
|
|
||||||
if (ch == -1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
const size_t count = encodeUTF8(_glfw.x11.keyName, (unsigned int) ch);
|
|
||||||
if (count == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
_glfw.x11.keyName[count] = '\0';
|
|
||||||
return _glfw.x11.keyName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformGetKeyScancode(int key)
|
int _glfwPlatformGetKeyScancode(int key)
|
||||||
{
|
{
|
||||||
return _glfw.x11.scancodes[key];
|
return glfw_xkb_sym_for_key(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||||
@@ -2857,7 +2720,7 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
|||||||
if (!_glfw.vk.KHR_surface)
|
if (!_glfw.vk.KHR_surface)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
|
if (!_glfw.vk.KHR_xcb_surface)
|
||||||
{
|
{
|
||||||
if (!_glfw.vk.KHR_xlib_surface)
|
if (!_glfw.vk.KHR_xlib_surface)
|
||||||
return;
|
return;
|
||||||
@@ -2867,7 +2730,7 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
|||||||
|
|
||||||
// NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
|
// NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
|
||||||
// not correctly implementing VK_KHR_xlib_surface
|
// not correctly implementing VK_KHR_xlib_surface
|
||||||
if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
|
if (_glfw.vk.KHR_xcb_surface)
|
||||||
extensions[1] = "VK_KHR_xcb_surface";
|
extensions[1] = "VK_KHR_xcb_surface";
|
||||||
else
|
else
|
||||||
extensions[1] = "VK_KHR_xlib_surface";
|
extensions[1] = "VK_KHR_xlib_surface";
|
||||||
@@ -2880,7 +2743,7 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
|
|||||||
VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
|
VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
|
||||||
_glfw.x11.screen));
|
_glfw.x11.screen));
|
||||||
|
|
||||||
if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
|
if (_glfw.vk.KHR_xcb_surface)
|
||||||
{
|
{
|
||||||
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
|
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
|
||||||
vkGetPhysicalDeviceXcbPresentationSupportKHR =
|
vkGetPhysicalDeviceXcbPresentationSupportKHR =
|
||||||
@@ -2931,7 +2794,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
const VkAllocationCallbacks* allocator,
|
const VkAllocationCallbacks* allocator,
|
||||||
VkSurfaceKHR* surface)
|
VkSurfaceKHR* surface)
|
||||||
{
|
{
|
||||||
if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
|
if (_glfw.vk.KHR_xcb_surface)
|
||||||
{
|
{
|
||||||
VkResult err;
|
VkResult err;
|
||||||
VkXcbSurfaceCreateInfoKHR sci;
|
VkXcbSurfaceCreateInfoKHR sci;
|
||||||
@@ -3044,4 +2907,3 @@ GLFWAPI const char* glfwGetX11SelectionString(void)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
return getSelectionString(_glfw.x11.PRIMARY);
|
return getSelectionString(_glfw.x11.PRIMARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
427
glfw/xkb_glfw.c
vendored
Normal file
427
glfw/xkb_glfw.c
vendored
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
//========================================================================
|
||||||
|
// GLFW 3.3 XKB - www.glfw.org
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//========================================================================
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "internal.h"
|
||||||
|
#include "xkb_glfw.h"
|
||||||
|
|
||||||
|
static GLFWbool debug_keyboard = GLFW_FALSE;
|
||||||
|
#define debug(...) if (debug_keyboard) printf(__VA_ARGS__);
|
||||||
|
|
||||||
|
#define map_key(key) { \
|
||||||
|
switch(key) { \
|
||||||
|
S(space, SPACE); \
|
||||||
|
S(apostrophe, APOSTROPHE); \
|
||||||
|
S(comma, COMMA); \
|
||||||
|
S(minus, MINUS); \
|
||||||
|
S(period, PERIOD); \
|
||||||
|
S(slash, SLASH); \
|
||||||
|
S(semicolon, SEMICOLON); \
|
||||||
|
S(equal, EQUAL); \
|
||||||
|
S(bracketleft, LEFT_BRACKET); \
|
||||||
|
S(backslash, BACKSLASH); \
|
||||||
|
S(bracketright, RIGHT_BRACKET); \
|
||||||
|
S(grave, GRAVE_ACCENT); \
|
||||||
|
S(Escape, ESCAPE); \
|
||||||
|
S(Return, ENTER); \
|
||||||
|
S(Tab, TAB); \
|
||||||
|
S(BackSpace, BACKSPACE); \
|
||||||
|
S(Insert, INSERT); \
|
||||||
|
S(Delete, DELETE); \
|
||||||
|
S(Right, RIGHT); \
|
||||||
|
S(Left, LEFT); \
|
||||||
|
S(Up, UP); \
|
||||||
|
S(Down, DOWN); \
|
||||||
|
S(Page_Up, PAGE_UP); \
|
||||||
|
S(Page_Down, PAGE_DOWN); \
|
||||||
|
S(Home, HOME); \
|
||||||
|
S(End, END); \
|
||||||
|
S(Caps_Lock, CAPS_LOCK); \
|
||||||
|
S(Scroll_Lock, SCROLL_LOCK); \
|
||||||
|
S(Num_Lock, NUM_LOCK); \
|
||||||
|
S(Print, PRINT_SCREEN); \
|
||||||
|
S(Pause, PAUSE); \
|
||||||
|
S(KP_Decimal, KP_DECIMAL); \
|
||||||
|
S(KP_Divide, KP_DIVIDE); \
|
||||||
|
S(KP_Multiply, KP_MULTIPLY); \
|
||||||
|
S(KP_Subtract, KP_SUBTRACT); \
|
||||||
|
S(KP_Add, KP_ADD); \
|
||||||
|
S(KP_Enter, KP_ENTER); \
|
||||||
|
S(KP_Equal, KP_EQUAL); \
|
||||||
|
F(KP_Home, HOME); \
|
||||||
|
F(KP_End, END); \
|
||||||
|
F(KP_Page_Up, PAGE_UP); \
|
||||||
|
F(KP_Page_Down, PAGE_DOWN); \
|
||||||
|
F(KP_Insert, INSERT); \
|
||||||
|
F(KP_Delete, DELETE); \
|
||||||
|
S(Shift_L, LEFT_SHIFT); \
|
||||||
|
S(Control_L, LEFT_CONTROL); \
|
||||||
|
S(Alt_L, LEFT_ALT); \
|
||||||
|
S(Super_L, LEFT_SUPER); \
|
||||||
|
S(Shift_R, RIGHT_SHIFT); \
|
||||||
|
S(Control_R, RIGHT_CONTROL); \
|
||||||
|
S(Alt_R, RIGHT_ALT); \
|
||||||
|
S(Super_R, RIGHT_SUPER); \
|
||||||
|
S(Menu, MENU); \
|
||||||
|
R(0, 9, 0, 9); \
|
||||||
|
R(a, z, A, Z); \
|
||||||
|
D(A, Z, A, Z); \
|
||||||
|
R(F1, F25, F1, F25); \
|
||||||
|
R(KP_0, KP_9, KP_0, KP_9); \
|
||||||
|
default: \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
glfw_key_for_sym(xkb_keysym_t key) {
|
||||||
|
#define S(f, t) case XKB_KEY_##f: return GLFW_KEY_##t
|
||||||
|
#define F(f, t) S(f, t)
|
||||||
|
#define R(s, e, gs, ...) case XKB_KEY_##s ... XKB_KEY_##e: return GLFW_KEY_##gs + key - XKB_KEY_##s
|
||||||
|
#define D(s, e, gs, ...) R(s, e, gs)
|
||||||
|
map_key(key)
|
||||||
|
return GLFW_KEY_UNKNOWN;
|
||||||
|
#undef F
|
||||||
|
#undef D
|
||||||
|
#undef R
|
||||||
|
#undef S
|
||||||
|
};
|
||||||
|
|
||||||
|
xkb_keysym_t
|
||||||
|
glfw_xkb_sym_for_key(int key) {
|
||||||
|
#define S(f, t) case GLFW_KEY_##t: return XKB_KEY_##f
|
||||||
|
#define F(...)
|
||||||
|
#define R(s, e, gs, ge) case GLFW_KEY_##gs ... GLFW_KEY_##ge: return XKB_KEY_##s + key - GLFW_KEY_##gs
|
||||||
|
#define D(...)
|
||||||
|
map_key(key)
|
||||||
|
return GLFW_KEY_UNKNOWN;
|
||||||
|
#undef F
|
||||||
|
#undef D
|
||||||
|
#undef R
|
||||||
|
#undef S
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _GLFW_X11
|
||||||
|
|
||||||
|
GLFWbool
|
||||||
|
glfw_xkb_set_x11_events_mask(void) {
|
||||||
|
if (!XkbSelectEvents(_glfw.x11.display, XkbUseCoreKbd, XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask, XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask)) {
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to set XKB events mask");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
return GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWbool
|
||||||
|
glfw_xkb_update_x11_keyboard_id(_GLFWXKBData *xkb) {
|
||||||
|
xkb->keyboard_device_id = -1;
|
||||||
|
xcb_connection_t* conn = XGetXCBConnection(_glfw.x11.display);
|
||||||
|
if (!conn) {
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to retrieve XCB connection");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
xkb->keyboard_device_id = xkb_x11_get_core_keyboard_device_id(conn);
|
||||||
|
if (xkb->keyboard_device_id == -1) {
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to retrieve core keyboard device id");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
return GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define xkb_glfw_load_keymap(keymap, ...) {\
|
||||||
|
xcb_connection_t* conn = XGetXCBConnection(_glfw.x11.display); \
|
||||||
|
if (conn) keymap = xkb_x11_keymap_new_from_device(xkb->context, conn, xkb->keyboard_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define xkb_glfw_load_state(keymap, state, ...) {\
|
||||||
|
xcb_connection_t* conn = XGetXCBConnection(_glfw.x11.display); \
|
||||||
|
if (conn) state = xkb_x11_state_new_from_device(keymap, conn, xkb->keyboard_device_id); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define xkb_glfw_load_keymap(keymap, map_str) keymap = xkb_keymap_new_from_string(xkb->context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
|
||||||
|
#define xkb_glfw_load_state(keymap, state, ...) state = xkb_state_new(keymap);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
glfw_xkb_release(_GLFWXKBData *xkb) {
|
||||||
|
if (xkb->composeState) {
|
||||||
|
xkb_compose_state_unref(xkb->composeState);
|
||||||
|
xkb->composeState = NULL;
|
||||||
|
}
|
||||||
|
if (xkb->keymap) {
|
||||||
|
xkb_keymap_unref(xkb->keymap);
|
||||||
|
xkb->keymap = NULL;
|
||||||
|
}
|
||||||
|
if (xkb->state) {
|
||||||
|
xkb_state_unref(xkb->state);
|
||||||
|
xkb->state = NULL;
|
||||||
|
}
|
||||||
|
if (xkb->clean_state) {
|
||||||
|
xkb_state_unref(xkb->clean_state);
|
||||||
|
xkb->clean_state = NULL;
|
||||||
|
}
|
||||||
|
if (xkb->context) {
|
||||||
|
xkb_context_unref(xkb->context);
|
||||||
|
xkb->context = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWbool
|
||||||
|
glfw_xkb_create_context(_GLFWXKBData *xkb) {
|
||||||
|
xkb->context = xkb_context_new(0);
|
||||||
|
debug_keyboard = getenv("GLFW_DEBUG_KEYBOARD") != NULL;
|
||||||
|
if (!xkb->context)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Failed to initialize XKB context");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
return GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWbool
|
||||||
|
glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) {
|
||||||
|
const char* locale = NULL;
|
||||||
|
struct xkb_state* state = NULL, *clean_state = NULL;
|
||||||
|
struct xkb_keymap* keymap = NULL;
|
||||||
|
struct xkb_compose_table* compose_table = NULL;
|
||||||
|
struct xkb_compose_state* compose_state = NULL;
|
||||||
|
(void)(map_str); // not needed on X11
|
||||||
|
GLFWbool ok = GLFW_FALSE;
|
||||||
|
|
||||||
|
xkb_glfw_load_keymap(keymap, map_str);
|
||||||
|
if (!keymap) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to compile XKB keymap");
|
||||||
|
else {
|
||||||
|
xkb_glfw_load_state(keymap, state);
|
||||||
|
clean_state = xkb_state_new(keymap);
|
||||||
|
if (!state || !clean_state) {
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB state");
|
||||||
|
xkb_keymap_unref(keymap); keymap = NULL;
|
||||||
|
} else {
|
||||||
|
ok = GLFW_TRUE;
|
||||||
|
/* Look up the preferred locale, falling back to "C" as default. */
|
||||||
|
locale = getenv("LC_ALL");
|
||||||
|
if (!locale) locale = getenv("LC_CTYPE");
|
||||||
|
if (!locale) locale = getenv("LANG");
|
||||||
|
if (!locale) locale = "C";
|
||||||
|
compose_table = xkb_compose_table_new_from_locale(xkb->context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||||
|
if (!compose_table) {
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose table");
|
||||||
|
} else {
|
||||||
|
compose_state = xkb_compose_state_new(compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||||
|
xkb_compose_table_unref(compose_table); compose_table = NULL;
|
||||||
|
if (!compose_state) {
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose state");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keymap && state && clean_state) {
|
||||||
|
if (xkb->composeState) xkb_compose_state_unref(xkb->composeState);
|
||||||
|
xkb->composeState = compose_state;
|
||||||
|
if (xkb->keymap) xkb_keymap_unref(xkb->keymap);
|
||||||
|
xkb->keymap = keymap;
|
||||||
|
if (xkb->state) xkb_state_unref(xkb->state);
|
||||||
|
xkb->state = state;
|
||||||
|
if (xkb->clean_state) xkb_state_unref(xkb->clean_state);
|
||||||
|
xkb->clean_state = clean_state;
|
||||||
|
}
|
||||||
|
if (xkb->keymap) {
|
||||||
|
#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx;
|
||||||
|
S(control, XKB_MOD_NAME_CTRL);
|
||||||
|
S(alt, XKB_MOD_NAME_ALT);
|
||||||
|
S(shift, XKB_MOD_NAME_SHIFT);
|
||||||
|
S(super, XKB_MOD_NAME_LOGO);
|
||||||
|
S(capsLock, XKB_MOD_NAME_CAPS);
|
||||||
|
S(numLock, XKB_MOD_NAME_NUM);
|
||||||
|
#undef S
|
||||||
|
size_t capacity = sizeof(xkb->unknownModifiers)/sizeof(xkb->unknownModifiers[0]), j = 0;
|
||||||
|
for (xkb_mod_index_t i = 0; i < capacity; i++) xkb->unknownModifiers[i] = XKB_MOD_INVALID;
|
||||||
|
for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap) && j < capacity - 1; i++) {
|
||||||
|
if (i != xkb->controlIdx && i != xkb->altIdx && i != xkb->shiftIdx && i != xkb->superIdx && i != xkb->capsLockIdx && i != xkb->numLockIdx) xkb->unknownModifiers[j++] = i;
|
||||||
|
}
|
||||||
|
xkb->modifiers = 0;
|
||||||
|
xkb->activeUnknownModifiers = 0;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline xkb_mod_mask_t
|
||||||
|
active_unknown_modifiers(_GLFWXKBData *xkb) {
|
||||||
|
size_t i = 0;
|
||||||
|
xkb_mod_mask_t ans = 0;
|
||||||
|
while (xkb->unknownModifiers[i] != XKB_MOD_INVALID) {
|
||||||
|
if (xkb_state_mod_index_is_active(xkb->state, xkb->unknownModifiers[i], XKB_STATE_MODS_EFFECTIVE)) ans |= (1 << xkb->unknownModifiers[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (!xkb->keymap) return;
|
||||||
|
xkb->modifiers = 0;
|
||||||
|
xkb_state_update_mask(xkb->state, depressed, latched, locked, base_group, latched_group, locked_group);
|
||||||
|
// We have to update the groups in clean_state, as they change for
|
||||||
|
// different keyboard layouts, see https://github.com/kovidgoyal/kitty/issues/488
|
||||||
|
xkb_state_update_mask(xkb->clean_state, 0, 0, 0, base_group, latched_group, locked_group);
|
||||||
|
#define S(attr, name) if (xkb_state_mod_index_is_active(xkb->state, xkb->attr##Idx, XKB_STATE_MODS_EFFECTIVE)) xkb->modifiers |= GLFW_MOD_##name
|
||||||
|
S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK);
|
||||||
|
#undef S
|
||||||
|
xkb->activeUnknownModifiers = active_unknown_modifiers(xkb);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWbool
|
||||||
|
glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t scancode) {
|
||||||
|
#ifdef _GLFW_WAYLAND
|
||||||
|
scancode += 8;
|
||||||
|
#endif
|
||||||
|
return xkb_keymap_key_repeats(xkb->keymap, scancode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char text[256];
|
||||||
|
|
||||||
|
static inline xkb_keysym_t
|
||||||
|
compose_symbol(_GLFWXKBData *xkb, xkb_keysym_t sym) {
|
||||||
|
if (sym == XKB_KEY_NoSymbol || !xkb->composeState) return sym;
|
||||||
|
if (xkb_compose_state_feed(xkb->composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym;
|
||||||
|
switch (xkb_compose_state_get_status(xkb->composeState)) {
|
||||||
|
case XKB_COMPOSE_COMPOSED:
|
||||||
|
xkb_compose_state_get_utf8(xkb->composeState, text, sizeof(text));
|
||||||
|
return xkb_compose_state_get_one_sym(xkb->composeState);
|
||||||
|
case XKB_COMPOSE_COMPOSING:
|
||||||
|
case XKB_COMPOSE_CANCELLED:
|
||||||
|
return XKB_KEY_NoSymbol;
|
||||||
|
case XKB_COMPOSE_NOTHING:
|
||||||
|
default:
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
glfw_xkb_keysym_name(xkb_keysym_t sym) {
|
||||||
|
static char name[256];
|
||||||
|
name[0] = 0;
|
||||||
|
xkb_keysym_get_name(sym, name, sizeof(name));
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
format_mods(unsigned int mods) {
|
||||||
|
static char buf[128];
|
||||||
|
char *p = buf, *s;
|
||||||
|
#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, "%s", x)
|
||||||
|
pr("mods: ");
|
||||||
|
s = p;
|
||||||
|
if (mods & GLFW_MOD_CONTROL) pr("ctrl+");
|
||||||
|
if (mods & GLFW_MOD_ALT) pr("alt+");
|
||||||
|
if (mods & GLFW_MOD_SHIFT) pr("shift+");
|
||||||
|
if (mods & GLFW_MOD_SUPER) pr("super+");
|
||||||
|
if (mods & GLFW_MOD_CAPS_LOCK) pr("capslock+");
|
||||||
|
if (mods & GLFW_MOD_NUM_LOCK) pr("numlock+");
|
||||||
|
if (p == s) pr("none");
|
||||||
|
else p--;
|
||||||
|
pr(" ");
|
||||||
|
#undef pr
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) {
|
||||||
|
static char buf[512];
|
||||||
|
char *p = buf, *s;
|
||||||
|
#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, "%s", x)
|
||||||
|
pr(name); pr(": ");
|
||||||
|
s = p;
|
||||||
|
for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap); i++) {
|
||||||
|
xkb_mod_mask_t m = 1 << i;
|
||||||
|
if (m & mods) { pr(xkb_keymap_mod_get_name(xkb->keymap, i)); pr("+"); }
|
||||||
|
}
|
||||||
|
if (p == s) pr("none");
|
||||||
|
else p--;
|
||||||
|
pr(" ");
|
||||||
|
#undef pr
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action) {
|
||||||
|
const xkb_keysym_t *syms, *clean_syms;
|
||||||
|
xkb_keysym_t glfw_sym;
|
||||||
|
xkb_keycode_t code_for_sym = scancode;
|
||||||
|
#ifdef _GLFW_WAYLAND
|
||||||
|
code_for_sym += 8;
|
||||||
|
#endif
|
||||||
|
debug("scancode: 0x%x release: %d ", scancode, action == GLFW_RELEASE);
|
||||||
|
int num_syms = xkb_state_key_get_syms(xkb->state, code_for_sym, &syms);
|
||||||
|
int num_clean_syms = xkb_state_key_get_syms(xkb->clean_state, code_for_sym, &clean_syms);
|
||||||
|
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("scancode: 0x%x num_syms: %d num_clean_syms: %d ignoring event\n", scancode, num_syms, num_clean_syms);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
glfw_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";
|
||||||
|
glfw_sym = compose_symbol(xkb, syms[0]);
|
||||||
|
if (glfw_sym == XKB_KEY_NoSymbol) {
|
||||||
|
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
|
||||||
|
// 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
|
||||||
|
// key). See https://github.com/kovidgoyal/kitty/issues/171#issuecomment-377557053
|
||||||
|
xkb_mod_mask_t consumed_unknown_mods = xkb_state_key_get_consumed_mods(xkb->state, code_for_sym) & xkb->activeUnknownModifiers;
|
||||||
|
if (xkb->activeUnknownModifiers) debug("%s", format_xkb_mods(xkb, "active_unknown_mods", xkb->activeUnknownModifiers));
|
||||||
|
if (consumed_unknown_mods) { debug("%s", format_xkb_mods(xkb, "consumed_unknown_mods", consumed_unknown_mods)); }
|
||||||
|
else glfw_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) & xkb->modifiers) == 0) xkb_state_key_get_utf8(xkb->state, code_for_sym, text, sizeof(text));
|
||||||
|
text_type = "text";
|
||||||
|
}
|
||||||
|
if ((1 <= text[0] && text[0] <= 31) || text[0] == 127) text[0] = 0; // dont send text for ascii control codes
|
||||||
|
if (text[0]) { debug("%s: %s ", text_type, text); }
|
||||||
|
}
|
||||||
|
int glfw_keycode = glfw_key_for_sym(glfw_sym);
|
||||||
|
debug("%sglfw_key: %s\n", format_mods(xkb->modifiers), _glfwGetKeyName(glfw_keycode));
|
||||||
|
_glfwInputKeyboard(window, glfw_keycode, glfw_sym, action, xkb->modifiers, text, 0);
|
||||||
|
}
|
||||||
83
glfw/xkb_glfw.h
vendored
Normal file
83
glfw/xkb_glfw.h
vendored
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
//========================================================================
|
||||||
|
// GLFW 3.3 XKB - www.glfw.org
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//========================================================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbcommon-compose.h>
|
||||||
|
#ifdef _GLFW_X11
|
||||||
|
#include <xkbcommon/xkbcommon-x11.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct xkb_context* context;
|
||||||
|
struct xkb_keymap* keymap;
|
||||||
|
struct xkb_state* state;
|
||||||
|
struct xkb_state* clean_state;
|
||||||
|
struct xkb_compose_state* composeState;
|
||||||
|
|
||||||
|
xkb_mod_index_t controlIdx;
|
||||||
|
xkb_mod_index_t altIdx;
|
||||||
|
xkb_mod_index_t shiftIdx;
|
||||||
|
xkb_mod_index_t superIdx;
|
||||||
|
xkb_mod_index_t capsLockIdx;
|
||||||
|
xkb_mod_index_t numLockIdx;
|
||||||
|
xkb_mod_mask_t controlMask;
|
||||||
|
xkb_mod_mask_t altMask;
|
||||||
|
xkb_mod_mask_t shiftMask;
|
||||||
|
xkb_mod_mask_t superMask;
|
||||||
|
xkb_mod_mask_t capsLockMask;
|
||||||
|
xkb_mod_mask_t numLockMask;
|
||||||
|
xkb_mod_mask_t activeUnknownModifiers;
|
||||||
|
unsigned int modifiers;
|
||||||
|
xkb_mod_index_t unknownModifiers[256];
|
||||||
|
|
||||||
|
#ifdef _GLFW_X11
|
||||||
|
int32_t keyboard_device_id;
|
||||||
|
GLFWbool available;
|
||||||
|
GLFWbool detectable;
|
||||||
|
int majorOpcode;
|
||||||
|
int eventBase;
|
||||||
|
int errorBase;
|
||||||
|
int major;
|
||||||
|
int minor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} _GLFWXKBData;
|
||||||
|
|
||||||
|
#ifdef _GLFW_X11
|
||||||
|
GLFWbool glfw_xkb_set_x11_events_mask(void);
|
||||||
|
GLFWbool glfw_xkb_update_x11_keyboard_id(_GLFWXKBData *xkb);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void glfw_xkb_release(_GLFWXKBData *xkb);
|
||||||
|
GLFWbool glfw_xkb_create_context(_GLFWXKBData *xkb);
|
||||||
|
GLFWbool 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);
|
||||||
|
GLFWbool glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t scancode);
|
||||||
|
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);
|
||||||
940
glfw/xkb_unicode.c
vendored
940
glfw/xkb_unicode.c
vendored
@@ -1,940 +0,0 @@
|
|||||||
//========================================================================
|
|
||||||
// GLFW 3.3 X11 - www.glfw.org
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
|
||||||
// Copyright (c) 2006-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.
|
|
||||||
//
|
|
||||||
//========================================================================
|
|
||||||
|
|
||||||
#include "internal.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Marcus: This code was originally written by Markus G. Kuhn.
|
|
||||||
* I have made some slight changes (trimmed it down a bit from >60 KB to
|
|
||||||
* 20 KB), but the functionality is the same.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This module converts keysym values into the corresponding ISO 10646
|
|
||||||
* (UCS, Unicode) values.
|
|
||||||
*
|
|
||||||
* The array keysymtab[] contains pairs of X11 keysym values for graphical
|
|
||||||
* characters and the corresponding Unicode value. The function
|
|
||||||
* _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary
|
|
||||||
* search, therefore keysymtab[] must remain SORTED by keysym value.
|
|
||||||
*
|
|
||||||
* We allow to represent any UCS character in the range U-00000000 to
|
|
||||||
* U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
|
|
||||||
* This admittedly does not cover the entire 31-bit space of UCS, but
|
|
||||||
* it does cover all of the characters up to U-10FFFF, which can be
|
|
||||||
* represented by UTF-16, and more, and it is very unlikely that higher
|
|
||||||
* UCS codes will ever be assigned by ISO. So to get Unicode character
|
|
||||||
* U+ABCD you can directly use keysym 0x0100abcd.
|
|
||||||
*
|
|
||||||
* Original author: Markus G. Kuhn <mkuhn@acm.org>, University of
|
|
||||||
* Cambridge, April 2001
|
|
||||||
*
|
|
||||||
* Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
|
|
||||||
* an initial draft of the mapping table.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
//************************************************************************
|
|
||||||
//**** KeySym to Unicode mapping table ****
|
|
||||||
//************************************************************************
|
|
||||||
|
|
||||||
static const struct codepair {
|
|
||||||
unsigned short keysym;
|
|
||||||
unsigned short ucs;
|
|
||||||
} keysymtab[] = {
|
|
||||||
{ 0x01a1, 0x0104 },
|
|
||||||
{ 0x01a2, 0x02d8 },
|
|
||||||
{ 0x01a3, 0x0141 },
|
|
||||||
{ 0x01a5, 0x013d },
|
|
||||||
{ 0x01a6, 0x015a },
|
|
||||||
{ 0x01a9, 0x0160 },
|
|
||||||
{ 0x01aa, 0x015e },
|
|
||||||
{ 0x01ab, 0x0164 },
|
|
||||||
{ 0x01ac, 0x0179 },
|
|
||||||
{ 0x01ae, 0x017d },
|
|
||||||
{ 0x01af, 0x017b },
|
|
||||||
{ 0x01b1, 0x0105 },
|
|
||||||
{ 0x01b2, 0x02db },
|
|
||||||
{ 0x01b3, 0x0142 },
|
|
||||||
{ 0x01b5, 0x013e },
|
|
||||||
{ 0x01b6, 0x015b },
|
|
||||||
{ 0x01b7, 0x02c7 },
|
|
||||||
{ 0x01b9, 0x0161 },
|
|
||||||
{ 0x01ba, 0x015f },
|
|
||||||
{ 0x01bb, 0x0165 },
|
|
||||||
{ 0x01bc, 0x017a },
|
|
||||||
{ 0x01bd, 0x02dd },
|
|
||||||
{ 0x01be, 0x017e },
|
|
||||||
{ 0x01bf, 0x017c },
|
|
||||||
{ 0x01c0, 0x0154 },
|
|
||||||
{ 0x01c3, 0x0102 },
|
|
||||||
{ 0x01c5, 0x0139 },
|
|
||||||
{ 0x01c6, 0x0106 },
|
|
||||||
{ 0x01c8, 0x010c },
|
|
||||||
{ 0x01ca, 0x0118 },
|
|
||||||
{ 0x01cc, 0x011a },
|
|
||||||
{ 0x01cf, 0x010e },
|
|
||||||
{ 0x01d0, 0x0110 },
|
|
||||||
{ 0x01d1, 0x0143 },
|
|
||||||
{ 0x01d2, 0x0147 },
|
|
||||||
{ 0x01d5, 0x0150 },
|
|
||||||
{ 0x01d8, 0x0158 },
|
|
||||||
{ 0x01d9, 0x016e },
|
|
||||||
{ 0x01db, 0x0170 },
|
|
||||||
{ 0x01de, 0x0162 },
|
|
||||||
{ 0x01e0, 0x0155 },
|
|
||||||
{ 0x01e3, 0x0103 },
|
|
||||||
{ 0x01e5, 0x013a },
|
|
||||||
{ 0x01e6, 0x0107 },
|
|
||||||
{ 0x01e8, 0x010d },
|
|
||||||
{ 0x01ea, 0x0119 },
|
|
||||||
{ 0x01ec, 0x011b },
|
|
||||||
{ 0x01ef, 0x010f },
|
|
||||||
{ 0x01f0, 0x0111 },
|
|
||||||
{ 0x01f1, 0x0144 },
|
|
||||||
{ 0x01f2, 0x0148 },
|
|
||||||
{ 0x01f5, 0x0151 },
|
|
||||||
{ 0x01f8, 0x0159 },
|
|
||||||
{ 0x01f9, 0x016f },
|
|
||||||
{ 0x01fb, 0x0171 },
|
|
||||||
{ 0x01fe, 0x0163 },
|
|
||||||
{ 0x01ff, 0x02d9 },
|
|
||||||
{ 0x02a1, 0x0126 },
|
|
||||||
{ 0x02a6, 0x0124 },
|
|
||||||
{ 0x02a9, 0x0130 },
|
|
||||||
{ 0x02ab, 0x011e },
|
|
||||||
{ 0x02ac, 0x0134 },
|
|
||||||
{ 0x02b1, 0x0127 },
|
|
||||||
{ 0x02b6, 0x0125 },
|
|
||||||
{ 0x02b9, 0x0131 },
|
|
||||||
{ 0x02bb, 0x011f },
|
|
||||||
{ 0x02bc, 0x0135 },
|
|
||||||
{ 0x02c5, 0x010a },
|
|
||||||
{ 0x02c6, 0x0108 },
|
|
||||||
{ 0x02d5, 0x0120 },
|
|
||||||
{ 0x02d8, 0x011c },
|
|
||||||
{ 0x02dd, 0x016c },
|
|
||||||
{ 0x02de, 0x015c },
|
|
||||||
{ 0x02e5, 0x010b },
|
|
||||||
{ 0x02e6, 0x0109 },
|
|
||||||
{ 0x02f5, 0x0121 },
|
|
||||||
{ 0x02f8, 0x011d },
|
|
||||||
{ 0x02fd, 0x016d },
|
|
||||||
{ 0x02fe, 0x015d },
|
|
||||||
{ 0x03a2, 0x0138 },
|
|
||||||
{ 0x03a3, 0x0156 },
|
|
||||||
{ 0x03a5, 0x0128 },
|
|
||||||
{ 0x03a6, 0x013b },
|
|
||||||
{ 0x03aa, 0x0112 },
|
|
||||||
{ 0x03ab, 0x0122 },
|
|
||||||
{ 0x03ac, 0x0166 },
|
|
||||||
{ 0x03b3, 0x0157 },
|
|
||||||
{ 0x03b5, 0x0129 },
|
|
||||||
{ 0x03b6, 0x013c },
|
|
||||||
{ 0x03ba, 0x0113 },
|
|
||||||
{ 0x03bb, 0x0123 },
|
|
||||||
{ 0x03bc, 0x0167 },
|
|
||||||
{ 0x03bd, 0x014a },
|
|
||||||
{ 0x03bf, 0x014b },
|
|
||||||
{ 0x03c0, 0x0100 },
|
|
||||||
{ 0x03c7, 0x012e },
|
|
||||||
{ 0x03cc, 0x0116 },
|
|
||||||
{ 0x03cf, 0x012a },
|
|
||||||
{ 0x03d1, 0x0145 },
|
|
||||||
{ 0x03d2, 0x014c },
|
|
||||||
{ 0x03d3, 0x0136 },
|
|
||||||
{ 0x03d9, 0x0172 },
|
|
||||||
{ 0x03dd, 0x0168 },
|
|
||||||
{ 0x03de, 0x016a },
|
|
||||||
{ 0x03e0, 0x0101 },
|
|
||||||
{ 0x03e7, 0x012f },
|
|
||||||
{ 0x03ec, 0x0117 },
|
|
||||||
{ 0x03ef, 0x012b },
|
|
||||||
{ 0x03f1, 0x0146 },
|
|
||||||
{ 0x03f2, 0x014d },
|
|
||||||
{ 0x03f3, 0x0137 },
|
|
||||||
{ 0x03f9, 0x0173 },
|
|
||||||
{ 0x03fd, 0x0169 },
|
|
||||||
{ 0x03fe, 0x016b },
|
|
||||||
{ 0x047e, 0x203e },
|
|
||||||
{ 0x04a1, 0x3002 },
|
|
||||||
{ 0x04a2, 0x300c },
|
|
||||||
{ 0x04a3, 0x300d },
|
|
||||||
{ 0x04a4, 0x3001 },
|
|
||||||
{ 0x04a5, 0x30fb },
|
|
||||||
{ 0x04a6, 0x30f2 },
|
|
||||||
{ 0x04a7, 0x30a1 },
|
|
||||||
{ 0x04a8, 0x30a3 },
|
|
||||||
{ 0x04a9, 0x30a5 },
|
|
||||||
{ 0x04aa, 0x30a7 },
|
|
||||||
{ 0x04ab, 0x30a9 },
|
|
||||||
{ 0x04ac, 0x30e3 },
|
|
||||||
{ 0x04ad, 0x30e5 },
|
|
||||||
{ 0x04ae, 0x30e7 },
|
|
||||||
{ 0x04af, 0x30c3 },
|
|
||||||
{ 0x04b0, 0x30fc },
|
|
||||||
{ 0x04b1, 0x30a2 },
|
|
||||||
{ 0x04b2, 0x30a4 },
|
|
||||||
{ 0x04b3, 0x30a6 },
|
|
||||||
{ 0x04b4, 0x30a8 },
|
|
||||||
{ 0x04b5, 0x30aa },
|
|
||||||
{ 0x04b6, 0x30ab },
|
|
||||||
{ 0x04b7, 0x30ad },
|
|
||||||
{ 0x04b8, 0x30af },
|
|
||||||
{ 0x04b9, 0x30b1 },
|
|
||||||
{ 0x04ba, 0x30b3 },
|
|
||||||
{ 0x04bb, 0x30b5 },
|
|
||||||
{ 0x04bc, 0x30b7 },
|
|
||||||
{ 0x04bd, 0x30b9 },
|
|
||||||
{ 0x04be, 0x30bb },
|
|
||||||
{ 0x04bf, 0x30bd },
|
|
||||||
{ 0x04c0, 0x30bf },
|
|
||||||
{ 0x04c1, 0x30c1 },
|
|
||||||
{ 0x04c2, 0x30c4 },
|
|
||||||
{ 0x04c3, 0x30c6 },
|
|
||||||
{ 0x04c4, 0x30c8 },
|
|
||||||
{ 0x04c5, 0x30ca },
|
|
||||||
{ 0x04c6, 0x30cb },
|
|
||||||
{ 0x04c7, 0x30cc },
|
|
||||||
{ 0x04c8, 0x30cd },
|
|
||||||
{ 0x04c9, 0x30ce },
|
|
||||||
{ 0x04ca, 0x30cf },
|
|
||||||
{ 0x04cb, 0x30d2 },
|
|
||||||
{ 0x04cc, 0x30d5 },
|
|
||||||
{ 0x04cd, 0x30d8 },
|
|
||||||
{ 0x04ce, 0x30db },
|
|
||||||
{ 0x04cf, 0x30de },
|
|
||||||
{ 0x04d0, 0x30df },
|
|
||||||
{ 0x04d1, 0x30e0 },
|
|
||||||
{ 0x04d2, 0x30e1 },
|
|
||||||
{ 0x04d3, 0x30e2 },
|
|
||||||
{ 0x04d4, 0x30e4 },
|
|
||||||
{ 0x04d5, 0x30e6 },
|
|
||||||
{ 0x04d6, 0x30e8 },
|
|
||||||
{ 0x04d7, 0x30e9 },
|
|
||||||
{ 0x04d8, 0x30ea },
|
|
||||||
{ 0x04d9, 0x30eb },
|
|
||||||
{ 0x04da, 0x30ec },
|
|
||||||
{ 0x04db, 0x30ed },
|
|
||||||
{ 0x04dc, 0x30ef },
|
|
||||||
{ 0x04dd, 0x30f3 },
|
|
||||||
{ 0x04de, 0x309b },
|
|
||||||
{ 0x04df, 0x309c },
|
|
||||||
{ 0x05ac, 0x060c },
|
|
||||||
{ 0x05bb, 0x061b },
|
|
||||||
{ 0x05bf, 0x061f },
|
|
||||||
{ 0x05c1, 0x0621 },
|
|
||||||
{ 0x05c2, 0x0622 },
|
|
||||||
{ 0x05c3, 0x0623 },
|
|
||||||
{ 0x05c4, 0x0624 },
|
|
||||||
{ 0x05c5, 0x0625 },
|
|
||||||
{ 0x05c6, 0x0626 },
|
|
||||||
{ 0x05c7, 0x0627 },
|
|
||||||
{ 0x05c8, 0x0628 },
|
|
||||||
{ 0x05c9, 0x0629 },
|
|
||||||
{ 0x05ca, 0x062a },
|
|
||||||
{ 0x05cb, 0x062b },
|
|
||||||
{ 0x05cc, 0x062c },
|
|
||||||
{ 0x05cd, 0x062d },
|
|
||||||
{ 0x05ce, 0x062e },
|
|
||||||
{ 0x05cf, 0x062f },
|
|
||||||
{ 0x05d0, 0x0630 },
|
|
||||||
{ 0x05d1, 0x0631 },
|
|
||||||
{ 0x05d2, 0x0632 },
|
|
||||||
{ 0x05d3, 0x0633 },
|
|
||||||
{ 0x05d4, 0x0634 },
|
|
||||||
{ 0x05d5, 0x0635 },
|
|
||||||
{ 0x05d6, 0x0636 },
|
|
||||||
{ 0x05d7, 0x0637 },
|
|
||||||
{ 0x05d8, 0x0638 },
|
|
||||||
{ 0x05d9, 0x0639 },
|
|
||||||
{ 0x05da, 0x063a },
|
|
||||||
{ 0x05e0, 0x0640 },
|
|
||||||
{ 0x05e1, 0x0641 },
|
|
||||||
{ 0x05e2, 0x0642 },
|
|
||||||
{ 0x05e3, 0x0643 },
|
|
||||||
{ 0x05e4, 0x0644 },
|
|
||||||
{ 0x05e5, 0x0645 },
|
|
||||||
{ 0x05e6, 0x0646 },
|
|
||||||
{ 0x05e7, 0x0647 },
|
|
||||||
{ 0x05e8, 0x0648 },
|
|
||||||
{ 0x05e9, 0x0649 },
|
|
||||||
{ 0x05ea, 0x064a },
|
|
||||||
{ 0x05eb, 0x064b },
|
|
||||||
{ 0x05ec, 0x064c },
|
|
||||||
{ 0x05ed, 0x064d },
|
|
||||||
{ 0x05ee, 0x064e },
|
|
||||||
{ 0x05ef, 0x064f },
|
|
||||||
{ 0x05f0, 0x0650 },
|
|
||||||
{ 0x05f1, 0x0651 },
|
|
||||||
{ 0x05f2, 0x0652 },
|
|
||||||
{ 0x06a1, 0x0452 },
|
|
||||||
{ 0x06a2, 0x0453 },
|
|
||||||
{ 0x06a3, 0x0451 },
|
|
||||||
{ 0x06a4, 0x0454 },
|
|
||||||
{ 0x06a5, 0x0455 },
|
|
||||||
{ 0x06a6, 0x0456 },
|
|
||||||
{ 0x06a7, 0x0457 },
|
|
||||||
{ 0x06a8, 0x0458 },
|
|
||||||
{ 0x06a9, 0x0459 },
|
|
||||||
{ 0x06aa, 0x045a },
|
|
||||||
{ 0x06ab, 0x045b },
|
|
||||||
{ 0x06ac, 0x045c },
|
|
||||||
{ 0x06ae, 0x045e },
|
|
||||||
{ 0x06af, 0x045f },
|
|
||||||
{ 0x06b0, 0x2116 },
|
|
||||||
{ 0x06b1, 0x0402 },
|
|
||||||
{ 0x06b2, 0x0403 },
|
|
||||||
{ 0x06b3, 0x0401 },
|
|
||||||
{ 0x06b4, 0x0404 },
|
|
||||||
{ 0x06b5, 0x0405 },
|
|
||||||
{ 0x06b6, 0x0406 },
|
|
||||||
{ 0x06b7, 0x0407 },
|
|
||||||
{ 0x06b8, 0x0408 },
|
|
||||||
{ 0x06b9, 0x0409 },
|
|
||||||
{ 0x06ba, 0x040a },
|
|
||||||
{ 0x06bb, 0x040b },
|
|
||||||
{ 0x06bc, 0x040c },
|
|
||||||
{ 0x06be, 0x040e },
|
|
||||||
{ 0x06bf, 0x040f },
|
|
||||||
{ 0x06c0, 0x044e },
|
|
||||||
{ 0x06c1, 0x0430 },
|
|
||||||
{ 0x06c2, 0x0431 },
|
|
||||||
{ 0x06c3, 0x0446 },
|
|
||||||
{ 0x06c4, 0x0434 },
|
|
||||||
{ 0x06c5, 0x0435 },
|
|
||||||
{ 0x06c6, 0x0444 },
|
|
||||||
{ 0x06c7, 0x0433 },
|
|
||||||
{ 0x06c8, 0x0445 },
|
|
||||||
{ 0x06c9, 0x0438 },
|
|
||||||
{ 0x06ca, 0x0439 },
|
|
||||||
{ 0x06cb, 0x043a },
|
|
||||||
{ 0x06cc, 0x043b },
|
|
||||||
{ 0x06cd, 0x043c },
|
|
||||||
{ 0x06ce, 0x043d },
|
|
||||||
{ 0x06cf, 0x043e },
|
|
||||||
{ 0x06d0, 0x043f },
|
|
||||||
{ 0x06d1, 0x044f },
|
|
||||||
{ 0x06d2, 0x0440 },
|
|
||||||
{ 0x06d3, 0x0441 },
|
|
||||||
{ 0x06d4, 0x0442 },
|
|
||||||
{ 0x06d5, 0x0443 },
|
|
||||||
{ 0x06d6, 0x0436 },
|
|
||||||
{ 0x06d7, 0x0432 },
|
|
||||||
{ 0x06d8, 0x044c },
|
|
||||||
{ 0x06d9, 0x044b },
|
|
||||||
{ 0x06da, 0x0437 },
|
|
||||||
{ 0x06db, 0x0448 },
|
|
||||||
{ 0x06dc, 0x044d },
|
|
||||||
{ 0x06dd, 0x0449 },
|
|
||||||
{ 0x06de, 0x0447 },
|
|
||||||
{ 0x06df, 0x044a },
|
|
||||||
{ 0x06e0, 0x042e },
|
|
||||||
{ 0x06e1, 0x0410 },
|
|
||||||
{ 0x06e2, 0x0411 },
|
|
||||||
{ 0x06e3, 0x0426 },
|
|
||||||
{ 0x06e4, 0x0414 },
|
|
||||||
{ 0x06e5, 0x0415 },
|
|
||||||
{ 0x06e6, 0x0424 },
|
|
||||||
{ 0x06e7, 0x0413 },
|
|
||||||
{ 0x06e8, 0x0425 },
|
|
||||||
{ 0x06e9, 0x0418 },
|
|
||||||
{ 0x06ea, 0x0419 },
|
|
||||||
{ 0x06eb, 0x041a },
|
|
||||||
{ 0x06ec, 0x041b },
|
|
||||||
{ 0x06ed, 0x041c },
|
|
||||||
{ 0x06ee, 0x041d },
|
|
||||||
{ 0x06ef, 0x041e },
|
|
||||||
{ 0x06f0, 0x041f },
|
|
||||||
{ 0x06f1, 0x042f },
|
|
||||||
{ 0x06f2, 0x0420 },
|
|
||||||
{ 0x06f3, 0x0421 },
|
|
||||||
{ 0x06f4, 0x0422 },
|
|
||||||
{ 0x06f5, 0x0423 },
|
|
||||||
{ 0x06f6, 0x0416 },
|
|
||||||
{ 0x06f7, 0x0412 },
|
|
||||||
{ 0x06f8, 0x042c },
|
|
||||||
{ 0x06f9, 0x042b },
|
|
||||||
{ 0x06fa, 0x0417 },
|
|
||||||
{ 0x06fb, 0x0428 },
|
|
||||||
{ 0x06fc, 0x042d },
|
|
||||||
{ 0x06fd, 0x0429 },
|
|
||||||
{ 0x06fe, 0x0427 },
|
|
||||||
{ 0x06ff, 0x042a },
|
|
||||||
{ 0x07a1, 0x0386 },
|
|
||||||
{ 0x07a2, 0x0388 },
|
|
||||||
{ 0x07a3, 0x0389 },
|
|
||||||
{ 0x07a4, 0x038a },
|
|
||||||
{ 0x07a5, 0x03aa },
|
|
||||||
{ 0x07a7, 0x038c },
|
|
||||||
{ 0x07a8, 0x038e },
|
|
||||||
{ 0x07a9, 0x03ab },
|
|
||||||
{ 0x07ab, 0x038f },
|
|
||||||
{ 0x07ae, 0x0385 },
|
|
||||||
{ 0x07af, 0x2015 },
|
|
||||||
{ 0x07b1, 0x03ac },
|
|
||||||
{ 0x07b2, 0x03ad },
|
|
||||||
{ 0x07b3, 0x03ae },
|
|
||||||
{ 0x07b4, 0x03af },
|
|
||||||
{ 0x07b5, 0x03ca },
|
|
||||||
{ 0x07b6, 0x0390 },
|
|
||||||
{ 0x07b7, 0x03cc },
|
|
||||||
{ 0x07b8, 0x03cd },
|
|
||||||
{ 0x07b9, 0x03cb },
|
|
||||||
{ 0x07ba, 0x03b0 },
|
|
||||||
{ 0x07bb, 0x03ce },
|
|
||||||
{ 0x07c1, 0x0391 },
|
|
||||||
{ 0x07c2, 0x0392 },
|
|
||||||
{ 0x07c3, 0x0393 },
|
|
||||||
{ 0x07c4, 0x0394 },
|
|
||||||
{ 0x07c5, 0x0395 },
|
|
||||||
{ 0x07c6, 0x0396 },
|
|
||||||
{ 0x07c7, 0x0397 },
|
|
||||||
{ 0x07c8, 0x0398 },
|
|
||||||
{ 0x07c9, 0x0399 },
|
|
||||||
{ 0x07ca, 0x039a },
|
|
||||||
{ 0x07cb, 0x039b },
|
|
||||||
{ 0x07cc, 0x039c },
|
|
||||||
{ 0x07cd, 0x039d },
|
|
||||||
{ 0x07ce, 0x039e },
|
|
||||||
{ 0x07cf, 0x039f },
|
|
||||||
{ 0x07d0, 0x03a0 },
|
|
||||||
{ 0x07d1, 0x03a1 },
|
|
||||||
{ 0x07d2, 0x03a3 },
|
|
||||||
{ 0x07d4, 0x03a4 },
|
|
||||||
{ 0x07d5, 0x03a5 },
|
|
||||||
{ 0x07d6, 0x03a6 },
|
|
||||||
{ 0x07d7, 0x03a7 },
|
|
||||||
{ 0x07d8, 0x03a8 },
|
|
||||||
{ 0x07d9, 0x03a9 },
|
|
||||||
{ 0x07e1, 0x03b1 },
|
|
||||||
{ 0x07e2, 0x03b2 },
|
|
||||||
{ 0x07e3, 0x03b3 },
|
|
||||||
{ 0x07e4, 0x03b4 },
|
|
||||||
{ 0x07e5, 0x03b5 },
|
|
||||||
{ 0x07e6, 0x03b6 },
|
|
||||||
{ 0x07e7, 0x03b7 },
|
|
||||||
{ 0x07e8, 0x03b8 },
|
|
||||||
{ 0x07e9, 0x03b9 },
|
|
||||||
{ 0x07ea, 0x03ba },
|
|
||||||
{ 0x07eb, 0x03bb },
|
|
||||||
{ 0x07ec, 0x03bc },
|
|
||||||
{ 0x07ed, 0x03bd },
|
|
||||||
{ 0x07ee, 0x03be },
|
|
||||||
{ 0x07ef, 0x03bf },
|
|
||||||
{ 0x07f0, 0x03c0 },
|
|
||||||
{ 0x07f1, 0x03c1 },
|
|
||||||
{ 0x07f2, 0x03c3 },
|
|
||||||
{ 0x07f3, 0x03c2 },
|
|
||||||
{ 0x07f4, 0x03c4 },
|
|
||||||
{ 0x07f5, 0x03c5 },
|
|
||||||
{ 0x07f6, 0x03c6 },
|
|
||||||
{ 0x07f7, 0x03c7 },
|
|
||||||
{ 0x07f8, 0x03c8 },
|
|
||||||
{ 0x07f9, 0x03c9 },
|
|
||||||
{ 0x08a1, 0x23b7 },
|
|
||||||
{ 0x08a2, 0x250c },
|
|
||||||
{ 0x08a3, 0x2500 },
|
|
||||||
{ 0x08a4, 0x2320 },
|
|
||||||
{ 0x08a5, 0x2321 },
|
|
||||||
{ 0x08a6, 0x2502 },
|
|
||||||
{ 0x08a7, 0x23a1 },
|
|
||||||
{ 0x08a8, 0x23a3 },
|
|
||||||
{ 0x08a9, 0x23a4 },
|
|
||||||
{ 0x08aa, 0x23a6 },
|
|
||||||
{ 0x08ab, 0x239b },
|
|
||||||
{ 0x08ac, 0x239d },
|
|
||||||
{ 0x08ad, 0x239e },
|
|
||||||
{ 0x08ae, 0x23a0 },
|
|
||||||
{ 0x08af, 0x23a8 },
|
|
||||||
{ 0x08b0, 0x23ac },
|
|
||||||
{ 0x08bc, 0x2264 },
|
|
||||||
{ 0x08bd, 0x2260 },
|
|
||||||
{ 0x08be, 0x2265 },
|
|
||||||
{ 0x08bf, 0x222b },
|
|
||||||
{ 0x08c0, 0x2234 },
|
|
||||||
{ 0x08c1, 0x221d },
|
|
||||||
{ 0x08c2, 0x221e },
|
|
||||||
{ 0x08c5, 0x2207 },
|
|
||||||
{ 0x08c8, 0x223c },
|
|
||||||
{ 0x08c9, 0x2243 },
|
|
||||||
{ 0x08cd, 0x21d4 },
|
|
||||||
{ 0x08ce, 0x21d2 },
|
|
||||||
{ 0x08cf, 0x2261 },
|
|
||||||
{ 0x08d6, 0x221a },
|
|
||||||
{ 0x08da, 0x2282 },
|
|
||||||
{ 0x08db, 0x2283 },
|
|
||||||
{ 0x08dc, 0x2229 },
|
|
||||||
{ 0x08dd, 0x222a },
|
|
||||||
{ 0x08de, 0x2227 },
|
|
||||||
{ 0x08df, 0x2228 },
|
|
||||||
{ 0x08ef, 0x2202 },
|
|
||||||
{ 0x08f6, 0x0192 },
|
|
||||||
{ 0x08fb, 0x2190 },
|
|
||||||
{ 0x08fc, 0x2191 },
|
|
||||||
{ 0x08fd, 0x2192 },
|
|
||||||
{ 0x08fe, 0x2193 },
|
|
||||||
{ 0x09e0, 0x25c6 },
|
|
||||||
{ 0x09e1, 0x2592 },
|
|
||||||
{ 0x09e2, 0x2409 },
|
|
||||||
{ 0x09e3, 0x240c },
|
|
||||||
{ 0x09e4, 0x240d },
|
|
||||||
{ 0x09e5, 0x240a },
|
|
||||||
{ 0x09e8, 0x2424 },
|
|
||||||
{ 0x09e9, 0x240b },
|
|
||||||
{ 0x09ea, 0x2518 },
|
|
||||||
{ 0x09eb, 0x2510 },
|
|
||||||
{ 0x09ec, 0x250c },
|
|
||||||
{ 0x09ed, 0x2514 },
|
|
||||||
{ 0x09ee, 0x253c },
|
|
||||||
{ 0x09ef, 0x23ba },
|
|
||||||
{ 0x09f0, 0x23bb },
|
|
||||||
{ 0x09f1, 0x2500 },
|
|
||||||
{ 0x09f2, 0x23bc },
|
|
||||||
{ 0x09f3, 0x23bd },
|
|
||||||
{ 0x09f4, 0x251c },
|
|
||||||
{ 0x09f5, 0x2524 },
|
|
||||||
{ 0x09f6, 0x2534 },
|
|
||||||
{ 0x09f7, 0x252c },
|
|
||||||
{ 0x09f8, 0x2502 },
|
|
||||||
{ 0x0aa1, 0x2003 },
|
|
||||||
{ 0x0aa2, 0x2002 },
|
|
||||||
{ 0x0aa3, 0x2004 },
|
|
||||||
{ 0x0aa4, 0x2005 },
|
|
||||||
{ 0x0aa5, 0x2007 },
|
|
||||||
{ 0x0aa6, 0x2008 },
|
|
||||||
{ 0x0aa7, 0x2009 },
|
|
||||||
{ 0x0aa8, 0x200a },
|
|
||||||
{ 0x0aa9, 0x2014 },
|
|
||||||
{ 0x0aaa, 0x2013 },
|
|
||||||
{ 0x0aae, 0x2026 },
|
|
||||||
{ 0x0aaf, 0x2025 },
|
|
||||||
{ 0x0ab0, 0x2153 },
|
|
||||||
{ 0x0ab1, 0x2154 },
|
|
||||||
{ 0x0ab2, 0x2155 },
|
|
||||||
{ 0x0ab3, 0x2156 },
|
|
||||||
{ 0x0ab4, 0x2157 },
|
|
||||||
{ 0x0ab5, 0x2158 },
|
|
||||||
{ 0x0ab6, 0x2159 },
|
|
||||||
{ 0x0ab7, 0x215a },
|
|
||||||
{ 0x0ab8, 0x2105 },
|
|
||||||
{ 0x0abb, 0x2012 },
|
|
||||||
{ 0x0abc, 0x2329 },
|
|
||||||
{ 0x0abe, 0x232a },
|
|
||||||
{ 0x0ac3, 0x215b },
|
|
||||||
{ 0x0ac4, 0x215c },
|
|
||||||
{ 0x0ac5, 0x215d },
|
|
||||||
{ 0x0ac6, 0x215e },
|
|
||||||
{ 0x0ac9, 0x2122 },
|
|
||||||
{ 0x0aca, 0x2613 },
|
|
||||||
{ 0x0acc, 0x25c1 },
|
|
||||||
{ 0x0acd, 0x25b7 },
|
|
||||||
{ 0x0ace, 0x25cb },
|
|
||||||
{ 0x0acf, 0x25af },
|
|
||||||
{ 0x0ad0, 0x2018 },
|
|
||||||
{ 0x0ad1, 0x2019 },
|
|
||||||
{ 0x0ad2, 0x201c },
|
|
||||||
{ 0x0ad3, 0x201d },
|
|
||||||
{ 0x0ad4, 0x211e },
|
|
||||||
{ 0x0ad6, 0x2032 },
|
|
||||||
{ 0x0ad7, 0x2033 },
|
|
||||||
{ 0x0ad9, 0x271d },
|
|
||||||
{ 0x0adb, 0x25ac },
|
|
||||||
{ 0x0adc, 0x25c0 },
|
|
||||||
{ 0x0add, 0x25b6 },
|
|
||||||
{ 0x0ade, 0x25cf },
|
|
||||||
{ 0x0adf, 0x25ae },
|
|
||||||
{ 0x0ae0, 0x25e6 },
|
|
||||||
{ 0x0ae1, 0x25ab },
|
|
||||||
{ 0x0ae2, 0x25ad },
|
|
||||||
{ 0x0ae3, 0x25b3 },
|
|
||||||
{ 0x0ae4, 0x25bd },
|
|
||||||
{ 0x0ae5, 0x2606 },
|
|
||||||
{ 0x0ae6, 0x2022 },
|
|
||||||
{ 0x0ae7, 0x25aa },
|
|
||||||
{ 0x0ae8, 0x25b2 },
|
|
||||||
{ 0x0ae9, 0x25bc },
|
|
||||||
{ 0x0aea, 0x261c },
|
|
||||||
{ 0x0aeb, 0x261e },
|
|
||||||
{ 0x0aec, 0x2663 },
|
|
||||||
{ 0x0aed, 0x2666 },
|
|
||||||
{ 0x0aee, 0x2665 },
|
|
||||||
{ 0x0af0, 0x2720 },
|
|
||||||
{ 0x0af1, 0x2020 },
|
|
||||||
{ 0x0af2, 0x2021 },
|
|
||||||
{ 0x0af3, 0x2713 },
|
|
||||||
{ 0x0af4, 0x2717 },
|
|
||||||
{ 0x0af5, 0x266f },
|
|
||||||
{ 0x0af6, 0x266d },
|
|
||||||
{ 0x0af7, 0x2642 },
|
|
||||||
{ 0x0af8, 0x2640 },
|
|
||||||
{ 0x0af9, 0x260e },
|
|
||||||
{ 0x0afa, 0x2315 },
|
|
||||||
{ 0x0afb, 0x2117 },
|
|
||||||
{ 0x0afc, 0x2038 },
|
|
||||||
{ 0x0afd, 0x201a },
|
|
||||||
{ 0x0afe, 0x201e },
|
|
||||||
{ 0x0ba3, 0x003c },
|
|
||||||
{ 0x0ba6, 0x003e },
|
|
||||||
{ 0x0ba8, 0x2228 },
|
|
||||||
{ 0x0ba9, 0x2227 },
|
|
||||||
{ 0x0bc0, 0x00af },
|
|
||||||
{ 0x0bc2, 0x22a5 },
|
|
||||||
{ 0x0bc3, 0x2229 },
|
|
||||||
{ 0x0bc4, 0x230a },
|
|
||||||
{ 0x0bc6, 0x005f },
|
|
||||||
{ 0x0bca, 0x2218 },
|
|
||||||
{ 0x0bcc, 0x2395 },
|
|
||||||
{ 0x0bce, 0x22a4 },
|
|
||||||
{ 0x0bcf, 0x25cb },
|
|
||||||
{ 0x0bd3, 0x2308 },
|
|
||||||
{ 0x0bd6, 0x222a },
|
|
||||||
{ 0x0bd8, 0x2283 },
|
|
||||||
{ 0x0bda, 0x2282 },
|
|
||||||
{ 0x0bdc, 0x22a2 },
|
|
||||||
{ 0x0bfc, 0x22a3 },
|
|
||||||
{ 0x0cdf, 0x2017 },
|
|
||||||
{ 0x0ce0, 0x05d0 },
|
|
||||||
{ 0x0ce1, 0x05d1 },
|
|
||||||
{ 0x0ce2, 0x05d2 },
|
|
||||||
{ 0x0ce3, 0x05d3 },
|
|
||||||
{ 0x0ce4, 0x05d4 },
|
|
||||||
{ 0x0ce5, 0x05d5 },
|
|
||||||
{ 0x0ce6, 0x05d6 },
|
|
||||||
{ 0x0ce7, 0x05d7 },
|
|
||||||
{ 0x0ce8, 0x05d8 },
|
|
||||||
{ 0x0ce9, 0x05d9 },
|
|
||||||
{ 0x0cea, 0x05da },
|
|
||||||
{ 0x0ceb, 0x05db },
|
|
||||||
{ 0x0cec, 0x05dc },
|
|
||||||
{ 0x0ced, 0x05dd },
|
|
||||||
{ 0x0cee, 0x05de },
|
|
||||||
{ 0x0cef, 0x05df },
|
|
||||||
{ 0x0cf0, 0x05e0 },
|
|
||||||
{ 0x0cf1, 0x05e1 },
|
|
||||||
{ 0x0cf2, 0x05e2 },
|
|
||||||
{ 0x0cf3, 0x05e3 },
|
|
||||||
{ 0x0cf4, 0x05e4 },
|
|
||||||
{ 0x0cf5, 0x05e5 },
|
|
||||||
{ 0x0cf6, 0x05e6 },
|
|
||||||
{ 0x0cf7, 0x05e7 },
|
|
||||||
{ 0x0cf8, 0x05e8 },
|
|
||||||
{ 0x0cf9, 0x05e9 },
|
|
||||||
{ 0x0cfa, 0x05ea },
|
|
||||||
{ 0x0da1, 0x0e01 },
|
|
||||||
{ 0x0da2, 0x0e02 },
|
|
||||||
{ 0x0da3, 0x0e03 },
|
|
||||||
{ 0x0da4, 0x0e04 },
|
|
||||||
{ 0x0da5, 0x0e05 },
|
|
||||||
{ 0x0da6, 0x0e06 },
|
|
||||||
{ 0x0da7, 0x0e07 },
|
|
||||||
{ 0x0da8, 0x0e08 },
|
|
||||||
{ 0x0da9, 0x0e09 },
|
|
||||||
{ 0x0daa, 0x0e0a },
|
|
||||||
{ 0x0dab, 0x0e0b },
|
|
||||||
{ 0x0dac, 0x0e0c },
|
|
||||||
{ 0x0dad, 0x0e0d },
|
|
||||||
{ 0x0dae, 0x0e0e },
|
|
||||||
{ 0x0daf, 0x0e0f },
|
|
||||||
{ 0x0db0, 0x0e10 },
|
|
||||||
{ 0x0db1, 0x0e11 },
|
|
||||||
{ 0x0db2, 0x0e12 },
|
|
||||||
{ 0x0db3, 0x0e13 },
|
|
||||||
{ 0x0db4, 0x0e14 },
|
|
||||||
{ 0x0db5, 0x0e15 },
|
|
||||||
{ 0x0db6, 0x0e16 },
|
|
||||||
{ 0x0db7, 0x0e17 },
|
|
||||||
{ 0x0db8, 0x0e18 },
|
|
||||||
{ 0x0db9, 0x0e19 },
|
|
||||||
{ 0x0dba, 0x0e1a },
|
|
||||||
{ 0x0dbb, 0x0e1b },
|
|
||||||
{ 0x0dbc, 0x0e1c },
|
|
||||||
{ 0x0dbd, 0x0e1d },
|
|
||||||
{ 0x0dbe, 0x0e1e },
|
|
||||||
{ 0x0dbf, 0x0e1f },
|
|
||||||
{ 0x0dc0, 0x0e20 },
|
|
||||||
{ 0x0dc1, 0x0e21 },
|
|
||||||
{ 0x0dc2, 0x0e22 },
|
|
||||||
{ 0x0dc3, 0x0e23 },
|
|
||||||
{ 0x0dc4, 0x0e24 },
|
|
||||||
{ 0x0dc5, 0x0e25 },
|
|
||||||
{ 0x0dc6, 0x0e26 },
|
|
||||||
{ 0x0dc7, 0x0e27 },
|
|
||||||
{ 0x0dc8, 0x0e28 },
|
|
||||||
{ 0x0dc9, 0x0e29 },
|
|
||||||
{ 0x0dca, 0x0e2a },
|
|
||||||
{ 0x0dcb, 0x0e2b },
|
|
||||||
{ 0x0dcc, 0x0e2c },
|
|
||||||
{ 0x0dcd, 0x0e2d },
|
|
||||||
{ 0x0dce, 0x0e2e },
|
|
||||||
{ 0x0dcf, 0x0e2f },
|
|
||||||
{ 0x0dd0, 0x0e30 },
|
|
||||||
{ 0x0dd1, 0x0e31 },
|
|
||||||
{ 0x0dd2, 0x0e32 },
|
|
||||||
{ 0x0dd3, 0x0e33 },
|
|
||||||
{ 0x0dd4, 0x0e34 },
|
|
||||||
{ 0x0dd5, 0x0e35 },
|
|
||||||
{ 0x0dd6, 0x0e36 },
|
|
||||||
{ 0x0dd7, 0x0e37 },
|
|
||||||
{ 0x0dd8, 0x0e38 },
|
|
||||||
{ 0x0dd9, 0x0e39 },
|
|
||||||
{ 0x0dda, 0x0e3a },
|
|
||||||
{ 0x0ddf, 0x0e3f },
|
|
||||||
{ 0x0de0, 0x0e40 },
|
|
||||||
{ 0x0de1, 0x0e41 },
|
|
||||||
{ 0x0de2, 0x0e42 },
|
|
||||||
{ 0x0de3, 0x0e43 },
|
|
||||||
{ 0x0de4, 0x0e44 },
|
|
||||||
{ 0x0de5, 0x0e45 },
|
|
||||||
{ 0x0de6, 0x0e46 },
|
|
||||||
{ 0x0de7, 0x0e47 },
|
|
||||||
{ 0x0de8, 0x0e48 },
|
|
||||||
{ 0x0de9, 0x0e49 },
|
|
||||||
{ 0x0dea, 0x0e4a },
|
|
||||||
{ 0x0deb, 0x0e4b },
|
|
||||||
{ 0x0dec, 0x0e4c },
|
|
||||||
{ 0x0ded, 0x0e4d },
|
|
||||||
{ 0x0df0, 0x0e50 },
|
|
||||||
{ 0x0df1, 0x0e51 },
|
|
||||||
{ 0x0df2, 0x0e52 },
|
|
||||||
{ 0x0df3, 0x0e53 },
|
|
||||||
{ 0x0df4, 0x0e54 },
|
|
||||||
{ 0x0df5, 0x0e55 },
|
|
||||||
{ 0x0df6, 0x0e56 },
|
|
||||||
{ 0x0df7, 0x0e57 },
|
|
||||||
{ 0x0df8, 0x0e58 },
|
|
||||||
{ 0x0df9, 0x0e59 },
|
|
||||||
{ 0x0ea1, 0x3131 },
|
|
||||||
{ 0x0ea2, 0x3132 },
|
|
||||||
{ 0x0ea3, 0x3133 },
|
|
||||||
{ 0x0ea4, 0x3134 },
|
|
||||||
{ 0x0ea5, 0x3135 },
|
|
||||||
{ 0x0ea6, 0x3136 },
|
|
||||||
{ 0x0ea7, 0x3137 },
|
|
||||||
{ 0x0ea8, 0x3138 },
|
|
||||||
{ 0x0ea9, 0x3139 },
|
|
||||||
{ 0x0eaa, 0x313a },
|
|
||||||
{ 0x0eab, 0x313b },
|
|
||||||
{ 0x0eac, 0x313c },
|
|
||||||
{ 0x0ead, 0x313d },
|
|
||||||
{ 0x0eae, 0x313e },
|
|
||||||
{ 0x0eaf, 0x313f },
|
|
||||||
{ 0x0eb0, 0x3140 },
|
|
||||||
{ 0x0eb1, 0x3141 },
|
|
||||||
{ 0x0eb2, 0x3142 },
|
|
||||||
{ 0x0eb3, 0x3143 },
|
|
||||||
{ 0x0eb4, 0x3144 },
|
|
||||||
{ 0x0eb5, 0x3145 },
|
|
||||||
{ 0x0eb6, 0x3146 },
|
|
||||||
{ 0x0eb7, 0x3147 },
|
|
||||||
{ 0x0eb8, 0x3148 },
|
|
||||||
{ 0x0eb9, 0x3149 },
|
|
||||||
{ 0x0eba, 0x314a },
|
|
||||||
{ 0x0ebb, 0x314b },
|
|
||||||
{ 0x0ebc, 0x314c },
|
|
||||||
{ 0x0ebd, 0x314d },
|
|
||||||
{ 0x0ebe, 0x314e },
|
|
||||||
{ 0x0ebf, 0x314f },
|
|
||||||
{ 0x0ec0, 0x3150 },
|
|
||||||
{ 0x0ec1, 0x3151 },
|
|
||||||
{ 0x0ec2, 0x3152 },
|
|
||||||
{ 0x0ec3, 0x3153 },
|
|
||||||
{ 0x0ec4, 0x3154 },
|
|
||||||
{ 0x0ec5, 0x3155 },
|
|
||||||
{ 0x0ec6, 0x3156 },
|
|
||||||
{ 0x0ec7, 0x3157 },
|
|
||||||
{ 0x0ec8, 0x3158 },
|
|
||||||
{ 0x0ec9, 0x3159 },
|
|
||||||
{ 0x0eca, 0x315a },
|
|
||||||
{ 0x0ecb, 0x315b },
|
|
||||||
{ 0x0ecc, 0x315c },
|
|
||||||
{ 0x0ecd, 0x315d },
|
|
||||||
{ 0x0ece, 0x315e },
|
|
||||||
{ 0x0ecf, 0x315f },
|
|
||||||
{ 0x0ed0, 0x3160 },
|
|
||||||
{ 0x0ed1, 0x3161 },
|
|
||||||
{ 0x0ed2, 0x3162 },
|
|
||||||
{ 0x0ed3, 0x3163 },
|
|
||||||
{ 0x0ed4, 0x11a8 },
|
|
||||||
{ 0x0ed5, 0x11a9 },
|
|
||||||
{ 0x0ed6, 0x11aa },
|
|
||||||
{ 0x0ed7, 0x11ab },
|
|
||||||
{ 0x0ed8, 0x11ac },
|
|
||||||
{ 0x0ed9, 0x11ad },
|
|
||||||
{ 0x0eda, 0x11ae },
|
|
||||||
{ 0x0edb, 0x11af },
|
|
||||||
{ 0x0edc, 0x11b0 },
|
|
||||||
{ 0x0edd, 0x11b1 },
|
|
||||||
{ 0x0ede, 0x11b2 },
|
|
||||||
{ 0x0edf, 0x11b3 },
|
|
||||||
{ 0x0ee0, 0x11b4 },
|
|
||||||
{ 0x0ee1, 0x11b5 },
|
|
||||||
{ 0x0ee2, 0x11b6 },
|
|
||||||
{ 0x0ee3, 0x11b7 },
|
|
||||||
{ 0x0ee4, 0x11b8 },
|
|
||||||
{ 0x0ee5, 0x11b9 },
|
|
||||||
{ 0x0ee6, 0x11ba },
|
|
||||||
{ 0x0ee7, 0x11bb },
|
|
||||||
{ 0x0ee8, 0x11bc },
|
|
||||||
{ 0x0ee9, 0x11bd },
|
|
||||||
{ 0x0eea, 0x11be },
|
|
||||||
{ 0x0eeb, 0x11bf },
|
|
||||||
{ 0x0eec, 0x11c0 },
|
|
||||||
{ 0x0eed, 0x11c1 },
|
|
||||||
{ 0x0eee, 0x11c2 },
|
|
||||||
{ 0x0eef, 0x316d },
|
|
||||||
{ 0x0ef0, 0x3171 },
|
|
||||||
{ 0x0ef1, 0x3178 },
|
|
||||||
{ 0x0ef2, 0x317f },
|
|
||||||
{ 0x0ef3, 0x3181 },
|
|
||||||
{ 0x0ef4, 0x3184 },
|
|
||||||
{ 0x0ef5, 0x3186 },
|
|
||||||
{ 0x0ef6, 0x318d },
|
|
||||||
{ 0x0ef7, 0x318e },
|
|
||||||
{ 0x0ef8, 0x11eb },
|
|
||||||
{ 0x0ef9, 0x11f0 },
|
|
||||||
{ 0x0efa, 0x11f9 },
|
|
||||||
{ 0x0eff, 0x20a9 },
|
|
||||||
{ 0x13a4, 0x20ac },
|
|
||||||
{ 0x13bc, 0x0152 },
|
|
||||||
{ 0x13bd, 0x0153 },
|
|
||||||
{ 0x13be, 0x0178 },
|
|
||||||
{ 0x20ac, 0x20ac },
|
|
||||||
{ 0xfe50, '`' },
|
|
||||||
{ 0xfe51, 0x00b4 },
|
|
||||||
{ 0xfe52, '^' },
|
|
||||||
{ 0xfe53, '~' },
|
|
||||||
{ 0xfe54, 0x00af },
|
|
||||||
{ 0xfe55, 0x02d8 },
|
|
||||||
{ 0xfe56, 0x02d9 },
|
|
||||||
{ 0xfe57, 0x00a8 },
|
|
||||||
{ 0xfe58, 0x02da },
|
|
||||||
{ 0xfe59, 0x02dd },
|
|
||||||
{ 0xfe5a, 0x02c7 },
|
|
||||||
{ 0xfe5b, 0x00b8 },
|
|
||||||
{ 0xfe5c, 0x02db },
|
|
||||||
{ 0xfe5d, 0x037a },
|
|
||||||
{ 0xfe5e, 0x309b },
|
|
||||||
{ 0xfe5f, 0x309c },
|
|
||||||
{ 0xfe63, '/' },
|
|
||||||
{ 0xfe64, 0x02bc },
|
|
||||||
{ 0xfe65, 0x02bd },
|
|
||||||
{ 0xfe66, 0x02f5 },
|
|
||||||
{ 0xfe67, 0x02f3 },
|
|
||||||
{ 0xfe68, 0x02cd },
|
|
||||||
{ 0xfe69, 0xa788 },
|
|
||||||
{ 0xfe6a, 0x02f7 },
|
|
||||||
{ 0xfe6e, ',' },
|
|
||||||
{ 0xfe6f, 0x00a4 },
|
|
||||||
{ 0xfe80, 'a' }, // XK_dead_a
|
|
||||||
{ 0xfe81, 'A' }, // XK_dead_A
|
|
||||||
{ 0xfe82, 'e' }, // XK_dead_e
|
|
||||||
{ 0xfe83, 'E' }, // XK_dead_E
|
|
||||||
{ 0xfe84, 'i' }, // XK_dead_i
|
|
||||||
{ 0xfe85, 'I' }, // XK_dead_I
|
|
||||||
{ 0xfe86, 'o' }, // XK_dead_o
|
|
||||||
{ 0xfe87, 'O' }, // XK_dead_O
|
|
||||||
{ 0xfe88, 'u' }, // XK_dead_u
|
|
||||||
{ 0xfe89, 'U' }, // XK_dead_U
|
|
||||||
{ 0xfe8a, 0x0259 },
|
|
||||||
{ 0xfe8b, 0x018f },
|
|
||||||
{ 0xfe8c, 0x00b5 },
|
|
||||||
{ 0xfe90, '_' },
|
|
||||||
{ 0xfe91, 0x02c8 },
|
|
||||||
{ 0xfe92, 0x02cc },
|
|
||||||
{ 0xff80 /*XKB_KEY_KP_Space*/, ' ' },
|
|
||||||
{ 0xff95 /*XKB_KEY_KP_7*/, 0x0037 },
|
|
||||||
{ 0xff96 /*XKB_KEY_KP_4*/, 0x0034 },
|
|
||||||
{ 0xff97 /*XKB_KEY_KP_8*/, 0x0038 },
|
|
||||||
{ 0xff98 /*XKB_KEY_KP_6*/, 0x0036 },
|
|
||||||
{ 0xff99 /*XKB_KEY_KP_2*/, 0x0032 },
|
|
||||||
{ 0xff9a /*XKB_KEY_KP_9*/, 0x0039 },
|
|
||||||
{ 0xff9b /*XKB_KEY_KP_3*/, 0x0033 },
|
|
||||||
{ 0xff9c /*XKB_KEY_KP_1*/, 0x0031 },
|
|
||||||
{ 0xff9d /*XKB_KEY_KP_5*/, 0x0035 },
|
|
||||||
{ 0xff9e /*XKB_KEY_KP_0*/, 0x0030 },
|
|
||||||
{ 0xffaa /*XKB_KEY_KP_Multiply*/, '*' },
|
|
||||||
{ 0xffab /*XKB_KEY_KP_Add*/, '+' },
|
|
||||||
{ 0xffac /*XKB_KEY_KP_Separator*/, ',' },
|
|
||||||
{ 0xffad /*XKB_KEY_KP_Subtract*/, '-' },
|
|
||||||
{ 0xffae /*XKB_KEY_KP_Decimal*/, '.' },
|
|
||||||
{ 0xffaf /*XKB_KEY_KP_Divide*/, '/' },
|
|
||||||
{ 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 },
|
|
||||||
{ 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 },
|
|
||||||
{ 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 },
|
|
||||||
{ 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 },
|
|
||||||
{ 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 },
|
|
||||||
{ 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 },
|
|
||||||
{ 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 },
|
|
||||||
{ 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 },
|
|
||||||
{ 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 },
|
|
||||||
{ 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 },
|
|
||||||
{ 0xffbd /*XKB_KEY_KP_Equal*/, '=' }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
////// GLFW internal API //////
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Convert XKB KeySym to Unicode
|
|
||||||
//
|
|
||||||
long _glfwKeySym2Unicode(unsigned int keysym)
|
|
||||||
{
|
|
||||||
int min = 0;
|
|
||||||
int max = sizeof(keysymtab) / sizeof(struct codepair) - 1;
|
|
||||||
int mid;
|
|
||||||
|
|
||||||
// First check for Latin-1 characters (1:1 mapping)
|
|
||||||
if ((keysym >= 0x0020 && keysym <= 0x007e) ||
|
|
||||||
(keysym >= 0x00a0 && keysym <= 0x00ff))
|
|
||||||
{
|
|
||||||
return keysym;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also check for directly encoded 24-bit UCS characters
|
|
||||||
if ((keysym & 0xff000000) == 0x01000000)
|
|
||||||
return keysym & 0x00ffffff;
|
|
||||||
|
|
||||||
// Binary search in table
|
|
||||||
while (max >= min)
|
|
||||||
{
|
|
||||||
mid = (min + max) / 2;
|
|
||||||
if (keysymtab[mid].keysym < keysym)
|
|
||||||
min = mid + 1;
|
|
||||||
else if (keysymtab[mid].keysym > keysym)
|
|
||||||
max = mid - 1;
|
|
||||||
else
|
|
||||||
return keysymtab[mid].ucs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No matching Unicode value found
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
28
glfw/xkb_unicode.h
vendored
28
glfw/xkb_unicode.h
vendored
@@ -1,28 +0,0 @@
|
|||||||
//========================================================================
|
|
||||||
// GLFW 3.3 Linux - www.glfw.org
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
//========================================================================
|
|
||||||
|
|
||||||
long _glfwKeySym2Unicode(unsigned int keysym);
|
|
||||||
|
|
||||||
124
kittens/ask/main.py
Normal file
124
kittens/ask/main.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from kitty.cli import parse_args
|
||||||
|
from kitty.constants import cache_dir
|
||||||
|
|
||||||
|
from ..tui.operations import alternate_screen, styled
|
||||||
|
|
||||||
|
readline = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_history_items():
|
||||||
|
return list(map(readline.get_history_item, range(1, readline.get_current_history_length() + 1)))
|
||||||
|
|
||||||
|
|
||||||
|
def sort_key(item):
|
||||||
|
return len(item), item.lower()
|
||||||
|
|
||||||
|
|
||||||
|
class HistoryCompleter:
|
||||||
|
|
||||||
|
def __init__(self, name=None):
|
||||||
|
self.matches = []
|
||||||
|
self.history_path = None
|
||||||
|
if name:
|
||||||
|
ddir = os.path.join(cache_dir(), 'ask')
|
||||||
|
try:
|
||||||
|
os.makedirs(ddir)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
self.history_path = os.path.join(ddir, name)
|
||||||
|
|
||||||
|
def complete(self, text, state):
|
||||||
|
response = None
|
||||||
|
if state == 0:
|
||||||
|
history_values = get_history_items()
|
||||||
|
if text:
|
||||||
|
self.matches = sorted(
|
||||||
|
(h for h in history_values if h and h.startswith(text)), key=sort_key)
|
||||||
|
else:
|
||||||
|
self.matches = []
|
||||||
|
try:
|
||||||
|
response = self.matches[state]
|
||||||
|
except IndexError:
|
||||||
|
response = None
|
||||||
|
return response
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if self.history_path:
|
||||||
|
if os.path.exists(self.history_path):
|
||||||
|
readline.read_history_file(self.history_path)
|
||||||
|
readline.set_completer(self.complete)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *a):
|
||||||
|
if self.history_path:
|
||||||
|
readline.write_history_file(self.history_path)
|
||||||
|
|
||||||
|
|
||||||
|
def option_text():
|
||||||
|
return '''\
|
||||||
|
--type -t
|
||||||
|
choices=line
|
||||||
|
default=line
|
||||||
|
Type of input. Defaults to asking for a line of text.
|
||||||
|
|
||||||
|
|
||||||
|
--message -m
|
||||||
|
The message to display to the user. If not specified a default
|
||||||
|
message is shown.
|
||||||
|
|
||||||
|
|
||||||
|
--name -n
|
||||||
|
The name for this question. Used to store history of previous answers which can
|
||||||
|
be used for completions and via the browse history readline bindings.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
# For some reason importing readline in a key handler in the main kitty process
|
||||||
|
# causes a crash of the python interpreter, probably because of some global
|
||||||
|
# lock
|
||||||
|
global readline
|
||||||
|
import readline as rl
|
||||||
|
readline = rl
|
||||||
|
from kitty.shell import init_readline
|
||||||
|
msg = 'Ask the user for input'
|
||||||
|
try:
|
||||||
|
args, items = parse_args(args[1:], option_text, '', msg, 'kitty ask')
|
||||||
|
except SystemExit as e:
|
||||||
|
if e.code != 0:
|
||||||
|
print(e.args[0])
|
||||||
|
input('Press enter to quit...')
|
||||||
|
raise SystemExit(e.code)
|
||||||
|
|
||||||
|
init_readline(readline)
|
||||||
|
ans = {'items': items}
|
||||||
|
|
||||||
|
with alternate_screen(), HistoryCompleter(args.name):
|
||||||
|
if args.message:
|
||||||
|
print(styled(args.message, bold=True))
|
||||||
|
|
||||||
|
prompt = '> '
|
||||||
|
try:
|
||||||
|
ans['response'] = input(prompt)
|
||||||
|
except (KeyboardInterrupt, EOFError):
|
||||||
|
pass
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def handle_result(args, data, target_window_id, boss):
|
||||||
|
if 'response' in data:
|
||||||
|
func, *args = data['items']
|
||||||
|
getattr(boss, func)(data['response'], *args)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
ans = main(sys.argv)
|
||||||
|
if ans:
|
||||||
|
print(ans)
|
||||||
0
kittens/diff/__init__.py
Normal file
0
kittens/diff/__init__.py
Normal file
138
kittens/diff/collect.py
Normal file
138
kittens/diff/collect.py
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from functools import lru_cache
|
||||||
|
from hashlib import md5
|
||||||
|
from mimetypes import guess_type
|
||||||
|
|
||||||
|
path_name_map = {}
|
||||||
|
|
||||||
|
|
||||||
|
class Collection:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.changes = {}
|
||||||
|
self.renames = {}
|
||||||
|
self.adds = set()
|
||||||
|
self.removes = set()
|
||||||
|
self.all_paths = []
|
||||||
|
self.type_map = {}
|
||||||
|
|
||||||
|
def add_change(self, left_path, right_path):
|
||||||
|
self.changes[left_path] = right_path
|
||||||
|
self.all_paths.append(left_path)
|
||||||
|
self.type_map[left_path] = 'diff'
|
||||||
|
|
||||||
|
def add_rename(self, left_path, right_path):
|
||||||
|
self.renames[left_path] = right_path
|
||||||
|
self.all_paths.append(left_path)
|
||||||
|
self.type_map[left_path] = 'rename'
|
||||||
|
|
||||||
|
def add_add(self, right_path):
|
||||||
|
self.adds.add(right_path)
|
||||||
|
self.all_paths.append(right_path)
|
||||||
|
self.type_map[right_path] = 'add'
|
||||||
|
|
||||||
|
def add_removal(self, left_path):
|
||||||
|
self.removes.add(left_path)
|
||||||
|
self.all_paths.append(left_path)
|
||||||
|
self.type_map[left_path] = 'removal'
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
self.all_paths.sort(key=path_name_map.get)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for path in self.all_paths:
|
||||||
|
typ = self.type_map[path]
|
||||||
|
data = self.changes[path] if typ == 'diff' else None
|
||||||
|
yield path, self.type_map[path], data
|
||||||
|
|
||||||
|
|
||||||
|
def collect_files(collection, left, right):
|
||||||
|
left_names, right_names = set(), set()
|
||||||
|
left_path_map, right_path_map = {}, {}
|
||||||
|
|
||||||
|
def walk(base, names, pmap):
|
||||||
|
for dirpath, dirnames, filenames in os.walk(base):
|
||||||
|
for filename in filenames:
|
||||||
|
path = os.path.abspath(os.path.join(dirpath, filename))
|
||||||
|
path_name_map[path] = name = os.path.relpath(path, base)
|
||||||
|
names.add(name)
|
||||||
|
pmap[name] = path
|
||||||
|
|
||||||
|
walk(left, left_names, left_path_map), walk(right, right_names, right_path_map)
|
||||||
|
common_names = left_names & right_names
|
||||||
|
changed_names = {n for n in common_names if data_for_path(left_path_map[n]) != data_for_path(right_path_map[n])}
|
||||||
|
for n in changed_names:
|
||||||
|
collection.add_change(left_path_map[n], right_path_map[n])
|
||||||
|
|
||||||
|
removed = left_names - common_names
|
||||||
|
added = right_names - common_names
|
||||||
|
ahash = {a: hash_for_path(right_path_map[a]) for a in added}
|
||||||
|
rhash = {r: hash_for_path(left_path_map[r]) for r in removed}
|
||||||
|
for name, rh in rhash.items():
|
||||||
|
for n, ah in ahash.items():
|
||||||
|
if ah == rh and data_for_path(left_path_map[name]) == data_for_path(right_path_map[n]):
|
||||||
|
collection.add_rename(left_path_map[name], right_path_map[name])
|
||||||
|
added.discard(n)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
collection.add_removal(left_path_map[name])
|
||||||
|
|
||||||
|
for name in added:
|
||||||
|
collection.add_add(right_path_map[name])
|
||||||
|
|
||||||
|
|
||||||
|
sanitize_pat = re.compile('[\x00-\x1f\x7f\x80-\x9f]')
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_sub(m):
|
||||||
|
return '<{:x}>'.format(ord(m.group()[0]))
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize(text):
|
||||||
|
return sanitize_pat.sub(sanitize_sub, text)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1024)
|
||||||
|
def mime_type_for_path(path):
|
||||||
|
return guess_type(path)[0] or 'application/octet-stream'
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1024)
|
||||||
|
def data_for_path(path):
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
ans = f.read()
|
||||||
|
if not mime_type_for_path(path).startswith('image/'):
|
||||||
|
try:
|
||||||
|
ans = ans.decode('utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
pass
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1024)
|
||||||
|
def lines_for_path(path):
|
||||||
|
data = data_for_path(path)
|
||||||
|
return tuple(map(sanitize, data.splitlines()))
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1024)
|
||||||
|
def hash_for_path(path):
|
||||||
|
md5(data_for_path(path)).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def create_collection(left, right):
|
||||||
|
collection = Collection()
|
||||||
|
if os.path.isdir(left):
|
||||||
|
collect_files(collection, left, right)
|
||||||
|
else:
|
||||||
|
pl, pr = os.path.abspath(left), os.path.abspath(right)
|
||||||
|
path_name_map[left] = pl
|
||||||
|
path_name_map[right] = pr
|
||||||
|
collection.add_change(pl, pr)
|
||||||
|
collection.finalize()
|
||||||
|
return collection
|
||||||
98
kittens/diff/config.py
Normal file
98
kittens/diff/config.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from kitty.config_utils import (
|
||||||
|
init_config, load_config as _load_config, merge_dicts, parse_config_base,
|
||||||
|
resolve_config, to_color
|
||||||
|
)
|
||||||
|
from kitty.constants import config_dir
|
||||||
|
from kitty.rgb import color_as_sgr
|
||||||
|
|
||||||
|
defaults = None
|
||||||
|
default_config_path = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), 'diff.conf'
|
||||||
|
)
|
||||||
|
|
||||||
|
formats = {
|
||||||
|
'title': '',
|
||||||
|
'margin': '',
|
||||||
|
'text': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def set_formats(opts):
|
||||||
|
formats['text'] = '38' + color_as_sgr(opts.foreground) + ';48' + color_as_sgr(opts.background)
|
||||||
|
formats['title'] = '38' + color_as_sgr(opts.title_fg) + ';48' + color_as_sgr(opts.title_bg) + ';1'
|
||||||
|
formats['margin'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.margin_bg)
|
||||||
|
formats['added_margin'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.added_margin_bg)
|
||||||
|
formats['removed_margin'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.removed_margin_bg)
|
||||||
|
formats['added'] = '38' + color_as_sgr(opts.foreground) + ';48' + color_as_sgr(opts.added_bg)
|
||||||
|
formats['removed'] = '38' + color_as_sgr(opts.foreground) + ';48' + color_as_sgr(opts.removed_bg)
|
||||||
|
formats['filler'] = '48' + color_as_sgr(opts.filler_bg)
|
||||||
|
formats['hunk_margin'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.hunk_margin_bg)
|
||||||
|
formats['hunk'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.hunk_bg)
|
||||||
|
formats['removed_highlight'] = '48' + color_as_sgr(opts.highlight_removed_bg)
|
||||||
|
formats['added_highlight'] = '48' + color_as_sgr(opts.highlight_added_bg)
|
||||||
|
|
||||||
|
|
||||||
|
type_map = {}
|
||||||
|
|
||||||
|
for name in (
|
||||||
|
'foreground background title_fg title_bg margin_bg margin_fg removed_bg removed_margin_bg added_bg added_margin_bg filler_bg hunk_bg hunk_margin_bg'
|
||||||
|
' highlight_removed_bg highlight_added_bg'
|
||||||
|
).split():
|
||||||
|
type_map[name] = to_color
|
||||||
|
|
||||||
|
|
||||||
|
def special_handling(*a):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def parse_config(lines, check_keys=True):
|
||||||
|
ans = {}
|
||||||
|
parse_config_base(
|
||||||
|
lines,
|
||||||
|
defaults,
|
||||||
|
type_map,
|
||||||
|
special_handling,
|
||||||
|
ans,
|
||||||
|
check_keys=check_keys
|
||||||
|
)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def merge_configs(defaults, vals):
|
||||||
|
ans = {}
|
||||||
|
for k, v in defaults.items():
|
||||||
|
if isinstance(v, dict):
|
||||||
|
newvals = vals.get(k, {})
|
||||||
|
ans[k] = merge_dicts(v, newvals)
|
||||||
|
else:
|
||||||
|
ans[k] = vals.get(k, v)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def parse_defaults(lines, check_keys=False):
|
||||||
|
return parse_config(lines, check_keys)
|
||||||
|
|
||||||
|
|
||||||
|
Options, defaults = init_config(default_config_path, parse_defaults)
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(*paths, overrides=None):
|
||||||
|
return _load_config(Options, defaults, parse_config, merge_configs, *paths, overrides=overrides)
|
||||||
|
|
||||||
|
|
||||||
|
SYSTEM_CONF = '/etc/xdg/kitty/diff.conf'
|
||||||
|
defconf = os.path.join(config_dir, 'diff.conf')
|
||||||
|
|
||||||
|
|
||||||
|
def init_config(args):
|
||||||
|
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
|
||||||
|
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
|
||||||
|
opts = load_config(*config, overrides=overrides)
|
||||||
|
set_formats(opts)
|
||||||
|
return opts
|
||||||
15
kittens/diff/diff.conf
Normal file
15
kittens/diff/diff.conf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
foreground black
|
||||||
|
background white
|
||||||
|
title_fg black
|
||||||
|
title_bg white
|
||||||
|
margin_bg #fafbfc
|
||||||
|
margin_fg #aaaaaa
|
||||||
|
removed_bg #ffeef0
|
||||||
|
highlight_removed_bg #fdb8c0
|
||||||
|
removed_margin_bg #ffdce0
|
||||||
|
added_bg #e6ffed
|
||||||
|
highlight_added_bg #acf2bd
|
||||||
|
added_margin_bg #cdffd8
|
||||||
|
filler_bg #fafbfc
|
||||||
|
hunk_margin_bg #dbedff
|
||||||
|
hunk_bg #f1f8ff
|
||||||
205
kittens/diff/git.py
Normal file
205
kittens/diff/git.py
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import concurrent.futures
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from .collect import lines_for_path
|
||||||
|
from .diff_speedup import changed_center
|
||||||
|
|
||||||
|
left_lines = right_lines = None
|
||||||
|
|
||||||
|
|
||||||
|
def run_diff(file1, file2, context=3):
|
||||||
|
# returns: ok, is_different, patch
|
||||||
|
p = subprocess.Popen([
|
||||||
|
'git', 'diff', '--no-color', '--no-ext-diff', '--exit-code', '-U' + str(context), '--no-index', '--'
|
||||||
|
] + [file1, file2],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
returncode = p.wait()
|
||||||
|
if returncode in (0, 1):
|
||||||
|
return True, returncode == 1, stdout.decode('utf-8')
|
||||||
|
return False, returncode, stderr.decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
class Chunk:
|
||||||
|
|
||||||
|
__slots__ = ('is_context', 'left_start', 'right_start', 'left_count', 'right_count', 'centers')
|
||||||
|
|
||||||
|
def __init__(self, left_start, right_start, is_context=False):
|
||||||
|
self.is_context = is_context
|
||||||
|
self.left_start = left_start
|
||||||
|
self.right_start = right_start
|
||||||
|
self.left_count = self.right_count = 0
|
||||||
|
self.centers = None
|
||||||
|
|
||||||
|
def add_line(self):
|
||||||
|
self.right_count += 1
|
||||||
|
|
||||||
|
def remove_line(self):
|
||||||
|
self.left_count += 1
|
||||||
|
|
||||||
|
def context_line(self):
|
||||||
|
self.left_count += 1
|
||||||
|
self.right_count += 1
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
if not self.is_context and self.left_count == self.right_count:
|
||||||
|
self.centers = tuple(changed_center(left_lines[self.left_start + i], right_lines[self.right_start + i]) for i in range(self.left_count))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Chunk(is_context={}, left_start={}, left_count={}, right_start={}, right_count={})'.format(
|
||||||
|
self.is_context, self.left_start, self.left_count, self.right_start, self.right_count)
|
||||||
|
|
||||||
|
|
||||||
|
class Hunk:
|
||||||
|
|
||||||
|
def __init__(self, title, left, right):
|
||||||
|
self.left_start, self.left_count = left
|
||||||
|
self.right_start, self.right_count = right
|
||||||
|
self.left_start -= 1 # 0-index
|
||||||
|
self.right_start -= 1 # 0-index
|
||||||
|
self.title = title
|
||||||
|
self.chunks = []
|
||||||
|
self.current_chunk = None
|
||||||
|
self.largest_line_number = max(self.left_start + self.left_count, self.right_start + self.right_count)
|
||||||
|
|
||||||
|
def new_chunk(self, is_context=False):
|
||||||
|
if self.chunks:
|
||||||
|
c = self.chunks[-1]
|
||||||
|
left_start = c.left_start + c.left_count
|
||||||
|
right_start = c.right_start + c.right_count
|
||||||
|
else:
|
||||||
|
left_start = self.left_start
|
||||||
|
right_start = self.right_start
|
||||||
|
return Chunk(left_start, right_start, is_context)
|
||||||
|
|
||||||
|
def ensure_diff_chunk(self):
|
||||||
|
if self.current_chunk is None:
|
||||||
|
self.current_chunk = self.new_chunk(is_context=False)
|
||||||
|
elif self.current_chunk.is_context:
|
||||||
|
self.chunks.append(self.current_chunk)
|
||||||
|
self.current_chunk = self.new_chunk(is_context=False)
|
||||||
|
|
||||||
|
def ensure_context_chunk(self):
|
||||||
|
if self.current_chunk is None:
|
||||||
|
self.current_chunk = self.new_chunk(is_context=True)
|
||||||
|
elif not self.current_chunk.is_context:
|
||||||
|
self.chunks.append(self.current_chunk)
|
||||||
|
self.current_chunk = self.new_chunk(is_context=True)
|
||||||
|
|
||||||
|
def add_line(self):
|
||||||
|
self.ensure_diff_chunk()
|
||||||
|
self.current_chunk.add_line()
|
||||||
|
|
||||||
|
def remove_line(self):
|
||||||
|
self.ensure_diff_chunk()
|
||||||
|
self.current_chunk.remove_line()
|
||||||
|
|
||||||
|
def context_line(self):
|
||||||
|
self.ensure_context_chunk()
|
||||||
|
self.current_chunk.context_line()
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
self.chunks.append(self.current_chunk)
|
||||||
|
del self.current_chunk
|
||||||
|
# Sanity check
|
||||||
|
c = self.chunks[-1]
|
||||||
|
if c.left_start + c.left_count != self.left_start + self.left_count:
|
||||||
|
raise ValueError('Left side line mismatch {} != {}'.format(c.left_start + c.left_count, self.left_start + self.left_count))
|
||||||
|
if c.right_start + c.right_count != self.right_start + self.right_count:
|
||||||
|
raise ValueError('Left side line mismatch {} != {}'.format(c.right_start + c.right_count, self.right_start + self.right_count))
|
||||||
|
for c in self.chunks:
|
||||||
|
c.finalize()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_range(x):
|
||||||
|
parts = x[1:].split(',', 1)
|
||||||
|
start = abs(int(parts[0]))
|
||||||
|
count = 1 if len(parts) < 2 else int(parts[1])
|
||||||
|
return start, count
|
||||||
|
|
||||||
|
|
||||||
|
def parse_hunk_header(line):
|
||||||
|
parts = tuple(filter(None, line.split('@@', 2)))
|
||||||
|
linespec = parts[0].strip()
|
||||||
|
title = ''
|
||||||
|
if len(parts) == 2:
|
||||||
|
title = parts[1].strip()
|
||||||
|
left, right = map(parse_range, linespec.split())
|
||||||
|
return Hunk(title, left, right)
|
||||||
|
|
||||||
|
|
||||||
|
class Patch:
|
||||||
|
|
||||||
|
def __init__(self, all_hunks):
|
||||||
|
self.all_hunks = all_hunks
|
||||||
|
self.largest_line_number = self.all_hunks[-1].largest_line_number if self.all_hunks else 0
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.all_hunks)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.all_hunks)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_patch(raw):
|
||||||
|
all_hunks = []
|
||||||
|
for line in raw.splitlines():
|
||||||
|
if line.startswith('@@ '):
|
||||||
|
current_hunk = parse_hunk_header(line)
|
||||||
|
all_hunks.append(current_hunk)
|
||||||
|
else:
|
||||||
|
if not all_hunks:
|
||||||
|
continue
|
||||||
|
q = line[0]
|
||||||
|
if q == '+':
|
||||||
|
all_hunks[-1].add_line()
|
||||||
|
elif q == '-':
|
||||||
|
all_hunks[-1].remove_line()
|
||||||
|
else:
|
||||||
|
all_hunks[-1].context_line()
|
||||||
|
for h in all_hunks:
|
||||||
|
h.finalize()
|
||||||
|
return Patch(all_hunks)
|
||||||
|
|
||||||
|
|
||||||
|
class Differ:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.jmap = {}
|
||||||
|
self.jobs = []
|
||||||
|
|
||||||
|
def add_diff(self, file1, file2):
|
||||||
|
self.jmap[file1] = file2
|
||||||
|
self.jobs.append(file1)
|
||||||
|
|
||||||
|
def __call__(self, context=3):
|
||||||
|
global left_lines, right_lines
|
||||||
|
ans = {}
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
|
||||||
|
jobs = {executor.submit(run_diff, key, self.jmap[key], context): key for key in self.jobs}
|
||||||
|
for future in concurrent.futures.as_completed(jobs):
|
||||||
|
key = jobs[future]
|
||||||
|
left_path, right_path = key, self.jmap[key]
|
||||||
|
try:
|
||||||
|
ok, returncode, output = future.result()
|
||||||
|
except FileNotFoundError as err:
|
||||||
|
return 'Could not find the {} executable. Is it in your PATH?'.format(err.filename)
|
||||||
|
except Exception as e:
|
||||||
|
return 'Running git diff for {} vs. {} generated an exception: {}'.format(left_path, right_path, e)
|
||||||
|
if not ok:
|
||||||
|
return output + '\nRunning git diff for {} vs. {} failed'.format(left_path, right_path)
|
||||||
|
left_lines = lines_for_path(left_path)
|
||||||
|
right_lines = lines_for_path(right_path)
|
||||||
|
try:
|
||||||
|
patch = parse_patch(output)
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
return traceback.format_exc() + '\nParsing diff for {} vs. {} failed'.format(key[0], key[1])
|
||||||
|
else:
|
||||||
|
ans[key] = patch
|
||||||
|
return ans
|
||||||
167
kittens/diff/main.py
Normal file
167
kittens/diff/main.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from functools import partial
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
from kitty.cli import CONFIG_HELP, appname, parse_args
|
||||||
|
from kitty.key_encoding import ESCAPE
|
||||||
|
|
||||||
|
from ..tui.handler import Handler
|
||||||
|
from ..tui.loop import Loop
|
||||||
|
from ..tui.operations import (
|
||||||
|
clear_screen, set_default_colors, set_line_wrapping, set_window_title
|
||||||
|
)
|
||||||
|
from .collect import create_collection, data_for_path
|
||||||
|
from .config import init_config
|
||||||
|
from .git import Differ
|
||||||
|
from .render import render_diff
|
||||||
|
|
||||||
|
INITIALIZING, COLLECTED, DIFFED = range(3)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_diff(collection, context):
|
||||||
|
d = Differ()
|
||||||
|
|
||||||
|
for path, item_type, changed_path in collection:
|
||||||
|
if item_type == 'diff':
|
||||||
|
is_binary = isinstance(data_for_path(path), bytes)
|
||||||
|
if not is_binary:
|
||||||
|
d.add_diff(path, changed_path)
|
||||||
|
|
||||||
|
return d(context)
|
||||||
|
|
||||||
|
|
||||||
|
class DiffHandler(Handler):
|
||||||
|
|
||||||
|
def __init__(self, args, opts, left, right):
|
||||||
|
self.state = INITIALIZING
|
||||||
|
self.opts = opts
|
||||||
|
self.left, self.right = left, right
|
||||||
|
self.report_traceback_on_exit = None
|
||||||
|
self.args = args
|
||||||
|
self.scroll_pos = 0
|
||||||
|
|
||||||
|
def create_collection(self):
|
||||||
|
self.start_job('collect', create_collection, self.left, self.right)
|
||||||
|
|
||||||
|
def generate_diff(self):
|
||||||
|
self.start_job('diff', generate_diff, self.collection, self.args.context)
|
||||||
|
|
||||||
|
def render_diff(self):
|
||||||
|
self.diff_lines = tuple(render_diff(self.collection, self.diff_map, self.args, self.screen_size.cols))
|
||||||
|
|
||||||
|
def init_terminal_state(self):
|
||||||
|
self.write(set_line_wrapping(False))
|
||||||
|
self.write(set_window_title('kitty +diff'))
|
||||||
|
self.write(set_default_colors(self.opts.foreground, self.opts.background))
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.init_terminal_state()
|
||||||
|
self.draw_screen()
|
||||||
|
self.create_collection()
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
self.write(set_default_colors())
|
||||||
|
|
||||||
|
def draw_screen(self):
|
||||||
|
if self.state < DIFFED:
|
||||||
|
self.write(clear_screen())
|
||||||
|
self.write(_('Calculating diff, please wait...'))
|
||||||
|
return
|
||||||
|
self.write(clear_screen())
|
||||||
|
for i in range(self.screen_size.rows - 1):
|
||||||
|
lpos = self.scroll_pos + i
|
||||||
|
if lpos >= len(self.diff_lines):
|
||||||
|
text = ''
|
||||||
|
else:
|
||||||
|
text = self.diff_lines[lpos].text
|
||||||
|
self.write(text)
|
||||||
|
self.write('\x1b[0m\n\r')
|
||||||
|
|
||||||
|
def on_key(self, key_event):
|
||||||
|
if self.state is INITIALIZING:
|
||||||
|
if key_event.key is ESCAPE:
|
||||||
|
self.quit_loop(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_resize(self, screen_size):
|
||||||
|
self.screen_size = screen_size
|
||||||
|
if self.state > COLLECTED:
|
||||||
|
self.render_diff()
|
||||||
|
self.draw_screen()
|
||||||
|
|
||||||
|
def on_job_done(self, job_id, job_result):
|
||||||
|
if 'tb' in job_result:
|
||||||
|
self.report_traceback_on_exit = job_result['tb']
|
||||||
|
self.quit_loop(1)
|
||||||
|
return
|
||||||
|
if job_id == 'collect':
|
||||||
|
self.collection = job_result['result']
|
||||||
|
self.generate_diff()
|
||||||
|
elif job_id == 'diff':
|
||||||
|
diff_map = job_result['result']
|
||||||
|
if isinstance(diff_map, str):
|
||||||
|
self.report_traceback_on_exit = diff_map
|
||||||
|
self.quit_loop(1)
|
||||||
|
return
|
||||||
|
self.state = DIFFED
|
||||||
|
self.diff_map = diff_map
|
||||||
|
self.render_diff()
|
||||||
|
self.draw_screen()
|
||||||
|
|
||||||
|
def on_interrupt(self):
|
||||||
|
self.quit_loop(1)
|
||||||
|
|
||||||
|
def on_eot(self):
|
||||||
|
self.quit_loop(1)
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONS = partial('''\
|
||||||
|
--context
|
||||||
|
type=int
|
||||||
|
default=3
|
||||||
|
Number of lines of context to show between changes.
|
||||||
|
|
||||||
|
|
||||||
|
--config
|
||||||
|
type=list
|
||||||
|
{config_help}
|
||||||
|
|
||||||
|
|
||||||
|
--override -o
|
||||||
|
type=list
|
||||||
|
Override individual configuration options, can be specified multiple times.
|
||||||
|
Syntax: |_ name=value|. For example: |_ -o background=gray|
|
||||||
|
|
||||||
|
'''.format, config_help=CONFIG_HELP.format(conf_name='diff', appname=appname))
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
msg = 'Show a side-by-side diff of the specified files/directories'
|
||||||
|
args, items = parse_args(args[1:], OPTIONS, 'file_or_directory file_or_directory', msg, 'kitty +kitten diff')
|
||||||
|
if len(items) != 2:
|
||||||
|
raise SystemExit('You must specify exactly two files/directories to compare')
|
||||||
|
left, right = items
|
||||||
|
if os.path.isdir(left) != os.path.isdir(right):
|
||||||
|
raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.')
|
||||||
|
opts = init_config(args)
|
||||||
|
|
||||||
|
loop = Loop()
|
||||||
|
handler = DiffHandler(args, opts, left, right)
|
||||||
|
loop.loop(handler)
|
||||||
|
if loop.return_code != 0:
|
||||||
|
if handler.report_traceback_on_exit:
|
||||||
|
print(handler.report_traceback_on_exit, file=sys.stderr)
|
||||||
|
raise SystemExit(loop.return_code)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_result(args, current_char, target_window_id, boss):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main(sys.argv)
|
||||||
334
kittens/diff/render.py
Normal file
334
kittens/diff/render.py
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
from gettext import gettext as _
|
||||||
|
from itertools import repeat
|
||||||
|
|
||||||
|
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||||
|
|
||||||
|
from .collect import data_for_path, lines_for_path, path_name_map, sanitize
|
||||||
|
from .config import formats
|
||||||
|
|
||||||
|
|
||||||
|
class HunkRef:
|
||||||
|
|
||||||
|
__slots__ = ('hunk_num', 'line_num', 'chunk_num')
|
||||||
|
|
||||||
|
def __init__(self, hunk_num, chunk_num=None, line_num=None):
|
||||||
|
self.hunk_num = hunk_num
|
||||||
|
self.chunk_num = chunk_num
|
||||||
|
self.line_num = line_num
|
||||||
|
|
||||||
|
|
||||||
|
class LineRef:
|
||||||
|
|
||||||
|
__slots__ = ('src_line_number', 'wrapped_line_idx')
|
||||||
|
|
||||||
|
def __init__(self, sln, wli):
|
||||||
|
self.src_line_number = sln
|
||||||
|
self.wrapped_line_idx = wli
|
||||||
|
|
||||||
|
|
||||||
|
class Reference:
|
||||||
|
|
||||||
|
__slots__ = ('path', 'extra')
|
||||||
|
|
||||||
|
def __init__(self, path, extra=None):
|
||||||
|
self.path = path
|
||||||
|
self.extra = extra
|
||||||
|
|
||||||
|
|
||||||
|
class Line:
|
||||||
|
|
||||||
|
__slots__ = ('text', 'ref')
|
||||||
|
|
||||||
|
def __init__(self, text, ref):
|
||||||
|
self.text = text
|
||||||
|
self.ref = ref
|
||||||
|
|
||||||
|
|
||||||
|
def yield_lines_from(iterator, reference):
|
||||||
|
for text in iterator:
|
||||||
|
yield Line(text, reference)
|
||||||
|
|
||||||
|
|
||||||
|
def human_readable(size, sep=' '):
|
||||||
|
""" Convert a size in bytes into a human readable form """
|
||||||
|
divisor, suffix = 1, "B"
|
||||||
|
for i, candidate in enumerate(('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')):
|
||||||
|
if size < (1 << ((i + 1) * 10)):
|
||||||
|
divisor, suffix = (1 << (i * 10)), candidate
|
||||||
|
break
|
||||||
|
size = str(float(size)/divisor)
|
||||||
|
if size.find(".") > -1:
|
||||||
|
size = size[:size.find(".")+2]
|
||||||
|
if size.endswith('.0'):
|
||||||
|
size = size[:-2]
|
||||||
|
return size + sep + suffix
|
||||||
|
|
||||||
|
|
||||||
|
def fit_in(text, count):
|
||||||
|
p = truncate_point_for_length(text, count)
|
||||||
|
if p >= len(text):
|
||||||
|
return text
|
||||||
|
if count > 1:
|
||||||
|
p = truncate_point_for_length(text, count - 1)
|
||||||
|
return text[:p] + '…'
|
||||||
|
|
||||||
|
|
||||||
|
def fill_in(text, sz):
|
||||||
|
w = wcswidth(text)
|
||||||
|
if w < sz:
|
||||||
|
text += ' ' * (sz - w)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def place_in(text, sz):
|
||||||
|
return fill_in(fit_in(text, sz), sz)
|
||||||
|
|
||||||
|
|
||||||
|
def format_func(which):
|
||||||
|
def formatted(text):
|
||||||
|
fmt = formats[which]
|
||||||
|
return '\x1b[' + fmt + 'm' + text + '\x1b[0m'
|
||||||
|
formatted.__name__ = which + '_format'
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
|
||||||
|
text_format = format_func('text')
|
||||||
|
title_format = format_func('title')
|
||||||
|
margin_format = format_func('margin')
|
||||||
|
added_format = format_func('added')
|
||||||
|
removed_format = format_func('removed')
|
||||||
|
removed_margin_format = format_func('removed_margin')
|
||||||
|
added_margin_format = format_func('added_margin')
|
||||||
|
filler_format = format_func('filler')
|
||||||
|
hunk_margin_format = format_func('hunk_margin')
|
||||||
|
hunk_format = format_func('hunk')
|
||||||
|
highlight_map = {'remove': ('removed_highlight', 'removed'), 'add': ('added_highlight', 'added')}
|
||||||
|
|
||||||
|
|
||||||
|
def highlight_boundaries(ltype):
|
||||||
|
s, e = highlight_map[ltype]
|
||||||
|
start = '\x1b[' + formats[s] + 'm'
|
||||||
|
stop = '\x1b[' + formats[e] + 'm'
|
||||||
|
return start, stop
|
||||||
|
|
||||||
|
|
||||||
|
def title_lines(left_path, args, columns, margin_size):
|
||||||
|
name = fit_in(sanitize(path_name_map[left_path]), columns - 2 * margin_size)
|
||||||
|
yield title_format(place_in(' ' + name, columns))
|
||||||
|
yield title_format('━' * columns)
|
||||||
|
|
||||||
|
|
||||||
|
def binary_lines(path, other_path, columns, margin_size):
|
||||||
|
template = _('Binary file: {}')
|
||||||
|
|
||||||
|
def fl(path):
|
||||||
|
text = template.format(human_readable(len(data_for_path(path))))
|
||||||
|
text = place_in(text, columns // 2 - margin_size)
|
||||||
|
return margin_format(' ' * margin_size) + text_format(text)
|
||||||
|
|
||||||
|
return fl(path) + fl(other_path)
|
||||||
|
|
||||||
|
|
||||||
|
def split_to_size(line, width):
|
||||||
|
if not line:
|
||||||
|
yield line
|
||||||
|
while line:
|
||||||
|
p = truncate_point_for_length(line, width)
|
||||||
|
yield line[:p]
|
||||||
|
line = line[p:]
|
||||||
|
|
||||||
|
|
||||||
|
def split_to_size_with_center(line, width, prefix_count, suffix_count, start, stop):
|
||||||
|
sz = len(line)
|
||||||
|
if prefix_count + suffix_count == sz:
|
||||||
|
yield from split_to_size(line, width)
|
||||||
|
return
|
||||||
|
suffix_pos = sz - suffix_count
|
||||||
|
pos = state = 0
|
||||||
|
while line:
|
||||||
|
p = truncate_point_for_length(line, width)
|
||||||
|
if state is 0:
|
||||||
|
if pos + p > prefix_count:
|
||||||
|
state = 1
|
||||||
|
a, line = line[:p], line[p:]
|
||||||
|
if pos + p > suffix_pos:
|
||||||
|
a = a[:suffix_pos - pos] + stop + a[suffix_pos - pos:]
|
||||||
|
state = 2
|
||||||
|
yield a[:prefix_count - pos] + start + a[prefix_count - pos:]
|
||||||
|
else:
|
||||||
|
yield line[:p]
|
||||||
|
line = line[p:]
|
||||||
|
elif state is 1:
|
||||||
|
if pos + p > suffix_pos:
|
||||||
|
state = 2
|
||||||
|
a, line = line[:p], line[p:]
|
||||||
|
yield start + a[:suffix_pos - pos] + stop + a[suffix_pos - pos:]
|
||||||
|
else:
|
||||||
|
yield start + line[:p]
|
||||||
|
line = line[p:]
|
||||||
|
elif state is 2:
|
||||||
|
yield line[:p]
|
||||||
|
line = line[p:]
|
||||||
|
pos += p
|
||||||
|
|
||||||
|
|
||||||
|
margin_bg_map = {'filler': filler_format, 'remove': removed_margin_format, 'add': added_margin_format, 'context': margin_format}
|
||||||
|
text_bg_map = {'filler': filler_format, 'remove': removed_format, 'add': added_format, 'context': text_format}
|
||||||
|
|
||||||
|
|
||||||
|
class DiffData:
|
||||||
|
|
||||||
|
def __init__(self, left_path, right_path, available_cols, margin_size):
|
||||||
|
self.left_path, self.right_path = left_path, right_path
|
||||||
|
self.available_cols = available_cols
|
||||||
|
self.margin_size = margin_size
|
||||||
|
self.left_lines, self.right_lines = map(lines_for_path, (left_path, right_path))
|
||||||
|
self.filler_line = render_diff_line('', '', 'filler', margin_size, available_cols)
|
||||||
|
self.left_filler_line = render_diff_line('', '', 'remove', margin_size, available_cols)
|
||||||
|
self.right_filler_line = render_diff_line('', '', 'add', margin_size, available_cols)
|
||||||
|
|
||||||
|
|
||||||
|
def render_diff_line(number, text, ltype, margin_size, available_cols):
|
||||||
|
margin = margin_bg_map[ltype](place_in(number, margin_size))
|
||||||
|
content = text_bg_map[ltype](fill_in(text or '', available_cols))
|
||||||
|
return margin + content
|
||||||
|
|
||||||
|
|
||||||
|
def render_diff_pair(left_line_number, left, left_is_change, right_line_number, right, right_is_change, is_first, margin_size, available_cols):
|
||||||
|
ltype = 'filler' if left_line_number is None else ('remove' if left_is_change else 'context')
|
||||||
|
rtype = 'filler' if right_line_number is None else ('add' if right_is_change else 'context')
|
||||||
|
return (
|
||||||
|
render_diff_line(left_line_number if is_first else None, left, ltype, margin_size, available_cols) +
|
||||||
|
render_diff_line(right_line_number if is_first else None, right, rtype, margin_size, available_cols)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def hunk_title(hunk_num, hunk, margin_size, available_cols):
|
||||||
|
m = hunk_margin_format(' ' * margin_size)
|
||||||
|
t = '@@ -{},{} +{},{} @@ {}'.format(hunk.left_start + 1, hunk.left_count, hunk.right_start + 1, hunk.right_count, hunk.title)
|
||||||
|
return m + hunk_format(place_in(t, available_cols))
|
||||||
|
|
||||||
|
|
||||||
|
def render_half_line(line_number, src, ltype, margin_size, available_cols, changed_center=None):
|
||||||
|
if changed_center is not None and changed_center[0]:
|
||||||
|
start, stop = highlight_boundaries(ltype)
|
||||||
|
lines = split_to_size_with_center(src[line_number], available_cols, changed_center[0], changed_center[1], start, stop)
|
||||||
|
else:
|
||||||
|
lines = split_to_size(src[line_number], available_cols)
|
||||||
|
line_number = str(line_number + 1)
|
||||||
|
for line in lines:
|
||||||
|
yield render_diff_line(line_number, line, ltype, margin_size, available_cols)
|
||||||
|
line_number = ''
|
||||||
|
|
||||||
|
|
||||||
|
def lines_for_chunk(data, hunk_num, chunk, chunk_num):
|
||||||
|
if chunk.is_context:
|
||||||
|
for i in range(chunk.left_count):
|
||||||
|
left_line_number = chunk.left_start + i
|
||||||
|
right_line_number = chunk.right_start + i
|
||||||
|
lines = split_to_size(data.left_lines[left_line_number], data.available_cols)
|
||||||
|
ref = Reference(data.left_path, HunkRef(hunk_num, chunk_num, i))
|
||||||
|
left_line_number = str(left_line_number + 1)
|
||||||
|
right_line_number = str(right_line_number + 1)
|
||||||
|
for text in lines:
|
||||||
|
line = render_diff_line(left_line_number, text, 'context', data.margin_size, data.available_cols)
|
||||||
|
if right_line_number == left_line_number:
|
||||||
|
r = line
|
||||||
|
else:
|
||||||
|
r = render_diff_line(right_line_number, text, 'context', data.margin_size, data.available_cols)
|
||||||
|
yield Line(line + r, ref)
|
||||||
|
left_line_number = right_line_number = ''
|
||||||
|
else:
|
||||||
|
common = min(chunk.left_count, chunk.right_count)
|
||||||
|
for i in range(max(chunk.left_count, chunk.right_count)):
|
||||||
|
ref = Reference(data.left_path, HunkRef(hunk_num, chunk_num, i))
|
||||||
|
ll, rl = [], []
|
||||||
|
if i < chunk.left_count:
|
||||||
|
ll.extend(render_half_line(
|
||||||
|
chunk.left_start + i, data.left_lines, 'remove', data.margin_size,
|
||||||
|
data.available_cols, None if chunk.centers is None else chunk.centers[i]))
|
||||||
|
if i < chunk.right_count:
|
||||||
|
rl.extend(render_half_line(
|
||||||
|
chunk.right_start + i, data.right_lines, 'add', data.margin_size,
|
||||||
|
data.available_cols, None if chunk.centers is None else chunk.centers[i]))
|
||||||
|
if i < common:
|
||||||
|
extra = len(ll) - len(rl)
|
||||||
|
if extra != 0:
|
||||||
|
if extra < 0:
|
||||||
|
x, fl = ll, data.left_filler_line
|
||||||
|
extra = -extra
|
||||||
|
else:
|
||||||
|
x, fl = rl, data.right_filler_line
|
||||||
|
x.extend(repeat(fl, extra))
|
||||||
|
else:
|
||||||
|
if ll:
|
||||||
|
x, count = rl, len(ll)
|
||||||
|
else:
|
||||||
|
x, count = ll, len(rl)
|
||||||
|
x.extend(repeat(data.filler_line, count))
|
||||||
|
for left_line, right_line in zip(ll, rl):
|
||||||
|
yield Line(left_line + right_line, ref)
|
||||||
|
|
||||||
|
|
||||||
|
def lines_for_diff(left_path, right_path, hunks, args, columns, margin_size):
|
||||||
|
available_cols = columns // 2 - margin_size
|
||||||
|
data = DiffData(left_path, right_path, available_cols, margin_size)
|
||||||
|
|
||||||
|
for hunk_num, hunk in enumerate(hunks):
|
||||||
|
yield Line(hunk_title(hunk_num, hunk, margin_size, columns - margin_size), Reference(left_path, HunkRef(hunk_num)))
|
||||||
|
for cnum, chunk in enumerate(hunk.chunks):
|
||||||
|
yield from lines_for_chunk(data, hunk_num, chunk, cnum)
|
||||||
|
|
||||||
|
|
||||||
|
def all_lines(path, args, columns, margin_size, is_add=True):
|
||||||
|
available_cols = columns // 2 - margin_size
|
||||||
|
ltype = 'add' if is_add else 'remove'
|
||||||
|
lines = lines_for_path(path)
|
||||||
|
filler = render_diff_line('', '', 'filler', margin_size, available_cols)
|
||||||
|
for line_number in range(len(lines)):
|
||||||
|
h = render_half_line(line_number, lines, ltype, margin_size, available_cols)
|
||||||
|
for i, hl in enumerate(h):
|
||||||
|
ref = Reference(path, LineRef(line_number, i))
|
||||||
|
text = (filler + h) if is_add else (h + filler)
|
||||||
|
yield Line(text, ref)
|
||||||
|
|
||||||
|
|
||||||
|
def rename_lines(path, other_path, args, columns, margin_size):
|
||||||
|
m = ' ' * margin_size
|
||||||
|
for line in split_to_size(margin_size + _('The file {0} was renamed to {1}').format(
|
||||||
|
sanitize(path_name_map[path]), sanitize(path_name_map[other_path])), columns - margin_size):
|
||||||
|
yield m + line
|
||||||
|
|
||||||
|
|
||||||
|
def render_diff(collection, diff_map, args, columns):
|
||||||
|
largest_line_number = 0
|
||||||
|
for path, item_type, other_path in collection:
|
||||||
|
if item_type == 'diff':
|
||||||
|
patch = diff_map.get(path)
|
||||||
|
if patch is not None:
|
||||||
|
largest_line_number = max(largest_line_number, patch.largest_line_number)
|
||||||
|
|
||||||
|
margin_size = max(3, len(str(largest_line_number)) + 1)
|
||||||
|
|
||||||
|
for path, item_type, other_path in collection:
|
||||||
|
item_ref = Reference(path)
|
||||||
|
if item_type == 'diff':
|
||||||
|
yield from yield_lines_from(title_lines(path, args, columns, margin_size), item_ref)
|
||||||
|
is_binary = isinstance(data_for_path(path), bytes)
|
||||||
|
if is_binary:
|
||||||
|
yield from yield_lines_from(binary_lines(path, other_path, columns, margin_size), item_ref)
|
||||||
|
else:
|
||||||
|
yield from lines_for_diff(path, other_path, diff_map[path], args, columns, margin_size)
|
||||||
|
elif item_type == 'add':
|
||||||
|
yield from yield_lines_from(title_lines(other_path, args, columns, margin_size), item_ref)
|
||||||
|
yield from all_lines(other_path, args, columns, margin_size, is_add=True)
|
||||||
|
elif item_type == 'removal':
|
||||||
|
yield from yield_lines_from(title_lines(path, args, columns, margin_size), item_ref)
|
||||||
|
yield from all_lines(path, args, columns, margin_size, is_add=False)
|
||||||
|
elif item_type == 'rename':
|
||||||
|
yield from yield_lines_from(title_lines(path, args, columns, margin_size), item_ref)
|
||||||
|
yield from yield_lines_from(rename_lines(path, other_path, args, columns, margin_size))
|
||||||
52
kittens/diff/speedup.c
Normal file
52
kittens/diff/speedup.c
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* speedup.c
|
||||||
|
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "data-types.h"
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
changed_center(PyObject *self UNUSED, PyObject *args) {
|
||||||
|
unsigned int prefix_count = 0, suffix_count = 0;
|
||||||
|
PyObject *lp, *rp;
|
||||||
|
if (!PyArg_ParseTuple(args, "UU", &lp, &rp)) return NULL;
|
||||||
|
const size_t left_len = PyUnicode_GET_LENGTH(lp), right_len = PyUnicode_GET_LENGTH(rp);
|
||||||
|
|
||||||
|
#define R(which, index) PyUnicode_READ(PyUnicode_KIND(which), PyUnicode_DATA(which), index)
|
||||||
|
while(prefix_count < MIN(left_len, right_len)) {
|
||||||
|
if (R(lp, prefix_count) != R(rp, prefix_count)) break;
|
||||||
|
prefix_count++;
|
||||||
|
}
|
||||||
|
if (left_len && right_len && prefix_count < MIN(left_len, right_len)) {
|
||||||
|
while(suffix_count < MIN(left_len - prefix_count, right_len - prefix_count)) {
|
||||||
|
if(R(lp, left_len - 1 - suffix_count) != R(rp, right_len - 1 - suffix_count)) break;
|
||||||
|
suffix_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef R
|
||||||
|
return Py_BuildValue("II", prefix_count, suffix_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
{"changed_center", (PyCFunction)changed_center, METH_VARARGS, ""},
|
||||||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PyModuleDef module = {
|
||||||
|
.m_base = PyModuleDef_HEAD_INIT,
|
||||||
|
.m_name = "diff_speedup", /* name of module */
|
||||||
|
.m_doc = NULL,
|
||||||
|
.m_size = -1,
|
||||||
|
.m_methods = module_methods
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORTED PyMODINIT_FUNC
|
||||||
|
PyInit_diff_speedup(void) {
|
||||||
|
PyObject *m;
|
||||||
|
|
||||||
|
m = PyModule_Create(&module);
|
||||||
|
if (m == NULL) return NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
0
kittens/hints/__init__.py
Normal file
0
kittens/hints/__init__.py
Normal file
326
kittens/hints/main.py
Normal file
326
kittens/hints/main.py
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
from functools import lru_cache, partial
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
from kitty.cli import parse_args
|
||||||
|
from kitty.fast_data_types import set_clipboard_string
|
||||||
|
from kitty.key_encoding import ESCAPE, backspace_key, enter_key
|
||||||
|
|
||||||
|
from ..tui.handler import Handler
|
||||||
|
from ..tui.loop import Loop
|
||||||
|
from ..tui.operations import (
|
||||||
|
clear_screen, faint, set_cursor_visible, set_window_title, styled
|
||||||
|
)
|
||||||
|
|
||||||
|
URL_PREFIXES = 'http https file ftp'.split()
|
||||||
|
HINT_ALPHABET = string.digits + string.ascii_lowercase
|
||||||
|
|
||||||
|
|
||||||
|
class Mark(object):
|
||||||
|
|
||||||
|
__slots__ = ('index', 'start', 'end', 'text')
|
||||||
|
|
||||||
|
def __init__(self, index, start, end, text):
|
||||||
|
self.index, self.start, self.end = index, start, end
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=2048)
|
||||||
|
def encode_hint(num):
|
||||||
|
res = ''
|
||||||
|
d = len(HINT_ALPHABET)
|
||||||
|
while not res or num > 0:
|
||||||
|
num, i = divmod(num, d)
|
||||||
|
res = HINT_ALPHABET[i] + res
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def decode_hint(x):
|
||||||
|
return int(x, 36)
|
||||||
|
|
||||||
|
|
||||||
|
def render(lines, current_input):
|
||||||
|
ans = []
|
||||||
|
|
||||||
|
def mark(m):
|
||||||
|
hint = encode_hint(m.index)
|
||||||
|
text = m.text
|
||||||
|
if current_input and not hint.startswith(current_input):
|
||||||
|
return faint(text)
|
||||||
|
hint = hint[len(current_input):] or ' '
|
||||||
|
text = text[len(hint):]
|
||||||
|
return styled(
|
||||||
|
hint,
|
||||||
|
fg='black',
|
||||||
|
bg='green',
|
||||||
|
bold=True
|
||||||
|
) + styled(
|
||||||
|
text, fg='gray', fg_intense=True, bold=True
|
||||||
|
)
|
||||||
|
|
||||||
|
for line, marks in lines:
|
||||||
|
if not marks:
|
||||||
|
ans.append(faint(line))
|
||||||
|
continue
|
||||||
|
buf = []
|
||||||
|
|
||||||
|
for i, m in enumerate(marks):
|
||||||
|
if i == 0 and m.start:
|
||||||
|
buf.append(faint(line[:m.start]))
|
||||||
|
buf.append(mark(m))
|
||||||
|
if m is not marks[-1]:
|
||||||
|
buf.append(faint(line[m.end:marks[i + 1].start]))
|
||||||
|
|
||||||
|
rest = line[marks[-1].end:]
|
||||||
|
if rest:
|
||||||
|
buf.append(faint(rest))
|
||||||
|
|
||||||
|
ans.append(''.join(buf))
|
||||||
|
return '\r\n'.join(ans)
|
||||||
|
|
||||||
|
|
||||||
|
class Hints(Handler):
|
||||||
|
|
||||||
|
def __init__(self, lines, index_map, args):
|
||||||
|
self.lines, self.index_map = tuple(lines), index_map
|
||||||
|
self.current_input = ''
|
||||||
|
self.current_text = None
|
||||||
|
self.args = args
|
||||||
|
self.window_title = _('Choose URL') if args.type == 'url' else _('Choose text')
|
||||||
|
self.chosen = None
|
||||||
|
|
||||||
|
def init_terminal_state(self):
|
||||||
|
self.write(set_cursor_visible(False))
|
||||||
|
self.write(set_window_title(self.window_title))
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.init_terminal_state()
|
||||||
|
self.draw_screen()
|
||||||
|
|
||||||
|
def on_text(self, text, in_bracketed_paste):
|
||||||
|
changed = False
|
||||||
|
for c in text:
|
||||||
|
if c in HINT_ALPHABET:
|
||||||
|
self.current_input += c
|
||||||
|
changed = True
|
||||||
|
if changed:
|
||||||
|
matches = [
|
||||||
|
m.text for idx, m in self.index_map.items()
|
||||||
|
if encode_hint(idx).startswith(self.current_input)
|
||||||
|
]
|
||||||
|
if len(matches) == 1:
|
||||||
|
self.chosen = matches[0]
|
||||||
|
self.quit_loop(0)
|
||||||
|
return
|
||||||
|
self.current_text = None
|
||||||
|
self.draw_screen()
|
||||||
|
|
||||||
|
def on_key(self, key_event):
|
||||||
|
if key_event is backspace_key:
|
||||||
|
self.current_input = self.current_input[:-1]
|
||||||
|
self.current_text = None
|
||||||
|
self.draw_screen()
|
||||||
|
elif key_event is enter_key and self.current_input:
|
||||||
|
idx = decode_hint(self.current_input)
|
||||||
|
self.chosen = self.index_map[idx].text
|
||||||
|
self.quit_loop(0)
|
||||||
|
elif key_event.key is ESCAPE:
|
||||||
|
self.quit_loop(1)
|
||||||
|
|
||||||
|
def on_interrupt(self):
|
||||||
|
self.quit_loop(1)
|
||||||
|
|
||||||
|
def on_eot(self):
|
||||||
|
self.quit_loop(1)
|
||||||
|
|
||||||
|
def on_resize(self, new_size):
|
||||||
|
Handler.on_resize(self, new_size)
|
||||||
|
self.draw_screen()
|
||||||
|
|
||||||
|
def draw_screen(self):
|
||||||
|
if self.current_text is None:
|
||||||
|
self.current_text = render(self.lines, self.current_input)
|
||||||
|
self.write(clear_screen())
|
||||||
|
self.write(self.current_text)
|
||||||
|
|
||||||
|
|
||||||
|
def regex_finditer(pat, minimum_match_length, line):
|
||||||
|
for m in pat.finditer(line):
|
||||||
|
s, e = m.span(pat.groups)
|
||||||
|
if e - s >= minimum_match_length:
|
||||||
|
yield s, e
|
||||||
|
|
||||||
|
|
||||||
|
closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>'}
|
||||||
|
|
||||||
|
|
||||||
|
def find_urls(pat, line):
|
||||||
|
for m in pat.finditer(line):
|
||||||
|
s, e = m.span()
|
||||||
|
if s > 4 and line[s - 5:s] == 'link:': # asciidoc URLs
|
||||||
|
url = line[s:e]
|
||||||
|
idx = url.rfind('[')
|
||||||
|
if idx > -1:
|
||||||
|
e -= len(url) - idx
|
||||||
|
while line[e - 1] in '.,?!' and e > 1: # remove trailing punctuation
|
||||||
|
e -= 1
|
||||||
|
# Detect a bracketed URL
|
||||||
|
if s > 0 and e > s + 4 and line[s-1] in '({[<' and line[e-1] == closing_bracket_map[line[s-1]]:
|
||||||
|
e -= 1
|
||||||
|
yield s, e
|
||||||
|
|
||||||
|
|
||||||
|
def mark(finditer, line, all_marks):
|
||||||
|
marks = []
|
||||||
|
for s, e in finditer(line):
|
||||||
|
idx = len(all_marks)
|
||||||
|
text = line[s:e]
|
||||||
|
marks.append(Mark(idx, s, e, text))
|
||||||
|
all_marks.append(marks[-1])
|
||||||
|
return line, marks
|
||||||
|
|
||||||
|
|
||||||
|
def run_loop(args, lines, index_map):
|
||||||
|
loop = Loop()
|
||||||
|
handler = Hints(lines, index_map, args)
|
||||||
|
loop.loop(handler)
|
||||||
|
if handler.chosen and loop.return_code == 0:
|
||||||
|
return {'match': handler.chosen, 'program': args.program}
|
||||||
|
raise SystemExit(loop.return_code)
|
||||||
|
|
||||||
|
|
||||||
|
def escape(chars):
|
||||||
|
return chars.replace('\\', '\\\\').replace('-', r'\-').replace(']', r'\]')
|
||||||
|
|
||||||
|
|
||||||
|
def run(args, text):
|
||||||
|
if args.type == 'url':
|
||||||
|
from .url_regex import url_delimiters
|
||||||
|
url_pat = '(?:{})://[^{}]{{3,}}'.format(
|
||||||
|
'|'.join(args.url_prefixes.split(',')), url_delimiters
|
||||||
|
)
|
||||||
|
finditer = partial(find_urls, re.compile(url_pat))
|
||||||
|
elif args.type == 'path':
|
||||||
|
finditer = partial(regex_finditer, re.compile(r'(?:\S*/\S+)|(?:\S+[.][a-zA-Z0-9]{2,5})'), args.minimum_match_length)
|
||||||
|
elif args.type == 'line':
|
||||||
|
finditer = partial(regex_finditer, re.compile(r'(?m)^\s*(.+)\s*$'), args.minimum_match_length)
|
||||||
|
elif args.type == 'word':
|
||||||
|
chars = args.word_characters
|
||||||
|
if chars is None:
|
||||||
|
import json
|
||||||
|
chars = json.loads(os.environ['KITTY_COMMON_OPTS'])['select_by_word_characters']
|
||||||
|
pat = re.compile(r'(?u)[{}\w]{{{},}}'.format(escape(chars), args.minimum_match_length))
|
||||||
|
finditer = partial(regex_finditer, pat, args.minimum_match_length)
|
||||||
|
else:
|
||||||
|
finditer = partial(regex_finditer, re.compile(args.regex), args.minimum_match_length)
|
||||||
|
lines = []
|
||||||
|
all_marks = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
marked = mark(finditer, line, all_marks)
|
||||||
|
lines.append(marked)
|
||||||
|
if not all_marks:
|
||||||
|
input(_('No {} found, press Enter to abort.').format(
|
||||||
|
'URLs' if args.type == 'url' else 'matches'
|
||||||
|
))
|
||||||
|
return
|
||||||
|
|
||||||
|
largest_index = all_marks[-1].index
|
||||||
|
for m in all_marks:
|
||||||
|
m.index = largest_index - m.index
|
||||||
|
index_map = {m.index: m for m in all_marks}
|
||||||
|
|
||||||
|
return run_loop(args, lines, index_map)
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONS = partial(r'''
|
||||||
|
--program
|
||||||
|
default=default
|
||||||
|
What program to use to open matched text. Defaults to the default open program
|
||||||
|
for the operating system. Use a value of - to paste the match into the
|
||||||
|
terminal window instead. A value of @ will copy the match to the clipboard.
|
||||||
|
|
||||||
|
|
||||||
|
--type
|
||||||
|
default=url
|
||||||
|
choices=url,regex,path,line,word
|
||||||
|
The type of text to search for.
|
||||||
|
|
||||||
|
|
||||||
|
--regex
|
||||||
|
default=(?m)^\s*(.+)\s*$
|
||||||
|
The regular expression to use when --type=regex. If you specify a 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.
|
||||||
|
|
||||||
|
|
||||||
|
--url-prefixes
|
||||||
|
default={0}
|
||||||
|
Comma separated list of recognized URL prefixes.
|
||||||
|
|
||||||
|
|
||||||
|
--word-characters
|
||||||
|
Characters to consider as part of a word. In addition, all characters marked as
|
||||||
|
alpha-numeric in the unicode database will be considered as word characters.
|
||||||
|
Defaults to the select_by_word_characters setting from kitty.conf.
|
||||||
|
|
||||||
|
|
||||||
|
--minimum-match-length
|
||||||
|
default=3
|
||||||
|
type=int
|
||||||
|
The minimum number of characters to consider a match.
|
||||||
|
'''.format, ','.join(sorted(URL_PREFIXES)))
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
msg = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
|
||||||
|
text = ''
|
||||||
|
if sys.stdin.isatty():
|
||||||
|
if '--help' not in args and '-h' not in args:
|
||||||
|
print('You must pass the text to be hinted on STDIN', file=sys.stderr)
|
||||||
|
input(_('Press Enter to quit'))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
text = sys.stdin.buffer.read().decode('utf-8')
|
||||||
|
sys.stdin = open('/dev/tty')
|
||||||
|
try:
|
||||||
|
args, items = parse_args(args[1:], OPTIONS, '', msg, 'hints')
|
||||||
|
except SystemExit as e:
|
||||||
|
if e.code != 0:
|
||||||
|
print(e.args[0], file=sys.stderr)
|
||||||
|
input(_('Press Enter to quit'))
|
||||||
|
return
|
||||||
|
if items:
|
||||||
|
print('Extra command line arguments present: {}'.format(' '.join(items)), file=sys.stderr)
|
||||||
|
input(_('Press Enter to quit'))
|
||||||
|
return
|
||||||
|
return run(args, text)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_result(args, data, target_window_id, boss):
|
||||||
|
program = data['program']
|
||||||
|
if program == '-':
|
||||||
|
w = boss.window_id_map.get(target_window_id)
|
||||||
|
if w is not None:
|
||||||
|
w.paste(data['match'])
|
||||||
|
elif program == '@':
|
||||||
|
set_clipboard_string(data['match'])
|
||||||
|
else:
|
||||||
|
cwd = None
|
||||||
|
w = boss.window_id_map.get(target_window_id)
|
||||||
|
if w is not None:
|
||||||
|
cwd = w.cwd_of_child
|
||||||
|
boss.open_url(data['match'], None if program == 'default' else program, cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Run with kitty +kitten hints
|
||||||
|
ans = main(sys.argv)
|
||||||
|
if ans:
|
||||||
|
print(ans)
|
||||||
@@ -1 +1 @@
|
|||||||
url_delimiters = '\x00-\x20\x7f-\xa0\xad\u0600-\u0605\u061c\u06dd\u070f\u08e2\u1680\u180e\u2000-\u200f\u2028-\u202f\u205f-\u2064\u2066-\u206f\u3000\ud800-\uf8ff\ufeff\ufff9-\ufffb\U000110bd\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f\U000f0000-\U000ffffd\U00100000-\U0010fffd' # noqa
|
url_delimiters = '\x00-\x20\x7f-\xa0\xad\u0600-\u0605\u061c\u06dd\u070f\u08e2\u1680\u180e\u2000-\u200f\u2028-\u202f\u205f-\u2064\u2066-\u206f\u3000\ud800-\uf8ff\ufeff\ufff9-\ufffb\U000110bd\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f\U000f0000-\U000ffffd\U00100000-\U0010fffd' # noqa
|
||||||
87
kittens/runner.py
Normal file
87
kittens/runner.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
aliases = {'url_hints': 'hints'}
|
||||||
|
|
||||||
|
|
||||||
|
def resolved_kitten(k):
|
||||||
|
return aliases.get(k, k)
|
||||||
|
|
||||||
|
|
||||||
|
def import_kitten_main_module(config_dir, kitten):
|
||||||
|
if kitten.endswith('.py'):
|
||||||
|
path = os.path.expanduser(kitten)
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
path = os.path.join(config_dir, path)
|
||||||
|
path = os.path.abspath(path)
|
||||||
|
if os.path.dirname(path):
|
||||||
|
sys.path.insert(0, os.path.dirname(path))
|
||||||
|
with open(path) as f:
|
||||||
|
src = f.read()
|
||||||
|
code = compile(src, path, 'exec')
|
||||||
|
g = {'__name__': 'kitten'}
|
||||||
|
exec(code, g)
|
||||||
|
return {'start': g['main'], 'end': g['handle_result']}
|
||||||
|
else:
|
||||||
|
kitten = resolved_kitten(kitten)
|
||||||
|
m = importlib.import_module('kittens.{}.main'.format(kitten))
|
||||||
|
return {'start': m.main, 'end': m.handle_result}
|
||||||
|
|
||||||
|
|
||||||
|
def create_kitten_handler(kitten, orig_args):
|
||||||
|
from kitty.constants import config_dir
|
||||||
|
kitten = resolved_kitten(kitten)
|
||||||
|
m = import_kitten_main_module(config_dir, kitten)
|
||||||
|
return partial(m['end'], [kitten] + orig_args)
|
||||||
|
|
||||||
|
|
||||||
|
def launch(args):
|
||||||
|
config_dir, kitten = args[:2]
|
||||||
|
kitten = resolved_kitten(kitten)
|
||||||
|
del args[:2]
|
||||||
|
args = [kitten] + args
|
||||||
|
os.environ['KITTY_CONFIG_DIRECTORY'] = config_dir
|
||||||
|
from kittens.tui.operations import clear_screen, reset_mode
|
||||||
|
m = import_kitten_main_module(config_dir, kitten)
|
||||||
|
try:
|
||||||
|
result = m['start'](args)
|
||||||
|
finally:
|
||||||
|
sys.stdin = sys.__stdin__
|
||||||
|
print(reset_mode('ALTERNATE_SCREEN') + clear_screen(), end='')
|
||||||
|
if result is not None:
|
||||||
|
import json
|
||||||
|
data = json.dumps(result)
|
||||||
|
print('OK:', len(data), data)
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize(output):
|
||||||
|
import json
|
||||||
|
if output.startswith('OK: '):
|
||||||
|
prefix, sz, rest = output.split(' ', 2)
|
||||||
|
return json.loads(rest[:int(sz)])
|
||||||
|
|
||||||
|
|
||||||
|
def run_kitten(kitten):
|
||||||
|
import runpy
|
||||||
|
kitten = resolved_kitten(kitten)
|
||||||
|
runpy.run_module('kittens.{}.main'.format(kitten), run_name='__main__')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
args = sys.argv[1:]
|
||||||
|
launch(args)
|
||||||
|
except Exception:
|
||||||
|
print('Unhandled exception running kitten:')
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
input('Press Enter to quit...')
|
||||||
@@ -5,9 +5,23 @@
|
|||||||
|
|
||||||
class Handler:
|
class Handler:
|
||||||
|
|
||||||
def initialize(self, screen_size, quit_loop, wakeup):
|
def _initialize(self, screen_size, quit_loop, wakeup, start_job):
|
||||||
self.screen_size, self.quit_loop = screen_size, quit_loop
|
self.screen_size, self.quit_loop = screen_size, quit_loop
|
||||||
self.wakeup = wakeup
|
self.wakeup = wakeup
|
||||||
|
self.start_job = start_job
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
|
def __exit__(self, *a):
|
||||||
|
del self.write_buf[:]
|
||||||
|
self.finalize()
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def on_resize(self, screen_size):
|
def on_resize(self, screen_size):
|
||||||
self.screen_size = screen_size
|
self.screen_size = screen_size
|
||||||
@@ -30,6 +44,12 @@ class Handler:
|
|||||||
def on_eot(self):
|
def on_eot(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_wakeup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_job_done(self, job_id, job_result):
|
||||||
|
pass
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ import tty
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from contextlib import closing, contextmanager
|
from contextlib import closing, contextmanager
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from queue import Empty, Queue
|
||||||
|
|
||||||
from kitty.fast_data_types import parse_input_from_terminal
|
from kitty.fast_data_types import parse_input_from_terminal, safe_pipe
|
||||||
from kitty.icat import screen_size
|
from kitty.icat import screen_size
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import (
|
||||||
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, C, D, backspace_key,
|
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, C, D, backspace_key,
|
||||||
@@ -24,7 +25,7 @@ from kitty.key_encoding import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .handler import Handler
|
from .handler import Handler
|
||||||
from .operations import init_state, reset_state, clear_screen
|
from .operations import clear_screen, init_state, reset_state
|
||||||
|
|
||||||
|
|
||||||
def log(*a, **kw):
|
def log(*a, **kw):
|
||||||
@@ -120,8 +121,7 @@ class UnhandledException(Handler):
|
|||||||
def __init__(self, tb):
|
def __init__(self, tb):
|
||||||
self.tb = tb
|
self.tb = tb
|
||||||
|
|
||||||
def initialize(self, screen_size, quit_loop, wakeup):
|
def initialize(self):
|
||||||
Handler.initialize(self, screen_size, quit_loop, wakeup)
|
|
||||||
self.write(clear_screen())
|
self.write(clear_screen())
|
||||||
self.write(self.tb.replace('\n', '\r\n'))
|
self.write(self.tb.replace('\n', '\r\n'))
|
||||||
self.write('\r\n')
|
self.write('\r\n')
|
||||||
@@ -142,10 +142,12 @@ class Loop:
|
|||||||
|
|
||||||
def __init__(self, input_fd=None, output_fd=None,
|
def __init__(self, input_fd=None, output_fd=None,
|
||||||
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
|
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
|
||||||
self.input_fd = input_fd or sys.stdin.fileno()
|
self.input_fd = sys.stdin.fileno() if input_fd is None else input_fd
|
||||||
self.output_fd = output_fd or sys.stdout.fileno()
|
self.output_fd = sys.stdout.fileno() if output_fd is None else output_fd
|
||||||
self.wakeup_read_fd, self.wakeup_write_fd = os.pipe()
|
self.wakeup_read_fd, self.wakeup_write_fd = safe_pipe()
|
||||||
self.sel = s = selectors.DefaultSelector()
|
# For some reason on macOS the DefaultSelector fails when input_fd is
|
||||||
|
# open('/dev/tty')
|
||||||
|
self.sel = s = selectors.PollSelector()
|
||||||
s.register(self.input_fd, selectors.EVENT_READ, self._read_ready)
|
s.register(self.input_fd, selectors.EVENT_READ, self._read_ready)
|
||||||
s.register(
|
s.register(
|
||||||
self.wakeup_read_fd, selectors.EVENT_READ, self._wakeup_ready
|
self.wakeup_read_fd, selectors.EVENT_READ, self._wakeup_ready
|
||||||
@@ -165,11 +167,32 @@ class Loop:
|
|||||||
self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)
|
self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)
|
||||||
if self.sanitize_bracketed_paste:
|
if self.sanitize_bracketed_paste:
|
||||||
self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)
|
self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)
|
||||||
|
self.jobs_queue = Queue()
|
||||||
|
|
||||||
|
def start_job(self, job_id, func, *args, **kw):
|
||||||
|
from threading import Thread
|
||||||
|
t = Thread(target=partial(self._run_job, job_id, func), args=args, kwargs=kw, name='LoopJob')
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
def _run_job(self, job_id, func, *args, **kw):
|
||||||
|
try:
|
||||||
|
result = func(*args, **kw)
|
||||||
|
except Exception as err:
|
||||||
|
import traceback
|
||||||
|
entry = {'id': job_id, 'exception': err, 'tb': traceback.format_exc()}
|
||||||
|
else:
|
||||||
|
entry = {'id': job_id, 'result': result}
|
||||||
|
self.jobs_queue.put(entry)
|
||||||
|
self._wakeup_write(b'j')
|
||||||
|
|
||||||
def _read_ready(self, handler):
|
def _read_ready(self, handler):
|
||||||
if not self.read_allowed:
|
if not self.read_allowed:
|
||||||
return
|
return
|
||||||
data = os.read(self.input_fd, io.DEFAULT_BUFFER_SIZE)
|
try:
|
||||||
|
data = os.read(self.input_fd, io.DEFAULT_BUFFER_SIZE)
|
||||||
|
except BlockingIOError:
|
||||||
|
return
|
||||||
if not data:
|
if not data:
|
||||||
raise EOFError('The input stream is closed')
|
raise EOFError('The input stream is closed')
|
||||||
data = self.decoder.decode(data)
|
data = self.decoder.decode(data)
|
||||||
@@ -278,6 +301,17 @@ class Loop:
|
|||||||
handler.on_term()
|
handler.on_term()
|
||||||
if b'i' in data:
|
if b'i' in data:
|
||||||
handler.on_interrupt()
|
handler.on_interrupt()
|
||||||
|
if b'j' in data:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
entry = self.jobs_queue.get_nowait()
|
||||||
|
except Empty:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
job_id = entry.pop('id')
|
||||||
|
handler.on_job_done(job_id, entry)
|
||||||
|
if b'1' in data:
|
||||||
|
handler.on_wakeup()
|
||||||
|
|
||||||
def _wakeup_write(self, val):
|
def _wakeup_write(self, val):
|
||||||
while not os.write(self.wakeup_write_fd, val):
|
while not os.write(self.wakeup_write_fd, val):
|
||||||
@@ -324,13 +358,40 @@ class Loop:
|
|||||||
handler._term_manager = term_manager
|
handler._term_manager = term_manager
|
||||||
keep_going = True
|
keep_going = True
|
||||||
try:
|
try:
|
||||||
handler.initialize(screen_size(), self.quit, self.wakeup)
|
handler._initialize(screen_size(), self.quit, self.wakeup, self.start_job)
|
||||||
|
with handler:
|
||||||
|
while keep_going:
|
||||||
|
has_data_to_write = bool(handler.write_buf)
|
||||||
|
if not has_data_to_write and not self.read_allowed:
|
||||||
|
break
|
||||||
|
if has_data_to_write != waiting_for_write:
|
||||||
|
waiting_for_write = has_data_to_write
|
||||||
|
self._modify_output_selector(waiting_for_write)
|
||||||
|
events = select()
|
||||||
|
for key, mask in events:
|
||||||
|
key.data(handler)
|
||||||
except Exception:
|
except Exception:
|
||||||
import traceback
|
import traceback
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
self.return_code = 1
|
self.return_code = 1
|
||||||
keep_going = False
|
keep_going = False
|
||||||
while keep_going:
|
|
||||||
|
finalize_output = b''.join(handler.write_buf).decode('utf-8')
|
||||||
|
|
||||||
|
if tb is not None:
|
||||||
|
self._report_error_loop(finalize_output + tb, term_manager)
|
||||||
|
if tb is None:
|
||||||
|
os.write(self.output_fd, finalize_output.encode('utf-8'))
|
||||||
|
|
||||||
|
def _report_error_loop(self, tb, term_manager):
|
||||||
|
select = self.sel.select
|
||||||
|
waiting_for_write = False
|
||||||
|
handler = UnhandledException(tb)
|
||||||
|
handler.write_buf = []
|
||||||
|
handler._term_manager = term_manager
|
||||||
|
handler._initialize(screen_size(), self.quit, self.wakeup, self.start_job)
|
||||||
|
with handler:
|
||||||
|
while True:
|
||||||
has_data_to_write = bool(handler.write_buf)
|
has_data_to_write = bool(handler.write_buf)
|
||||||
if not has_data_to_write and not self.read_allowed:
|
if not has_data_to_write and not self.read_allowed:
|
||||||
break
|
break
|
||||||
@@ -339,31 +400,4 @@ class Loop:
|
|||||||
self._modify_output_selector(waiting_for_write)
|
self._modify_output_selector(waiting_for_write)
|
||||||
events = select()
|
events = select()
|
||||||
for key, mask in events:
|
for key, mask in events:
|
||||||
try:
|
key.data(handler)
|
||||||
key.data(handler)
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
self.return_code = 1
|
|
||||||
keep_going = False
|
|
||||||
break
|
|
||||||
if tb is not None:
|
|
||||||
self._report_error_loop(tb, term_manager)
|
|
||||||
|
|
||||||
def _report_error_loop(self, tb, term_manager):
|
|
||||||
select = self.sel.select
|
|
||||||
waiting_for_write = False
|
|
||||||
handler = UnhandledException(tb)
|
|
||||||
handler.write_buf = []
|
|
||||||
handler._term_manager = term_manager
|
|
||||||
handler.initialize(screen_size(), self.quit, self.wakeup)
|
|
||||||
while True:
|
|
||||||
has_data_to_write = bool(handler.write_buf)
|
|
||||||
if not has_data_to_write and not self.read_allowed:
|
|
||||||
break
|
|
||||||
if has_data_to_write != waiting_for_write:
|
|
||||||
waiting_for_write = has_data_to_write
|
|
||||||
self._modify_output_selector(waiting_for_write)
|
|
||||||
events = select()
|
|
||||||
for key, mask in events:
|
|
||||||
key.data(handler)
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from kitty.rgb import Color, color_as_sharp, to_color
|
||||||
from kitty.terminfo import string_capabilities
|
from kitty.terminfo import string_capabilities
|
||||||
|
|
||||||
S7C1T = '\033 F'
|
S7C1T = '\033 F'
|
||||||
@@ -151,3 +153,24 @@ def cursor(write):
|
|||||||
write(SAVE_CURSOR)
|
write(SAVE_CURSOR)
|
||||||
yield
|
yield
|
||||||
write(RESTORE_CURSOR)
|
write(RESTORE_CURSOR)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def alternate_screen(f=None):
|
||||||
|
f = f or sys.stdout
|
||||||
|
print(set_mode('ALTERNATE_SCREEN'), end='', file=f)
|
||||||
|
yield
|
||||||
|
print(reset_mode('ALTERNATE_SCREEN'), end='', file=f)
|
||||||
|
|
||||||
|
|
||||||
|
def set_default_colors(fg=None, bg=None):
|
||||||
|
ans = ''
|
||||||
|
if fg is None:
|
||||||
|
ans += '\x1b]110\x1b\\'
|
||||||
|
else:
|
||||||
|
ans += '\x1b]10;{}\x1b\\'.format(color_as_sharp(fg if isinstance(fg, Color) else to_color(fg)))
|
||||||
|
if bg is None:
|
||||||
|
ans += '\x1b]111\x1b\\'
|
||||||
|
else:
|
||||||
|
ans += '\x1b]11;{}\x1b\\'.format(color_as_sharp(bg if isinstance(bg, Color) else to_color(bg)))
|
||||||
|
return ans
|
||||||
|
|||||||
@@ -3,15 +3,13 @@
|
|||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shlex
|
|
||||||
import string
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from kitty.config import cached_values_for
|
from kitty.config import cached_values_for
|
||||||
from kitty.constants import config_dir
|
from kitty.constants import config_dir, editor
|
||||||
from kitty.fast_data_types import wcswidth
|
from kitty.fast_data_types import wcswidth
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import (
|
||||||
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
|
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
|
||||||
@@ -44,7 +42,7 @@ def codepoint_ok(code):
|
|||||||
@lru_cache(maxsize=256)
|
@lru_cache(maxsize=256)
|
||||||
def points_for_word(w):
|
def points_for_word(w):
|
||||||
from .unicode_names import codepoints_for_word
|
from .unicode_names import codepoints_for_word
|
||||||
return codepoints_for_word(w)
|
return codepoints_for_word(w.lower())
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=4096)
|
@lru_cache(maxsize=4096)
|
||||||
@@ -58,7 +56,7 @@ def name(cp):
|
|||||||
@lru_cache(maxsize=256)
|
@lru_cache(maxsize=256)
|
||||||
def codepoints_matching_search(parts):
|
def codepoints_matching_search(parts):
|
||||||
ans = []
|
ans = []
|
||||||
if parts and parts[0]:
|
if parts and parts[0] and len(parts[0]) > 1:
|
||||||
codepoints = points_for_word(parts[0])
|
codepoints = points_for_word(parts[0])
|
||||||
for word in parts[1:]:
|
for word in parts[1:]:
|
||||||
pts = points_for_word(word)
|
pts = points_for_word(word)
|
||||||
@@ -207,7 +205,7 @@ class Table:
|
|||||||
col_width = longest + 2
|
col_width = longest + 2
|
||||||
col_width = min(col_width, 40)
|
col_width = min(col_width, 40)
|
||||||
space_for_desc = col_width - 2 - idx_size - 4
|
space_for_desc = col_width - 2 - idx_size - 4
|
||||||
num_cols = self.num_cols = cols // col_width
|
num_cols = self.num_cols = max(cols // col_width, 1)
|
||||||
buf = []
|
buf = []
|
||||||
a = buf.append
|
a = buf.append
|
||||||
rows_left = rows
|
rows_left = rows
|
||||||
@@ -224,6 +222,8 @@ class Table:
|
|||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
def move_current(self, rows=0, cols=0):
|
def move_current(self, rows=0, cols=0):
|
||||||
|
if len(self.codepoints) == 0:
|
||||||
|
return
|
||||||
if cols:
|
if cols:
|
||||||
self.current_idx = (self.current_idx + len(self.codepoints) + cols) % len(self.codepoints)
|
self.current_idx = (self.current_idx + len(self.codepoints) + cols) % len(self.codepoints)
|
||||||
self.layout_dirty = True
|
self.layout_dirty = True
|
||||||
@@ -327,8 +327,7 @@ class UnicodeInput(Handler):
|
|||||||
self.write(set_line_wrapping(False))
|
self.write(set_line_wrapping(False))
|
||||||
self.write(set_window_title(_('Unicode input')))
|
self.write(set_window_title(_('Unicode input')))
|
||||||
|
|
||||||
def initialize(self, *args):
|
def initialize(self):
|
||||||
Handler.initialize(self, *args)
|
|
||||||
self.init_terminal_state()
|
self.init_terminal_state()
|
||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
|
|
||||||
@@ -361,9 +360,9 @@ class UnicodeInput(Handler):
|
|||||||
y += 1
|
y += 1
|
||||||
|
|
||||||
if self.mode is NAME:
|
if self.mode is NAME:
|
||||||
writeln(_('Enter the hex code for the character'))
|
|
||||||
elif self.mode is HEX:
|
|
||||||
writeln(_('Enter words from the name of the character'))
|
writeln(_('Enter words from the name of the character'))
|
||||||
|
elif self.mode is HEX:
|
||||||
|
writeln(_('Enter the hex code for the character'))
|
||||||
else:
|
else:
|
||||||
writeln(_('Enter the index for the character you want from the list below'))
|
writeln(_('Enter the index for the character you want from the list below'))
|
||||||
self.write(self.prompt)
|
self.write(self.prompt)
|
||||||
@@ -426,7 +425,6 @@ class UnicodeInput(Handler):
|
|||||||
if not os.path.exists(favorites_path):
|
if not os.path.exists(favorites_path):
|
||||||
with open(favorites_path, 'wb') as f:
|
with open(favorites_path, 'wb') as f:
|
||||||
f.write(serialize_favorites(load_favorites()).encode('utf-8'))
|
f.write(serialize_favorites(load_favorites()).encode('utf-8'))
|
||||||
editor = shlex.split(os.environ.get('EDITOR', 'vim'))
|
|
||||||
with self.suspend():
|
with self.suspend():
|
||||||
p = subprocess.Popen(editor + [favorites_path])
|
p = subprocess.Popen(editor + [favorites_path])
|
||||||
if p.wait() == 0:
|
if p.wait() == 0:
|
||||||
@@ -454,17 +452,34 @@ class UnicodeInput(Handler):
|
|||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args):
|
||||||
loop = Loop()
|
loop = Loop()
|
||||||
with cached_values_for('unicode-input') as cached_values:
|
with cached_values_for('unicode-input') as cached_values:
|
||||||
handler = UnicodeInput(cached_values)
|
handler = UnicodeInput(cached_values)
|
||||||
loop.loop(handler)
|
loop.loop(handler)
|
||||||
if handler.current_char and loop.return_code == 0:
|
if handler.current_char and loop.return_code == 0:
|
||||||
print('OK:', hex(ord(handler.current_char))[2:])
|
|
||||||
try:
|
try:
|
||||||
handler.recent.remove(ord(handler.current_char))
|
handler.recent.remove(ord(handler.current_char))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
recent = [ord(handler.current_char)] + handler.recent
|
recent = [ord(handler.current_char)] + handler.recent
|
||||||
cached_values['recent'] = recent[:len(DEFAULT_SET)]
|
cached_values['recent'] = recent[:len(DEFAULT_SET)]
|
||||||
raise SystemExit(loop.return_code)
|
return handler.current_char
|
||||||
|
if loop.return_code != 0:
|
||||||
|
raise SystemExit(loop.return_code)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_result(args, current_char, target_window_id, boss):
|
||||||
|
w = boss.window_id_map.get(target_window_id)
|
||||||
|
if w is not None:
|
||||||
|
w.paste(current_char)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|||||||
64558
kittens/unicode_input/names.h
generated
64558
kittens/unicode_input/names.h
generated
File diff suppressed because one or more lines are too long
@@ -8,33 +8,62 @@
|
|||||||
#include "names.h"
|
#include "names.h"
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
all_words(PyObject *self UNUSED) {
|
all_words(PYNOARG) {
|
||||||
PyObject *ans = PyTuple_New(arraysz(idx_to_word));
|
PyObject *ans = PyTuple_New(arraysz(all_words_map));
|
||||||
if (!ans) return NULL;
|
if (!ans) return NULL;
|
||||||
for (size_t i = 0; i < arraysz(idx_to_word); i++) {
|
for (size_t i = 0; i < arraysz(all_words_map); i++) {
|
||||||
PyObject *w = PyUnicode_FromString(idx_to_word[i]);
|
PyObject *w = PyUnicode_FromString(all_words_map[i]);
|
||||||
if (w == NULL) { Py_DECREF(ans); return NULL; }
|
if (w == NULL) { Py_DECREF(ans); return NULL; }
|
||||||
PyTuple_SET_ITEM(ans, i, w);
|
PyTuple_SET_ITEM(ans, i, w);
|
||||||
}
|
}
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
add_matches(const word_trie *wt, char_type *codepoints, size_t *pos, const size_t sz) {
|
||||||
|
size_t num = mark_groups[wt->match_offset];
|
||||||
|
for (size_t i = wt->match_offset + 1; i < wt->match_offset + 1 + num && *pos < sz; i++, (*pos)++) {
|
||||||
|
codepoints[*pos] = mark_to_cp[mark_groups[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_trie_node(const word_trie *wt, char_type *codepoints, size_t *pos, const size_t sz) {
|
||||||
|
if (wt->match_offset) add_matches(wt, codepoints, pos, sz);
|
||||||
|
size_t num_children = children_array[wt->children_offset];
|
||||||
|
if (!num_children) return;
|
||||||
|
for (size_t c = wt->children_offset + 1; c < wt->children_offset + 1 + num_children; c++) {
|
||||||
|
if (*pos > sz) return;
|
||||||
|
uint32_t x = children_array[c];
|
||||||
|
process_trie_node(&all_trie_nodes[x >> 8], codepoints, pos, sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline PyObject*
|
static inline PyObject*
|
||||||
codepoints_for_word(const char *word, size_t len) {
|
codepoints_for_word(const char *word, size_t len) {
|
||||||
PyObject *ans = PyFrozenSet_New(NULL); if (ans == NULL) return NULL;
|
const word_trie *wt = all_trie_nodes;
|
||||||
const unsigned short *words = words_for_first_letter[(unsigned)*word];
|
for (size_t i = 0; i < len; i++) {
|
||||||
if (words == NULL) return ans;
|
unsigned char ch = word[i];
|
||||||
for (unsigned short i = 1; i <= words[0]; i++) {
|
size_t num_children = children_array[wt->children_offset];
|
||||||
unsigned short word_idx = words[i];
|
if (!num_children) return PyFrozenSet_New(NULL);
|
||||||
const char *w = idx_to_word[word_idx];
|
bool found = false;
|
||||||
if (strncmp(word, w, len) == 0 && strlen(w) == len) {
|
for (size_t c = wt->children_offset + 1; c < wt->children_offset + 1 + num_children; c++) {
|
||||||
const char_type* codepoints = codepoints_for_word_idx[word_idx];
|
uint32_t x = children_array[c];
|
||||||
for (char_type i = 1; i <= codepoints[0]; i++) {
|
if ((x & 0xff) == ch) {
|
||||||
PyObject *t = PyLong_FromUnsignedLong(codepoints[i]); if (t == NULL) { Py_DECREF(ans); return NULL; }
|
found = true;
|
||||||
int ret = PySet_Add(ans, t); Py_DECREF(t); if (ret != 0) { Py_DECREF(ans); return NULL; }
|
wt = &all_trie_nodes[x >> 8];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (!found) return PyFrozenSet_New(NULL);
|
||||||
|
}
|
||||||
|
static char_type codepoints[1024];
|
||||||
|
size_t cpos = 0;
|
||||||
|
process_trie_node(wt, codepoints, &cpos, arraysz(codepoints));
|
||||||
|
PyObject *ans = PyFrozenSet_New(NULL); if (ans == NULL) return NULL;
|
||||||
|
for (size_t i = 0; i < cpos; i++) {
|
||||||
|
PyObject *t = PyLong_FromUnsignedLong(codepoints[i]); if (t == NULL) { Py_DECREF(ans); return NULL; }
|
||||||
|
int ret = PySet_Add(ans, t); Py_DECREF(t); if (ret != 0) { Py_DECREF(ans); return NULL; }
|
||||||
}
|
}
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
@@ -56,12 +85,13 @@ nfc(PyObject *self UNUSED, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
METHODB(all_words, METH_NOARGS),
|
{"all_words", (PyCFunction)all_words, METH_NOARGS, ""},
|
||||||
{"codepoints_for_word", (PyCFunction)cfw, METH_VARARGS, ""},
|
{"codepoints_for_word", (PyCFunction)cfw, METH_VARARGS, ""},
|
||||||
{"name_for_codepoint", (PyCFunction)nfc, METH_VARARGS, ""},
|
{"name_for_codepoint", (PyCFunction)nfc, METH_VARARGS, ""},
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX >= 0x03000000
|
||||||
static struct PyModuleDef module = {
|
static struct PyModuleDef module = {
|
||||||
.m_base = PyModuleDef_HEAD_INIT,
|
.m_base = PyModuleDef_HEAD_INIT,
|
||||||
.m_name = "unicode_names", /* name of module */
|
.m_name = "unicode_names", /* name of module */
|
||||||
@@ -79,3 +109,13 @@ PyInit_unicode_names(void) {
|
|||||||
if (m == NULL) return NULL;
|
if (m == NULL) return NULL;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
EXPORTED
|
||||||
|
initunicode_names(void) {
|
||||||
|
PyObject *m;
|
||||||
|
m = Py_InitModule3("unicode_names", module_methods,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
if (m == NULL) return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# vim:fileencoding=utf-8
|
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
|
||||||
|
|
||||||
import re
|
|
||||||
import string
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from collections import namedtuple
|
|
||||||
from functools import lru_cache, partial
|
|
||||||
from gettext import gettext as _
|
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
|
||||||
from kitty.key_encoding import ESCAPE, backspace_key, enter_key
|
|
||||||
from kitty.utils import command_for_open
|
|
||||||
|
|
||||||
from ..tui.handler import Handler
|
|
||||||
from ..tui.loop import Loop
|
|
||||||
from ..tui.operations import (
|
|
||||||
clear_screen, faint, set_cursor_visible, set_window_title, styled
|
|
||||||
)
|
|
||||||
|
|
||||||
Mark = namedtuple('Mark', 'index start end text')
|
|
||||||
URL_PREFIXES = 'http https file ftp'.split()
|
|
||||||
HINT_ALPHABET = string.digits + string.ascii_lowercase
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=2048)
|
|
||||||
def encode_hint(num):
|
|
||||||
res = ''
|
|
||||||
d = len(HINT_ALPHABET)
|
|
||||||
while not res or num > 0:
|
|
||||||
num, i = divmod(num, d)
|
|
||||||
res = HINT_ALPHABET[i] + res
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def decode_hint(x):
|
|
||||||
return int(x, 36)
|
|
||||||
|
|
||||||
|
|
||||||
def render(lines, current_input):
|
|
||||||
ans = []
|
|
||||||
|
|
||||||
def mark(m):
|
|
||||||
hint = encode_hint(m.index)
|
|
||||||
text = m.text
|
|
||||||
if current_input and not hint.startswith(current_input):
|
|
||||||
return faint(text)
|
|
||||||
hint = hint[len(current_input):] or ' '
|
|
||||||
text = text[len(hint):]
|
|
||||||
return styled(
|
|
||||||
hint,
|
|
||||||
fg='black',
|
|
||||||
bg='green',
|
|
||||||
bold=True
|
|
||||||
) + styled(
|
|
||||||
text, fg='gray', fg_intense=True, bold=True
|
|
||||||
)
|
|
||||||
|
|
||||||
for line, marks in lines:
|
|
||||||
if not marks:
|
|
||||||
ans.append(faint(line))
|
|
||||||
continue
|
|
||||||
buf = []
|
|
||||||
|
|
||||||
for i, m in enumerate(marks):
|
|
||||||
if i == 0 and m.start:
|
|
||||||
buf.append(faint(line[:m.start]))
|
|
||||||
buf.append(mark(m))
|
|
||||||
if m is not marks[-1]:
|
|
||||||
buf.append(faint(line[m.end:marks[i + 1].start]))
|
|
||||||
|
|
||||||
rest = line[marks[-1].end:]
|
|
||||||
if rest:
|
|
||||||
buf.append(faint(rest))
|
|
||||||
|
|
||||||
ans.append(''.join(buf))
|
|
||||||
return '\r\n'.join(ans)
|
|
||||||
|
|
||||||
|
|
||||||
class URLHints(Handler):
|
|
||||||
|
|
||||||
def __init__(self, lines, index_map):
|
|
||||||
self.lines, self.index_map = tuple(lines), index_map
|
|
||||||
self.current_input = ''
|
|
||||||
self.current_text = None
|
|
||||||
self.chosen = None
|
|
||||||
|
|
||||||
def init_terminal_state(self):
|
|
||||||
self.write(set_cursor_visible(False))
|
|
||||||
self.write(set_window_title(_('Choose URL')))
|
|
||||||
|
|
||||||
def initialize(self, *args):
|
|
||||||
Handler.initialize(self, *args)
|
|
||||||
self.init_terminal_state()
|
|
||||||
self.draw_screen()
|
|
||||||
|
|
||||||
def on_text(self, text, in_bracketed_paste):
|
|
||||||
changed = False
|
|
||||||
for c in text:
|
|
||||||
if c in HINT_ALPHABET:
|
|
||||||
self.current_input += c
|
|
||||||
changed = True
|
|
||||||
if changed:
|
|
||||||
matches = [
|
|
||||||
t for idx, t in self.index_map.items()
|
|
||||||
if encode_hint(idx).startswith(self.current_input)
|
|
||||||
]
|
|
||||||
if len(matches) == 1:
|
|
||||||
self.chosen = matches[0]
|
|
||||||
self.quit_loop(0)
|
|
||||||
return
|
|
||||||
self.current_text = None
|
|
||||||
self.draw_screen()
|
|
||||||
|
|
||||||
def on_key(self, key_event):
|
|
||||||
if key_event is backspace_key:
|
|
||||||
self.current_input = self.current_input[:-1]
|
|
||||||
self.current_text = None
|
|
||||||
self.draw_screen()
|
|
||||||
elif key_event is enter_key and self.current_input:
|
|
||||||
idx = decode_hint(self.current_input)
|
|
||||||
self.chosen = self.index_map[idx]
|
|
||||||
self.quit_loop(0)
|
|
||||||
elif key_event.key is ESCAPE:
|
|
||||||
self.quit_loop(1)
|
|
||||||
|
|
||||||
def on_interrupt(self):
|
|
||||||
self.quit_loop(1)
|
|
||||||
|
|
||||||
def on_eot(self):
|
|
||||||
self.quit_loop(1)
|
|
||||||
|
|
||||||
def on_resize(self, new_size):
|
|
||||||
Handler.on_resize(self, new_size)
|
|
||||||
self.draw_screen()
|
|
||||||
|
|
||||||
def draw_screen(self):
|
|
||||||
if self.current_text is None:
|
|
||||||
self.current_text = render(self.lines, self.current_input)
|
|
||||||
self.write(clear_screen())
|
|
||||||
self.write(self.current_text)
|
|
||||||
|
|
||||||
|
|
||||||
def regex_finditer(pat, line):
|
|
||||||
for m in pat.finditer(line):
|
|
||||||
s, e = m.span()
|
|
||||||
if e - s > 2:
|
|
||||||
yield s, e
|
|
||||||
|
|
||||||
|
|
||||||
def find_urls(pat, line):
|
|
||||||
for m in pat.finditer(line):
|
|
||||||
s, e = m.span()
|
|
||||||
if s > 4 and line[s - 5:s] == 'link:': # asciidoc URLs
|
|
||||||
url = line[s:e]
|
|
||||||
idx = url.rfind('[')
|
|
||||||
if idx > -1:
|
|
||||||
e -= len(url) - idx
|
|
||||||
yield s, e
|
|
||||||
|
|
||||||
|
|
||||||
def mark(finditer, line, index_map):
|
|
||||||
marks = []
|
|
||||||
for s, e in finditer(line):
|
|
||||||
idx = len(index_map)
|
|
||||||
text = line[s:e]
|
|
||||||
marks.append(Mark(idx, s, e, text))
|
|
||||||
index_map[idx] = text
|
|
||||||
return line, marks
|
|
||||||
|
|
||||||
|
|
||||||
def run(args, source_file=None):
|
|
||||||
if source_file is None:
|
|
||||||
text = sys.stdin.buffer.read().decode('utf-8')
|
|
||||||
sys.stdin = open('/dev/tty')
|
|
||||||
else:
|
|
||||||
with open(source_file, 'r') as f:
|
|
||||||
text = f.read()
|
|
||||||
if args.regex is None:
|
|
||||||
from .url_regex import url_delimiters
|
|
||||||
url_pat = '(?:{})://[^{}]{{3,}}'.format(
|
|
||||||
'|'.join(args.url_prefixes.split(',')), url_delimiters
|
|
||||||
)
|
|
||||||
finditer = partial(find_urls, re.compile(url_pat))
|
|
||||||
else:
|
|
||||||
finditer = partial(regex_finditer, re.compile(args.regex))
|
|
||||||
lines = []
|
|
||||||
index_map = {}
|
|
||||||
for line in text.splitlines():
|
|
||||||
marked = mark(finditer, line, index_map)
|
|
||||||
lines.append(marked)
|
|
||||||
if not index_map:
|
|
||||||
input(_('No URLs found, press Enter to abort.'))
|
|
||||||
return
|
|
||||||
|
|
||||||
loop = Loop()
|
|
||||||
handler = URLHints(lines, index_map)
|
|
||||||
loop.loop(handler)
|
|
||||||
if handler.chosen and loop.return_code == 0:
|
|
||||||
cmd = command_for_open(args.program)
|
|
||||||
ret = subprocess.Popen(cmd + [handler.chosen]).wait()
|
|
||||||
if ret != 0:
|
|
||||||
print('URL handler "{}" failed with return code: {}'.format(' '.join(cmd), ret), file=sys.stderr)
|
|
||||||
input('Press Enter to quit')
|
|
||||||
loop.return_code = ret
|
|
||||||
raise SystemExit(loop.return_code)
|
|
||||||
|
|
||||||
|
|
||||||
OPTIONS = partial('''\
|
|
||||||
--program
|
|
||||||
default=default
|
|
||||||
What program to use to open matched URLs. Defaults
|
|
||||||
to the default URL open program for the operating system.
|
|
||||||
|
|
||||||
|
|
||||||
--regex
|
|
||||||
Instead of searching for URLs search for the specified regular
|
|
||||||
expression instead.
|
|
||||||
|
|
||||||
|
|
||||||
--url-prefixes
|
|
||||||
default={0}
|
|
||||||
Comma separeted list of recognized URL prefixes. Defaults to:
|
|
||||||
{0}
|
|
||||||
'''.format, ','.join(sorted(URL_PREFIXES)))
|
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
|
||||||
msg = 'Highlight URLs inside the specified text'
|
|
||||||
try:
|
|
||||||
args, items = parse_args(args[1:], OPTIONS, '[path to file or omit to use stdin]', msg, 'url_hints')
|
|
||||||
except SystemExit as e:
|
|
||||||
print(e.args[0], file=sys.stderr)
|
|
||||||
input('Press enter to quit...')
|
|
||||||
return 1
|
|
||||||
run(args, (items or [None])[0])
|
|
||||||
@@ -3,6 +3,7 @@ uniform uvec2 viewport;
|
|||||||
uniform vec3 default_bg;
|
uniform vec3 default_bg;
|
||||||
uniform vec3 active_border_color;
|
uniform vec3 active_border_color;
|
||||||
uniform vec3 inactive_border_color;
|
uniform vec3 inactive_border_color;
|
||||||
|
uniform vec3 bell_border_color;
|
||||||
in uvec4 rect; // left, top, right, bottom
|
in uvec4 rect; // left, top, right, bottom
|
||||||
in uint rect_color;
|
in uint rect_color;
|
||||||
out vec3 color;
|
out vec3 color;
|
||||||
@@ -37,5 +38,5 @@ void main() {
|
|||||||
gl_Position = vec4(to_opengl(rect[pos.x], rect[pos.y]), 0, 1);
|
gl_Position = vec4(to_opengl(rect[pos.x], rect[pos.y]), 0, 1);
|
||||||
int rc = int(rect_color);
|
int rc = int(rect_color);
|
||||||
vec3 window_bg = vec3(to_color(rect_color >> 24), to_color(rect_color >> 16), to_color(rect_color >> 8));
|
vec3 window_bg = vec3(to_color(rect_color >> 24), to_color(rect_color >> 16), to_color(rect_color >> 8));
|
||||||
color = float(1 & rc) * default_bg + float((2 & rc) >> 1) * active_border_color + float((4 & rc) >> 2) * inactive_border_color + float((8 & rc) >> 3) * window_bg;
|
color = float(1 & rc) * default_bg + float((2 & rc) >> 1) * active_border_color + float((4 & rc) >> 2) * inactive_border_color + float((8 & rc) >> 3) * window_bg + float((16 & rc) >> 4) * bell_border_color;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class Borders:
|
|||||||
g = w.geometry
|
g = w.geometry
|
||||||
if bw > 0 and draw_window_borders:
|
if bw > 0 and draw_window_borders:
|
||||||
# Draw the border rectangles
|
# Draw the border rectangles
|
||||||
color = 2 if w is active_window else 4
|
color = 2 if w is active_window else (16 if w.needs_attention else 4)
|
||||||
border(
|
border(
|
||||||
self.os_window_id, self.tab_id,
|
self.os_window_id, self.tab_id,
|
||||||
color, bw, g.left - fw, g.top - fw, g.right + fw,
|
color, bw, g.left - fw, g.top - fw, g.right + fw,
|
||||||
|
|||||||
341
kitty/boss.py
341
kitty/boss.py
@@ -2,29 +2,37 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from weakref import WeakValueDictionary
|
from weakref import WeakValueDictionary
|
||||||
|
|
||||||
from .cli import create_opts, parse_args
|
from .cli import create_opts, parse_args
|
||||||
from .config import MINIMUM_FONT_SIZE, initial_window_size
|
from .config import (
|
||||||
from .constants import appname, set_boss, wakeup
|
MINIMUM_FONT_SIZE, initial_window_size, prepare_config_file_for_editing
|
||||||
|
)
|
||||||
|
from .config_utils import to_cmdline
|
||||||
|
from .constants import appname, config_dir, editor, set_boss
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
ChildMonitor, create_os_window, current_os_window, destroy_global_data,
|
ChildMonitor, create_os_window, current_os_window, destroy_global_data,
|
||||||
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
|
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
|
||||||
layout_sprite_map, mark_os_window_for_close, set_clipboard_string,
|
layout_sprite_map, mark_os_window_for_close, set_clipboard_string,
|
||||||
set_dpi_from_os_window, show_window, toggle_fullscreen,
|
set_dpi_from_os_window, set_in_sequence_mode, show_window,
|
||||||
viewport_for_window
|
toggle_fullscreen, viewport_for_window
|
||||||
)
|
)
|
||||||
from .fonts.render import prerender, resize_fonts, set_font_family
|
from .fonts.render import prerender, resize_fonts, set_font_family
|
||||||
from .keys import get_shortcut
|
from .keys import get_shortcut, shortcut_matches
|
||||||
from .remote_control import handle_cmd
|
from .remote_control import handle_cmd
|
||||||
|
from .rgb import Color, color_from_int
|
||||||
from .session import create_session
|
from .session import create_session
|
||||||
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
|
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
|
||||||
from .utils import (
|
from .utils import (
|
||||||
end_startup_notification, get_primary_selection, init_startup_notification,
|
end_startup_notification, get_primary_selection, init_startup_notification,
|
||||||
open_url, safe_print, set_primary_selection, single_instance
|
log_error, open_url, parse_address_spec, remove_socket_file, safe_print,
|
||||||
|
set_primary_selection, single_instance
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -33,6 +41,16 @@ def initialize_renderer():
|
|||||||
prerender()
|
prerender()
|
||||||
|
|
||||||
|
|
||||||
|
def listen_on(spec):
|
||||||
|
import socket
|
||||||
|
family, address, socket_path = parse_address_spec(spec)
|
||||||
|
s = socket.socket(family)
|
||||||
|
atexit.register(remove_socket_file, s, socket_path)
|
||||||
|
s.bind(address)
|
||||||
|
s.listen()
|
||||||
|
return s.fileno()
|
||||||
|
|
||||||
|
|
||||||
class DumpCommands: # {{{
|
class DumpCommands: # {{{
|
||||||
|
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
@@ -64,16 +82,21 @@ class Boss:
|
|||||||
|
|
||||||
def __init__(self, os_window_id, opts, args, cached_values):
|
def __init__(self, os_window_id, opts, args, cached_values):
|
||||||
self.window_id_map = WeakValueDictionary()
|
self.window_id_map = WeakValueDictionary()
|
||||||
|
self.startup_colors = {k: opts[k] for k in opts if isinstance(opts[k], Color)}
|
||||||
|
self.pending_sequences = None
|
||||||
self.cached_values = cached_values
|
self.cached_values = cached_values
|
||||||
self.os_window_map = {}
|
self.os_window_map = {}
|
||||||
self.cursor_blinking = True
|
self.cursor_blinking = True
|
||||||
self.shutting_down = False
|
self.shutting_down = False
|
||||||
talk_fd = getattr(single_instance, 'socket', None)
|
talk_fd = getattr(single_instance, 'socket', None)
|
||||||
talk_fd = -1 if talk_fd is None else talk_fd.fileno()
|
talk_fd = -1 if talk_fd is None else talk_fd.fileno()
|
||||||
|
listen_fd = -1
|
||||||
|
if opts.allow_remote_control and args.listen_on:
|
||||||
|
listen_fd = listen_on(args.listen_on)
|
||||||
self.child_monitor = ChildMonitor(
|
self.child_monitor = ChildMonitor(
|
||||||
self.on_child_death,
|
self.on_child_death,
|
||||||
DumpCommands(args) if args.dump_commands or args.dump_bytes else None,
|
DumpCommands(args) if args.dump_commands or args.dump_bytes else None,
|
||||||
talk_fd
|
talk_fd, listen_fd
|
||||||
)
|
)
|
||||||
set_boss(self)
|
set_boss(self)
|
||||||
self.current_font_size = opts.font_size
|
self.current_font_size = opts.font_size
|
||||||
@@ -98,6 +121,7 @@ class Boss:
|
|||||||
self.os_window_map[os_window_id] = tm
|
self.os_window_map[os_window_id] = tm
|
||||||
if dpi_changed:
|
if dpi_changed:
|
||||||
self.on_dpi_change(os_window_id)
|
self.on_dpi_change(os_window_id)
|
||||||
|
return os_window_id
|
||||||
|
|
||||||
def list_os_windows(self):
|
def list_os_windows(self):
|
||||||
for os_window_id, tm in self.os_window_map.items():
|
for os_window_id, tm in self.os_window_map.items():
|
||||||
@@ -106,33 +130,58 @@ class Boss:
|
|||||||
'tabs': list(tm.list_tabs()),
|
'tabs': list(tm.list_tabs()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_tab_managers(self):
|
||||||
|
yield from self.os_window_map.values()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_tabs(self):
|
||||||
|
for tm in self.all_tab_managers:
|
||||||
|
yield from tm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_windows(self):
|
||||||
|
for tab in self.all_tabs:
|
||||||
|
yield from tab
|
||||||
|
|
||||||
def match_windows(self, match):
|
def match_windows(self, match):
|
||||||
field, exp = match.split(':', 1)
|
try:
|
||||||
pat = re.compile(exp)
|
field, exp = match.split(':', 1)
|
||||||
for tm in self.os_window_map.values():
|
except ValueError:
|
||||||
for tab in tm:
|
return
|
||||||
for window in tab:
|
if field == 'num':
|
||||||
if window.matches(field, pat):
|
tab = self.active_tab
|
||||||
yield window
|
if tab is not None:
|
||||||
|
try:
|
||||||
|
w = tab.get_nth_window(int(exp))
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
if w is not None:
|
||||||
|
yield w
|
||||||
|
else:
|
||||||
|
pat = re.compile(exp)
|
||||||
|
for window in self.all_windows:
|
||||||
|
if window.matches(field, pat):
|
||||||
|
yield window
|
||||||
|
|
||||||
def tab_for_window(self, window):
|
def tab_for_window(self, window):
|
||||||
for tm in self.os_window_map.values():
|
for tab in self.all_tabs:
|
||||||
for tab in tm:
|
for w in tab:
|
||||||
for w in tab:
|
if w.id == window.id:
|
||||||
if w.id == window.id:
|
return tab
|
||||||
return tab
|
|
||||||
|
|
||||||
def match_tabs(self, match):
|
def match_tabs(self, match):
|
||||||
field, exp = match.split(':', 1)
|
try:
|
||||||
|
field, exp = match.split(':', 1)
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
pat = re.compile(exp)
|
pat = re.compile(exp)
|
||||||
tms = tuple(self.os_window_map.values())
|
|
||||||
found = False
|
found = False
|
||||||
if field in ('title', 'id'):
|
if field in ('title', 'id'):
|
||||||
for tm in tms:
|
for tab in self.all_tabs:
|
||||||
for tab in tm:
|
if tab.matches(field, pat):
|
||||||
if tab.matches(field, pat):
|
yield tab
|
||||||
yield tab
|
found = True
|
||||||
found = True
|
|
||||||
if not found:
|
if not found:
|
||||||
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
||||||
for tab in tabs:
|
for tab in tabs:
|
||||||
@@ -152,7 +201,7 @@ class Boss:
|
|||||||
def _new_os_window(self, args, cwd_from=None):
|
def _new_os_window(self, args, cwd_from=None):
|
||||||
sw = self.args_to_special_window(args, cwd_from) if args else None
|
sw = self.args_to_special_window(args, cwd_from) if args else None
|
||||||
startup_session = create_session(self.opts, special_window=sw, cwd_from=cwd_from)
|
startup_session = create_session(self.opts, special_window=sw, cwd_from=cwd_from)
|
||||||
self.add_os_window(startup_session)
|
return self.add_os_window(startup_session)
|
||||||
|
|
||||||
def new_os_window(self, *args):
|
def new_os_window(self, *args):
|
||||||
self._new_os_window(args)
|
self._new_os_window(args)
|
||||||
@@ -166,30 +215,45 @@ class Boss:
|
|||||||
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
|
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
|
||||||
self.window_id_map[window.id] = window
|
self.window_id_map[window.id] = window
|
||||||
|
|
||||||
def peer_messages_received(self, messages):
|
def _handle_remote_command(self, cmd, window=None):
|
||||||
import json
|
response = None
|
||||||
for msg in messages:
|
if self.opts.allow_remote_control or getattr(window, 'allow_remote_control', False):
|
||||||
msg = json.loads(msg.decode('utf-8'))
|
try:
|
||||||
|
response = handle_cmd(self, window, cmd)
|
||||||
|
except Exception as err:
|
||||||
|
import traceback
|
||||||
|
response = {'ok': False, 'error': str(err)}
|
||||||
|
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'}
|
||||||
|
return response
|
||||||
|
|
||||||
|
def peer_message_received(self, msg):
|
||||||
|
msg = msg.decode('utf-8')
|
||||||
|
cmd_prefix = '\x1bP@kitty-cmd'
|
||||||
|
if msg.startswith(cmd_prefix):
|
||||||
|
cmd = msg[len(cmd_prefix):-2]
|
||||||
|
response = self._handle_remote_command(cmd)
|
||||||
|
if response is not None:
|
||||||
|
response = (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8')
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
msg = json.loads(msg)
|
||||||
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
|
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
|
||||||
startup_id = msg.get('startup_id')
|
startup_id = msg.get('startup_id')
|
||||||
args, rest = parse_args(msg['args'][1:])
|
args, rest = parse_args(msg['args'][1:])
|
||||||
args.args = rest
|
args.args = rest
|
||||||
opts = create_opts(args)
|
opts = create_opts(args)
|
||||||
session = create_session(opts, args)
|
if not os.path.isabs(args.directory):
|
||||||
|
args.directory = os.path.join(msg['cwd'], args.directory)
|
||||||
|
session = create_session(opts, args, respect_cwd=True)
|
||||||
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id)
|
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id)
|
||||||
else:
|
else:
|
||||||
safe_print('Unknown message received from peer, ignoring')
|
log_error('Unknown message received from peer, ignoring')
|
||||||
|
|
||||||
def handle_remote_cmd(self, cmd, window=None):
|
def handle_remote_cmd(self, cmd, window=None):
|
||||||
response = None
|
response = self._handle_remote_command(cmd, window)
|
||||||
if self.opts.allow_remote_control:
|
|
||||||
try:
|
|
||||||
response = handle_cmd(self, window, cmd)
|
|
||||||
except Exception as err:
|
|
||||||
import traceback
|
|
||||||
response = {'ok': False, 'error': str(err), 'tb': traceback.format_exc()}
|
|
||||||
else:
|
|
||||||
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control yes to your kitty.conf'}
|
|
||||||
if response is not None:
|
if response is not None:
|
||||||
if window is not None:
|
if window is not None:
|
||||||
window.send_cmd_response(response)
|
window.send_cmd_response(response)
|
||||||
@@ -259,19 +323,16 @@ class Boss:
|
|||||||
tm.resize()
|
tm.resize()
|
||||||
|
|
||||||
def increase_font_size(self):
|
def increase_font_size(self):
|
||||||
self.change_font_size(
|
self.set_font_size(
|
||||||
min(
|
min(
|
||||||
self.opts.font_size * 5, self.current_font_size +
|
self.opts.font_size * 5, self.current_font_size +
|
||||||
self.opts.font_size_delta))
|
self.opts.font_size_delta))
|
||||||
|
|
||||||
def decrease_font_size(self):
|
def decrease_font_size(self):
|
||||||
self.change_font_size(
|
self.set_font_size(self.current_font_size - self.opts.font_size_delta)
|
||||||
max(
|
|
||||||
MINIMUM_FONT_SIZE, self.current_font_size -
|
|
||||||
self.opts.font_size_delta))
|
|
||||||
|
|
||||||
def restore_font_size(self):
|
def restore_font_size(self):
|
||||||
self.change_font_size(self.opts.font_size)
|
self.set_font_size(self.opts.font_size)
|
||||||
|
|
||||||
def _change_font_size(self, new_size=None, on_dpi_change=False):
|
def _change_font_size(self, new_size=None, on_dpi_change=False):
|
||||||
if new_size is not None:
|
if new_size is not None:
|
||||||
@@ -289,7 +350,8 @@ class Boss:
|
|||||||
tm.refresh_sprite_positions()
|
tm.refresh_sprite_positions()
|
||||||
glfw_post_empty_event()
|
glfw_post_empty_event()
|
||||||
|
|
||||||
def change_font_size(self, new_size):
|
def set_font_size(self, new_size):
|
||||||
|
new_size = max(MINIMUM_FONT_SIZE, new_size)
|
||||||
if new_size == self.current_font_size:
|
if new_size == self.current_font_size:
|
||||||
return
|
return
|
||||||
self._change_font_size(new_size)
|
self._change_font_size(new_size)
|
||||||
@@ -317,8 +379,37 @@ class Boss:
|
|||||||
def dispatch_special_key(self, key, scancode, action, mods):
|
def dispatch_special_key(self, key, scancode, action, mods):
|
||||||
# Handles shortcuts, return True if the key was consumed
|
# Handles shortcuts, return True if the key was consumed
|
||||||
key_action = get_shortcut(self.opts.keymap, mods, key, scancode)
|
key_action = get_shortcut(self.opts.keymap, mods, key, scancode)
|
||||||
self.current_key_press_info = key, scancode, action, mods
|
if key_action is None:
|
||||||
return self.dispatch_action(key_action)
|
sequences = get_shortcut(self.opts.sequence_map, mods, key, scancode)
|
||||||
|
if sequences:
|
||||||
|
self.pending_sequences = sequences
|
||||||
|
set_in_sequence_mode(True)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.current_key_press_info = key, scancode, action, mods
|
||||||
|
return self.dispatch_action(key_action)
|
||||||
|
|
||||||
|
def process_sequence(self, key, scancode, 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):
|
||||||
|
seq = seq[1:]
|
||||||
|
if seq:
|
||||||
|
remaining[seq] = key_action
|
||||||
|
else:
|
||||||
|
matched_action = key_action
|
||||||
|
|
||||||
|
if remaining:
|
||||||
|
self.pending_sequences = remaining
|
||||||
|
else:
|
||||||
|
self.pending_sequences = None
|
||||||
|
set_in_sequence_mode(False)
|
||||||
|
if matched_action is not None:
|
||||||
|
self.dispatch_action(matched_action)
|
||||||
|
|
||||||
def default_bg_changed_for(self, window_id):
|
def default_bg_changed_for(self, window_id):
|
||||||
w = self.window_id_map.get(window_id)
|
w = self.window_id_map.get(window_id)
|
||||||
@@ -360,6 +451,12 @@ class Boss:
|
|||||||
w = tm.active_window
|
w = tm.active_window
|
||||||
if w is not None:
|
if w is not None:
|
||||||
w.focus_changed(focused)
|
w.focus_changed(focused)
|
||||||
|
tm.mark_tab_bar_dirty()
|
||||||
|
|
||||||
|
def update_tab_bar_data(self, os_window_id):
|
||||||
|
tm = self.os_window_map.get(os_window_id)
|
||||||
|
if tm is not None:
|
||||||
|
tm.update_tab_bar_data()
|
||||||
|
|
||||||
def on_drop(self, os_window_id, paths):
|
def on_drop(self, os_window_id, paths):
|
||||||
tm = self.os_window_map.get(os_window_id)
|
tm = self.os_window_map.get(os_window_id)
|
||||||
@@ -383,70 +480,111 @@ class Boss:
|
|||||||
SpecialWindow(
|
SpecialWindow(
|
||||||
self.opts.scrollback_pager, data, _('History'), overlay_for=window.id))
|
self.opts.scrollback_pager, data, _('History'), overlay_for=window.id))
|
||||||
|
|
||||||
def input_unicode_character(self):
|
def edit_config_file(self, *a):
|
||||||
|
confpath = prepare_config_file_for_editing()
|
||||||
|
# On macOS vim fails to handle SIGWINCH if it occurs early, so add a
|
||||||
|
# small delay.
|
||||||
|
cmd = ['kitty', '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + editor + [confpath]
|
||||||
|
self.new_os_window(*cmd)
|
||||||
|
|
||||||
|
def get_output(self, source_window, num_lines=1):
|
||||||
|
output = ''
|
||||||
|
s = source_window.screen
|
||||||
|
if num_lines is None:
|
||||||
|
num_lines = s.lines
|
||||||
|
for i in range(min(num_lines, s.lines)):
|
||||||
|
output += str(s.linebuf.line(i))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _run_kitten(self, kitten, args=(), type_of_input='none'):
|
||||||
w = self.active_window
|
w = self.active_window
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if w is not None and tab is not None and w.overlay_for is None:
|
if w is not None and tab is not None and w.overlay_for is None:
|
||||||
overlay_window = tab.new_special_window(
|
orig_args, args = list(args), list(args)
|
||||||
SpecialWindow(
|
args[0:0] = [config_dir, kitten]
|
||||||
['kitty', '+runpy', 'from kittens.unicode_input.main import main; main()'],
|
|
||||||
overlay_for=w.id))
|
|
||||||
overlay_window.action_on_close = partial(self.send_unicode_character, w.id)
|
|
||||||
|
|
||||||
def send_unicode_character(self, target_window_id, source_window):
|
|
||||||
w = self.window_id_map.get(target_window_id)
|
|
||||||
if w is not None:
|
|
||||||
output = str(source_window.screen.linebuf.line(0))
|
|
||||||
if output.startswith('OK: '):
|
|
||||||
try:
|
|
||||||
text = chr(int(output.partition(' ')[2], 16))
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
else:
|
|
||||||
w.paste(text)
|
|
||||||
|
|
||||||
def run_simple_kitten(self, type_of_input, kitten, *args):
|
|
||||||
import shlex
|
|
||||||
w = self.active_window
|
|
||||||
tab = self.active_tab
|
|
||||||
if w is not None and tab is not None and w.overlay_for is None:
|
|
||||||
cmdline = args[0] if args else ''
|
|
||||||
args = shlex.split(cmdline) if cmdline else []
|
|
||||||
if '--program' not in cmdline:
|
|
||||||
args.extend(('--program', self.opts.open_url_with))
|
|
||||||
if type_of_input in ('text', 'history', 'ansi', 'ansi-history'):
|
if type_of_input in ('text', 'history', 'ansi', 'ansi-history'):
|
||||||
data = w.as_text(as_ansi='ansi' in type_of_input, add_history='history' in type_of_input).encode('utf-8')
|
data = w.as_text(as_ansi='ansi' in type_of_input, add_history='history' in type_of_input).encode('utf-8')
|
||||||
elif type_of_input == 'none':
|
elif type_of_input == 'none':
|
||||||
data = None
|
data = None
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
||||||
tab.new_special_window(
|
from kittens.runner import create_kitten_handler
|
||||||
|
end_kitten = create_kitten_handler(kitten, orig_args)
|
||||||
|
copts = {k: self.opts[k] for k in ('select_by_word_characters', 'open_url_with')}
|
||||||
|
overlay_window = tab.new_special_window(
|
||||||
SpecialWindow(
|
SpecialWindow(
|
||||||
['kitty', '+runpy', 'from kittens.{}.main import main; main()'.format(kitten)] + args,
|
['kitty', '+runpy', 'from kittens.runner import main; main()'] + args,
|
||||||
stdin=data,
|
stdin=data,
|
||||||
|
env={'KITTY_COMMON_OPTS': json.dumps(copts), 'PYTHONWARNINGS': 'ignore'},
|
||||||
overlay_for=w.id))
|
overlay_for=w.id))
|
||||||
|
overlay_window.action_on_close = partial(self.on_kitten_finish, w.id, end_kitten)
|
||||||
|
|
||||||
|
def run_kitten(self, type_of_input, kitten, *args):
|
||||||
|
import shlex
|
||||||
|
cmdline = args[0] if args else ''
|
||||||
|
args = shlex.split(cmdline) if cmdline else []
|
||||||
|
self._run_kitten(kitten, args, type_of_input)
|
||||||
|
|
||||||
|
def on_kitten_finish(self, target_window_id, end_kitten, source_window):
|
||||||
|
output = self.get_output(source_window, num_lines=None)
|
||||||
|
from kittens.runner import deserialize
|
||||||
|
data = deserialize(output)
|
||||||
|
if data is not None:
|
||||||
|
end_kitten(data, target_window_id, self)
|
||||||
|
|
||||||
|
def input_unicode_character(self):
|
||||||
|
self._run_kitten('unicode_input')
|
||||||
|
|
||||||
|
def set_tab_title(self):
|
||||||
|
tab = self.active_tab
|
||||||
|
if tab:
|
||||||
|
args = ['--name=tab-title', '--message', _('Enter the new title for this tab below.'), 'do_set_tab_title', str(tab.id)]
|
||||||
|
self._run_kitten('ask', args)
|
||||||
|
|
||||||
|
def do_set_tab_title(self, title, tab_id):
|
||||||
|
tm = self.active_tab_manager
|
||||||
|
if tm is not None and title:
|
||||||
|
tab_id = int(tab_id)
|
||||||
|
for tab in tm.tabs:
|
||||||
|
if tab.id == tab_id:
|
||||||
|
tab.set_title(title)
|
||||||
|
break
|
||||||
|
|
||||||
|
def kitty_shell(self, window_type):
|
||||||
|
cmd = ['kitty', '@']
|
||||||
|
if window_type == 'tab':
|
||||||
|
window = self._new_tab(cmd).active_window
|
||||||
|
elif window_type == 'os_window':
|
||||||
|
os_window_id = self._new_os_window(cmd)
|
||||||
|
window = self.os_window_map[os_window_id].active_window
|
||||||
|
elif window_type == 'overlay':
|
||||||
|
w = self.active_window
|
||||||
|
tab = self.active_tab
|
||||||
|
if w is not None and tab is not None and w.overlay_for is None:
|
||||||
|
window = tab.new_special_window(SpecialWindow(cmd, overlay_for=w.id))
|
||||||
|
else:
|
||||||
|
window = None
|
||||||
|
else:
|
||||||
|
window = self._new_window(cmd)
|
||||||
|
if window is not None:
|
||||||
|
window.allow_remote_control = True
|
||||||
|
|
||||||
def switch_focus_to(self, window_idx):
|
def switch_focus_to(self, window_idx):
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
tab.set_active_window_idx(window_idx)
|
tab.set_active_window_idx(window_idx)
|
||||||
old_focus = tab.active_window
|
|
||||||
if not old_focus.destroyed:
|
|
||||||
old_focus.focus_changed(False)
|
|
||||||
tab.active_window.focus_changed(True)
|
|
||||||
|
|
||||||
def open_url(self, url):
|
def open_url(self, url, program=None, cwd=None):
|
||||||
if url:
|
if url:
|
||||||
open_url(url, self.opts.open_url_with)
|
if isinstance(program, str):
|
||||||
|
program = to_cmdline(program)
|
||||||
|
open_url(url, program or self.opts.open_url_with, cwd=cwd)
|
||||||
|
|
||||||
def open_url_lines(self, lines):
|
def open_url_lines(self, lines, program=None):
|
||||||
self.open_url(''.join(lines))
|
self.open_url(''.join(lines), program)
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self.shutting_down = True
|
self.shutting_down = True
|
||||||
self.child_monitor.shutdown_monitor()
|
self.child_monitor.shutdown_monitor()
|
||||||
wakeup()
|
|
||||||
self.child_monitor.join()
|
|
||||||
del self.child_monitor
|
del self.child_monitor
|
||||||
for tm in self.os_window_map.values():
|
for tm in self.os_window_map.values():
|
||||||
tm.destroy()
|
tm.destroy()
|
||||||
@@ -482,6 +620,11 @@ class Boss:
|
|||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.goto_tab(tab_num - 1)
|
tm.goto_tab(tab_num - 1)
|
||||||
|
|
||||||
|
def set_active_tab(self, tab):
|
||||||
|
tm = self.active_tab_manager
|
||||||
|
if tm is not None:
|
||||||
|
tm.set_active_tab(tab)
|
||||||
|
|
||||||
def next_tab(self):
|
def next_tab(self):
|
||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
@@ -533,7 +676,7 @@ class Boss:
|
|||||||
special_window = self.args_to_special_window(args, cwd_from=cwd_from)
|
special_window = self.args_to_special_window(args, cwd_from=cwd_from)
|
||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.new_tab(special_window=special_window, cwd_from=cwd_from)
|
return tm.new_tab(special_window=special_window, cwd_from=cwd_from)
|
||||||
|
|
||||||
def new_tab(self, *args):
|
def new_tab(self, *args):
|
||||||
self._new_tab(args)
|
self._new_tab(args)
|
||||||
@@ -547,9 +690,9 @@ class Boss:
|
|||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if tab is not None:
|
if tab is not None:
|
||||||
if args:
|
if args:
|
||||||
tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from))
|
return tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from))
|
||||||
else:
|
else:
|
||||||
tab.new_window(cwd_from=cwd_from)
|
return tab.new_window(cwd_from=cwd_from)
|
||||||
|
|
||||||
def new_window(self, *args):
|
def new_window(self, *args):
|
||||||
self._new_window(args)
|
self._new_window(args)
|
||||||
@@ -570,3 +713,11 @@ class Boss:
|
|||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.move_tab(-1)
|
tm.move_tab(-1)
|
||||||
|
|
||||||
|
def patch_colors(self, spec, configured=False):
|
||||||
|
if configured:
|
||||||
|
for k, v in spec.items():
|
||||||
|
if hasattr(self.opts, k):
|
||||||
|
setattr(self.opts, k, color_from_int(v))
|
||||||
|
for tm in self.all_tab_managers:
|
||||||
|
tm.tab_bar.patch_colors(spec)
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
layout(std140) uniform CellRenderData {
|
layout(std140) uniform CellRenderData {
|
||||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||||
|
|
||||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style;
|
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||||
|
|
||||||
int color1, color2;
|
|
||||||
|
|
||||||
uint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
uint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
||||||
|
|
||||||
@@ -21,7 +19,7 @@ layout(location=1) in uvec4 sprite_coords;
|
|||||||
layout(location=2) in uint is_selected;
|
layout(location=2) in uint is_selected;
|
||||||
|
|
||||||
|
|
||||||
|
const int fg_index_map[] = int[3](0, 1, 0);
|
||||||
const uvec2 cell_pos_map[] = uvec2[4](
|
const uvec2 cell_pos_map[] = uvec2[4](
|
||||||
uvec2(1, 0), // right, top
|
uvec2(1, 0), // right, top
|
||||||
uvec2(1, 1), // right, bottom
|
uvec2(1, 1), // right, bottom
|
||||||
@@ -135,10 +133,10 @@ void main() {
|
|||||||
|
|
||||||
// set cell color indices {{{
|
// set cell color indices {{{
|
||||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||||
ivec2 color_indices = ivec2(color1, color2);
|
|
||||||
uint text_attrs = sprite_coords[3];
|
uint text_attrs = sprite_coords[3];
|
||||||
int fg_index = color_indices[(text_attrs >> 6) & REVERSE_MASK];
|
uint is_inverted = ((text_attrs >> 6) & REVERSE_MASK) + inverted;
|
||||||
int bg_index = color_indices[1 - fg_index];
|
int fg_index = fg_index_map[is_inverted];
|
||||||
|
int bg_index = 1 - fg_index;
|
||||||
float cursor = is_cursor(c, r);
|
float cursor = is_cursor(c, r);
|
||||||
vec3 bg = to_color(colors[bg_index], default_colors[bg_index]);
|
vec3 bg = to_color(colors[bg_index], default_colors[bg_index]);
|
||||||
// }}}
|
// }}}
|
||||||
|
|||||||
@@ -5,16 +5,7 @@
|
|||||||
* Distributed under terms of the GPL3 license.
|
* Distributed under terms of the GPL3 license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#include "threading.h"
|
||||||
#include <pthread.h>
|
|
||||||
// I cant figure out how to get pthread.h to include this definition on macOS. MACOSX_DEPLOYMENT_TARGET does not work.
|
|
||||||
extern int pthread_setname_np(const char *name);
|
|
||||||
#else
|
|
||||||
// Need _GNU_SOURCE for pthread_setname_np on linux
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <pthread.h>
|
|
||||||
#undef _GNU_SOURCE
|
|
||||||
#endif
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
@@ -28,12 +19,17 @@ extern int pthread_setname_np(const char *name);
|
|||||||
extern PyTypeObject Screen_Type;
|
extern PyTypeObject Screen_Type;
|
||||||
|
|
||||||
#define EXTRA_FDS 2
|
#define EXTRA_FDS 2
|
||||||
|
#ifndef MSG_NOSIGNAL
|
||||||
|
// Apple does not implement MSG_NOSIGNAL
|
||||||
|
#define MSG_NOSIGNAL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static void (*parse_func)(Screen*, PyObject*);
|
static void (*parse_func)(Screen*, PyObject*);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *data;
|
char *data;
|
||||||
size_t sz;
|
size_t sz;
|
||||||
|
int fd;
|
||||||
} Message;
|
} Message;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -44,7 +40,7 @@ typedef struct {
|
|||||||
bool shutting_down;
|
bool shutting_down;
|
||||||
pthread_t io_thread, talk_thread;
|
pthread_t io_thread, talk_thread;
|
||||||
|
|
||||||
int talk_fd;
|
int talk_fd, listen_fd;
|
||||||
Message *messages;
|
Message *messages;
|
||||||
size_t messages_capacity, messages_count;
|
size_t messages_capacity, messages_count;
|
||||||
} ChildMonitor;
|
} ChildMonitor;
|
||||||
@@ -63,6 +59,9 @@ static const Child EMPTY_CHILD = {0};
|
|||||||
pthread_mutex_##op(&screen->which##_buf_lock);
|
pthread_mutex_##op(&screen->which##_buf_lock);
|
||||||
#define children_mutex(op) \
|
#define children_mutex(op) \
|
||||||
pthread_mutex_##op(&children_lock);
|
pthread_mutex_##op(&children_lock);
|
||||||
|
#define peer_mutex(op) \
|
||||||
|
pthread_mutex_##op(&talk_data.peer_lock);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static Child children[MAX_CHILDREN] = {{0}};
|
static Child children[MAX_CHILDREN] = {{0}};
|
||||||
@@ -72,23 +71,12 @@ static unsigned long remove_notify[MAX_CHILDREN] = {0};
|
|||||||
static size_t add_queue_count = 0, remove_queue_count = 0;
|
static size_t add_queue_count = 0, remove_queue_count = 0;
|
||||||
static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
|
static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
|
||||||
static pthread_mutex_t children_lock;
|
static pthread_mutex_t children_lock;
|
||||||
static bool signal_received = false;
|
static bool kill_signal_received = false;
|
||||||
static ChildMonitor *the_monitor = NULL;
|
static ChildMonitor *the_monitor = NULL;
|
||||||
static uint8_t drain_buf[1024];
|
static uint8_t drain_buf[1024];
|
||||||
static int signal_fds[2], wakeup_fds[2];
|
static int signal_fds[2], wakeup_fds[2];
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
set_thread_name(const char *name) {
|
|
||||||
int ret = 0;
|
|
||||||
#ifdef __APPLE__
|
|
||||||
ret = pthread_setname_np(name);
|
|
||||||
#else
|
|
||||||
ret = pthread_setname_np(pthread_self(), name);
|
|
||||||
#endif
|
|
||||||
if (ret != 0) perror("Failed to set thread name");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Main thread functions {{{
|
// Main thread functions {{{
|
||||||
|
|
||||||
@@ -139,11 +127,11 @@ static PyObject *
|
|||||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||||
ChildMonitor *self;
|
ChildMonitor *self;
|
||||||
PyObject *dump_callback, *death_notify;
|
PyObject *dump_callback, *death_notify;
|
||||||
int talk_fd = -1;
|
int talk_fd = -1, listen_fd = -1;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
|
if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
|
||||||
if (!PyArg_ParseTuple(args, "OO|i", &death_notify, &dump_callback, &talk_fd)) return NULL;
|
if (!PyArg_ParseTuple(args, "OO|ii", &death_notify, &dump_callback, &talk_fd, &listen_fd)) return NULL;
|
||||||
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
|
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
|
||||||
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
|
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -151,12 +139,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
if (!self_pipe(wakeup_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
if (!self_pipe(wakeup_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
if (!self_pipe(signal_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
if (!self_pipe(signal_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
struct sigaction act = {.sa_handler=handle_signal};
|
struct sigaction act = {.sa_handler=handle_signal};
|
||||||
if (sigaction(SIGINT, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
#define SA(which) { if (sigaction(which, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError); if (siginterrupt(which, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);}
|
||||||
if (sigaction(SIGTERM, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD);
|
||||||
if (siginterrupt(SIGINT, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
#undef SA
|
||||||
if (siginterrupt(SIGTERM, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
||||||
self->talk_fd = talk_fd;
|
self->talk_fd = talk_fd;
|
||||||
|
self->listen_fd = listen_fd;
|
||||||
if (self == NULL) return PyErr_NoMemory();
|
if (self == NULL) return PyErr_NoMemory();
|
||||||
self->death_notify = death_notify; Py_INCREF(death_notify);
|
self->death_notify = death_notify; Py_INCREF(death_notify);
|
||||||
if (dump_callback != Py_None) {
|
if (dump_callback != Py_None) {
|
||||||
@@ -205,12 +193,17 @@ wakeup_io_loop(bool in_signal_handler) {
|
|||||||
|
|
||||||
static void* io_loop(void *data);
|
static void* io_loop(void *data);
|
||||||
static void* talk_loop(void *data);
|
static void* talk_loop(void *data);
|
||||||
|
static void send_response(int fd, const char *msg, size_t msg_sz);
|
||||||
|
static void wakeup_talk_loop(bool);
|
||||||
|
static bool talk_thread_started = false;
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
start(ChildMonitor *self) {
|
start(PyObject *s, PyObject *a UNUSED) {
|
||||||
#define start_doc "start() -> Start the I/O thread"
|
#define start_doc "start() -> Start the I/O thread"
|
||||||
if (self->talk_fd > -1) {
|
ChildMonitor *self = (ChildMonitor*)s;
|
||||||
|
if (self->talk_fd > -1 || self->listen_fd > -1) {
|
||||||
if (pthread_create(&self->talk_thread, NULL, talk_loop, self) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
if (pthread_create(&self->talk_thread, NULL, talk_loop, self) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
talk_thread_started = true;
|
||||||
}
|
}
|
||||||
int ret = pthread_create(&self->io_thread, NULL, io_loop, self);
|
int ret = pthread_create(&self->io_thread, NULL, io_loop, self);
|
||||||
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
@@ -218,17 +211,9 @@ start(ChildMonitor *self) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
join(ChildMonitor *self) {
|
|
||||||
#define join_doc "join() -> Wait for the I/O thread to finish"
|
|
||||||
int ret = pthread_join(self->io_thread, NULL);
|
|
||||||
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
wakeup(ChildMonitor UNUSED *self) {
|
wakeup(PYNOARG) {
|
||||||
#define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there."
|
#define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there."
|
||||||
wakeup_io_loop(false);
|
wakeup_io_loop(false);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -266,7 +251,7 @@ schedule_write_to_child(unsigned long id, const char *data, size_t sz) {
|
|||||||
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
|
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
|
||||||
if (space_left < sz) {
|
if (space_left < sz) {
|
||||||
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
|
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
|
||||||
fprintf(stderr, "Too much data being sent to child with id: %lu, ignoring it\n", id);
|
log_error("Too much data being sent to child with id: %lu, ignoring it", id);
|
||||||
screen_mutex(unlock, write);
|
screen_mutex(unlock, write);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -301,11 +286,21 @@ needs_write(ChildMonitor UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
shutdown_monitor(ChildMonitor *self) {
|
shutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {
|
||||||
#define shutdown_monitor_doc "shutdown_monitor() -> Shutdown the monitor loop."
|
#define shutdown_monitor_doc "shutdown_monitor() -> Shutdown the monitor loop."
|
||||||
signal(SIGINT, SIG_DFL);
|
signal(SIGINT, SIG_DFL);
|
||||||
signal(SIGTERM, SIG_DFL);
|
signal(SIGTERM, SIG_DFL);
|
||||||
|
signal(SIGCHLD, SIG_DFL);
|
||||||
self->shutting_down = true;
|
self->shutting_down = true;
|
||||||
|
wakeup_talk_loop(false);
|
||||||
|
wakeup_io_loop(false);
|
||||||
|
int ret = pthread_join(self->io_thread, NULL);
|
||||||
|
if (ret != 0) return PyErr_Format(PyExc_OSError, "Failed to join() I/O thread with error: %s", strerror(ret));
|
||||||
|
if (talk_thread_started) {
|
||||||
|
ret = pthread_join(self->talk_thread, NULL);
|
||||||
|
if (ret != 0) return PyErr_Format(PyExc_OSError, "Failed to join() talk thread with error: %s", strerror(ret));
|
||||||
|
}
|
||||||
|
talk_thread_started = false;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,14 +338,14 @@ parse_input(ChildMonitor *self) {
|
|||||||
if (msg) {
|
if (msg) {
|
||||||
for (size_t i = 0; i < self->messages_count; i++) {
|
for (size_t i = 0; i < self->messages_count; i++) {
|
||||||
Message *m = self->messages + i;
|
Message *m = self->messages + i;
|
||||||
PyTuple_SET_ITEM(msg, i, PyBytes_FromStringAndSize(m->data, m->sz));
|
PyTuple_SET_ITEM(msg, i, Py_BuildValue("y#i", m->data, (int)m->sz, m->fd));
|
||||||
free(m->data); m->data = NULL; m->sz = 0;
|
free(m->data); m->data = NULL; m->sz = 0;
|
||||||
}
|
}
|
||||||
self->messages_count = 0;
|
self->messages_count = 0;
|
||||||
} else fatal("Out of memory");
|
} else fatal("Out of memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UNLIKELY(signal_received)) {
|
if (UNLIKELY(kill_signal_received)) {
|
||||||
global_state.close_all_windows = true;
|
global_state.close_all_windows = true;
|
||||||
} else {
|
} else {
|
||||||
count = self->count;
|
count = self->count;
|
||||||
@@ -361,7 +356,13 @@ parse_input(ChildMonitor *self) {
|
|||||||
}
|
}
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
if (msg) {
|
if (msg) {
|
||||||
call_boss(peer_messages_received, "(O)", msg);
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(msg); i++) {
|
||||||
|
PyObject *resp = PyObject_CallMethod(global_state.boss, "peer_message_received", "O", PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 0));
|
||||||
|
int peer_fd = (int)PyLong_AsLong(PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 1));
|
||||||
|
if (resp && PyBytes_Check(resp)) send_response(peer_fd, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
|
||||||
|
else { send_response(peer_fd, NULL, 0); if (!resp) PyErr_Print(); }
|
||||||
|
Py_CLEAR(resp);
|
||||||
|
}
|
||||||
Py_CLEAR(msg);
|
Py_CLEAR(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +412,7 @@ pty_resize(int fd, struct winsize *dim) {
|
|||||||
if (ioctl(fd, TIOCSWINSZ, dim) == -1) {
|
if (ioctl(fd, TIOCSWINSZ, dim) == -1) {
|
||||||
if (errno == EINTR) continue;
|
if (errno == EINTR) continue;
|
||||||
if (errno != EBADF && errno != ENOTTY) {
|
if (errno != EBADF && errno != ENOTTY) {
|
||||||
fprintf(stderr, "Failed to resize tty associated with fd: %d with error: %s", fd, strerror(errno));
|
log_error("Failed to resize tty associated with fd: %d with error: %s", fd, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,7 +440,7 @@ resize_pty(ChildMonitor *self, PyObject *args) {
|
|||||||
if (fd == -1) FIND(add_queue, add_queue_count);
|
if (fd == -1) FIND(add_queue, add_queue_count);
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);
|
if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);
|
||||||
} else fprintf(stderr, "Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)\n", window_id, self->count, add_queue_count);
|
} else log_error("Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)", window_id, self->count, add_queue_count);
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -568,8 +569,13 @@ simple_render_screen(PyObject UNUSED *self, PyObject *args) {
|
|||||||
static inline bool
|
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) {
|
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) {
|
||||||
#define TD os_window->tab_bar_render_data
|
#define TD os_window->tab_bar_render_data
|
||||||
bool needs_render = false;
|
bool needs_render = os_window->needs_render;
|
||||||
|
os_window->needs_render = false;
|
||||||
if (TD.screen && os_window->num_tabs > 1) {
|
if (TD.screen && os_window->num_tabs > 1) {
|
||||||
|
if (!os_window->tab_bar_data_updated) {
|
||||||
|
call_boss(update_tab_bar_data, "K", os_window->id);
|
||||||
|
os_window->tab_bar_data_updated = true;
|
||||||
|
}
|
||||||
if (send_cell_data_to_gpu(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window)) needs_render = true;
|
if (send_cell_data_to_gpu(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window)) needs_render = true;
|
||||||
}
|
}
|
||||||
if (OPT(mouse_hide_wait) > 0 && now - os_window->last_mouse_activity_at > OPT(mouse_hide_wait)) hide_mouse(os_window);
|
if (OPT(mouse_hide_wait) > 0 && now - os_window->last_mouse_activity_at > OPT(mouse_hide_wait)) hide_mouse(os_window);
|
||||||
@@ -689,7 +695,7 @@ thread_write(void *x) {
|
|||||||
set_thread_name("KittyWriteStdin");
|
set_thread_name("KittyWriteStdin");
|
||||||
FILE *f = fdopen(data->fd, "w");
|
FILE *f = fdopen(data->fd, "w");
|
||||||
if (fwrite(data->buf, 1, data->sz, f) != data->sz) {
|
if (fwrite(data->buf, 1, data->sz, f) != data->sz) {
|
||||||
fprintf(stderr, "Failed to write all data\n");
|
log_error("Failed to write all data");
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
free_twd(data);
|
free_twd(data);
|
||||||
@@ -733,8 +739,32 @@ process_pending_resizes(double now) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
close_all_windows() {
|
||||||
|
for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true);
|
||||||
|
global_state.close_all_windows = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
process_pending_closes(ChildMonitor *self) {
|
||||||
|
bool has_open_windows = false;
|
||||||
|
for (size_t w = global_state.num_os_windows; w > 0; w--) {
|
||||||
|
OSWindow *os_window = global_state.os_windows + w - 1;
|
||||||
|
if (should_os_window_close(os_window)) {
|
||||||
|
destroy_os_window(os_window);
|
||||||
|
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height);
|
||||||
|
for (size_t t=0; t < os_window->num_tabs; t++) {
|
||||||
|
Tab *tab = os_window->tabs + t;
|
||||||
|
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);
|
||||||
|
}
|
||||||
|
remove_os_window(os_window->id);
|
||||||
|
} else has_open_windows = true;
|
||||||
|
}
|
||||||
|
return has_open_windows;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
main_loop(ChildMonitor *self) {
|
main_loop(ChildMonitor *self, PyObject *a UNUSED) {
|
||||||
#define main_loop_doc "The main thread loop"
|
#define main_loop_doc "The main thread loop"
|
||||||
bool has_open_windows = true;
|
bool has_open_windows = true;
|
||||||
|
|
||||||
@@ -744,23 +774,8 @@ main_loop(ChildMonitor *self) {
|
|||||||
render(now);
|
render(now);
|
||||||
wait_for_events();
|
wait_for_events();
|
||||||
parse_input(self);
|
parse_input(self);
|
||||||
if (global_state.close_all_windows) {
|
if (global_state.close_all_windows) close_all_windows();
|
||||||
for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true);
|
has_open_windows = process_pending_closes(self);
|
||||||
global_state.close_all_windows = false;
|
|
||||||
}
|
|
||||||
has_open_windows = false;
|
|
||||||
for (size_t w = global_state.num_os_windows; w > 0; w--) {
|
|
||||||
OSWindow *os_window = global_state.os_windows + w - 1;
|
|
||||||
if (should_os_window_close(os_window)) {
|
|
||||||
destroy_os_window(os_window);
|
|
||||||
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->viewport_width, os_window->viewport_height);
|
|
||||||
for (size_t t=0; t < os_window->num_tabs; t++) {
|
|
||||||
Tab *tab = os_window->tabs + t;
|
|
||||||
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);
|
|
||||||
}
|
|
||||||
remove_os_window(os_window->id);
|
|
||||||
} else has_open_windows = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -873,6 +888,58 @@ drain_fd(int fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
read_signals(int fd, bool *kill_signal, bool *child_died) {
|
||||||
|
static char buf[256];
|
||||||
|
while(true) {
|
||||||
|
ssize_t len = read(fd, buf, sizeof(buf));
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
if (errno != EIO) perror("Call to read() from read_signals() failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (ssize_t i = 0; i < len; i++) {
|
||||||
|
switch(buf[i]) {
|
||||||
|
case SIGCHLD:
|
||||||
|
*child_died = true; break;
|
||||||
|
case SIGINT:
|
||||||
|
case SIGTERM:
|
||||||
|
*kill_signal = true; break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
mark_child_for_removal(ChildMonitor *self, pid_t pid) {
|
||||||
|
children_mutex(lock);
|
||||||
|
for (size_t i = 0; i < self->count; i++) {
|
||||||
|
if (children[i].pid == pid) {
|
||||||
|
children[i].needs_removal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
children_mutex(unlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
reap_children(ChildMonitor *self, bool enable_close_on_child_death) {
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
(void)self;
|
||||||
|
while(true) {
|
||||||
|
pid = waitpid(-1, &status, WNOHANG);
|
||||||
|
if (pid == -1) {
|
||||||
|
if (errno != EINTR) break;
|
||||||
|
} else if (pid > 0) {
|
||||||
|
if (enable_close_on_child_death) mark_child_for_removal(self, pid);
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
write_to_child(int fd, Screen *screen) {
|
write_to_child(int fd, Screen *screen) {
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
@@ -924,10 +991,10 @@ io_loop(void *data) {
|
|||||||
if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup
|
if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup
|
||||||
if (fds[1].revents && POLLIN) {
|
if (fds[1].revents && POLLIN) {
|
||||||
data_received = true;
|
data_received = true;
|
||||||
drain_fd(fds[1].fd);
|
bool kill_signal = false, child_died = false;
|
||||||
children_mutex(lock);
|
read_signals(fds[1].fd, &kill_signal, &child_died);
|
||||||
signal_received = true;
|
if (kill_signal) { children_mutex(lock); kill_signal_received = true; children_mutex(unlock); }
|
||||||
children_mutex(unlock);
|
if (child_died) reap_children(self, OPT(close_on_child_death));
|
||||||
}
|
}
|
||||||
for (i = 0; i < self->count; i++) {
|
for (i = 0; i < self->count; i++) {
|
||||||
if (fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {
|
if (fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {
|
||||||
@@ -948,7 +1015,7 @@ io_loop(void *data) {
|
|||||||
children_mutex(lock);
|
children_mutex(lock);
|
||||||
children[i].needs_removal = true;
|
children[i].needs_removal = true;
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
fprintf(stderr, "The child %lu had its fd unexpectedly closed\n", children[i].id);
|
log_error("The child %lu had its fd unexpectedly closed", children[i].id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_POLL_EVENTS
|
#ifdef DEBUG_POLL_EVENTS
|
||||||
@@ -975,58 +1042,258 @@ io_loop(void *data) {
|
|||||||
|
|
||||||
// {{{ Talk thread functions
|
// {{{ Talk thread functions
|
||||||
|
|
||||||
static inline void
|
#define MAX_PEERS 256
|
||||||
handle_peer(ChildMonitor *self, int s) {
|
#define MAX_LISTENERS 2
|
||||||
size_t bufsz = 0;
|
|
||||||
char *buf = NULL;
|
|
||||||
size_t buf_used = 0;
|
|
||||||
|
|
||||||
while(true) {
|
typedef struct {
|
||||||
if (buf_used >= bufsz) {
|
char *data;
|
||||||
bufsz = MAX(1024, bufsz) * 2;
|
size_t capacity, used;
|
||||||
if (bufsz > 1024 * 1024) return;
|
int fd;
|
||||||
buf = realloc(buf, bufsz);
|
bool finished, close_socket;
|
||||||
if (buf == NULL) return;
|
} PeerReadData;
|
||||||
}
|
static PeerReadData empty_prd = {.fd = -1, 0};
|
||||||
ssize_t n = recv(s, buf + buf_used, bufsz - buf_used, 0);
|
|
||||||
if (n == 0) break;
|
typedef struct {
|
||||||
if (n < 0) {
|
char *data;
|
||||||
if (errno == EINTR) continue;
|
size_t sz, pos;
|
||||||
perror("Error reading from talk peer");
|
int fd;
|
||||||
|
bool finished;
|
||||||
|
} PeerWriteData;
|
||||||
|
static PeerWriteData empty_pwd = {.fd = -1, 0};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t num_listen_fds, num_talk_fds, num_reads, num_writes, num_queued_writes;
|
||||||
|
struct pollfd fds[MAX_PEERS + MAX_LISTENERS + 1];
|
||||||
|
PeerReadData reads[MAX_LISTENERS];
|
||||||
|
PeerWriteData writes[MAX_LISTENERS];
|
||||||
|
PeerWriteData queued_writes[MAX_LISTENERS];
|
||||||
|
int wakeup_fds[2];
|
||||||
|
pthread_mutex_t peer_lock;
|
||||||
|
} TalkData;
|
||||||
|
|
||||||
|
static TalkData talk_data = {0};
|
||||||
|
#define nuke_socket(s) { shutdown(s, SHUT_RDWR); close(s); }
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
accept_peer(int listen_fd, bool shutting_down) {
|
||||||
|
int peer = accept(listen_fd, NULL, NULL);
|
||||||
|
if (UNLIKELY(peer == -1)) {
|
||||||
|
if (errno == EINTR) return true;
|
||||||
|
if (!shutting_down) perror("accept() on talk socket failed!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
|
||||||
|
if (fd_idx < arraysz(talk_data.fds) && talk_data.num_reads < arraysz(talk_data.reads)) {
|
||||||
|
talk_data.fds[fd_idx].fd = peer; talk_data.fds[fd_idx].events = POLLIN;
|
||||||
|
talk_data.reads[talk_data.num_reads] = empty_prd; talk_data.reads[talk_data.num_reads++].fd = peer;
|
||||||
|
talk_data.num_talk_fds++;
|
||||||
|
} else nuke_socket(peer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
read_from_peer(ChildMonitor *self, int s) {
|
||||||
|
bool read_finished = false;
|
||||||
|
for (size_t i = 0; i < talk_data.num_reads; i++) {
|
||||||
|
PeerReadData *rd = talk_data.reads + i;
|
||||||
|
#define failed(msg) { read_finished = true; log_error("%s", msg); rd->finished = true; rd->close_socket = true; break; }
|
||||||
|
if (rd->fd == s) {
|
||||||
|
if (rd->used >= rd->capacity) {
|
||||||
|
if (rd->capacity >= 1024 * 1024) failed("Ignoring too large message from peer");
|
||||||
|
rd->capacity = MAX(8192, rd->capacity * 2);
|
||||||
|
rd->data = realloc(rd->data, rd->capacity);
|
||||||
|
if (!rd->data) failed("Out of memory");
|
||||||
|
}
|
||||||
|
ssize_t n = recv(s, rd->data + rd->used, rd->capacity - rd->used, 0);
|
||||||
|
if (n == 0) {
|
||||||
|
read_finished = true; rd->finished = true;
|
||||||
|
children_mutex(lock);
|
||||||
|
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
|
||||||
|
Message *m = self->messages + self->messages_count++;
|
||||||
|
m->data = rd->data; rd->data = NULL; m->sz = rd->used; m->fd = s;
|
||||||
|
children_mutex(unlock);
|
||||||
|
wakeup_main_loop();
|
||||||
|
} else if (n < 0) {
|
||||||
|
if (errno != EINTR) {
|
||||||
|
perror("Error reading from talk peer");
|
||||||
|
failed("");
|
||||||
|
}
|
||||||
|
} else rd->used += n;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buf_used += n;
|
|
||||||
}
|
}
|
||||||
if (buf_used) {
|
#undef failed
|
||||||
children_mutex(lock);
|
return read_finished;
|
||||||
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
|
}
|
||||||
Message *m = self->messages + self->messages_count++;
|
|
||||||
m->data = buf; m->sz = buf_used;
|
static inline bool
|
||||||
children_mutex(unlock);
|
write_to_peer(int fd) {
|
||||||
wakeup_main_loop();
|
bool write_finished = false;
|
||||||
} else free(buf);
|
for (size_t i = 0; i < talk_data.num_writes; i++) {
|
||||||
|
PeerWriteData *wd = talk_data.writes + i;
|
||||||
|
#define failed(msg) { write_finished = true; log_error("%s", msg); wd->finished = true; break; }
|
||||||
|
if (wd->fd == fd) {
|
||||||
|
ssize_t n = send(fd, wd->data + wd->pos, wd->sz - wd->pos, MSG_NOSIGNAL);
|
||||||
|
if (n == 0) { failed("send() to peer failed to send any data"); }
|
||||||
|
else if (n < 0) {
|
||||||
|
if (errno != EINTR) { perror("write() to peer socket failed with error"); failed(""); }
|
||||||
|
} else {
|
||||||
|
wd->pos += n;
|
||||||
|
if (wd->pos >= wd->sz) { write_finished = true; wd->finished = true; }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#undef failed
|
||||||
|
return write_finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
remove_poll_fd(int fd) {
|
||||||
|
size_t count = talk_data.num_talk_fds + talk_data.num_listen_fds;
|
||||||
|
for (size_t i = talk_data.num_listen_fds; i < count; i++) {
|
||||||
|
struct pollfd *pfd = talk_data.fds + i;
|
||||||
|
if (pfd->fd == fd) {
|
||||||
|
size_t num_to_right = count - 1 - i;
|
||||||
|
if (num_to_right) memmove(talk_data.fds + i, talk_data.fds + i + 1, num_to_right * sizeof(struct pollfd));
|
||||||
|
talk_data.num_talk_fds--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
prune_finished_reads() {
|
||||||
|
if (!talk_data.num_reads) return;
|
||||||
|
for (ssize_t i = talk_data.num_reads - 1; i >= 0; i--) {
|
||||||
|
PeerReadData *rd = talk_data.reads + i;
|
||||||
|
if (rd->finished) {
|
||||||
|
remove_poll_fd(rd->fd);
|
||||||
|
if (rd->close_socket) { nuke_socket(rd->fd); }
|
||||||
|
else shutdown(rd->fd, SHUT_RD);
|
||||||
|
free(rd->data);
|
||||||
|
ssize_t num_to_right = talk_data.num_reads - 1 - i;
|
||||||
|
if (num_to_right > 0) memmove(talk_data.reads + i, talk_data.reads + i + 1, num_to_right * sizeof(PeerReadData));
|
||||||
|
else talk_data.reads[i] = empty_prd;
|
||||||
|
talk_data.num_reads--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
prune_finished_writes() {
|
||||||
|
if (!talk_data.num_writes) return;
|
||||||
|
for (ssize_t i = talk_data.num_writes - 1; i >= 0; i--) {
|
||||||
|
PeerWriteData *wd = talk_data.writes + i;
|
||||||
|
if (wd->finished) {
|
||||||
|
remove_poll_fd(wd->fd);
|
||||||
|
shutdown(wd->fd, SHUT_WR); close(wd->fd);
|
||||||
|
free(wd->data);
|
||||||
|
ssize_t num_to_right = talk_data.num_writes - 1 - i;
|
||||||
|
if (num_to_right > 0) memmove(talk_data.writes + i, talk_data.writes + i + 1, num_to_right * sizeof(PeerWriteData));
|
||||||
|
else talk_data.writes[i] = empty_pwd;
|
||||||
|
talk_data.num_writes--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wakeup_talk_loop(bool in_signal_handler) {
|
||||||
|
if (talk_data.wakeup_fds[1] <= 0) return;
|
||||||
|
while(true) {
|
||||||
|
ssize_t ret = write(talk_data.wakeup_fds[1], "w", 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
if (!in_signal_handler) perror("Failed to write to talk wakeup fd with error");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
move_queued_writes() {
|
||||||
|
while (talk_data.num_queued_writes) {
|
||||||
|
PeerWriteData *src = talk_data.queued_writes + --talk_data.num_queued_writes;
|
||||||
|
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
|
||||||
|
if (fd_idx < arraysz(talk_data.fds) && talk_data.num_writes < arraysz(talk_data.writes)) {
|
||||||
|
talk_data.fds[fd_idx].fd = src->fd; talk_data.fds[fd_idx].events = POLLOUT;
|
||||||
|
talk_data.writes[talk_data.num_writes++] = *src;
|
||||||
|
talk_data.num_talk_fds++;
|
||||||
|
} else {
|
||||||
|
log_error("Cannot send response to peer, too many peers");
|
||||||
|
free(src->data); nuke_socket(src->fd);
|
||||||
|
}
|
||||||
|
*src = empty_pwd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void*
|
static void*
|
||||||
talk_loop(void *data) {
|
talk_loop(void *data) {
|
||||||
// The I/O thread loop
|
// The talk thread loop
|
||||||
|
|
||||||
ChildMonitor *self = (ChildMonitor*)data;
|
ChildMonitor *self = (ChildMonitor*)data;
|
||||||
set_thread_name("KittyTalkMon");
|
set_thread_name("KittyPeerMon");
|
||||||
|
if ((pthread_mutex_init(&talk_data.peer_lock, NULL)) != 0) { perror("Failed to create peer mutex"); return 0; }
|
||||||
|
if (!self_pipe(talk_data.wakeup_fds)) { perror("Failed to create wakeup fds for talk thread"); return 0; }
|
||||||
|
#define add_listener(which) \
|
||||||
|
if (self->which > -1) { \
|
||||||
|
talk_data.fds[talk_data.num_listen_fds].fd = self->which; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN; \
|
||||||
|
}
|
||||||
|
add_listener(talk_fd); add_listener(listen_fd);
|
||||||
|
#undef add_listener
|
||||||
|
talk_data.fds[talk_data.num_listen_fds].fd = talk_data.wakeup_fds[0]; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN;
|
||||||
|
|
||||||
while (LIKELY(!self->shutting_down)) {
|
while (LIKELY(!self->shutting_down)) {
|
||||||
int peer = accept(self->talk_fd, NULL, NULL);
|
for (size_t i = 0; i < talk_data.num_listen_fds + talk_data.num_talk_fds; i++) { talk_data.fds[i].revents = 0; }
|
||||||
if (peer == -1) {
|
int ret = poll(talk_data.fds, talk_data.num_listen_fds + talk_data.num_talk_fds, -1);
|
||||||
if (errno == EINTR) continue;
|
if (ret > 0) {
|
||||||
if (!self->shutting_down) perror("accept() on talk socket failed!");
|
bool has_finished_reads = false, has_finished_writes = false;
|
||||||
break;
|
for (size_t i = 0; i < talk_data.num_listen_fds - 1; i++) {
|
||||||
}
|
if (talk_data.fds[i].revents & POLLIN) {if (!accept_peer(talk_data.fds[i].fd, self->shutting_down)) goto end; }
|
||||||
handle_peer(self, peer);
|
}
|
||||||
shutdown(peer, SHUT_RDWR);
|
if (talk_data.fds[talk_data.num_listen_fds - 1].revents & POLLIN) drain_fd(talk_data.fds[talk_data.num_listen_fds - 1].fd); // wakeup
|
||||||
close(peer);
|
for (size_t i = talk_data.num_listen_fds; i < talk_data.num_talk_fds + talk_data.num_listen_fds; i++) {
|
||||||
|
if (talk_data.fds[i].revents & (POLLIN | POLLHUP)) { if (read_from_peer(self, talk_data.fds[i].fd)) has_finished_reads = true; }
|
||||||
|
if (talk_data.fds[i].revents & POLLOUT) { if (write_to_peer(talk_data.fds[i].fd)) has_finished_writes = true; }
|
||||||
|
}
|
||||||
|
if (has_finished_reads) prune_finished_reads();
|
||||||
|
if (has_finished_writes) prune_finished_writes();
|
||||||
|
peer_mutex(lock);
|
||||||
|
if (talk_data.num_queued_writes) move_queued_writes();
|
||||||
|
peer_mutex(unlock);
|
||||||
|
} else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror("poll() on talk fds failed"); }
|
||||||
}
|
}
|
||||||
|
end:
|
||||||
|
close(talk_data.wakeup_fds[0]); close(talk_data.wakeup_fds[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
add_peer_writer(int fd, const char* msg, size_t msg_sz) {
|
||||||
|
bool ok = false;
|
||||||
|
peer_mutex(lock);
|
||||||
|
if (talk_data.num_queued_writes < arraysz(talk_data.queued_writes)) {
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes] = empty_pwd;
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes].data = malloc(msg_sz);
|
||||||
|
if (talk_data.queued_writes[talk_data.num_queued_writes].data) {
|
||||||
|
memcpy(talk_data.queued_writes[talk_data.num_queued_writes].data, msg, msg_sz);
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes].sz = msg_sz;
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes++].fd = fd;
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
} else log_error("Cannot send response to peer, too many peers");
|
||||||
|
peer_mutex(unlock);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_response(int fd, const char *msg, size_t msg_sz) {
|
||||||
|
if (msg == NULL) { shutdown(fd, SHUT_WR); close(fd); return; }
|
||||||
|
if (!add_peer_writer(fd, msg, msg_sz)) { shutdown(fd, SHUT_WR); close(fd); }
|
||||||
|
else wakeup_talk_loop(false);
|
||||||
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
@@ -1034,7 +1301,6 @@ static PyMethodDef methods[] = {
|
|||||||
METHOD(add_child, METH_VARARGS)
|
METHOD(add_child, METH_VARARGS)
|
||||||
METHOD(needs_write, METH_VARARGS)
|
METHOD(needs_write, METH_VARARGS)
|
||||||
METHOD(start, METH_NOARGS)
|
METHOD(start, METH_NOARGS)
|
||||||
METHOD(join, METH_NOARGS)
|
|
||||||
METHOD(wakeup, METH_NOARGS)
|
METHOD(wakeup, METH_NOARGS)
|
||||||
METHOD(shutdown_monitor, METH_NOARGS)
|
METHOD(shutdown_monitor, METH_NOARGS)
|
||||||
METHOD(main_loop, METH_NOARGS)
|
METHOD(main_loop, METH_NOARGS)
|
||||||
@@ -1056,8 +1322,16 @@ PyTypeObject ChildMonitor_Type = {
|
|||||||
.tp_new = new,
|
.tp_new = new,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
safe_pipe(PYNOARG) {
|
||||||
|
int fds[2] = {0};
|
||||||
|
if (!self_pipe(fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
return Py_BuildValue("ii", fds[0], fds[1]);
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
METHOD(simple_render_screen, METH_VARARGS)
|
METHOD(simple_render_screen, METH_VARARGS)
|
||||||
|
METHODB(safe_pipe, METH_NOARGS),
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ static PyObject*
|
|||||||
spawn(PyObject *self UNUSED, PyObject *args) {
|
spawn(PyObject *self UNUSED, PyObject *args) {
|
||||||
PyObject *argv_p, *env_p;
|
PyObject *argv_p, *env_p;
|
||||||
int master, slave, stdin_read_fd, stdin_write_fd;
|
int master, slave, stdin_read_fd, stdin_write_fd;
|
||||||
char* cwd;
|
char *cwd, *exe;
|
||||||
if (!PyArg_ParseTuple(args, "sO!O!iiii", &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
|
if (!PyArg_ParseTuple(args, "ssO!O!iiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
|
||||||
char name[2048] = {0};
|
char name[2048] = {0};
|
||||||
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
char **argv = serialize_string_tuple(argv_p);
|
char **argv = serialize_string_tuple(argv_p);
|
||||||
@@ -76,7 +76,7 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
|||||||
close(tfd);
|
close(tfd);
|
||||||
|
|
||||||
environ = env;
|
environ = env;
|
||||||
execvp(argv[0], argv);
|
execvp(exe, argv);
|
||||||
// Report the failure and exec a shell instead, so that we are not left
|
// Report the failure and exec a shell instead, so that we are not left
|
||||||
// with a forked but not exec'ed process
|
// with a forked but not exec'ed process
|
||||||
write_to_stderr("Failed to launch child: ");
|
write_to_stderr("Failed to launch child: ");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import os
|
|||||||
|
|
||||||
import kitty.fast_data_types as fast_data_types
|
import kitty.fast_data_types as fast_data_types
|
||||||
|
|
||||||
from .constants import terminfo_dir, is_macos
|
from .constants import is_macos, shell_path, terminfo_dir
|
||||||
|
|
||||||
|
|
||||||
def cwd_of_process(pid):
|
def cwd_of_process(pid):
|
||||||
@@ -70,7 +70,13 @@ class Child:
|
|||||||
if os.path.isdir(terminfo_dir):
|
if os.path.isdir(terminfo_dir):
|
||||||
env['TERMINFO'] = terminfo_dir
|
env['TERMINFO'] = terminfo_dir
|
||||||
env = tuple('{}={}'.format(k, v) for k, v in env.items())
|
env = tuple('{}={}'.format(k, v) for k, v in env.items())
|
||||||
pid = fast_data_types.spawn(self.cwd, tuple(self.argv), env, master, slave, stdin_read_fd, stdin_write_fd)
|
argv = list(self.argv)
|
||||||
|
exe = argv[0]
|
||||||
|
if is_macos and exe == shell_path:
|
||||||
|
# Some macOS machines need the shell to have argv[0] prefixed by
|
||||||
|
# hyphen, see https://github.com/kovidgoyal/kitty/issues/247
|
||||||
|
argv[0] = ('-' + exe.split('/')[-1])
|
||||||
|
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd)
|
||||||
os.close(slave)
|
os.close(slave)
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self.child_fd = master
|
self.child_fd = master
|
||||||
|
|||||||
186
kitty/cli.py
186
kitty/cli.py
@@ -2,16 +2,36 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
|
||||||
import sys
|
import sys
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
from .config import load_config
|
from .config import defaults, load_config
|
||||||
from .constants import appname, defconf, is_macos, str_version
|
from .config_utils import resolve_config
|
||||||
|
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
|
|
||||||
is_macos
|
CONFIG_HELP = '''\
|
||||||
|
Specify a path to the configuration file(s) to use. All configuration files are
|
||||||
|
merged onto the builtin {conf_name}.conf, overriding the builtin values. This option
|
||||||
|
can be specified multiple times to read multiple configuration files in
|
||||||
|
sequence, which are merged. Use the special value NONE to not load a config
|
||||||
|
file.
|
||||||
|
|
||||||
|
If this option is not specified, config files are searched for in the order:
|
||||||
|
"$XDG_CONFIG_HOME/{appname}/{conf_name}.conf", "~/.config/{appname}/{conf_name}.conf", {macos_confpath}
|
||||||
|
"$XDG_CONFIG_DIRS/{appname}/{conf_name}.conf". The first one that exists is used as the
|
||||||
|
config file.
|
||||||
|
|
||||||
|
If the environment variable "KITTY_CONFIG_DIRECTORY" is specified, that
|
||||||
|
directory is always used and the above searching does not happen.
|
||||||
|
|
||||||
|
If "/etc/xdg/{appname}/{conf_name}.conf" exists it is merged before (i.e. with lower
|
||||||
|
priority) than any user config files. It can be used to specify system-wide
|
||||||
|
defaults for all users.
|
||||||
|
'''.replace('{macos_confpath}', '~/Library/Preferences/{appname}/{conf_name}.conf, ' if is_macos else '')
|
||||||
|
|
||||||
OPTIONS = '''
|
OPTIONS = '''
|
||||||
--class
|
--class
|
||||||
dest=cls
|
dest=cls
|
||||||
@@ -30,12 +50,9 @@ Set the window title. This will override any title set by the program running in
|
|||||||
only use this if you are running a program that does not set titles.
|
only use this if you are running a program that does not set titles.
|
||||||
|
|
||||||
|
|
||||||
--config
|
--config -c
|
||||||
type=list
|
type=list
|
||||||
default={config_path}
|
{config_help}
|
||||||
Specify a path to the configuration file(s) to use.
|
|
||||||
Can be specified multiple times to read multiple configuration files in sequence, which are merged.
|
|
||||||
|
|
||||||
|
|
||||||
--override -o
|
--override -o
|
||||||
type=list
|
type=list
|
||||||
@@ -43,10 +60,6 @@ Override individual configuration options, can be specified multiple times.
|
|||||||
Syntax: |_ name=value|. For example: |_ -o font_size=20|
|
Syntax: |_ name=value|. For example: |_ -o font_size=20|
|
||||||
|
|
||||||
|
|
||||||
--cmd -c
|
|
||||||
Run python code in the kitty context
|
|
||||||
|
|
||||||
|
|
||||||
--directory -d
|
--directory -d
|
||||||
default=.
|
default=.
|
||||||
Change to the specified directory when launching
|
Change to the specified directory when launching
|
||||||
@@ -84,6 +97,16 @@ with the same |_ --instance-group| will result in new windows being created
|
|||||||
in the first |_ {appname}| instance within that group
|
in the first |_ {appname}| instance within that group
|
||||||
|
|
||||||
|
|
||||||
|
--listen-on
|
||||||
|
Tell kitty to listen on the specified address for control
|
||||||
|
messages. For example, |_ --listen-on=unix:/tmp/mykitty| or
|
||||||
|
|_ --listen-on=tcp:localhost:12345|. On Linux systems, you can also use abstract
|
||||||
|
UNIX sockets, not associated with a file, like this: |_ --listen-on=unix:@mykitty|.
|
||||||
|
To control kitty, you can send it commands with |_ kitty @| using the |_ --to| option
|
||||||
|
to specify this address. Note that this option will be ignored, unless you set
|
||||||
|
|_ allow_remote_control| to yes in |_ kitty.conf|
|
||||||
|
|
||||||
|
|
||||||
# Debugging options
|
# Debugging options
|
||||||
|
|
||||||
--version -v
|
--version -v
|
||||||
@@ -107,6 +130,21 @@ Path to file in which to store the raw bytes received from the child process
|
|||||||
--debug-gl
|
--debug-gl
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Debug OpenGL commands. This will cause all OpenGL calls to check for errors instead of ignoring them. Useful when debugging rendering problems
|
Debug OpenGL commands. This will cause all OpenGL calls to check for errors instead of ignoring them. Useful when debugging rendering problems
|
||||||
|
|
||||||
|
|
||||||
|
--debug-font-fallback
|
||||||
|
type=bool-set
|
||||||
|
Print out information about the selection of fallback fonts for characters not present in the main font.
|
||||||
|
|
||||||
|
|
||||||
|
--debug-config
|
||||||
|
type=bool-set
|
||||||
|
Print out information about the system and kitty configuration.
|
||||||
|
|
||||||
|
|
||||||
|
--execute -e
|
||||||
|
type=bool-set
|
||||||
|
!
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@@ -186,10 +224,10 @@ def parse_option_spec(spec=OPTIONS):
|
|||||||
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
|
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
|
||||||
elif state is HELP:
|
elif state is HELP:
|
||||||
if line:
|
if line:
|
||||||
current_cmd['help'] += ' ' + line
|
current_cmd['help'] += ' ' + line.lstrip()
|
||||||
else:
|
else:
|
||||||
if prev_line:
|
if prev_line:
|
||||||
current_cmd['help'] += '\n'
|
current_cmd['help'] += '\n\n\t'
|
||||||
else:
|
else:
|
||||||
state = NORMAL
|
state = NORMAL
|
||||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||||
@@ -213,8 +251,12 @@ def prettify(text):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def version():
|
def version(add_rev=False):
|
||||||
return '{} {} created by {}'.format(italic(appname), green(str_version), title('Kovid Goyal'))
|
rev = ''
|
||||||
|
from . import fast_data_types
|
||||||
|
if add_rev and hasattr(fast_data_types, 'KITTY_VCS_REV'):
|
||||||
|
rev = ' ({})'.format(fast_data_types.KITTY_VCS_REV[:10])
|
||||||
|
return '{} {}{} created by {}'.format(italic(appname), green(str_version), rev, title('Kovid Goyal'))
|
||||||
|
|
||||||
|
|
||||||
def wrap(text, limit=80):
|
def wrap(text, limit=80):
|
||||||
@@ -252,6 +294,7 @@ def wrap(text, limit=80):
|
|||||||
|
|
||||||
def print_help_for_seq(seq, usage, message, appname):
|
def print_help_for_seq(seq, usage, message, appname):
|
||||||
from kitty.icat import screen_size
|
from kitty.icat import screen_size
|
||||||
|
screen_size.changed = True
|
||||||
try:
|
try:
|
||||||
linesz = min(screen_size().cols, 76)
|
linesz = min(screen_size().cols, 76)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
@@ -271,8 +314,9 @@ def print_help_for_seq(seq, usage, message, appname):
|
|||||||
lines.append('')
|
lines.append('')
|
||||||
a((' ' * leading_indent) + j.join(lines))
|
a((' ' * leading_indent) + j.join(lines))
|
||||||
|
|
||||||
usage = usage or '[program-to-run ...]'
|
usage = '[program-to-run ...]' if usage is None else usage
|
||||||
a('{}: {} [options] {}'.format(title('Usage'), bold(yellow(appname)), usage))
|
optstring = '[options] ' if seq else ''
|
||||||
|
a('{}: {} {}{}'.format(title('Usage'), bold(yellow(appname)), optstring, usage))
|
||||||
a('')
|
a('')
|
||||||
message = message or (
|
message = message or (
|
||||||
'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
|
'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
|
||||||
@@ -286,12 +330,15 @@ def print_help_for_seq(seq, usage, message, appname):
|
|||||||
if isinstance(opt, str):
|
if isinstance(opt, str):
|
||||||
a('{}:'.format(title(opt)))
|
a('{}:'.format(title(opt)))
|
||||||
continue
|
continue
|
||||||
|
help_text = opt['help']
|
||||||
|
if help_text == '!':
|
||||||
|
continue # hidden option
|
||||||
a(' ' + ', '.join(map(green, sorted(opt['aliases']))))
|
a(' ' + ', '.join(map(green, sorted(opt['aliases']))))
|
||||||
if not opt.get('type', '').startswith('bool-'):
|
if not opt.get('type', '').startswith('bool-'):
|
||||||
blocks[-1] += '={}'.format(italic(opt['dest'].upper()))
|
blocks[-1] += '={}'.format(italic(opt['dest'].upper()))
|
||||||
if opt.get('help'):
|
if opt.get('help'):
|
||||||
defval = opt.get('default')
|
defval = opt.get('default')
|
||||||
t = opt['help'].replace('%default', str(defval))
|
t = help_text.replace('%default', str(defval))
|
||||||
wa(prettify(t.strip()), indent=4)
|
wa(prettify(t.strip()), indent=4)
|
||||||
if defval is not None:
|
if defval is not None:
|
||||||
wa('Default: {}'.format(defval), indent=4)
|
wa('Default: {}'.format(defval), indent=4)
|
||||||
@@ -300,7 +347,8 @@ def print_help_for_seq(seq, usage, message, appname):
|
|||||||
a('')
|
a('')
|
||||||
|
|
||||||
text = '\n'.join(blocks) + '\n\n' + version()
|
text = '\n'.join(blocks) + '\n\n' + version()
|
||||||
if sys.stdout.isatty():
|
if print_help_for_seq.allow_pager and sys.stdout.isatty():
|
||||||
|
import subprocess
|
||||||
p = subprocess.Popen(['less', '-isRXF'], stdin=subprocess.PIPE)
|
p = subprocess.Popen(['less', '-isRXF'], stdin=subprocess.PIPE)
|
||||||
p.communicate(text.encode('utf-8'))
|
p.communicate(text.encode('utf-8'))
|
||||||
raise SystemExit(p.wait())
|
raise SystemExit(p.wait())
|
||||||
@@ -308,6 +356,9 @@ def print_help_for_seq(seq, usage, message, appname):
|
|||||||
print(text)
|
print(text)
|
||||||
|
|
||||||
|
|
||||||
|
print_help_for_seq.allow_pager = True
|
||||||
|
|
||||||
|
|
||||||
def defval_for_opt(opt):
|
def defval_for_opt(opt):
|
||||||
dv = opt.get('default')
|
dv = opt.get('default')
|
||||||
typ = opt.get('type', '')
|
typ = opt.get('type', '')
|
||||||
@@ -437,7 +488,7 @@ def parse_cmdline(oc, disabled, args=None):
|
|||||||
def options_spec():
|
def options_spec():
|
||||||
if not hasattr(options_spec, 'ans'):
|
if not hasattr(options_spec, 'ans'):
|
||||||
options_spec.ans = OPTIONS.format(
|
options_spec.ans = OPTIONS.format(
|
||||||
appname=appname, config_path=defconf,
|
appname=appname, config_help=CONFIG_HELP.format(appname=appname, conf_name=appname),
|
||||||
window_layout_choices=', '.join(all_layouts)
|
window_layout_choices=', '.join(all_layouts)
|
||||||
)
|
)
|
||||||
return options_spec.ans
|
return options_spec.ans
|
||||||
@@ -450,8 +501,95 @@ def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=
|
|||||||
return parse_cmdline(oc, disabled, args=args)
|
return parse_cmdline(oc, disabled, args=args)
|
||||||
|
|
||||||
|
|
||||||
def create_opts(args):
|
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
||||||
config = args.config or (defconf, )
|
|
||||||
|
|
||||||
|
def print_shortcut(key_sequence, action):
|
||||||
|
if not getattr(print_shortcut, 'maps', None):
|
||||||
|
from kitty.keys import defines
|
||||||
|
v = vars(defines)
|
||||||
|
mmap = {m[len('GLFW_MOD_'):].lower(): x for m, x in v.items() if m.startswith('GLFW_MOD_')}
|
||||||
|
kmap = {k[len('GLFW_KEY_'):].lower(): x for k, x in v.items() if k.startswith('GLFW_KEY_')}
|
||||||
|
krmap = {v: k for k, v in kmap.items()}
|
||||||
|
print_shortcut.maps = mmap, krmap
|
||||||
|
mmap, krmap = print_shortcut.maps
|
||||||
|
keys = []
|
||||||
|
for key in key_sequence:
|
||||||
|
names = []
|
||||||
|
mods, key = key
|
||||||
|
for name, val in mmap.items():
|
||||||
|
if mods & val:
|
||||||
|
names.append(name)
|
||||||
|
if key:
|
||||||
|
names.append(krmap[key])
|
||||||
|
keys.append('+'.join(names))
|
||||||
|
|
||||||
|
print('\t', ' > '.join(keys), action)
|
||||||
|
|
||||||
|
|
||||||
|
def print_shortcut_changes(defns, text, changes):
|
||||||
|
if changes:
|
||||||
|
print(title(text))
|
||||||
|
|
||||||
|
for k in sorted(changes):
|
||||||
|
print_shortcut(k, defns[k])
|
||||||
|
|
||||||
|
|
||||||
|
def compare_keymaps(final, initial):
|
||||||
|
added = set(final) - set(initial)
|
||||||
|
removed = set(initial) - set(final)
|
||||||
|
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
|
||||||
|
print_shortcut_changes(final, 'Added shortcuts:', added)
|
||||||
|
print_shortcut_changes(initial, 'Removed shortcuts:', removed)
|
||||||
|
print_shortcut_changes(final, 'Changed shortcuts:', changed)
|
||||||
|
|
||||||
|
|
||||||
|
def flatten_sequence_map(m):
|
||||||
|
ans = {}
|
||||||
|
for k, rest_map in m.items():
|
||||||
|
for r, action in rest_map.items():
|
||||||
|
ans[(k,) + (r)] = action
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def compare_opts(opts):
|
||||||
|
print('\nConfig options different from defaults:')
|
||||||
|
default_opts = load_config()
|
||||||
|
|
||||||
|
for f in sorted(defaults._fields):
|
||||||
|
if getattr(opts, f) != getattr(defaults, f):
|
||||||
|
if f in ('key_definitions', 'keymap', 'sequence_map'):
|
||||||
|
continue
|
||||||
|
print(title('{:20s}'.format(f)), getattr(opts, f))
|
||||||
|
|
||||||
|
final, initial = opts.keymap, default_opts.keymap
|
||||||
|
final = {(k,): v for k, v in final.items()}
|
||||||
|
initial = {(k,): v for k, v in initial.items()}
|
||||||
|
final_s, initial_s = map(flatten_sequence_map, (opts.sequence_map, default_opts.sequence_map))
|
||||||
|
final.update(final_s)
|
||||||
|
initial.update(initial_s)
|
||||||
|
compare_keymaps(final, initial)
|
||||||
|
|
||||||
|
|
||||||
|
def create_opts(args, debug_config=False):
|
||||||
|
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
|
||||||
|
if debug_config:
|
||||||
|
print(version(add_rev=True))
|
||||||
|
print(' '.join(os.uname()))
|
||||||
|
if is_macos:
|
||||||
|
import subprocess
|
||||||
|
print(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())
|
||||||
|
else:
|
||||||
|
print('Running under:', green('Wayland' if is_wayland else 'X11'))
|
||||||
|
if os.path.exists('/etc/issue'):
|
||||||
|
print(open('/etc/issue', encoding='utf-8', errors='replace').read().strip())
|
||||||
|
if os.path.exists('/etc/lsb-release'):
|
||||||
|
print(open('/etc/lsb-release', encoding='utf-8', errors='replace').read().strip())
|
||||||
|
config = tuple(x for x in config if os.path.exists(x))
|
||||||
|
if config:
|
||||||
|
print(green('Loaded config files:'), ', '.join(config))
|
||||||
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
|
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
|
||||||
opts = load_config(*config, overrides=overrides)
|
opts = load_config(*config, overrides=overrides)
|
||||||
|
if debug_config:
|
||||||
|
compare_opts(opts)
|
||||||
return opts
|
return opts
|
||||||
|
|||||||
549
kitty/cmds.py
Normal file
549
kitty/cmds.py
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from .cli import parse_args
|
||||||
|
from .config import parse_config, parse_send_text_bytes
|
||||||
|
from .constants import appname
|
||||||
|
from .tabs import SpecialWindow
|
||||||
|
from .utils import non_blocking_read
|
||||||
|
|
||||||
|
|
||||||
|
class MatchError(ValueError):
|
||||||
|
|
||||||
|
hide_traceback = True
|
||||||
|
|
||||||
|
def __init__(self, expression, target='windows'):
|
||||||
|
ValueError.__init__(self, 'No matching {} for expression: {}'.format(target, expression))
|
||||||
|
|
||||||
|
|
||||||
|
def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='...'):
|
||||||
|
|
||||||
|
def w(func):
|
||||||
|
func.short_desc = short_desc
|
||||||
|
func.argspec = argspec
|
||||||
|
func.desc = desc or short_desc
|
||||||
|
func.name = func.__name__[4:].replace('_', '-')
|
||||||
|
func.options_spec = options_spec
|
||||||
|
func.is_cmd = True
|
||||||
|
func.impl = lambda: globals()[func.__name__[4:]]
|
||||||
|
func.no_response = no_response
|
||||||
|
return func
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
MATCH_WINDOW_OPTION = '''\
|
||||||
|
--match -m
|
||||||
|
The window to match. Match specifications are of the form:
|
||||||
|
|_ field:regexp|. Where field can be one of: id, title, pid, cwd, cmdline, num.
|
||||||
|
You can use the |_ ls| command to get a list of windows. Note that for
|
||||||
|
numeric fields such as id, pid and num the expression is interpreted as a number,
|
||||||
|
not a regular expression. The field num refers to the window position in the current tab,
|
||||||
|
starting from zero and counting clockwise (this is the same as the order in which the
|
||||||
|
windows are reported by the |_ ls| command).
|
||||||
|
'''
|
||||||
|
MATCH_TAB_OPTION = '''\
|
||||||
|
--match -m
|
||||||
|
The tab to match. Match specifications are of the form:
|
||||||
|
|_ field:regexp|. Where field can be one of: id, title, pid, cwd, cmdline.
|
||||||
|
You can use the |_ ls| command to get a list of tabs. Note that for
|
||||||
|
numeric fields such as id and pid the expression is interpreted as a number,
|
||||||
|
not a regular expression. When using title or id, first a matching tab is
|
||||||
|
looked for and if not found a matching window is looked for, and the tab
|
||||||
|
for that window is used.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
# ls {{{
|
||||||
|
@cmd(
|
||||||
|
'List all tabs/windows',
|
||||||
|
'List all windows. The list is returned as JSON tree. The top-level is a list of'
|
||||||
|
' operating system {appname} windows. Each OS window has an |_ id| and a list'
|
||||||
|
' of |_ tabs|. Each tab has its own |_ id|, a |_ title| and a list of |_ windows|.'
|
||||||
|
' Each window has an |_ id|, |_ title|, |_ current working directory|, |_ process id (PID)| and'
|
||||||
|
' |_ command-line| of the process running in the window.\n\n'
|
||||||
|
'You can use these criteria to select windows/tabs for the other commands.'.format(appname=appname),
|
||||||
|
argspec=''
|
||||||
|
)
|
||||||
|
def cmd_ls(global_opts, opts, args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def ls(boss, window):
|
||||||
|
data = list(boss.list_os_windows())
|
||||||
|
data = json.dumps(data, indent=2, sort_keys=True)
|
||||||
|
return data
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# set_font_size {{{
|
||||||
|
@cmd(
|
||||||
|
'Set the font size in all windows',
|
||||||
|
'Sets the font size to the specified size, in pts.',
|
||||||
|
argspec='FONT_SIZE'
|
||||||
|
)
|
||||||
|
def cmd_set_font_size(global_opts, opts, args):
|
||||||
|
try:
|
||||||
|
return {'size': float(args[0])}
|
||||||
|
except IndexError:
|
||||||
|
raise SystemExit('No font size specified')
|
||||||
|
|
||||||
|
|
||||||
|
def set_font_size(boss, window, payload):
|
||||||
|
boss.set_font_size(payload['size'])
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# send_text {{{
|
||||||
|
@cmd(
|
||||||
|
'Send arbitrary text to specified windows',
|
||||||
|
'Send arbitrary text to specified windows. The text follows Python'
|
||||||
|
' escaping rules. So you can use escapes like |_ \\x1b| to send control codes'
|
||||||
|
' and |_ \\u21fa| to send unicode characters. If you use the |_ --match| option'
|
||||||
|
' the text will be sent to all matched windows. By default, text is sent to'
|
||||||
|
' only the currently active window.',
|
||||||
|
options_spec=MATCH_WINDOW_OPTION + '''\n
|
||||||
|
--stdin
|
||||||
|
type=bool-set
|
||||||
|
Read the text to be sent from |_ stdin|. Note that in this case the text is sent as is,
|
||||||
|
not interpreted for escapes. If stdin is a terminal, you can press Ctrl-D to end reading.
|
||||||
|
|
||||||
|
|
||||||
|
--from-file
|
||||||
|
Path to a file whose contents you wish to send. Note that in this case the file contents
|
||||||
|
are sent as is, not interpreted for escapes.
|
||||||
|
''',
|
||||||
|
no_response=True,
|
||||||
|
argspec='[TEXT TO SEND]'
|
||||||
|
)
|
||||||
|
def cmd_send_text(global_opts, opts, args):
|
||||||
|
limit = 1024
|
||||||
|
ret = {'match': opts.match, 'is_binary': False}
|
||||||
|
|
||||||
|
def pipe(src=sys.stdin):
|
||||||
|
ret['is_binary'] = True
|
||||||
|
import select
|
||||||
|
with non_blocking_read() as fd:
|
||||||
|
keep_going = True
|
||||||
|
while keep_going:
|
||||||
|
rd = select.select([fd], [], [])
|
||||||
|
if rd:
|
||||||
|
data = sys.stdin.buffer.read()
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
data = data.decode('utf-8')
|
||||||
|
if '\x04' in data:
|
||||||
|
data = data[:data.index('\x04')]
|
||||||
|
keep_going = False
|
||||||
|
while data:
|
||||||
|
ret['text'] = data[:limit]
|
||||||
|
yield ret
|
||||||
|
data = data[limit:]
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def chunks(text):
|
||||||
|
ret['is_binary'] = False
|
||||||
|
while text:
|
||||||
|
ret['text'] = text[:limit]
|
||||||
|
yield ret
|
||||||
|
text = text[limit:]
|
||||||
|
|
||||||
|
def file_pipe(path):
|
||||||
|
ret['is_binary'] = True
|
||||||
|
with open(path, encoding='utf-8') as f:
|
||||||
|
while True:
|
||||||
|
data = f.read(limit)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
ret['text'] = data
|
||||||
|
yield ret
|
||||||
|
|
||||||
|
sources = []
|
||||||
|
if opts.stdin:
|
||||||
|
sources.append(pipe())
|
||||||
|
|
||||||
|
if opts.from_file:
|
||||||
|
sources.append(file_pipe(opts.from_file))
|
||||||
|
|
||||||
|
text = ' '.join(args)
|
||||||
|
sources.append(chunks(text))
|
||||||
|
|
||||||
|
def chain():
|
||||||
|
for src in sources:
|
||||||
|
yield from src
|
||||||
|
return chain()
|
||||||
|
|
||||||
|
|
||||||
|
def send_text(boss, window, payload):
|
||||||
|
windows = [boss.active_window]
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
windows = tuple(boss.match_windows(match))
|
||||||
|
data = payload['text'].encode('utf-8') if payload['is_binary'] else parse_send_text_bytes(payload['text'])
|
||||||
|
for window in windows:
|
||||||
|
if window is not None:
|
||||||
|
window.write_to_child(data)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# set_window_title {{{
|
||||||
|
@cmd(
|
||||||
|
'Set the window title',
|
||||||
|
'Set the title for the specified window(s). If you use the |_ --match| option'
|
||||||
|
' the title will be set for all matched windows. By default, only the window'
|
||||||
|
' in which the command is run is affected. If you do not specify a title, the'
|
||||||
|
' last title set by the child process running in the window will be used.',
|
||||||
|
options_spec=MATCH_WINDOW_OPTION,
|
||||||
|
argspec='TITLE ...'
|
||||||
|
)
|
||||||
|
def cmd_set_window_title(global_opts, opts, args):
|
||||||
|
return {'title': ' '.join(args), 'match': opts.match}
|
||||||
|
|
||||||
|
|
||||||
|
def set_window_title(boss, window, payload):
|
||||||
|
windows = [window or boss.active_window]
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
windows = tuple(boss.match_windows(match))
|
||||||
|
if not windows:
|
||||||
|
raise MatchError(match)
|
||||||
|
for window in windows:
|
||||||
|
if window:
|
||||||
|
window.set_title(payload['title'])
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# set_tab_title {{{
|
||||||
|
@cmd(
|
||||||
|
'Set the tab title',
|
||||||
|
'Set the title for the specified tab(s). If you use the |_ --match| option'
|
||||||
|
' the title will be set for all matched tabs. By default, only the tab'
|
||||||
|
' in which the command is run is affected. If you do not specify a title, the'
|
||||||
|
' title of the currently active window in the tab is used.',
|
||||||
|
options_spec=MATCH_TAB_OPTION,
|
||||||
|
argspec='TITLE ...'
|
||||||
|
)
|
||||||
|
def cmd_set_tab_title(global_opts, opts, args):
|
||||||
|
return {'title': ' '.join(args), 'match': opts.match}
|
||||||
|
|
||||||
|
|
||||||
|
def set_tab_title(boss, window, payload):
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
tabs = tuple(boss.match_tabs(match))
|
||||||
|
if not tabs:
|
||||||
|
raise MatchError(match, 'tabs')
|
||||||
|
else:
|
||||||
|
tabs = [boss.tab_for_window(window) if window else boss.active_tab]
|
||||||
|
for tab in tabs:
|
||||||
|
if tab:
|
||||||
|
tab.set_title(payload['title'])
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# close_window {{{
|
||||||
|
@cmd(
|
||||||
|
'Close the specified window(s)',
|
||||||
|
options_spec=MATCH_WINDOW_OPTION + '''\n
|
||||||
|
--self
|
||||||
|
type=bool-set
|
||||||
|
If specified close the window this command is run in, rather than the active window.
|
||||||
|
''',
|
||||||
|
argspec=''
|
||||||
|
)
|
||||||
|
def cmd_close_window(global_opts, opts, args):
|
||||||
|
return {'match': opts.match, 'self': opts.self}
|
||||||
|
|
||||||
|
|
||||||
|
def close_window(boss, window, payload):
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
windows = tuple(boss.match_windows(match))
|
||||||
|
if not windows:
|
||||||
|
raise MatchError(match)
|
||||||
|
else:
|
||||||
|
windows = [window if window and payload['self'] else boss.active_window]
|
||||||
|
for window in windows:
|
||||||
|
if window:
|
||||||
|
boss.close_window(window)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# close_tab {{{
|
||||||
|
@cmd(
|
||||||
|
'Close the specified tab(s)',
|
||||||
|
options_spec=MATCH_TAB_OPTION + '''\n
|
||||||
|
--self
|
||||||
|
type=bool-set
|
||||||
|
If specified close the tab this command is run in, rather than the active tab.
|
||||||
|
''',
|
||||||
|
argspec=''
|
||||||
|
)
|
||||||
|
def cmd_close_tab(global_opts, opts, args):
|
||||||
|
return {'match': opts.match, 'self': opts.self}
|
||||||
|
|
||||||
|
|
||||||
|
def close_tab(boss, window, payload):
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
tabs = tuple(boss.match_tabs(match))
|
||||||
|
if not tabs:
|
||||||
|
raise MatchError(match, 'tabs')
|
||||||
|
else:
|
||||||
|
tabs = [boss.tab_for_window(window) if window and payload['self'] else boss.active_tab]
|
||||||
|
for tab in tabs:
|
||||||
|
if window:
|
||||||
|
if tab:
|
||||||
|
boss.close_tab(tab)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# new_window {{{
|
||||||
|
@cmd(
|
||||||
|
'Open new window',
|
||||||
|
'Open a new window in the specified tab. If you use the |_ --match| option'
|
||||||
|
' the first matching tab is used. Otherwise the currently active tab is used.'
|
||||||
|
' 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:\n'
|
||||||
|
'|_ kitty @ new-window --title Email mutt|',
|
||||||
|
options_spec=MATCH_TAB_OPTION + '''\n
|
||||||
|
--title
|
||||||
|
The title for the new window. By default it will use the title set by the
|
||||||
|
program running in it.
|
||||||
|
|
||||||
|
|
||||||
|
--cwd
|
||||||
|
The initial working directory for the new window.
|
||||||
|
|
||||||
|
|
||||||
|
--keep-focus
|
||||||
|
type=bool-set
|
||||||
|
Keep the current window focused instead of switching to the newly opened window
|
||||||
|
|
||||||
|
|
||||||
|
--new-tab
|
||||||
|
type=bool-set
|
||||||
|
Open a new tab
|
||||||
|
|
||||||
|
|
||||||
|
--tab-title
|
||||||
|
When using --new-tab set the title of the tab.
|
||||||
|
''',
|
||||||
|
argspec='[CMD ...]'
|
||||||
|
)
|
||||||
|
def cmd_new_window(global_opts, opts, args):
|
||||||
|
return {'match': opts.match, 'title': opts.title, 'cwd': opts.cwd,
|
||||||
|
'new_tab': opts.new_tab, 'tab_title': opts.tab_title,
|
||||||
|
'keep_focus': opts.keep_focus, 'args': args or []}
|
||||||
|
|
||||||
|
|
||||||
|
def new_window(boss, window, payload):
|
||||||
|
w = SpecialWindow(cmd=payload['args'] or None, override_title=payload['title'], cwd=payload['cwd'])
|
||||||
|
old_window = boss.active_window
|
||||||
|
if payload['new_tab']:
|
||||||
|
boss._new_tab(w)
|
||||||
|
tab = boss.active_tab
|
||||||
|
if payload['tab_title']:
|
||||||
|
tab.set_title(payload['tab_title'])
|
||||||
|
wid = boss.active_window.id
|
||||||
|
if payload['keep_focus'] and old_window:
|
||||||
|
boss.set_active_window(old_window)
|
||||||
|
return str(wid)
|
||||||
|
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
tabs = tuple(boss.match_tabs(match))
|
||||||
|
if not tabs:
|
||||||
|
raise MatchError(match, 'tabs')
|
||||||
|
else:
|
||||||
|
tabs = [boss.active_tab]
|
||||||
|
tab = tabs[0]
|
||||||
|
w = tab.new_special_window(w)
|
||||||
|
if payload['keep_focus'] and old_window:
|
||||||
|
boss.set_active_window(old_window)
|
||||||
|
return str(w.id)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# focus_window {{{
|
||||||
|
@cmd(
|
||||||
|
'Focus the specified window',
|
||||||
|
options_spec=MATCH_WINDOW_OPTION,
|
||||||
|
argspec='',
|
||||||
|
)
|
||||||
|
def cmd_focus_window(global_opts, opts, args):
|
||||||
|
return {'match': opts.match}
|
||||||
|
|
||||||
|
|
||||||
|
def focus_window(boss, window, payload):
|
||||||
|
windows = [window or boss.active_window]
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
windows = tuple(boss.match_windows(match))
|
||||||
|
if not windows:
|
||||||
|
raise MatchError(match)
|
||||||
|
for window in windows:
|
||||||
|
if window:
|
||||||
|
boss.set_active_window(window)
|
||||||
|
break
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# focus_tab {{{
|
||||||
|
@cmd(
|
||||||
|
'Focus the specified tab',
|
||||||
|
'The active window in the specified tab will be focused.',
|
||||||
|
options_spec=MATCH_TAB_OPTION,
|
||||||
|
argspec='',
|
||||||
|
)
|
||||||
|
def cmd_focus_tab(global_opts, opts, args):
|
||||||
|
return {'match': opts.match}
|
||||||
|
|
||||||
|
|
||||||
|
def focus_tab(boss, window, payload):
|
||||||
|
match = payload['match']
|
||||||
|
tabs = tuple(boss.match_tabs(match))
|
||||||
|
if not tabs:
|
||||||
|
raise MatchError(match, 'tabs')
|
||||||
|
tab = tabs[0]
|
||||||
|
boss.set_active_tab(tab)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# get_text {{{
|
||||||
|
@cmd(
|
||||||
|
'Get text from the specified window',
|
||||||
|
options_spec=MATCH_WINDOW_OPTION + '''\n
|
||||||
|
--extent
|
||||||
|
default=screen
|
||||||
|
choices=screen, all, selection
|
||||||
|
What text to get. The default of screen means all text currently on the screen. all means
|
||||||
|
all the screen+scrollback and selection means currently selected text.
|
||||||
|
|
||||||
|
|
||||||
|
--ansi
|
||||||
|
type=bool-set
|
||||||
|
By default, only plain text is returned. If you specify this flag, the text will
|
||||||
|
include the formatting escape codes for colors/bold/italic/etc. Note that when
|
||||||
|
getting the current selection, the result is always plain text.
|
||||||
|
|
||||||
|
|
||||||
|
--self
|
||||||
|
type=bool-set
|
||||||
|
If specified get text from the window this command is run in, rather than the active window.
|
||||||
|
''',
|
||||||
|
argspec=''
|
||||||
|
)
|
||||||
|
def cmd_get_text(global_opts, opts, args):
|
||||||
|
return {'match': opts.match, 'extent': opts.extent, 'ansi': opts.ansi, 'self': opts.self}
|
||||||
|
|
||||||
|
|
||||||
|
def get_text(boss, window, payload):
|
||||||
|
match = payload['match']
|
||||||
|
if match:
|
||||||
|
windows = tuple(boss.match_windows(match))
|
||||||
|
if not windows:
|
||||||
|
raise MatchError(match)
|
||||||
|
else:
|
||||||
|
windows = [window if window and payload['self'] else boss.active_window]
|
||||||
|
window = windows[0]
|
||||||
|
if payload['extent'] == 'selection':
|
||||||
|
ans = window.text_for_selection()
|
||||||
|
else:
|
||||||
|
ans = window.as_text(as_ansi=bool(payload['ansi']), add_history=True)
|
||||||
|
return ans
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# set_colors {{{
|
||||||
|
@cmd(
|
||||||
|
'Set terminal colors',
|
||||||
|
'Set the terminal colors for the specified windows/tabs (defaults to active window). You can either specify the path to a conf file'
|
||||||
|
' (in the same format as kitty.conf) to read the colors from or you can specify individual colors,'
|
||||||
|
' for example: kitty @ set-colors foreground=red background=white',
|
||||||
|
options_spec='''\
|
||||||
|
--all -a
|
||||||
|
type=bool-set
|
||||||
|
By default, colors are only changed for the currently active window. This option will
|
||||||
|
cause colors to be changed in all windows.
|
||||||
|
|
||||||
|
|
||||||
|
--configured -c
|
||||||
|
type=bool-set
|
||||||
|
Also change the configured colors (i.e. the colors kitty will use for new
|
||||||
|
windows or after a reset).
|
||||||
|
|
||||||
|
|
||||||
|
--reset
|
||||||
|
type=bool-set
|
||||||
|
Restore all colors to the values they had at kitty startup. Note that if you specify
|
||||||
|
this option, any color arguments are ignored and --configured and --all are implied.
|
||||||
|
''' + '\n\n' + MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t'),
|
||||||
|
argspec='COLOR_OR_FILE ...'
|
||||||
|
)
|
||||||
|
def cmd_set_colors(global_opts, opts, args):
|
||||||
|
from .rgb import color_as_int, Color
|
||||||
|
colors = {}
|
||||||
|
if not opts.reset:
|
||||||
|
for spec in args:
|
||||||
|
if '=' in spec:
|
||||||
|
colors.update(parse_config((spec.replace('=', ' '),)))
|
||||||
|
else:
|
||||||
|
with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:
|
||||||
|
colors.update(parse_config(f))
|
||||||
|
colors = {k: color_as_int(v) for k, v in colors.items() if isinstance(v, Color)}
|
||||||
|
return {
|
||||||
|
'title': ' '.join(args), 'match_window': opts.match, 'match_tab': opts.match_tab,
|
||||||
|
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset, 'colors': colors, 'reset': opts.reset
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def set_colors(boss, window, payload):
|
||||||
|
from .rgb import color_as_int
|
||||||
|
if payload['all']:
|
||||||
|
windows = tuple(boss.all_windows)
|
||||||
|
else:
|
||||||
|
windows = (window or boss.active_window,)
|
||||||
|
if payload['match_window']:
|
||||||
|
windows = tuple(boss.match_windows(payload['match_window']))
|
||||||
|
if not windows:
|
||||||
|
raise MatchError(payload['match_window'])
|
||||||
|
if payload['match_tab']:
|
||||||
|
tabs = tuple(boss.match_tabs(payload['match_tab']))
|
||||||
|
if not tabs:
|
||||||
|
raise MatchError(payload['match_tab'], 'tabs')
|
||||||
|
for tab in tabs:
|
||||||
|
windows += tuple(tab)
|
||||||
|
if payload['reset']:
|
||||||
|
payload['colors'] = {k: color_as_int(v) for k, v in boss.startup_colors.items()}
|
||||||
|
profiles = tuple(w.screen.color_profile for w in windows)
|
||||||
|
from .fast_data_types import patch_color_profiles
|
||||||
|
patch_color_profiles(payload['colors'], profiles, payload['configured'])
|
||||||
|
boss.patch_colors(payload['colors'], payload['configured'])
|
||||||
|
default_bg_changed = 'background' in payload['colors']
|
||||||
|
for w in windows:
|
||||||
|
if default_bg_changed:
|
||||||
|
boss.default_bg_changed_for(w.id)
|
||||||
|
w.refresh()
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_subcommand_cli(func, args):
|
||||||
|
opts, items = parse_args(args[1:], (func.options_spec or '\n').format, func.argspec, func.desc, '{} @ {}'.format(appname, func.name))
|
||||||
|
return opts, items
|
||||||
|
|
||||||
|
|
||||||
|
def display_subcommand_help(func):
|
||||||
|
try:
|
||||||
|
parse_args(['--help'], (func.options_spec or '\n').format, func.argspec, func.desc, func.name)
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
@@ -23,8 +23,6 @@ typedef void* rusage_info_t; // needed for libproc.h
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static NSMenuItem* title_menu = NULL;
|
static NSMenuItem* title_menu = NULL;
|
||||||
static bool change_titlebar_color = false;
|
|
||||||
static color_type titlebar_color = 0;
|
|
||||||
|
|
||||||
|
|
||||||
static NSString*
|
static NSString*
|
||||||
@@ -59,11 +57,35 @@ find_app_name(void) {
|
|||||||
return @"kitty";
|
return @"kitty";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface GlobalMenuTarget : NSObject
|
||||||
|
+ (GlobalMenuTarget *) shared_instance;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GlobalMenuTarget
|
||||||
|
|
||||||
|
- (void) show_preferences : (id)sender {
|
||||||
|
(void)sender;
|
||||||
|
call_boss(edit_config_file, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (GlobalMenuTarget *) shared_instance
|
||||||
|
{
|
||||||
|
static GlobalMenuTarget *sharedGlobalMenuTarget = nil;
|
||||||
|
@synchronized(self)
|
||||||
|
{
|
||||||
|
if (!sharedGlobalMenuTarget)
|
||||||
|
sharedGlobalMenuTarget = [[GlobalMenuTarget alloc] init];
|
||||||
|
return sharedGlobalMenuTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
void
|
void
|
||||||
cocoa_create_global_menu(void) {
|
cocoa_create_global_menu(void) {
|
||||||
NSString* app_name = find_app_name();
|
NSString* app_name = find_app_name();
|
||||||
NSMenu* bar = [[NSMenu alloc] init];
|
NSMenu* bar = [[NSMenu alloc] init];
|
||||||
|
GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];
|
||||||
[NSApp setMainMenu:bar];
|
[NSApp setMainMenu:bar];
|
||||||
|
|
||||||
NSMenuItem* appMenuItem =
|
NSMenuItem* appMenuItem =
|
||||||
@@ -75,6 +97,9 @@ cocoa_create_global_menu(void) {
|
|||||||
action:@selector(orderFrontStandardAboutPanel:)
|
action:@selector(orderFrontStandardAboutPanel:)
|
||||||
keyEquivalent:@""];
|
keyEquivalent:@""];
|
||||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
NSMenuItem* preferences_menu_item = [[NSMenuItem alloc] initWithTitle:@"Preferences..." action:@selector(show_preferences:) keyEquivalent:@","];
|
||||||
|
[preferences_menu_item setTarget:global_menu_target];
|
||||||
|
[appMenu addItem:preferences_menu_item];
|
||||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name]
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name]
|
||||||
action:@selector(hide:)
|
action:@selector(hide:)
|
||||||
keyEquivalent:@"h"];
|
keyEquivalent:@"h"];
|
||||||
@@ -124,7 +149,7 @@ cocoa_create_global_menu(void) {
|
|||||||
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||||
[NSApp setWindowsMenu:windowMenu];
|
[NSApp setWindowsMenu:windowMenu];
|
||||||
[windowMenu release];
|
[windowMenu release];
|
||||||
|
[preferences_menu_item release];
|
||||||
|
|
||||||
[bar release];
|
[bar release];
|
||||||
}
|
}
|
||||||
@@ -180,32 +205,14 @@ cwd_of_process(PyObject *self UNUSED, PyObject *pid_) {
|
|||||||
return PyUnicode_FromString(vpi.pvi_cdir.vip_path);
|
return PyUnicode_FromString(vpi.pvi_cdir.vip_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline color_type
|
void
|
||||||
color_as_int(PyObject *color) {
|
cocoa_set_hide_from_tasks(void) {
|
||||||
#define I(n, s) ((PyLong_AsUnsignedLong(PyTuple_GET_ITEM(color, n)) & 0xff) << s)
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
||||||
return (I(0, 16) | I(1, 8) | I(2, 0)) & 0xffffff;
|
|
||||||
#undef I
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
macos_change_titlebar_color(PyObject *self UNUSED, PyObject *val) {
|
|
||||||
if (val == Py_None) change_titlebar_color = false;
|
|
||||||
else if (val == Py_True) {
|
|
||||||
change_titlebar_color = true;
|
|
||||||
titlebar_color = OPT(background);
|
|
||||||
} else {
|
|
||||||
if (!PyTuple_Check(val)) { PyErr_SetString(PyExc_TypeError, "Not a color tuple"); return NULL; }
|
|
||||||
change_titlebar_color = true;
|
|
||||||
titlebar_color = color_as_int(val);
|
|
||||||
}
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cocoa_set_titlebar_color(void *w)
|
cocoa_set_titlebar_color(void *w, color_type titlebar_color)
|
||||||
{
|
{
|
||||||
if (!change_titlebar_color) return;
|
|
||||||
|
|
||||||
NSWindow *window = (NSWindow *)w;
|
NSWindow *window = (NSWindow *)w;
|
||||||
|
|
||||||
double red = ((titlebar_color >> 16) & 0xFF) / 255.0;
|
double red = ((titlebar_color >> 16) & 0xFF) / 255.0;
|
||||||
@@ -232,7 +239,6 @@ cocoa_set_titlebar_color(void *w)
|
|||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
||||||
{"cwd_of_process", (PyCFunction)cwd_of_process, METH_O, ""},
|
{"cwd_of_process", (PyCFunction)cwd_of_process, METH_O, ""},
|
||||||
{"macos_change_titlebar_color", (PyCFunction)macos_change_titlebar_color, METH_O, ""},
|
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
* Distributed under terms of the GPL3 license.
|
* Distributed under terms of the GPL3 license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define EXTRA_INIT if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
|
|
||||||
@@ -29,23 +30,29 @@ static uint32_t FG_BG_256[256] = {
|
|||||||
0xffffff, // 15
|
0xffffff, // 15
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject* create_256_color_table() {
|
static inline void
|
||||||
// colors 16..232: the 6x6x6 color cube
|
init_FG_BG_table() {
|
||||||
const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
if (UNLIKELY(FG_BG_256[255] == 0)) {
|
||||||
uint8_t i, j=16;
|
// colors 16..232: the 6x6x6 color cube
|
||||||
for(i = 0; i < 217; i++, j++) {
|
const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||||
uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];
|
uint8_t i, j=16;
|
||||||
FG_BG_256[j] = (r << 16) | (g << 8) | b;
|
for(i = 0; i < 217; i++, j++) {
|
||||||
}
|
uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];
|
||||||
// colors 233..255: grayscale
|
FG_BG_256[j] = (r << 16) | (g << 8) | b;
|
||||||
for(i = 1; i < 24; i++, j++) {
|
}
|
||||||
uint8_t v = 8 + i * 10;
|
// colors 233..255: grayscale
|
||||||
FG_BG_256[j] = (v << 16) | (v << 8) | v;
|
for(i = 1; i < 24; i++, j++) {
|
||||||
|
uint8_t v = 8 + i * 10;
|
||||||
|
FG_BG_256[j] = (v << 16) | (v << 8) | v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *ans = PyTuple_New(255);
|
PyObject* create_256_color_table() {
|
||||||
|
init_FG_BG_table();
|
||||||
|
PyObject *ans = PyTuple_New(arraysz(FG_BG_256));
|
||||||
if (ans == NULL) return PyErr_NoMemory();
|
if (ans == NULL) return PyErr_NoMemory();
|
||||||
for (i=0; i < 255; i++) {
|
for (size_t i=0; i < arraysz(FG_BG_256); i++) {
|
||||||
PyObject *temp = PyLong_FromUnsignedLong(FG_BG_256[i]);
|
PyObject *temp = PyLong_FromUnsignedLong(FG_BG_256[i]);
|
||||||
if (temp == NULL) { Py_CLEAR(ans); return NULL; }
|
if (temp == NULL) { Py_CLEAR(ans); return NULL; }
|
||||||
PyTuple_SET_ITEM(ans, i, temp);
|
PyTuple_SET_ITEM(ans, i, temp);
|
||||||
@@ -59,7 +66,7 @@ new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
|||||||
|
|
||||||
self = (ColorProfile *)type->tp_alloc(type, 0);
|
self = (ColorProfile *)type->tp_alloc(type, 0);
|
||||||
if (self != NULL) {
|
if (self != NULL) {
|
||||||
if (FG_BG_256[255] == 0) create_256_color_table();
|
init_FG_BG_table();
|
||||||
memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256));
|
memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256));
|
||||||
memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
|
memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
|
||||||
self->dirty = true;
|
self->dirty = true;
|
||||||
@@ -80,12 +87,10 @@ alloc_color_profile() {
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
||||||
#define update_ansi_color_table_doc "Update the 16 basic colors"
|
#define update_ansi_color_table_doc "Update the 256 basic colors"
|
||||||
index_type i;
|
|
||||||
|
|
||||||
if (!PyList_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a list"); return NULL; }
|
if (!PyList_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a list"); return NULL; }
|
||||||
if (PyList_GET_SIZE(val) != 16) { PyErr_SetString(PyExc_TypeError, "color table must have 16 items"); return NULL; }
|
if (PyList_GET_SIZE(val) != arraysz(FG_BG_256)) { PyErr_SetString(PyExc_TypeError, "color table must have 256 items"); return NULL; }
|
||||||
for (i = 0; i < 16; i++) {
|
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||||
self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, i));
|
self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, i));
|
||||||
self->orig_color_table[i] = self->color_table[i];
|
self->orig_color_table[i] = self->color_table[i];
|
||||||
}
|
}
|
||||||
@@ -93,6 +98,43 @@ update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
||||||
|
PyObject *spec, *profiles, *v; ColorProfile *self; int change_configured;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!O!p", &PyDict_Type, &spec, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
||||||
|
char key[32] = {0};
|
||||||
|
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||||
|
snprintf(key, sizeof(key) - 1, "color%zu", i);
|
||||||
|
v = PyDict_GetItemString(spec, key);
|
||||||
|
if (v) {
|
||||||
|
color_type color = PyLong_AsUnsignedLong(v);
|
||||||
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {
|
||||||
|
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);
|
||||||
|
self->color_table[i] = color;
|
||||||
|
if (change_configured) self->orig_color_table[i] = color;
|
||||||
|
self->dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define S(config_name, profile_name) { \
|
||||||
|
v = PyDict_GetItemString(spec, #config_name); \
|
||||||
|
if (v) { \
|
||||||
|
color_type color = PyLong_AsUnsignedLong(v); \
|
||||||
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) { \
|
||||||
|
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i); \
|
||||||
|
self->overridden.profile_name = (color << 8) | 2; \
|
||||||
|
if (change_configured) self->configured.profile_name = color; \
|
||||||
|
self->dirty = true; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color);
|
||||||
|
S(selection_foreground, highlight_fg); S(selection_background, highlight_bg);
|
||||||
|
#undef S
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
color_type
|
color_type
|
||||||
colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) {
|
colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) {
|
||||||
color_type t = entry & 0xFF, r;
|
color_type t = entry & 0xFF, r;
|
||||||
@@ -133,7 +175,7 @@ as_color(ColorProfile *self, PyObject *val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
reset_color_table(ColorProfile *self) {
|
reset_color_table(ColorProfile *self, PyObject *a UNUSED) {
|
||||||
#define reset_color_table_doc "Reset all customized colors back to defaults"
|
#define reset_color_table_doc "Reset all customized colors back to defaults"
|
||||||
memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));
|
memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));
|
||||||
self->dirty = true;
|
self->dirty = true;
|
||||||
@@ -179,11 +221,15 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
color_table_address(ColorProfile *self) {
|
color_table_address(ColorProfile *self, PyObject *a UNUSED) {
|
||||||
#define color_table_address_doc "Pointer address to start of color table"
|
#define color_table_address_doc "Pointer address to start of color table"
|
||||||
return PyLong_FromVoidPtr((void*)self->color_table);
|
return PyLong_FromVoidPtr((void*)self->color_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
|
||||||
|
return create_256_color_table();
|
||||||
|
}
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
|
|
||||||
@@ -236,5 +282,12 @@ PyTypeObject ColorProfile_Type = {
|
|||||||
.tp_new = new,
|
.tp_new = new,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
METHODB(default_color_table, METH_NOARGS),
|
||||||
|
METHODB(patch_color_profiles, METH_VARARGS),
|
||||||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
INIT_TYPE(ColorProfile)
|
INIT_TYPE(ColorProfile)
|
||||||
// }}}
|
// }}}
|
||||||
|
|||||||
311
kitty/config.py
311
kitty/config.py
@@ -6,21 +6,20 @@ import ast
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shlex
|
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from . import fast_data_types as defines
|
from . import fast_data_types as defines
|
||||||
from .config_utils import (
|
from .config_utils import (
|
||||||
init_config, parse_config_base, positive_float, positive_int, to_bool,
|
init_config, load_config as _load_config, merge_dicts, parse_config_base,
|
||||||
to_color, unit_float
|
positive_float, positive_int, to_bool, to_cmdline, to_color, unit_float
|
||||||
)
|
)
|
||||||
from .constants import cache_dir
|
from .constants import cache_dir, defconf
|
||||||
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
from .utils import safe_print
|
from .rgb import color_as_int, color_from_int
|
||||||
|
from .utils import log_error
|
||||||
|
|
||||||
MINIMUM_FONT_SIZE = 4
|
MINIMUM_FONT_SIZE = 4
|
||||||
|
|
||||||
@@ -47,22 +46,20 @@ def to_cursor_shape(x):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_mods(parts):
|
mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER', '⌥': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_mods(parts, sc):
|
||||||
|
|
||||||
def map_mod(m):
|
def map_mod(m):
|
||||||
return {'CTRL': 'CONTROL', 'CMD': 'SUPER', 'OPTION': 'ALT'}.get(m, m)
|
return mod_map.get(m, m)
|
||||||
|
|
||||||
mods = 0
|
mods = 0
|
||||||
for m in parts:
|
for m in parts:
|
||||||
try:
|
try:
|
||||||
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
|
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
safe_print(
|
log_error('Shortcut: {} has unknown modifier, ignoring'.format(sc))
|
||||||
'Shortcut: {} has an unknown modifier, ignoring'.format(
|
|
||||||
parts.join('+')
|
|
||||||
),
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
return mods
|
return mods
|
||||||
@@ -84,12 +81,14 @@ named_keys = {
|
|||||||
|
|
||||||
def parse_shortcut(sc):
|
def parse_shortcut(sc):
|
||||||
parts = sc.split('+')
|
parts = sc.split('+')
|
||||||
mods = parse_mods(parts[:-1])
|
mods = parse_mods(parts[:-1], sc)
|
||||||
|
if mods is None:
|
||||||
|
return None, None
|
||||||
key = parts[-1].upper()
|
key = parts[-1].upper()
|
||||||
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
||||||
if key is not None:
|
if key is not None:
|
||||||
return mods, key
|
return mods, key
|
||||||
return None, None
|
return mods, None
|
||||||
|
|
||||||
|
|
||||||
KeyAction = namedtuple('KeyAction', 'func args')
|
KeyAction = namedtuple('KeyAction', 'func args')
|
||||||
@@ -111,37 +110,82 @@ def parse_key_action(action):
|
|||||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||||
elif func == 'send_text':
|
elif func == 'send_text':
|
||||||
args = rest.split(' ', 1)
|
args = rest.split(' ', 1)
|
||||||
elif func == 'run_simple_kitten':
|
if len(args) > 0:
|
||||||
|
try:
|
||||||
|
args[1] = parse_send_text_bytes(args[1])
|
||||||
|
except Exception:
|
||||||
|
log_error('Ignoring invalid send_text string: ' + args[1])
|
||||||
|
args[1] = ''
|
||||||
|
elif func in ('run_kitten', 'run_simple_kitten'):
|
||||||
|
if func == 'run_simple_kitten':
|
||||||
|
func = 'run_kitten'
|
||||||
args = rest.split(' ', 2)
|
args = rest.split(' ', 2)
|
||||||
elif func == 'goto_tab':
|
elif func == 'goto_tab':
|
||||||
args = (max(0, int(rest)), )
|
args = (max(0, int(rest)), )
|
||||||
|
elif func == 'goto_layout' or func == 'kitty_shell':
|
||||||
|
args = [rest]
|
||||||
|
elif func == 'set_font_size':
|
||||||
|
args = (float(rest),)
|
||||||
elif func in shlex_actions:
|
elif func in shlex_actions:
|
||||||
args = shlex.split(rest)
|
args = to_cmdline(rest)
|
||||||
return KeyAction(func, args)
|
return KeyAction(func, args)
|
||||||
|
|
||||||
|
|
||||||
def parse_key(val, keymap):
|
all_key_actions = set()
|
||||||
|
sequence_sep = '>'
|
||||||
|
|
||||||
|
|
||||||
|
class KeyDefinition:
|
||||||
|
|
||||||
|
def __init__(self, is_sequence, action, mods, key, rest=()):
|
||||||
|
self.is_sequence = is_sequence
|
||||||
|
self.action = action
|
||||||
|
self.trigger = mods, key
|
||||||
|
self.rest = rest
|
||||||
|
|
||||||
|
def resolve(self, kitty_mod):
|
||||||
|
self.trigger = defines.resolve_key_mods(kitty_mod, self.trigger[0]), self.trigger[1]
|
||||||
|
self.rest = tuple((defines.resolve_key_mods(kitty_mod, mods), key) for mods, key in self.rest)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_key(val, key_definitions):
|
||||||
sc, action = val.partition(' ')[::2]
|
sc, action = val.partition(' ')[::2]
|
||||||
sc, action = sc.strip(), action.strip()
|
sc, action = sc.strip().strip(sequence_sep), action.strip()
|
||||||
if not sc or not action:
|
if not sc or not action:
|
||||||
return
|
return
|
||||||
mods, key = parse_shortcut(sc)
|
is_sequence = sequence_sep in sc
|
||||||
if key is None:
|
if is_sequence:
|
||||||
safe_print(
|
trigger = None
|
||||||
'Shortcut: {} has an unknown key, ignoring'.format(val),
|
rest = []
|
||||||
file=sys.stderr
|
for part in sc.split(sequence_sep):
|
||||||
)
|
mods, key = parse_shortcut(part)
|
||||||
return
|
if key is None:
|
||||||
|
if mods is not None:
|
||||||
|
log_error('Shortcut: {} has unknown key, ignoring'.format(sc))
|
||||||
|
return
|
||||||
|
if trigger is None:
|
||||||
|
trigger = mods, key
|
||||||
|
else:
|
||||||
|
rest.append((mods, key))
|
||||||
|
rest = tuple(rest)
|
||||||
|
else:
|
||||||
|
mods, key = parse_shortcut(sc)
|
||||||
|
if key is None:
|
||||||
|
if mods is not None:
|
||||||
|
log_error('Shortcut: {} has unknown key, ignoring'.format(sc))
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
paction = parse_key_action(action)
|
paction = parse_key_action(action)
|
||||||
except Exception:
|
except Exception:
|
||||||
safe_print(
|
log_error('Invalid shortcut action: {}. Ignoring.'.format(
|
||||||
'Invalid shortcut action: {}. Ignoring.'.format(action),
|
action))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if paction is not None:
|
if paction is not None:
|
||||||
keymap[(mods, key)] = paction
|
all_key_actions.add(paction.func)
|
||||||
|
if is_sequence:
|
||||||
|
key_definitions.append(KeyDefinition(True, paction, trigger[0], trigger[1], rest))
|
||||||
|
else:
|
||||||
|
key_definitions.append(KeyDefinition(False, paction, mods, key))
|
||||||
|
|
||||||
|
|
||||||
def parse_symbol_map(val):
|
def parse_symbol_map(val):
|
||||||
@@ -149,9 +193,8 @@ def parse_symbol_map(val):
|
|||||||
symbol_map = {}
|
symbol_map = {}
|
||||||
|
|
||||||
def abort():
|
def abort():
|
||||||
safe_print(
|
log_error('Symbol map: {} is invalid, ignoring'.format(
|
||||||
'Symbol map: {} is invalid, ignoring'.format(val), file=sys.stderr
|
val))
|
||||||
)
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if len(parts) < 2:
|
if len(parts) < 2:
|
||||||
@@ -182,14 +225,12 @@ def parse_send_text_bytes(text):
|
|||||||
).encode('utf-8')
|
).encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def parse_send_text(val, keymap):
|
def parse_send_text(val, key_definitions):
|
||||||
parts = val.split(' ')
|
parts = val.split(' ')
|
||||||
|
|
||||||
def abort(msg):
|
def abort(msg):
|
||||||
safe_print(
|
log_error('Send text: {} is invalid ({}), ignoring'.format(
|
||||||
'Send text: {} is invalid ({}), ignoring'.format(val, msg),
|
val, msg))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if len(parts) < 3:
|
if len(parts) < 3:
|
||||||
@@ -197,11 +238,11 @@ def parse_send_text(val, keymap):
|
|||||||
mode, sc = parts[:2]
|
mode, sc = parts[:2]
|
||||||
text = ' '.join(parts[2:])
|
text = ' '.join(parts[2:])
|
||||||
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
||||||
return parse_key(key_str, keymap)
|
return parse_key(key_str, key_definitions)
|
||||||
|
|
||||||
|
|
||||||
def to_modifiers(val):
|
def to_modifiers(val):
|
||||||
return parse_mods(val.split('+'))
|
return parse_mods(val.split('+'), val) or 0
|
||||||
|
|
||||||
|
|
||||||
def to_layout_names(raw):
|
def to_layout_names(raw):
|
||||||
@@ -223,10 +264,10 @@ def adjust_line_height(x):
|
|||||||
def macos_titlebar_color(x):
|
def macos_titlebar_color(x):
|
||||||
x = x.strip('"')
|
x = x.strip('"')
|
||||||
if x == 'system':
|
if x == 'system':
|
||||||
return
|
return 0
|
||||||
if x == 'background':
|
if x == 'background':
|
||||||
return True
|
return 1
|
||||||
return to_color(x)
|
return (color_as_int(to_color(x)) << 8) | 2
|
||||||
|
|
||||||
|
|
||||||
def box_drawing_scale(x):
|
def box_drawing_scale(x):
|
||||||
@@ -271,7 +312,8 @@ type_map = {
|
|||||||
'adjust_line_height': adjust_line_height,
|
'adjust_line_height': adjust_line_height,
|
||||||
'adjust_column_width': adjust_line_height,
|
'adjust_column_width': adjust_line_height,
|
||||||
'scrollback_lines': positive_int,
|
'scrollback_lines': positive_int,
|
||||||
'scrollback_pager': shlex.split,
|
'scrollback_pager': to_cmdline,
|
||||||
|
'open_url_with': to_cmdline,
|
||||||
'font_size': to_font_size,
|
'font_size': to_font_size,
|
||||||
'font_size_delta': positive_float,
|
'font_size_delta': positive_float,
|
||||||
'focus_follows_mouse': to_bool,
|
'focus_follows_mouse': to_bool,
|
||||||
@@ -281,6 +323,7 @@ type_map = {
|
|||||||
'repaint_delay': positive_int,
|
'repaint_delay': positive_int,
|
||||||
'input_delay': positive_int,
|
'input_delay': positive_int,
|
||||||
'sync_to_monitor': to_bool,
|
'sync_to_monitor': to_bool,
|
||||||
|
'close_on_child_death': to_bool,
|
||||||
'window_border_width': positive_float,
|
'window_border_width': positive_float,
|
||||||
'window_margin_width': positive_float,
|
'window_margin_width': positive_float,
|
||||||
'window_padding_width': positive_float,
|
'window_padding_width': positive_float,
|
||||||
@@ -296,10 +339,10 @@ type_map = {
|
|||||||
'initial_window_width': positive_int,
|
'initial_window_width': positive_int,
|
||||||
'initial_window_height': positive_int,
|
'initial_window_height': positive_int,
|
||||||
'macos_hide_titlebar': to_bool,
|
'macos_hide_titlebar': to_bool,
|
||||||
|
'macos_hide_from_tasks': to_bool,
|
||||||
'macos_option_as_alt': to_bool,
|
'macos_option_as_alt': to_bool,
|
||||||
'macos_titlebar_color': macos_titlebar_color,
|
'macos_titlebar_color': macos_titlebar_color,
|
||||||
'box_drawing_scale': box_drawing_scale,
|
'box_drawing_scale': box_drawing_scale,
|
||||||
'x11_bell_volume': int,
|
|
||||||
'background_opacity': unit_float,
|
'background_opacity': unit_float,
|
||||||
'tab_separator': tab_separator,
|
'tab_separator': tab_separator,
|
||||||
'active_tab_font_style': tab_font_style,
|
'active_tab_font_style': tab_font_style,
|
||||||
@@ -307,16 +350,20 @@ type_map = {
|
|||||||
'inactive_text_alpha': unit_float,
|
'inactive_text_alpha': unit_float,
|
||||||
'url_style': url_style,
|
'url_style': url_style,
|
||||||
'copy_on_select': to_bool,
|
'copy_on_select': to_bool,
|
||||||
|
'window_alert_on_bell': to_bool,
|
||||||
'tab_bar_edge': tab_bar_edge,
|
'tab_bar_edge': tab_bar_edge,
|
||||||
|
'bell_on_tab': to_bool,
|
||||||
|
'kitty_mod': to_modifiers,
|
||||||
|
'clear_all_shortcuts': to_bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in (
|
for name in (
|
||||||
'foreground background cursor active_border_color inactive_border_color'
|
'foreground background cursor active_border_color inactive_border_color'
|
||||||
' selection_foreground selection_background url_color'
|
' selection_foreground selection_background url_color bell_border_color'
|
||||||
).split():
|
).split():
|
||||||
type_map[name] = to_color
|
type_map[name] = to_color
|
||||||
for i in range(16):
|
for i in range(256):
|
||||||
type_map['color%d' % i] = to_color
|
type_map['color{}'.format(i)] = to_color
|
||||||
for a in ('active', 'inactive'):
|
for a in ('active', 'inactive'):
|
||||||
for b in ('foreground', 'background'):
|
for b in ('foreground', 'background'):
|
||||||
type_map['%s_tab_%s' % (a, b)] = to_color
|
type_map['%s_tab_%s' % (a, b)] = to_color
|
||||||
@@ -324,15 +371,19 @@ for a in ('active', 'inactive'):
|
|||||||
|
|
||||||
def special_handling(key, val, ans):
|
def special_handling(key, val, ans):
|
||||||
if key == 'map':
|
if key == 'map':
|
||||||
parse_key(val, ans['keymap'])
|
parse_key(val, ans['key_definitions'])
|
||||||
return True
|
return True
|
||||||
if key == 'symbol_map':
|
if key == 'symbol_map':
|
||||||
ans['symbol_map'].update(parse_symbol_map(val))
|
ans['symbol_map'].update(parse_symbol_map(val))
|
||||||
return True
|
return True
|
||||||
if key == 'send_text':
|
if key == 'send_text':
|
||||||
# For legacy compatibility
|
# For legacy compatibility
|
||||||
parse_send_text(val, ans['keymap'])
|
parse_send_text(val, ans['key_definitions'])
|
||||||
return True
|
return True
|
||||||
|
if key == 'clear_all_shortcuts':
|
||||||
|
if to_bool(val):
|
||||||
|
ans['key_definitions'] = [None]
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
defaults = None
|
defaults = None
|
||||||
@@ -342,10 +393,7 @@ default_config_path = os.path.join(
|
|||||||
|
|
||||||
|
|
||||||
def parse_config(lines, check_keys=True):
|
def parse_config(lines, check_keys=True):
|
||||||
ans = {
|
ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': []}
|
||||||
'keymap': {},
|
|
||||||
'symbol_map': {},
|
|
||||||
}
|
|
||||||
parse_config_base(
|
parse_config_base(
|
||||||
lines,
|
lines,
|
||||||
defaults,
|
defaults,
|
||||||
@@ -357,65 +405,38 @@ def parse_config(lines, check_keys=True):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
Options, defaults = init_config(default_config_path, parse_config)
|
def parse_defaults(lines, check_keys=False):
|
||||||
actions = frozenset(a.func for a in defaults.keymap.values()) | frozenset(
|
ans = parse_config(lines, check_keys)
|
||||||
'combine send_text goto_tab new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
dfctl = defines.default_color_table()
|
||||||
|
|
||||||
|
for i in range(16, 256):
|
||||||
|
k = 'color{}'.format(i)
|
||||||
|
ans.setdefault(k, color_from_int(dfctl[i]))
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
Options, defaults = init_config(default_config_path, parse_defaults)
|
||||||
|
actions = frozenset(all_key_actions) | frozenset(
|
||||||
|
'run_simple_kitten combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
||||||
split()
|
split()
|
||||||
)
|
)
|
||||||
no_op_actions = frozenset({'noop', 'no-op', 'no_op'})
|
no_op_actions = frozenset({'noop', 'no-op', 'no_op'})
|
||||||
|
|
||||||
|
|
||||||
def merge_keymaps(defaults, newvals):
|
|
||||||
ans = defaults.copy()
|
|
||||||
for k, v in newvals.items():
|
|
||||||
f = v.func
|
|
||||||
if f in no_op_actions:
|
|
||||||
ans.pop(k, None)
|
|
||||||
continue
|
|
||||||
if f in actions:
|
|
||||||
ans[k] = v
|
|
||||||
return ans
|
|
||||||
|
|
||||||
|
|
||||||
def merge_dicts(defaults, newvals):
|
|
||||||
ans = defaults.copy()
|
|
||||||
ans.update(newvals)
|
|
||||||
return ans
|
|
||||||
|
|
||||||
|
|
||||||
def merge_configs(defaults, vals):
|
def merge_configs(defaults, vals):
|
||||||
ans = {}
|
ans = {}
|
||||||
for k, v in defaults.items():
|
for k, v in defaults.items():
|
||||||
if isinstance(v, dict):
|
if isinstance(v, dict):
|
||||||
newvals = vals.get(k, {})
|
newvals = vals.get(k, {})
|
||||||
if k == 'keymap':
|
ans[k] = merge_dicts(v, newvals)
|
||||||
ans['keymap'] = merge_keymaps(v, newvals)
|
elif k == 'key_definitions':
|
||||||
else:
|
ans['key_definitions'] = v + vals.get('key_definitions', [])
|
||||||
ans[k] = merge_dicts(v, newvals)
|
|
||||||
else:
|
else:
|
||||||
ans[k] = vals.get(k, v)
|
ans[k] = vals.get(k, v)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def load_config(*paths, overrides=None) -> Options:
|
def build_ansi_color_table(opts=defaults):
|
||||||
ans = defaults._asdict()
|
|
||||||
for path in paths:
|
|
||||||
if not path:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
f = open(path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
continue
|
|
||||||
with f:
|
|
||||||
vals = parse_config(f)
|
|
||||||
ans = merge_configs(ans, vals)
|
|
||||||
if overrides is not None:
|
|
||||||
vals = parse_config(overrides)
|
|
||||||
ans = merge_configs(ans, vals)
|
|
||||||
return Options(**ans)
|
|
||||||
|
|
||||||
|
|
||||||
def build_ansi_color_table(opts: Options = defaults):
|
|
||||||
|
|
||||||
def as_int(x):
|
def as_int(x):
|
||||||
return (x[0] << 16) | (x[1] << 8) | x[2]
|
return (x[0] << 16) | (x[1] << 8) | x[2]
|
||||||
@@ -423,10 +444,11 @@ def build_ansi_color_table(opts: Options = defaults):
|
|||||||
def col(i):
|
def col(i):
|
||||||
return as_int(getattr(opts, 'color{}'.format(i)))
|
return as_int(getattr(opts, 'color{}'.format(i)))
|
||||||
|
|
||||||
return list(map(col, range(16)))
|
return list(map(col, range(256)))
|
||||||
|
|
||||||
|
|
||||||
def atomic_save(data, path):
|
def atomic_save(data, path):
|
||||||
|
import tempfile
|
||||||
fd, p = tempfile.mkstemp(dir=os.path.dirname(path), suffix='.tmp')
|
fd, p = tempfile.mkstemp(dir=os.path.dirname(path), suffix='.tmp')
|
||||||
try:
|
try:
|
||||||
with os.fdopen(fd, 'wb') as f:
|
with os.fdopen(fd, 'wb') as f:
|
||||||
@@ -438,11 +460,8 @@ def atomic_save(data, path):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
safe_print(
|
log_error('Failed to delete temp file {} for atomic save with error: {}'.format(
|
||||||
'Failed to delete temp file {} for atomic save with error: {}'.
|
p, err))
|
||||||
format(p, err),
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@@ -455,10 +474,8 @@ def cached_values_for(name):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
safe_print(
|
log_error('Failed to load cached in {} values with error: {}'.format(
|
||||||
'Failed to load cached in {} values with error: {}'.format(name, err),
|
name, err))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
|
|
||||||
yield cached_values
|
yield cached_values
|
||||||
|
|
||||||
@@ -466,10 +483,8 @@ def cached_values_for(name):
|
|||||||
data = json.dumps(cached_values).encode('utf-8')
|
data = json.dumps(cached_values).encode('utf-8')
|
||||||
atomic_save(data, cached_path)
|
atomic_save(data, cached_path)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
safe_print(
|
log_error('Failed to save cached values with error: {}'.format(
|
||||||
'Failed to save cached values with error: {}'.format(err),
|
err))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def initial_window_size(opts, cached_values):
|
def initial_window_size(opts, cached_values):
|
||||||
@@ -479,5 +494,71 @@ def initial_window_size(opts, cached_values):
|
|||||||
try:
|
try:
|
||||||
w, h = map(int, ws)
|
w, h = map(int, ws)
|
||||||
except Exception:
|
except Exception:
|
||||||
safe_print('Invalid cached window size, ignoring', file=sys.stderr)
|
log_error('Invalid cached window size, ignoring')
|
||||||
return w, h
|
return w, h
|
||||||
|
|
||||||
|
|
||||||
|
def commented_out_default_config():
|
||||||
|
with open(default_config_path, encoding='utf-8', errors='replace') as f:
|
||||||
|
config = f.read()
|
||||||
|
lines = []
|
||||||
|
for line in config.splitlines():
|
||||||
|
if line.strip() and not line.startswith('#'):
|
||||||
|
line = '# ' + line
|
||||||
|
lines.append(line)
|
||||||
|
config = '\n'.join(lines)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_config_file_for_editing():
|
||||||
|
if not os.path.exists(defconf):
|
||||||
|
d = os.path.dirname(defconf)
|
||||||
|
try:
|
||||||
|
os.makedirs(d)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
with open(defconf, 'w') as f:
|
||||||
|
f.write(commented_out_default_config())
|
||||||
|
return defconf
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_keys(opts):
|
||||||
|
defns = []
|
||||||
|
for d in opts.key_definitions:
|
||||||
|
if d is None: # clear_all_shortcuts
|
||||||
|
defns = []
|
||||||
|
else:
|
||||||
|
defns.append(d)
|
||||||
|
for d in defns:
|
||||||
|
d.resolve(opts.kitty_mod)
|
||||||
|
keymap = {}
|
||||||
|
sequence_map = {}
|
||||||
|
|
||||||
|
for defn in defns:
|
||||||
|
is_no_op = defn.action.func in no_op_actions
|
||||||
|
if defn.is_sequence:
|
||||||
|
keymap.pop(defn.trigger, None)
|
||||||
|
s = sequence_map.setdefault(defn.trigger, {})
|
||||||
|
if is_no_op:
|
||||||
|
s.pop(defn.rest, None)
|
||||||
|
if not s:
|
||||||
|
del sequence_map[defn.trigger]
|
||||||
|
else:
|
||||||
|
s[defn.rest] = defn.action
|
||||||
|
else:
|
||||||
|
sequence_map.pop(defn.trigger, None)
|
||||||
|
if is_no_op:
|
||||||
|
keymap.pop(defn.trigger, None)
|
||||||
|
else:
|
||||||
|
keymap[defn.trigger] = defn.action
|
||||||
|
opts.keymap = keymap
|
||||||
|
opts.sequence_map = sequence_map
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(*paths, overrides=None):
|
||||||
|
opts = _load_config(Options, defaults, parse_config, merge_configs, *paths, overrides=overrides)
|
||||||
|
finalize_keys(opts)
|
||||||
|
if opts.background_opacity < 1.0 and opts.macos_titlebar_color:
|
||||||
|
log_error('Cannot use both macos_titlebar_color and background_opacity')
|
||||||
|
opts.macos_titlebar_color = 0
|
||||||
|
return opts
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import shlex
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from .utils import safe_print
|
|
||||||
from .rgb import to_color as as_color
|
from .rgb import to_color as as_color
|
||||||
|
from .utils import log_error
|
||||||
|
|
||||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||||
|
|
||||||
@@ -32,36 +32,136 @@ def to_bool(x):
|
|||||||
return x.lower() in 'y yes true'.split()
|
return x.lower() in 'y yes true'.split()
|
||||||
|
|
||||||
|
|
||||||
|
def to_cmdline(x):
|
||||||
|
ans = shlex.split(x)
|
||||||
|
ans[0] = os.path.expandvars(os.path.expanduser(ans[0]))
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes):
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith('#'):
|
||||||
|
return
|
||||||
|
m = key_pat.match(line)
|
||||||
|
if m is not None:
|
||||||
|
key, val = m.groups()
|
||||||
|
if special_handling(key, val, ans):
|
||||||
|
return
|
||||||
|
if key == 'include':
|
||||||
|
val = val.strip()
|
||||||
|
if not os.path.isabs(val):
|
||||||
|
val = os.path.join(base_path_for_includes, val)
|
||||||
|
try:
|
||||||
|
with open(val, encoding='utf-8', errors='replace') as include:
|
||||||
|
_parse(include, type_map, special_handling, ans, all_keys)
|
||||||
|
except FileNotFoundError:
|
||||||
|
log_error('Could not find included config file: {}, ignoring'.format(val))
|
||||||
|
except EnvironmentError:
|
||||||
|
log_error('Could not read from included config file: {}, ignoring'.format(val))
|
||||||
|
return
|
||||||
|
if all_keys is not None and key not in all_keys:
|
||||||
|
log_error('Ignoring unknown config key: {}'.format(key))
|
||||||
|
return
|
||||||
|
tm = type_map.get(key)
|
||||||
|
if tm is not None:
|
||||||
|
val = tm(val)
|
||||||
|
ans[key] = val
|
||||||
|
|
||||||
|
|
||||||
|
def _parse(lines, type_map, special_handling, ans, all_keys):
|
||||||
|
name = getattr(lines, 'name', None)
|
||||||
|
if name:
|
||||||
|
base_path_for_includes = os.path.dirname(os.path.abspath(name))
|
||||||
|
else:
|
||||||
|
from .constants import config_dir
|
||||||
|
base_path_for_includes = config_dir
|
||||||
|
for line in lines:
|
||||||
|
parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes)
|
||||||
|
|
||||||
|
|
||||||
def parse_config_base(
|
def parse_config_base(
|
||||||
lines, defaults, type_map, special_handling, ans, check_keys=True
|
lines, defaults, type_map, special_handling, ans, check_keys=True
|
||||||
):
|
):
|
||||||
if check_keys:
|
all_keys = defaults._asdict() if check_keys else None
|
||||||
all_keys = defaults._asdict()
|
_parse(lines, type_map, special_handling, ans, all_keys)
|
||||||
for line in lines:
|
|
||||||
line = line.strip()
|
|
||||||
if not line or line.startswith('#'):
|
def create_options_class(keys):
|
||||||
|
keys = tuple(sorted(keys))
|
||||||
|
slots = keys + ('_fields',)
|
||||||
|
|
||||||
|
def __init__(self, kw):
|
||||||
|
for k, v in kw.items():
|
||||||
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(keys)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(keys)
|
||||||
|
|
||||||
|
def __getitem__(self, i):
|
||||||
|
if isinstance(i, int):
|
||||||
|
i = keys[i]
|
||||||
|
try:
|
||||||
|
return getattr(self, i)
|
||||||
|
except AttributeError:
|
||||||
|
raise KeyError('No option named: {}'.format(i))
|
||||||
|
|
||||||
|
def _asdict(self):
|
||||||
|
return {k: getattr(self, k) for k in self._fields}
|
||||||
|
|
||||||
|
def _replace(self, **kw):
|
||||||
|
ans = self._asdict()
|
||||||
|
ans.update(kw)
|
||||||
|
return self.__class__(ans)
|
||||||
|
|
||||||
|
ans = type('Options', (), {
|
||||||
|
'__slots__': slots, '__init__': __init__, '_asdict': _asdict, '_replace': _replace, '__iter__': __iter__,
|
||||||
|
'__len__': __len__, '__getitem__': __getitem__
|
||||||
|
})
|
||||||
|
ans._fields = keys
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def merge_dicts(defaults, newvals):
|
||||||
|
ans = defaults.copy()
|
||||||
|
ans.update(newvals)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_config(SYSTEM_CONF, defconf, config_files_on_cmd_line):
|
||||||
|
if config_files_on_cmd_line:
|
||||||
|
if 'NONE' not in config_files_on_cmd_line:
|
||||||
|
yield SYSTEM_CONF
|
||||||
|
for cf in config_files_on_cmd_line:
|
||||||
|
yield cf
|
||||||
|
else:
|
||||||
|
yield SYSTEM_CONF
|
||||||
|
yield defconf
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(Options, defaults, parse_config, merge_configs, *paths, overrides=None):
|
||||||
|
ans = defaults._asdict()
|
||||||
|
for path in paths:
|
||||||
|
if not path:
|
||||||
continue
|
continue
|
||||||
m = key_pat.match(line)
|
try:
|
||||||
if m is not None:
|
f = open(path, encoding='utf-8', errors='replace')
|
||||||
key, val = m.groups()
|
except FileNotFoundError:
|
||||||
if special_handling(key, val, ans):
|
continue
|
||||||
continue
|
with f:
|
||||||
if check_keys:
|
vals = parse_config(f)
|
||||||
if key not in all_keys:
|
ans = merge_configs(ans, vals)
|
||||||
safe_print(
|
if overrides is not None:
|
||||||
'Ignoring unknown config key: {}'.format(key),
|
vals = parse_config(overrides)
|
||||||
file=sys.stderr
|
ans = merge_configs(ans, vals)
|
||||||
)
|
return Options(ans)
|
||||||
continue
|
|
||||||
tm = type_map.get(key)
|
|
||||||
if tm is not None:
|
|
||||||
val = tm(val)
|
|
||||||
ans[key] = val
|
|
||||||
|
|
||||||
|
|
||||||
def init_config(defaults_path, parse_config):
|
def init_config(defaults_path, parse_config):
|
||||||
with open(defaults_path, encoding='utf-8') as f:
|
with open(defaults_path, encoding='utf-8', errors='replace') as f:
|
||||||
defaults = parse_config(f.read().splitlines(), check_keys=False)
|
defaults = parse_config(f, check_keys=False)
|
||||||
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
Options = create_options_class(defaults.keys())
|
||||||
defaults = Options(**defaults)
|
defaults = Options(defaults)
|
||||||
return Options, defaults
|
return Options, defaults
|
||||||
|
|||||||
@@ -4,17 +4,19 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from .fast_data_types import set_boss as set_c_boss
|
from .fast_data_types import set_boss as set_c_boss
|
||||||
|
|
||||||
appname = 'kitty'
|
appname = 'kitty'
|
||||||
version = (0, 8, 0)
|
version = (0, 9, 1)
|
||||||
str_version = '.'.join(map(str, version))
|
str_version = '.'.join(map(str, version))
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
is_macos = 'darwin' in _plat
|
is_macos = 'darwin' in _plat
|
||||||
base = os.path.dirname(os.path.abspath(__file__))
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
editor = shlex.split(os.environ.get('EDITOR', 'vim'))
|
||||||
|
|
||||||
|
|
||||||
ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy')
|
ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy')
|
||||||
@@ -25,7 +27,22 @@ def _get_config_dir():
|
|||||||
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
|
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
|
||||||
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
|
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
|
||||||
|
|
||||||
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or ('~/Library/Preferences' if is_macos else '~/.config')))
|
locations = []
|
||||||
|
if 'XDG_CONFIG_HOME' in os.environ:
|
||||||
|
locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME'])))
|
||||||
|
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 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)
|
ans = os.path.join(candidate, appname)
|
||||||
os.makedirs(ans, exist_ok=True)
|
os.makedirs(ans, exist_ok=True)
|
||||||
return ans
|
return ans
|
||||||
@@ -44,10 +61,7 @@ def _get_cache_dir():
|
|||||||
else:
|
else:
|
||||||
candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')
|
candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')
|
||||||
candidate = os.path.join(os.path.expanduser(candidate), appname)
|
candidate = os.path.join(os.path.expanduser(candidate), appname)
|
||||||
try:
|
os.makedirs(candidate, exist_ok=True)
|
||||||
os.makedirs(candidate)
|
|
||||||
except FileExistsError:
|
|
||||||
pass
|
|
||||||
return candidate
|
return candidate
|
||||||
|
|
||||||
|
|
||||||
@@ -77,7 +91,10 @@ logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba')
|
|||||||
try:
|
try:
|
||||||
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print('Failed to read login shell from /etc/passwd for current user, falling back to /bin/sh', file=sys.stderr)
|
try:
|
||||||
|
print('Failed to read login shell via getpwuid() for current user, falling back to /bin/sh', file=sys.stderr)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
shell_path = '/bin/sh'
|
shell_path = '/bin/sh'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ cursor_as_sgr(Cursor *self, Cursor *prev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
reset_display_attrs(Cursor *self) {
|
reset_display_attrs(Cursor *self, PyObject *a UNUSED) {
|
||||||
#define reset_display_attrs_doc "Reset all display attributes to unset"
|
#define reset_display_attrs_doc "Reset all display attributes to unset"
|
||||||
cursor_reset_display_attrs(self);
|
cursor_reset_display_attrs(self);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -272,7 +272,7 @@ void cursor_copy_to(Cursor *src, Cursor *dest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
copy(Cursor *self);
|
copy(Cursor *self, PyObject*);
|
||||||
#define copy_doc "Create a clone of this cursor"
|
#define copy_doc "Create a clone of this cursor"
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
@@ -342,7 +342,7 @@ cursor_copy(Cursor *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
copy(Cursor *self) {
|
copy(Cursor *self, PyObject *a UNUSED) {
|
||||||
return (PyObject*)cursor_copy(self);
|
return (PyObject*)cursor_copy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
#ifdef WITH_PROFILER
|
#ifdef WITH_PROFILER
|
||||||
#include <gperftools/profiler.h>
|
#include <gperftools/profiler.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -88,31 +87,6 @@ pyset_iutf8(PyObject UNUSED *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
handle_sigchld(int UNUSED signum, siginfo_t *sinfo, void UNUSED *unused) {
|
|
||||||
if (sinfo->si_code != CLD_EXITED) return;
|
|
||||||
int sav_errno = errno, status;
|
|
||||||
while(true) {
|
|
||||||
if (waitpid(sinfo->si_pid, &status, WNOHANG) == -1) {
|
|
||||||
if (errno != EINTR) break;
|
|
||||||
} else break;
|
|
||||||
}
|
|
||||||
// wakeup I/O loop as without this on macOS sometimes poll() does not detect the fd close, so
|
|
||||||
// kitty does not detect child death.
|
|
||||||
wakeup_io_loop(true);
|
|
||||||
errno = sav_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
install_sigchld_handler(PyObject UNUSED *self) {
|
|
||||||
struct sigaction sa;
|
|
||||||
sa.sa_flags = SA_SIGINFO;
|
|
||||||
sa.sa_sigaction = handle_sigchld;
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
if (sigaction(SIGCHLD, &sa, NULL) == -1) return PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WITH_PROFILER
|
#ifdef WITH_PROFILER
|
||||||
static PyObject*
|
static PyObject*
|
||||||
start_profiler(PyObject UNUSED *self, PyObject *args) {
|
start_profiler(PyObject UNUSED *self, PyObject *args) {
|
||||||
@@ -135,7 +109,6 @@ static PyMethodDef module_methods[] = {
|
|||||||
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},
|
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},
|
||||||
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
||||||
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
||||||
{"install_sigchld_handler", (PyCFunction)install_sigchld_handler, METH_NOARGS, ""},
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
METHODB(user_cache_dir, METH_NOARGS),
|
METHODB(user_cache_dir, METH_NOARGS),
|
||||||
#endif
|
#endif
|
||||||
@@ -174,6 +147,7 @@ extern bool init_graphics(PyObject *module);
|
|||||||
extern bool init_shaders(PyObject *module);
|
extern bool init_shaders(PyObject *module);
|
||||||
extern bool init_mouse(PyObject *module);
|
extern bool init_mouse(PyObject *module);
|
||||||
extern bool init_kittens(PyObject *module);
|
extern bool init_kittens(PyObject *module);
|
||||||
|
extern bool init_logging(PyObject *module);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
extern int init_CoreText(PyObject *);
|
extern int init_CoreText(PyObject *);
|
||||||
extern bool init_cocoa(PyObject *module);
|
extern bool init_cocoa(PyObject *module);
|
||||||
@@ -193,6 +167,7 @@ PyInit_fast_data_types(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m != NULL) {
|
if (m != NULL) {
|
||||||
|
if (!init_logging(m)) return NULL;
|
||||||
if (!init_LineBuf(m)) return NULL;
|
if (!init_LineBuf(m)) return NULL;
|
||||||
if (!init_HistoryBuf(m)) return NULL;
|
if (!init_HistoryBuf(m)) return NULL;
|
||||||
if (!init_Line(m)) return NULL;
|
if (!init_Line(m)) return NULL;
|
||||||
@@ -218,16 +193,15 @@ PyInit_fast_data_types(void) {
|
|||||||
#endif
|
#endif
|
||||||
if (!init_fonts(m)) return NULL;
|
if (!init_fonts(m)) return NULL;
|
||||||
|
|
||||||
#define OOF(n) #n, offsetof(Cell, n)
|
|
||||||
if (PyModule_AddObject(m, "CELL", Py_BuildValue("{sI sI sI sI sI sI sI sI sI}",
|
|
||||||
OOF(ch), OOF(fg), OOF(bg), OOF(decoration_fg), OOF(cc_idx), OOF(sprite_x), OOF(sprite_y), OOF(sprite_z), "size", sizeof(Cell))) != 0) return NULL;
|
|
||||||
#undef OOF
|
|
||||||
PyModule_AddIntConstant(m, "BOLD", BOLD_SHIFT);
|
PyModule_AddIntConstant(m, "BOLD", BOLD_SHIFT);
|
||||||
PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT);
|
PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT);
|
||||||
PyModule_AddIntConstant(m, "REVERSE", REVERSE_SHIFT);
|
PyModule_AddIntConstant(m, "REVERSE", REVERSE_SHIFT);
|
||||||
PyModule_AddIntConstant(m, "STRIKETHROUGH", STRIKE_SHIFT);
|
PyModule_AddIntConstant(m, "STRIKETHROUGH", STRIKE_SHIFT);
|
||||||
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
|
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
|
||||||
PyModule_AddStringMacro(m, ERROR_PREFIX);
|
PyModule_AddStringMacro(m, ERROR_PREFIX);
|
||||||
|
#ifdef KITTY_VCS_REV
|
||||||
|
PyModule_AddStringMacro(m, KITTY_VCS_REV);
|
||||||
|
#endif
|
||||||
PyModule_AddIntMacro(m, CURSOR_BLOCK);
|
PyModule_AddIntMacro(m, CURSOR_BLOCK);
|
||||||
PyModule_AddIntMacro(m, CURSOR_BEAM);
|
PyModule_AddIntMacro(m, CURSOR_BEAM);
|
||||||
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
|
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
// Required minimum OpenGL version
|
// Required minimum OpenGL version
|
||||||
#define OPENGL_REQUIRED_VERSION_MAJOR 3
|
#define OPENGL_REQUIRED_VERSION_MAJOR 3
|
||||||
#define OPENGL_REQUIRED_VERSION_MINOR 3
|
#define OPENGL_REQUIRED_VERSION_MINOR 3
|
||||||
|
#define GLFW_MOD_KITTY 1024
|
||||||
#define UNUSED __attribute__ ((unused))
|
#define UNUSED __attribute__ ((unused))
|
||||||
|
#define PYNOARG PyObject *__a1 UNUSED, PyObject *__a2 UNUSED
|
||||||
#define EXPORTED __attribute__ ((visibility ("default")))
|
#define EXPORTED __attribute__ ((visibility ("default")))
|
||||||
#define LIKELY(x) __builtin_expect (!!(x), 1)
|
#define LIKELY(x) __builtin_expect (!!(x), 1)
|
||||||
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
|
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
|
||||||
@@ -26,7 +28,8 @@
|
|||||||
#define xstr(s) str(s)
|
#define xstr(s) str(s)
|
||||||
#define str(s) #s
|
#define str(s) #s
|
||||||
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
||||||
#define fatal(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); }
|
void log_error(const char *fmt, ...);
|
||||||
|
#define fatal(...) { log_error(__VA_ARGS__); exit(EXIT_FAILURE); }
|
||||||
|
|
||||||
typedef unsigned long long id_type;
|
typedef unsigned long long id_type;
|
||||||
typedef uint32_t char_type;
|
typedef uint32_t char_type;
|
||||||
@@ -158,15 +161,18 @@ typedef struct {
|
|||||||
Line *line;
|
Line *line;
|
||||||
} LineBuf;
|
} LineBuf;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Cell *cells;
|
||||||
|
line_attrs_type *line_attrs;
|
||||||
|
} HistoryBufSegment;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
|
|
||||||
Cell *buf;
|
index_type xnum, ynum, num_segments;
|
||||||
index_type xnum, ynum;
|
HistoryBufSegment* segments;
|
||||||
Line *line;
|
Line *line;
|
||||||
index_type start_of_data, count;
|
index_type start_of_data, count;
|
||||||
line_attrs_type *line_attrs;
|
|
||||||
} HistoryBuf;
|
} HistoryBuf;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -207,7 +213,7 @@ typedef struct {
|
|||||||
|
|
||||||
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
|
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
|
||||||
|
|
||||||
#define left_shift_line(line, at, num) \
|
#define left_shift_line(line, at, num) { \
|
||||||
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
|
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
|
||||||
COPY_CELL(line, __i__ + (num), line, __i__) \
|
COPY_CELL(line, __i__ + (num), line, __i__) \
|
||||||
} \
|
} \
|
||||||
@@ -215,7 +221,8 @@ typedef struct {
|
|||||||
(line)->cells[(at)].ch = BLANK_CHAR; \
|
(line)->cells[(at)].ch = BLANK_CHAR; \
|
||||||
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
|
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
|
||||||
clear_sprite_position((line)->cells[(at)]); \
|
clear_sprite_position((line)->cells[(at)]); \
|
||||||
}
|
}\
|
||||||
|
}
|
||||||
|
|
||||||
#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \
|
#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \
|
||||||
if ((base)->capacity < num) { \
|
if ((base)->capacity < num) { \
|
||||||
@@ -262,6 +269,5 @@ void wakeup_io_loop(bool);
|
|||||||
void scroll_event(double, double);
|
void scroll_event(double, double);
|
||||||
void fake_scroll(int, bool);
|
void fake_scroll(int, bool);
|
||||||
void set_special_key_combo(int glfw_key, int mods);
|
void set_special_key_combo(int glfw_key, int mods);
|
||||||
void on_text_input(unsigned int codepoint, int mods);
|
void on_key_input(int key, int scancode, int action, int mods, const char*, int);
|
||||||
void on_key_input(int key, int scancode, int action, int mods);
|
|
||||||
void request_window_attention(id_type, bool);
|
void request_window_attention(id_type, bool);
|
||||||
|
|||||||
2
kitty/emoji.h
generated
2
kitty/emoji.h
generated
@@ -1,4 +1,4 @@
|
|||||||
// unicode data, built from the unicode standard on: 2018-02-09
|
// unicode data, built from the unicode standard on: 2018-05-01
|
||||||
// see gen-wcwidth.py
|
// see gen-wcwidth.py
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
|
|||||||
@@ -330,19 +330,39 @@ face_has_codepoint(PyObject* face, char_type cp) {
|
|||||||
return glyph_id_for_codepoint(face, cp) > 0;
|
return glyph_id_for_codepoint(face, cp) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
has_emoji_presentation(Cell *cell) {
|
||||||
|
return (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
has_cell_text(Font *self, Cell *cell) {
|
has_cell_text(Font *self, Cell *cell) {
|
||||||
if (!face_has_codepoint(self->face, cell->ch)) return false;
|
if (!face_has_codepoint(self->face, cell->ch)) return false;
|
||||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||||
if (!face_has_codepoint(self->face, codepoint_for_mark(cell->cc_idx[i]))) return false;
|
combining_type cc_idx = cell->cc_idx[i];
|
||||||
|
if (cc_idx == VS15 || cc_idx == VS16) continue;
|
||||||
|
if (!face_has_codepoint(self->face, codepoint_for_mark(cc_idx))) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
|
||||||
|
printf("U+%x ", cell->ch);
|
||||||
|
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||||
|
printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
|
||||||
|
}
|
||||||
|
if (bold) printf("bold ");
|
||||||
|
if (italic) printf("italic ");
|
||||||
|
if (emoji_presentation) printf("emoji_presentation ");
|
||||||
|
PyObject_Print(face, stdout, 0);
|
||||||
|
if (new_face) printf(" (new face)");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
static inline ssize_t
|
static inline ssize_t
|
||||||
load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation) {
|
load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation) {
|
||||||
if (fonts.fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return MISSING_FONT; }
|
if (fonts.fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; }
|
||||||
ssize_t f;
|
ssize_t f;
|
||||||
|
|
||||||
if (bold) f = fonts.italic_font_idx > 0 ? fonts.bi_font_idx : fonts.bold_font_idx;
|
if (bold) f = fonts.italic_font_idx > 0 ? fonts.bi_font_idx : fonts.bold_font_idx;
|
||||||
@@ -352,6 +372,7 @@ load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation)
|
|||||||
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic, emoji_presentation);
|
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic, emoji_presentation);
|
||||||
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
|
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
|
||||||
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
|
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
|
||||||
|
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face, true);
|
||||||
set_size_for_face(face, cell_height, true);
|
set_size_for_face(face, cell_height, true);
|
||||||
|
|
||||||
ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true);
|
ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true);
|
||||||
@@ -364,17 +385,17 @@ load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation)
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline ssize_t
|
static inline ssize_t
|
||||||
fallback_font(Cell *cell) {
|
fallback_font(Cell *cell) {
|
||||||
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
||||||
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
||||||
bool emoji_presentation = (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15;
|
bool emoji_presentation = has_emoji_presentation(cell);
|
||||||
|
|
||||||
// Check if one of the existing fallback fonts has this text
|
// Check if one of the existing fallback fonts has this text
|
||||||
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
|
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
|
||||||
Font *ff = fonts.fonts +j;
|
Font *ff = fonts.fonts +j;
|
||||||
if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) {
|
if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) {
|
||||||
|
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, ff->face, false);
|
||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,7 +439,7 @@ START_ALLOW_CASE_RANGE
|
|||||||
ans = fonts.bi_font_idx; break;
|
ans = fonts.bi_font_idx; break;
|
||||||
}
|
}
|
||||||
if (ans < 0) ans = fonts.medium_font_idx;
|
if (ans < 0) ans = fonts.medium_font_idx;
|
||||||
if (has_cell_text(fonts.fonts + ans, cell)) return ans;
|
if (!has_emoji_presentation(cell) && has_cell_text(fonts.fonts + ans, cell)) return ans;
|
||||||
return fallback_font(cell);
|
return fallback_font(cell);
|
||||||
}
|
}
|
||||||
END_ALLOW_CASE_RANGE
|
END_ALLOW_CASE_RANGE
|
||||||
@@ -744,7 +765,7 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
|
|||||||
if (num_cells_consumed) {
|
if (num_cells_consumed) {
|
||||||
if (num_cells_consumed > MAX_GLYPHS_IN_GROUP) {
|
if (num_cells_consumed > MAX_GLYPHS_IN_GROUP) {
|
||||||
// Nasty, a single glyph used more than MAX_GLYPHS_IN_GROUP cells, we cannot render this case correctly
|
// Nasty, a single glyph used more than MAX_GLYPHS_IN_GROUP cells, we cannot render this case correctly
|
||||||
fprintf(stderr, "The glyph: %u needs more than %u cells, cannot render it\n", glyph_id, MAX_GLYPHS_IN_GROUP);
|
log_error("The glyph: %u needs more than %u cells, cannot render it", glyph_id, MAX_GLYPHS_IN_GROUP);
|
||||||
current_group->num_glyphs--;
|
current_group->num_glyphs--;
|
||||||
while (num_cells_consumed) {
|
while (num_cells_consumed) {
|
||||||
G(group_idx)++; current_group = G(groups) + G(group_idx);
|
G(group_idx)++; current_group = G(groups) + G(group_idx);
|
||||||
@@ -773,6 +794,17 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
|
|||||||
#undef MAX_GLYPHS_IN_GROUP
|
#undef MAX_GLYPHS_IN_GROUP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
merge_groups_for_pua_space_ligature() {
|
||||||
|
if (G(group_idx) == 1) {
|
||||||
|
Group *g = G(groups), *g1 = G(groups) + 1;
|
||||||
|
g->num_cells += g1->num_cells;
|
||||||
|
g->num_glyphs += g1->num_glyphs;
|
||||||
|
g->num_glyphs = MIN(g->num_glyphs, MAX_NUM_EXTRA_GLYPHS + 1);
|
||||||
|
G(group_idx) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_groups(Font *font) {
|
render_groups(Font *font) {
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
@@ -783,7 +815,10 @@ render_groups(Font *font) {
|
|||||||
/* printf("Group: idx: %u num_cells: %u num_glyphs: %u first_glyph_idx: %u first_cell_idx: %u total_num_glyphs: %zu\n", */
|
/* printf("Group: idx: %u num_cells: %u num_glyphs: %u first_glyph_idx: %u first_cell_idx: %u total_num_glyphs: %zu\n", */
|
||||||
/* idx, group->num_cells, group->num_glyphs, group->first_glyph_idx, group->first_cell_idx, group_state.num_glyphs); */
|
/* idx, group->num_cells, group->num_glyphs, group->first_glyph_idx, group->first_cell_idx, group_state.num_glyphs); */
|
||||||
glyph_index primary = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;
|
glyph_index primary = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;
|
||||||
for (unsigned i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) ed.data[i-1] = G(info)[group->first_glyph_idx + i].codepoint;
|
unsigned int i;
|
||||||
|
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(group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed);
|
render_group(group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed);
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
@@ -817,7 +852,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||||||
|
|
||||||
PyObject *eg = PyTuple_New(MAX_NUM_EXTRA_GLYPHS);
|
PyObject *eg = PyTuple_New(MAX_NUM_EXTRA_GLYPHS);
|
||||||
for (size_t g = 0; g < MAX_NUM_EXTRA_GLYPHS; g++) PyTuple_SET_ITEM(eg, g, Py_BuildValue("H", g + 1 < group->num_glyphs ? G(info)[group->first_glyph_idx + g].codepoint : 0));
|
for (size_t g = 0; g < MAX_NUM_EXTRA_GLYPHS; g++) PyTuple_SET_ITEM(eg, g, Py_BuildValue("H", g + 1 < group->num_glyphs ? G(info)[group->first_glyph_idx + g].codepoint : 0));
|
||||||
PyList_Append(ans, Py_BuildValue("IIIN", group->num_cells, group->num_glyphs, first_glyph, eg));
|
PyList_Append(ans, Py_BuildValue("IIHN", group->num_cells, group->num_glyphs, first_glyph, eg));
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
if (face) { Py_CLEAR(face); free(font); }
|
if (face) { Py_CLEAR(face); free(font); }
|
||||||
@@ -826,10 +861,11 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||||||
#undef G
|
#undef G
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
|
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) {
|
||||||
switch(font_idx) {
|
switch(font_idx) {
|
||||||
default:
|
default:
|
||||||
shape_run(first_cell, num_cells, &fonts.fonts[font_idx]);
|
shape_run(first_cell, num_cells, &fonts.fonts[font_idx]);
|
||||||
|
if (pua_space_ligature) merge_groups_for_pua_space_ligature();
|
||||||
render_groups(&fonts.fonts[font_idx]);
|
render_groups(&fonts.fonts[font_idx]);
|
||||||
break;
|
break;
|
||||||
case BLANK_FONT:
|
case BLANK_FONT:
|
||||||
@@ -844,9 +880,14 @@ render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_private_use(char_type ch) {
|
||||||
|
return (0xe000 <= ch && ch <= 0xf8ff) || (0xF0000 <= ch && ch <= 0xFFFFF) || (0x100000 <= ch && ch <= 0x10FFFF);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
render_line(Line *line) {
|
render_line(Line *line) {
|
||||||
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx);
|
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false);
|
||||||
ssize_t run_font_idx = NO_FONT;
|
ssize_t run_font_idx = NO_FONT;
|
||||||
index_type first_cell_in_run, i;
|
index_type first_cell_in_run, i;
|
||||||
attrs_type prev_width = 0;
|
attrs_type prev_width = 0;
|
||||||
@@ -854,6 +895,22 @@ render_line(Line *line) {
|
|||||||
if (prev_width == 2) { prev_width = 0; continue; }
|
if (prev_width == 2) { prev_width = 0; continue; }
|
||||||
Cell *cell = line->cells + i;
|
Cell *cell = line->cells + i;
|
||||||
ssize_t cell_font_idx = font_for_cell(cell);
|
ssize_t cell_font_idx = font_for_cell(cell);
|
||||||
|
if (is_private_use(cell->ch) && i + 1 < line->xnum && (line->cells[i+1].ch == ' ' || line->cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) {
|
||||||
|
// We have a private use char followed by a space char, render it as a two cell ligature.
|
||||||
|
Cell *space_cell = line->cells + i+1;
|
||||||
|
// Ensure the space cell uses the foreground colors from the PUA cell
|
||||||
|
// This is needed because there are stupid applications like
|
||||||
|
// powerline that use PUA+space with different foreground colors
|
||||||
|
// for the space and the PUA. See for example: https://github.com/kovidgoyal/kitty/issues/467
|
||||||
|
space_cell->fg = cell->fg; space_cell->decoration_fg = cell->decoration_fg;
|
||||||
|
RENDER;
|
||||||
|
render_run(line->cells + i, 2, cell_font_idx, true);
|
||||||
|
run_font_idx = NO_FONT;
|
||||||
|
first_cell_in_run = i + 2;
|
||||||
|
prev_width = line->cells[i+1].attrs & WIDTH_MASK;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
prev_width = cell->attrs & WIDTH_MASK;
|
prev_width = cell->attrs & WIDTH_MASK;
|
||||||
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
|
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
|
||||||
if (run_font_idx == cell_font_idx) continue;
|
if (run_font_idx == cell_font_idx) continue;
|
||||||
@@ -931,7 +988,7 @@ static PyObject*
|
|||||||
test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
||||||
glyph_index glyph;
|
glyph_index glyph;
|
||||||
ExtraGlyphs extra_glyphs = {{0}};
|
ExtraGlyphs extra_glyphs = {{0}};
|
||||||
if (!PyArg_ParseTuple(args, "H|I", &glyph, &extra_glyphs.data)) return NULL;
|
if (!PyArg_ParseTuple(args, "H|H", &glyph, &extra_glyphs.data)) return NULL;
|
||||||
int error;
|
int error;
|
||||||
SpritePosition *pos = sprite_position_for(&fonts.fonts[fonts.medium_font_idx], glyph, &extra_glyphs, 0, &error);
|
SpritePosition *pos = sprite_position_for(&fonts.fonts[fonts.medium_font_idx], glyph, &extra_glyphs, 0, &error);
|
||||||
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
|
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
|
||||||
@@ -1015,7 +1072,7 @@ concat_cells(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
current_fonts(PyObject UNUSED *self) {
|
current_fonts(PYNOARG) {
|
||||||
PyObject *ans = PyDict_New();
|
PyObject *ans = PyDict_New();
|
||||||
if (!ans) return NULL;
|
if (!ans) return NULL;
|
||||||
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fonts.fonts[val].face) != 0) { goto error; }}
|
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fonts.fonts[val].face) != 0) { goto error; }}
|
||||||
@@ -1046,7 +1103,7 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
|
|||||||
static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)];
|
static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)];
|
||||||
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
|
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
|
||||||
cell.ch = char_buf[0];
|
cell.ch = char_buf[0];
|
||||||
for (unsigned i = 0; i + 1 < PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
|
for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
|
||||||
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
||||||
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
||||||
ssize_t ans = fallback_font(&cell);
|
ssize_t ans = fallback_font(&cell);
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
|
|
||||||
from kitty.fast_data_types import coretext_all_fonts
|
from kitty.fast_data_types import coretext_all_fonts
|
||||||
from kitty.utils import safe_print
|
from kitty.utils import log_error
|
||||||
|
|
||||||
attr_map = {(False, False): 'font_family',
|
attr_map = {(False, False): 'font_family',
|
||||||
(True, False): 'bold_font',
|
(True, False): 'bold_font',
|
||||||
@@ -63,10 +62,7 @@ def find_best_match(family, bold=False, italic=False):
|
|||||||
# Let CoreText choose the font if the family exists, otherwise
|
# Let CoreText choose the font if the family exists, otherwise
|
||||||
# fallback to Menlo
|
# fallback to Menlo
|
||||||
if q not in font_map['family_map']:
|
if q not in font_map['family_map']:
|
||||||
safe_print(
|
log_error('The font {} was not found, falling back to Menlo'.format(family))
|
||||||
'The font {} was not found, falling back to Menlo'.format(family),
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
family = 'Menlo'
|
family = 'Menlo'
|
||||||
return {
|
return {
|
||||||
'monospace': True,
|
'monospace': True,
|
||||||
|
|||||||
@@ -214,15 +214,20 @@ ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_dat
|
|||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
face_from_descriptor(PyObject *descriptor) {
|
face_from_descriptor(PyObject *descriptor) {
|
||||||
#define D(key, conv) { PyObject *t = PyDict_GetItemString(descriptor, #key); if (t == NULL) return NULL; key = conv(t); t = NULL; }
|
#define D(key, conv, missing_ok) { \
|
||||||
char *path;
|
PyObject *t = PyDict_GetItemString(descriptor, #key); \
|
||||||
long index;
|
if (t == NULL) { \
|
||||||
bool hinting;
|
if (!missing_ok) { PyErr_SetString(PyExc_KeyError, "font descriptor is missing the key: " #key); return NULL; } \
|
||||||
long hint_style;
|
} else key = conv(t); \
|
||||||
D(path, PyUnicode_AsUTF8);
|
}
|
||||||
D(index, PyLong_AsLong);
|
char *path = NULL;
|
||||||
D(hinting, PyObject_IsTrue);
|
long index = 0;
|
||||||
D(hint_style, PyLong_AsLong);
|
bool hinting = false;
|
||||||
|
long hint_style = 0;
|
||||||
|
D(path, PyUnicode_AsUTF8, false);
|
||||||
|
D(index, PyLong_AsLong, true);
|
||||||
|
D(hinting, PyObject_IsTrue, true);
|
||||||
|
D(hint_style, PyLong_AsLong, true);
|
||||||
#undef D
|
#undef D
|
||||||
Face *self = (Face *)Face_Type.tp_alloc(&Face_Type, 0);
|
Face *self = (Face *)Face_Type.tp_alloc(&Face_Type, 0);
|
||||||
if (self != NULL) {
|
if (self != NULL) {
|
||||||
@@ -279,7 +284,7 @@ calc_cell_width(Face *self) {
|
|||||||
for (char_type i = 32; i < 128; i++) {
|
for (char_type i = 32; i < 128; i++) {
|
||||||
int glyph_index = FT_Get_Char_Index(self->face, i);
|
int glyph_index = FT_Get_Char_Index(self->face, i);
|
||||||
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
|
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
|
||||||
ans = MAX(ans, (unsigned long)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
ans = MAX(ans, (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ans;
|
return ans;
|
||||||
@@ -324,7 +329,6 @@ typedef struct {
|
|||||||
unsigned int factor, right_edge;
|
unsigned int factor, right_edge;
|
||||||
} ProcessedBitmap;
|
} ProcessedBitmap;
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
trim_borders(ProcessedBitmap *ans, size_t extra) {
|
trim_borders(ProcessedBitmap *ans, size_t extra) {
|
||||||
bool column_has_text = false;
|
bool column_has_text = false;
|
||||||
@@ -354,9 +358,13 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
|
|||||||
ans->rows = bitmap->rows;
|
ans->rows = bitmap->rows;
|
||||||
ans->pixel_mode = bitmap->pixel_mode;
|
ans->pixel_mode = bitmap->pixel_mode;
|
||||||
if (ans->width > max_width) {
|
if (ans->width > max_width) {
|
||||||
size_t extra = bitmap->width - max_width;
|
size_t extra = ans->width - max_width;
|
||||||
if (italic && extra < cell_width / 2) {
|
if (italic && extra < cell_width / 2) {
|
||||||
trim_borders(ans, extra);
|
trim_borders(ans, extra);
|
||||||
|
} else if (extra == 2 && num_cells == 1) {
|
||||||
|
// there exist fonts that have bitmaps just a couple of pixels
|
||||||
|
// wider than their advances, rather than rescale, which looks
|
||||||
|
// bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352
|
||||||
} else if (rescale && self->is_scalable && extra > 1) {
|
} else if (rescale && self->is_scalable && extra > 1) {
|
||||||
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
|
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
|
||||||
float ar = (float)max_width / (float)bitmap->width;
|
float ar = (float)max_width / (float)bitmap->width;
|
||||||
@@ -532,7 +540,8 @@ render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *inf
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
display_name(Face *self) {
|
display_name(PyObject *s, PyObject *a UNUSED) {
|
||||||
|
Face *self = (Face*)s;
|
||||||
const char *psname = FT_Get_Postscript_Name(self->face);
|
const char *psname = FT_Get_Postscript_Name(self->face);
|
||||||
if (psname) return Py_BuildValue("s", psname);
|
if (psname) return Py_BuildValue("s", psname);
|
||||||
Py_INCREF(self->path);
|
Py_INCREF(self->path);
|
||||||
@@ -540,8 +549,8 @@ display_name(Face *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
extra_data(Face *self) {
|
extra_data(PyObject *self, PyObject *a UNUSED) {
|
||||||
return PyLong_FromVoidPtr(self->extra_data);
|
return PyLong_FromVoidPtr(((Face*)self)->extra_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
|
|||||||
6265
kitty/gl-wrapper.c
generated
6265
kitty/gl-wrapper.c
generated
File diff suppressed because it is too large
Load Diff
43
kitty/gl-wrapper.h
generated
43
kitty/gl-wrapper.h
generated
@@ -1,7 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
OpenGL loader generated by glad 0.1.20a0 on Fri May 4 18:11:55 2018.
|
||||||
OpenGL loader generated by glad 0.1.15a0 on Wed Nov 8 14:58:53 2017.
|
|
||||||
|
|
||||||
Language/Generator: C/C++ Debug
|
Language/Generator: C/C++ Debug
|
||||||
Specification: gl
|
Specification: gl
|
||||||
APIs: gl=3.3
|
APIs: gl=3.3
|
||||||
@@ -15,22 +13,17 @@
|
|||||||
Loader: True
|
Loader: True
|
||||||
Local files: False
|
Local files: False
|
||||||
Omit khrplatform: False
|
Omit khrplatform: False
|
||||||
|
|
||||||
Commandline:
|
Commandline:
|
||||||
--profile="core" --api="gl=3.3" --generator="c-debug" --spec="gl" --extensions="GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,GL_ARB_texture_storage,GL_KHR_debug"
|
--profile="core" --api="gl=3.3" --generator="c-debug" --spec="gl" --extensions="GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,GL_ARB_texture_storage,GL_KHR_debug"
|
||||||
Online:
|
Online:
|
||||||
http://glad.dav1d.de/#profile=core&language=c-debug&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_copy_image&extensions=GL_ARB_multisample&extensions=GL_ARB_robustness&extensions=GL_ARB_texture_storage&extensions=GL_KHR_debug
|
http://glad.dav1d.de/#profile=core&language=c-debug&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_copy_image&extensions=GL_ARB_multisample&extensions=GL_ARB_robustness&extensions=GL_ARB_texture_storage&extensions=GL_KHR_debug
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef __glad_h_
|
#ifndef __glad_h_
|
||||||
#define __glad_h_
|
#define __glad_h_
|
||||||
|
|
||||||
#ifdef __gl_h_
|
#ifdef __gl_h_
|
||||||
#error OpenGL header already included, remove this include, glad already provides it
|
#error OpenGL header already included, remove this include, glad already provides it
|
||||||
#endif
|
#endif
|
||||||
#define __gl_h_
|
#define __gl_h_
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_LEAN_AND_MEAN 1
|
#define WIN32_LEAN_AND_MEAN 1
|
||||||
@@ -40,25 +33,20 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef APIENTRY
|
#ifndef APIENTRY
|
||||||
#define APIENTRY
|
#define APIENTRY
|
||||||
#endif
|
#endif
|
||||||
#ifndef APIENTRYP
|
#ifndef APIENTRYP
|
||||||
#define APIENTRYP APIENTRY *
|
#define APIENTRYP APIENTRY *
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct gladGLversionStruct {
|
struct gladGLversionStruct {
|
||||||
int major;
|
int major;
|
||||||
int minor;
|
int minor;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void* (* GLADloadproc)(const char *name);
|
typedef void* (* GLADloadproc)(const char *name);
|
||||||
|
|
||||||
#ifndef GLAPI
|
#ifndef GLAPI
|
||||||
# if defined(GLAD_GLAPI_EXPORT)
|
# if defined(GLAD_GLAPI_EXPORT)
|
||||||
# if defined(_WIN32) || defined(__CYGWIN__)
|
# if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
@@ -84,17 +72,11 @@ typedef void* (* GLADloadproc)(const char *name);
|
|||||||
# define GLAPI extern
|
# define GLAPI extern
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GLAPI struct gladGLversionStruct GLVersion;
|
GLAPI struct gladGLversionStruct GLVersion;
|
||||||
|
|
||||||
GLAPI int gladLoadGL(void);
|
GLAPI int gladLoadGL(void);
|
||||||
|
|
||||||
GLAPI int gladLoadGLLoader(GLADloadproc);
|
GLAPI int gladLoadGLLoader(GLADloadproc);
|
||||||
|
|
||||||
|
|
||||||
#define GLAD_DEBUG
|
#define GLAD_DEBUG
|
||||||
typedef void (* GLADcallback)(const char *name, void *funcptr, int len_args, ...);
|
typedef void (* GLADcallback)(const char *name, void *funcptr, int len_args, ...);
|
||||||
|
|
||||||
GLAPI void glad_set_pre_callback(GLADcallback cb);
|
GLAPI void glad_set_pre_callback(GLADcallback cb);
|
||||||
GLAPI void glad_set_post_callback(GLADcallback cb);
|
GLAPI void glad_set_post_callback(GLADcallback cb);
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
@@ -1112,11 +1094,11 @@ typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap);
|
|||||||
GLAPI PFNGLENABLEPROC glad_glEnable;
|
GLAPI PFNGLENABLEPROC glad_glEnable;
|
||||||
GLAPI PFNGLENABLEPROC glad_debug_glEnable;
|
GLAPI PFNGLENABLEPROC glad_debug_glEnable;
|
||||||
#define glEnable glad_debug_glEnable
|
#define glEnable glad_debug_glEnable
|
||||||
typedef void (APIENTRYP PFNGLFINISHPROC)();
|
typedef void (APIENTRYP PFNGLFINISHPROC)(void);
|
||||||
GLAPI PFNGLFINISHPROC glad_glFinish;
|
GLAPI PFNGLFINISHPROC glad_glFinish;
|
||||||
GLAPI PFNGLFINISHPROC glad_debug_glFinish;
|
GLAPI PFNGLFINISHPROC glad_debug_glFinish;
|
||||||
#define glFinish glad_debug_glFinish
|
#define glFinish glad_debug_glFinish
|
||||||
typedef void (APIENTRYP PFNGLFLUSHPROC)();
|
typedef void (APIENTRYP PFNGLFLUSHPROC)(void);
|
||||||
GLAPI PFNGLFLUSHPROC glad_glFlush;
|
GLAPI PFNGLFLUSHPROC glad_glFlush;
|
||||||
GLAPI PFNGLFLUSHPROC glad_debug_glFlush;
|
GLAPI PFNGLFLUSHPROC glad_debug_glFlush;
|
||||||
#define glFlush glad_debug_glFlush
|
#define glFlush glad_debug_glFlush
|
||||||
@@ -1164,7 +1146,7 @@ typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data);
|
|||||||
GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev;
|
GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev;
|
||||||
GLAPI PFNGLGETDOUBLEVPROC glad_debug_glGetDoublev;
|
GLAPI PFNGLGETDOUBLEVPROC glad_debug_glGetDoublev;
|
||||||
#define glGetDoublev glad_debug_glGetDoublev
|
#define glGetDoublev glad_debug_glGetDoublev
|
||||||
typedef GLenum (APIENTRYP PFNGLGETERRORPROC)();
|
typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void);
|
||||||
GLAPI PFNGLGETERRORPROC glad_glGetError;
|
GLAPI PFNGLGETERRORPROC glad_glGetError;
|
||||||
GLAPI PFNGLGETERRORPROC glad_debug_glGetError;
|
GLAPI PFNGLGETERRORPROC glad_debug_glGetError;
|
||||||
#define glGetError glad_debug_glGetError
|
#define glGetError glad_debug_glGetError
|
||||||
@@ -1204,7 +1186,7 @@ typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap);
|
|||||||
GLAPI PFNGLISENABLEDPROC glad_glIsEnabled;
|
GLAPI PFNGLISENABLEDPROC glad_glIsEnabled;
|
||||||
GLAPI PFNGLISENABLEDPROC glad_debug_glIsEnabled;
|
GLAPI PFNGLISENABLEDPROC glad_debug_glIsEnabled;
|
||||||
#define glIsEnabled glad_debug_glIsEnabled
|
#define glIsEnabled glad_debug_glIsEnabled
|
||||||
typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble near, GLdouble far);
|
typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f);
|
||||||
GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange;
|
GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange;
|
||||||
GLAPI PFNGLDEPTHRANGEPROC glad_debug_glDepthRange;
|
GLAPI PFNGLDEPTHRANGEPROC glad_debug_glDepthRange;
|
||||||
#define glDepthRange glad_debug_glDepthRange
|
#define glDepthRange glad_debug_glDepthRange
|
||||||
@@ -1484,7 +1466,7 @@ typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);
|
|||||||
GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;
|
GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;
|
||||||
GLAPI PFNGLCOMPILESHADERPROC glad_debug_glCompileShader;
|
GLAPI PFNGLCOMPILESHADERPROC glad_debug_glCompileShader;
|
||||||
#define glCompileShader glad_debug_glCompileShader
|
#define glCompileShader glad_debug_glCompileShader
|
||||||
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)();
|
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void);
|
||||||
GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
|
GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
|
||||||
GLAPI PFNGLCREATEPROGRAMPROC glad_debug_glCreateProgram;
|
GLAPI PFNGLCREATEPROGRAMPROC glad_debug_glCreateProgram;
|
||||||
#define glCreateProgram glad_debug_glCreateProgram
|
#define glCreateProgram glad_debug_glCreateProgram
|
||||||
@@ -1884,7 +1866,7 @@ typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode);
|
|||||||
GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;
|
GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;
|
||||||
GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_debug_glBeginTransformFeedback;
|
GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_debug_glBeginTransformFeedback;
|
||||||
#define glBeginTransformFeedback glad_debug_glBeginTransformFeedback
|
#define glBeginTransformFeedback glad_debug_glBeginTransformFeedback
|
||||||
typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)();
|
typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void);
|
||||||
GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;
|
GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;
|
||||||
GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_debug_glEndTransformFeedback;
|
GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_debug_glEndTransformFeedback;
|
||||||
#define glEndTransformFeedback glad_debug_glEndTransformFeedback
|
#define glEndTransformFeedback glad_debug_glEndTransformFeedback
|
||||||
@@ -1912,7 +1894,7 @@ typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode)
|
|||||||
GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;
|
GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;
|
||||||
GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_debug_glBeginConditionalRender;
|
GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_debug_glBeginConditionalRender;
|
||||||
#define glBeginConditionalRender glad_debug_glBeginConditionalRender
|
#define glBeginConditionalRender glad_debug_glBeginConditionalRender
|
||||||
typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)();
|
typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(void);
|
||||||
GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;
|
GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;
|
||||||
GLAPI PFNGLENDCONDITIONALRENDERPROC glad_debug_glEndConditionalRender;
|
GLAPI PFNGLENDCONDITIONALRENDERPROC glad_debug_glEndConditionalRender;
|
||||||
#define glEndConditionalRender glad_debug_glEndConditionalRender
|
#define glEndConditionalRender glad_debug_glEndConditionalRender
|
||||||
@@ -2678,7 +2660,7 @@ GLAPI PFNGLSAMPLECOVERAGEARBPROC glad_debug_glSampleCoverageARB;
|
|||||||
#ifndef GL_ARB_robustness
|
#ifndef GL_ARB_robustness
|
||||||
#define GL_ARB_robustness 1
|
#define GL_ARB_robustness 1
|
||||||
GLAPI int GLAD_GL_ARB_robustness;
|
GLAPI int GLAD_GL_ARB_robustness;
|
||||||
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC)();
|
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC)(void);
|
||||||
GLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB;
|
GLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB;
|
||||||
GLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_debug_glGetGraphicsResetStatusARB;
|
GLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_debug_glGetGraphicsResetStatusARB;
|
||||||
#define glGetGraphicsResetStatusARB glad_debug_glGetGraphicsResetStatusARB
|
#define glGetGraphicsResetStatusARB glad_debug_glGetGraphicsResetStatusARB
|
||||||
@@ -2798,7 +2780,7 @@ typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsiz
|
|||||||
GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup;
|
GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup;
|
||||||
GLAPI PFNGLPUSHDEBUGGROUPPROC glad_debug_glPushDebugGroup;
|
GLAPI PFNGLPUSHDEBUGGROUPPROC glad_debug_glPushDebugGroup;
|
||||||
#define glPushDebugGroup glad_debug_glPushDebugGroup
|
#define glPushDebugGroup glad_debug_glPushDebugGroup
|
||||||
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)();
|
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void);
|
||||||
GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;
|
GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;
|
||||||
GLAPI PFNGLPOPDEBUGGROUPPROC glad_debug_glPopDebugGroup;
|
GLAPI PFNGLPOPDEBUGGROUPPROC glad_debug_glPopDebugGroup;
|
||||||
#define glPopDebugGroup glad_debug_glPopDebugGroup
|
#define glPopDebugGroup glad_debug_glPopDebugGroup
|
||||||
@@ -2842,7 +2824,7 @@ typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GL
|
|||||||
GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR;
|
GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR;
|
||||||
GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_debug_glPushDebugGroupKHR;
|
GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_debug_glPushDebugGroupKHR;
|
||||||
#define glPushDebugGroupKHR glad_debug_glPushDebugGroupKHR
|
#define glPushDebugGroupKHR glad_debug_glPushDebugGroupKHR
|
||||||
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)();
|
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void);
|
||||||
GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR;
|
GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR;
|
||||||
GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_debug_glPopDebugGroupKHR;
|
GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_debug_glPopDebugGroupKHR;
|
||||||
#define glPopDebugGroupKHR glad_debug_glPopDebugGroupKHR
|
#define glPopDebugGroupKHR glad_debug_glPopDebugGroupKHR
|
||||||
@@ -2867,11 +2849,8 @@ GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR;
|
|||||||
GLAPI PFNGLGETPOINTERVKHRPROC glad_debug_glGetPointervKHR;
|
GLAPI PFNGLGETPOINTERVKHRPROC glad_debug_glGetPointervKHR;
|
||||||
#define glGetPointervKHR glad_debug_glGetPointervKHR
|
#define glGetPointervKHR glad_debug_glGetPointervKHR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int init_glad(GLADloadproc, int);
|
int init_glad(GLADloadproc, int);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ gl_init() {
|
|||||||
ARB_TEST(texture_storage);
|
ARB_TEST(texture_storage);
|
||||||
#undef ARB_TEST
|
#undef ARB_TEST
|
||||||
glad_loaded = true;
|
glad_loaded = true;
|
||||||
|
if (global_state.debug_gl) printf("GL version string: '%s' Detected version: %d.%d\n", glGetString(GL_VERSION), GLVersion.major, GLVersion.minor);
|
||||||
if (GLVersion.major < OPENGL_REQUIRED_VERSION_MAJOR || (GLVersion.major == OPENGL_REQUIRED_VERSION_MAJOR && GLVersion.minor < OPENGL_REQUIRED_VERSION_MINOR)) {
|
if (GLVersion.major < OPENGL_REQUIRED_VERSION_MAJOR || (GLVersion.major == OPENGL_REQUIRED_VERSION_MAJOR && GLVersion.minor < OPENGL_REQUIRED_VERSION_MINOR)) {
|
||||||
fatal("OpenGL version is %d.%d, version >= 3.3 required for kitty", GLVersion.major, GLVersion.minor);
|
fatal("OpenGL version is %d.%d, version >= 3.3 required for kitty", GLVersion.major, GLVersion.minor);
|
||||||
}
|
}
|
||||||
@@ -117,7 +118,7 @@ compile_shader(GLenum shader_type, const char *source) {
|
|||||||
if (ret != GL_TRUE) {
|
if (ret != GL_TRUE) {
|
||||||
GLsizei len;
|
GLsizei len;
|
||||||
glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);
|
glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);
|
||||||
fprintf(stderr, "Failed to compile GLSL shader!\n%s", glbuf);
|
log_error("Failed to compile GLSL shader!\n%s", glbuf);
|
||||||
glDeleteShader(shader_id);
|
glDeleteShader(shader_id);
|
||||||
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
12
kitty/glfw-wrapper.c
generated
12
kitty/glfw-wrapper.c
generated
@@ -254,14 +254,8 @@ load_glfw(const char* path) {
|
|||||||
*(void **) (&glfwSetCursor_impl) = dlsym(handle, "glfwSetCursor");
|
*(void **) (&glfwSetCursor_impl) = dlsym(handle, "glfwSetCursor");
|
||||||
if (glfwSetCursor_impl == NULL) fail("Failed to load glfw function glfwSetCursor with error: %s", dlerror());
|
if (glfwSetCursor_impl == NULL) fail("Failed to load glfw function glfwSetCursor with error: %s", dlerror());
|
||||||
|
|
||||||
*(void **) (&glfwSetKeyCallback_impl) = dlsym(handle, "glfwSetKeyCallback");
|
*(void **) (&glfwSetKeyboardCallback_impl) = dlsym(handle, "glfwSetKeyboardCallback");
|
||||||
if (glfwSetKeyCallback_impl == NULL) fail("Failed to load glfw function glfwSetKeyCallback with error: %s", dlerror());
|
if (glfwSetKeyboardCallback_impl == NULL) fail("Failed to load glfw function glfwSetKeyboardCallback with error: %s", dlerror());
|
||||||
|
|
||||||
*(void **) (&glfwSetCharCallback_impl) = dlsym(handle, "glfwSetCharCallback");
|
|
||||||
if (glfwSetCharCallback_impl == NULL) fail("Failed to load glfw function glfwSetCharCallback with error: %s", dlerror());
|
|
||||||
|
|
||||||
*(void **) (&glfwSetCharModsCallback_impl) = dlsym(handle, "glfwSetCharModsCallback");
|
|
||||||
if (glfwSetCharModsCallback_impl == NULL) fail("Failed to load glfw function glfwSetCharModsCallback with error: %s", dlerror());
|
|
||||||
|
|
||||||
*(void **) (&glfwSetMouseButtonCallback_impl) = dlsym(handle, "glfwSetMouseButtonCallback");
|
*(void **) (&glfwSetMouseButtonCallback_impl) = dlsym(handle, "glfwSetMouseButtonCallback");
|
||||||
if (glfwSetMouseButtonCallback_impl == NULL) fail("Failed to load glfw function glfwSetMouseButtonCallback with error: %s", dlerror());
|
if (glfwSetMouseButtonCallback_impl == NULL) fail("Failed to load glfw function glfwSetMouseButtonCallback with error: %s", dlerror());
|
||||||
@@ -363,6 +357,8 @@ load_glfw(const char* path) {
|
|||||||
|
|
||||||
*(void **) (&glfwGetCocoaMonitor_impl) = dlsym(handle, "glfwGetCocoaMonitor");
|
*(void **) (&glfwGetCocoaMonitor_impl) = dlsym(handle, "glfwGetCocoaMonitor");
|
||||||
|
|
||||||
|
*(void **) (&glfwSetCocoaTextInputFilter_impl) = dlsym(handle, "glfwSetCocoaTextInputFilter");
|
||||||
|
|
||||||
*(void **) (&glfwGetX11Display_impl) = dlsym(handle, "glfwGetX11Display");
|
*(void **) (&glfwGetX11Display_impl) = dlsym(handle, "glfwGetX11Display");
|
||||||
|
|
||||||
*(void **) (&glfwGetX11Window_impl) = dlsym(handle, "glfwGetX11Window");
|
*(void **) (&glfwGetX11Window_impl) = dlsym(handle, "glfwGetX11Window");
|
||||||
|
|||||||
85
kitty/glfw-wrapper.h
generated
85
kitty/glfw-wrapper.h
generated
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int);
|
||||||
|
|
||||||
|
|
||||||
/*! @name GLFW version macros
|
/*! @name GLFW version macros
|
||||||
@@ -1133,9 +1134,15 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
|||||||
*/
|
*/
|
||||||
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
||||||
|
|
||||||
/*! @brief The function signature for keyboard key callbacks.
|
/*! @brief The function signature for key callbacks.
|
||||||
*
|
*
|
||||||
* This is the function signature for keyboard key callback functions.
|
* This is the function signature for key callback functions.
|
||||||
|
* 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
|
||||||
|
* will report the text "+" and the key as GLFW_KEY_EQUAL. The reported key takes into
|
||||||
|
* account any current keyboard maps defined in the OS. So with a dvorak mapping, pressing
|
||||||
|
* the "s" key will generate text "o" and GLFW_KEY_O.
|
||||||
*
|
*
|
||||||
* @param[in] window The window that received the event.
|
* @param[in] window The window that received the event.
|
||||||
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
||||||
@@ -1143,56 +1150,22 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
|||||||
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
||||||
* held down.
|
* held down.
|
||||||
|
* @param[in] text UTF-8 encoded text generated by this key event or empty string.
|
||||||
|
* @param[in] reserved Reserved for future use.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* than the unshifted key. So for example, if ISO_Shift_Level_5 is used to
|
||||||
|
* convert the key A into UP GLFW will report the key as UP with no modifiers.
|
||||||
*
|
*
|
||||||
* @sa @ref input_key
|
* @sa @ref input_key
|
||||||
* @sa @ref glfwSetKeyCallback
|
* @sa @ref glfwSetKeyboardCallback
|
||||||
*
|
*
|
||||||
* @since Added in version 1.0.
|
* @since Added in version 4.0.
|
||||||
* @glfw3 Added window handle, scancode and modifier mask parameters.
|
|
||||||
*
|
*
|
||||||
* @ingroup input
|
* @ingroup input
|
||||||
*/
|
*/
|
||||||
typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int);
|
typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, int);
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character callback functions.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharCallback
|
|
||||||
*
|
|
||||||
* @since Added in version 2.4.
|
|
||||||
* @glfw3 Added window handle parameter.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character with modifiers
|
|
||||||
* callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character with modifiers callback
|
|
||||||
* functions. It is called for each input character, regardless of what
|
|
||||||
* modifier keys are held down.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
|
||||||
* held down.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharModsCallback
|
|
||||||
*
|
|
||||||
* @deprecated Scheduled for removal in version 4.0.
|
|
||||||
*
|
|
||||||
* @since Added in version 3.1.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for file drop callbacks.
|
/*! @brief The function signature for file drop callbacks.
|
||||||
*
|
*
|
||||||
@@ -1585,7 +1558,7 @@ typedef void (*glfwRequestWindowAttention_func)(GLFWwindow*);
|
|||||||
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
|
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
|
||||||
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
|
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
|
||||||
|
|
||||||
typedef int (*glfwWindowBell_func)(GLFWwindow*, int64_t);
|
typedef int (*glfwWindowBell_func)(GLFWwindow*);
|
||||||
glfwWindowBell_func glfwWindowBell_impl;
|
glfwWindowBell_func glfwWindowBell_impl;
|
||||||
#define glfwWindowBell glfwWindowBell_impl
|
#define glfwWindowBell glfwWindowBell_impl
|
||||||
|
|
||||||
@@ -1713,17 +1686,9 @@ typedef void (*glfwSetCursor_func)(GLFWwindow*, GLFWcursor*);
|
|||||||
glfwSetCursor_func glfwSetCursor_impl;
|
glfwSetCursor_func glfwSetCursor_impl;
|
||||||
#define glfwSetCursor glfwSetCursor_impl
|
#define glfwSetCursor glfwSetCursor_impl
|
||||||
|
|
||||||
typedef GLFWkeyfun (*glfwSetKeyCallback_func)(GLFWwindow*, GLFWkeyfun);
|
typedef GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboardfun);
|
||||||
glfwSetKeyCallback_func glfwSetKeyCallback_impl;
|
glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl;
|
||||||
#define glfwSetKeyCallback glfwSetKeyCallback_impl
|
#define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl
|
||||||
|
|
||||||
typedef GLFWcharfun (*glfwSetCharCallback_func)(GLFWwindow*, GLFWcharfun);
|
|
||||||
glfwSetCharCallback_func glfwSetCharCallback_impl;
|
|
||||||
#define glfwSetCharCallback glfwSetCharCallback_impl
|
|
||||||
|
|
||||||
typedef GLFWcharmodsfun (*glfwSetCharModsCallback_func)(GLFWwindow*, GLFWcharmodsfun);
|
|
||||||
glfwSetCharModsCallback_func glfwSetCharModsCallback_impl;
|
|
||||||
#define glfwSetCharModsCallback glfwSetCharModsCallback_impl
|
|
||||||
|
|
||||||
typedef GLFWmousebuttonfun (*glfwSetMouseButtonCallback_func)(GLFWwindow*, GLFWmousebuttonfun);
|
typedef GLFWmousebuttonfun (*glfwSetMouseButtonCallback_func)(GLFWwindow*, GLFWmousebuttonfun);
|
||||||
glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;
|
glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;
|
||||||
@@ -1861,6 +1826,10 @@ typedef uint32_t (*glfwGetCocoaMonitor_func)(GLFWmonitor*);
|
|||||||
glfwGetCocoaMonitor_func glfwGetCocoaMonitor_impl;
|
glfwGetCocoaMonitor_func glfwGetCocoaMonitor_impl;
|
||||||
#define glfwGetCocoaMonitor glfwGetCocoaMonitor_impl
|
#define glfwGetCocoaMonitor glfwGetCocoaMonitor_impl
|
||||||
|
|
||||||
|
typedef GLFWcocoatextinputfilterfun (*glfwSetCocoaTextInputFilter_func)(GLFWwindow*, GLFWcocoatextinputfilterfun);
|
||||||
|
glfwSetCocoaTextInputFilter_func glfwSetCocoaTextInputFilter_impl;
|
||||||
|
#define glfwSetCocoaTextInputFilter glfwSetCocoaTextInputFilter_impl
|
||||||
|
|
||||||
typedef void* (*glfwGetX11Display_func)();
|
typedef void* (*glfwGetX11Display_func)();
|
||||||
glfwGetX11Display_func glfwGetX11Display_impl;
|
glfwGetX11Display_func glfwGetX11Display_impl;
|
||||||
#define glfwGetX11Display glfwGetX11Display_impl
|
#define glfwGetX11Display glfwGetX11Display_impl
|
||||||
|
|||||||
142
kitty/glfw.c
142
kitty/glfw.c
@@ -9,13 +9,15 @@
|
|||||||
#include "glfw-wrapper.h"
|
#include "glfw-wrapper.h"
|
||||||
extern bool cocoa_make_window_resizable(void *w);
|
extern bool cocoa_make_window_resizable(void *w);
|
||||||
extern void cocoa_create_global_menu(void);
|
extern void cocoa_create_global_menu(void);
|
||||||
extern void cocoa_set_titlebar_color(void *w);
|
extern void cocoa_set_hide_from_tasks(void);
|
||||||
|
extern void cocoa_set_titlebar_color(void *w, color_type color);
|
||||||
|
|
||||||
#if GLFW_KEY_LAST >= MAX_KEY_COUNT
|
#if GLFW_KEY_LAST >= MAX_KEY_COUNT
|
||||||
#error "glfw has too many keys, you should increase MAX_KEY_COUNT"
|
#error "glfw has too many keys, you should increase MAX_KEY_COUNT"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
|
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
|
||||||
|
static bool event_loop_blocking_with_no_timeout = false;
|
||||||
|
|
||||||
void
|
void
|
||||||
update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
||||||
@@ -30,6 +32,8 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
|||||||
window->has_pending_resizes = false;
|
window->has_pending_resizes = false;
|
||||||
window->viewport_width = MAX(window->viewport_width, 100);
|
window->viewport_width = MAX(window->viewport_width, 100);
|
||||||
window->viewport_height = MAX(window->viewport_height, 100);
|
window->viewport_height = MAX(window->viewport_height, 100);
|
||||||
|
window->window_width = MAX(w, 100);
|
||||||
|
window->window_height = MAX(h, 100);
|
||||||
if (notify_boss) {
|
if (notify_boss) {
|
||||||
call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False);
|
call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False);
|
||||||
}
|
}
|
||||||
@@ -72,9 +76,20 @@ is_window_ready_for_callbacks() {
|
|||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
show_mouse_cursor(GLFWwindow *w) {
|
show_mouse_cursor(GLFWwindow *w) {
|
||||||
if (glfwGetInputMode(w, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
|
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On Cocoa, glfwWaitEvents() can block indefinitely because of the way Cocoa
|
||||||
|
// works. See https://github.com/glfw/glfw/issues/1251. I have noticed this
|
||||||
|
// happening in particular with window resize events, when waiting with no
|
||||||
|
// timeout. See https://github.com/kovidgoyal/kitty/issues/458
|
||||||
|
// So we use an unlovely hack to workaround that case
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define unjam_event_loop() { if (event_loop_blocking_with_no_timeout) wakeup_main_loop(); }
|
||||||
|
#else
|
||||||
|
#define unjam_event_loop()
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
||||||
if (!set_callback_window(w)) return;
|
if (!set_callback_window(w)) return;
|
||||||
@@ -82,7 +97,8 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
|||||||
OSWindow *window = global_state.callback_os_window;
|
OSWindow *window = global_state.callback_os_window;
|
||||||
window->has_pending_resizes = true; global_state.has_pending_resizes = true;
|
window->has_pending_resizes = true; global_state.has_pending_resizes = true;
|
||||||
window->last_resize_event_at = monotonic();
|
window->last_resize_event_at = monotonic();
|
||||||
} else fprintf(stderr, "Ignoring resize request for tiny size: %dx%d\n", width, height);
|
unjam_event_loop();
|
||||||
|
} else log_error("Ignoring resize request for tiny size: %dx%d", width, height);
|
||||||
global_state.callback_os_window = NULL;
|
global_state.callback_os_window = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +110,13 @@ refresh_callback(GLFWwindow *w) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
char_mods_callback(GLFWwindow *w, unsigned int codepoint, int mods) {
|
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods, const char* text, int state) {
|
||||||
if (!set_callback_window(w)) return;
|
|
||||||
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
|
|
||||||
if (is_window_ready_for_callbacks()) on_text_input(codepoint, mods);
|
|
||||||
global_state.callback_os_window = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) {
|
|
||||||
if (!set_callback_window(w)) return;
|
if (!set_callback_window(w)) return;
|
||||||
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
|
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
|
||||||
if (key >= 0 && key <= GLFW_KEY_LAST) {
|
if (key >= 0 && key <= GLFW_KEY_LAST) {
|
||||||
global_state.callback_os_window->is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
|
global_state.callback_os_window->is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
|
||||||
if (is_window_ready_for_callbacks()) on_key_input(key, scancode, action, mods);
|
|
||||||
}
|
}
|
||||||
|
if (is_window_ready_for_callbacks()) on_key_input(key, scancode, action, mods, text, state);
|
||||||
global_state.callback_os_window = NULL;
|
global_state.callback_os_window = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +237,11 @@ static GLFWimage logo = {0};
|
|||||||
static PyObject*
|
static PyObject*
|
||||||
set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
|
set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
|
||||||
Py_ssize_t sz;
|
Py_ssize_t sz;
|
||||||
if(!PyArg_ParseTuple(args, "s#ii", &(logo.pixels), &sz, &(logo.width), &(logo.height))) return NULL;
|
const char *logo_data;
|
||||||
|
if(!PyArg_ParseTuple(args, "s#ii", &(logo_data), &sz, &(logo.width), &(logo.height))) return NULL;
|
||||||
|
sz = (MAX(logo.width * logo.height, sz));
|
||||||
|
logo.pixels = malloc(sz);
|
||||||
|
if (logo.pixels) memcpy(logo.pixels, logo_data, sz);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,14 +308,31 @@ set_dpi_from_os_window(OSWindow *w) {
|
|||||||
global_state.logical_dpi_y = yscale * factor;
|
global_state.logical_dpi_y = yscale * factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_first_window = true;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
static int
|
||||||
|
filter_option(int key UNUSED, int mods, unsigned int scancode UNUSED) {
|
||||||
|
return ((mods == GLFW_MOD_ALT) || (mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))) ? 1 : 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
set_titlebar_color(OSWindow *w, color_type color) {
|
||||||
|
if (w->handle && (!w->last_titlebar_color || (w->last_titlebar_color & 0xffffff) != (color & 0xffffff))) {
|
||||||
|
w->last_titlebar_color = (1 << 24) | (color & 0xffffff);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
cocoa_set_titlebar_color(glfwGetCocoaWindow(w->handle), color);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
create_os_window(PyObject UNUSED *self, PyObject *args) {
|
create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||||
int width, height, x = -1, y = -1;
|
int width, height, x = -1, y = -1;
|
||||||
char *title, *wm_class_class, *wm_class_name;
|
char *title, *wm_class_class, *wm_class_name;
|
||||||
PyObject *load_programs = NULL;
|
PyObject *load_programs = NULL;
|
||||||
if (!PyArg_ParseTuple(args, "iisss|Oiii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
|
if (!PyArg_ParseTuple(args, "iisss|Oii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
|
||||||
bool is_first_window = standard_cursor == NULL;
|
|
||||||
|
|
||||||
if (is_first_window) {
|
if (is_first_window) {
|
||||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||||
@@ -336,11 +365,11 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
PyErr_SetString(PyExc_ValueError, "Too many windows");
|
PyErr_SetString(PyExc_ValueError, "Too many windows");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
bool want_semi_transparent = (1.0 - OPT(background_opacity) > 0.1) ? true : false;
|
bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) ? true : false;
|
||||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
|
||||||
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
||||||
if (glfw_window == NULL) {
|
if (glfw_window == NULL) {
|
||||||
fprintf(stderr, "Failed to create a window at size: %dx%d, using a standard size instead.\n", width, height);
|
log_error("Failed to create a window at size: %dx%d, using a standard size instead.", width, height);
|
||||||
glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
||||||
}
|
}
|
||||||
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
|
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
|
||||||
@@ -356,7 +385,11 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
Py_DECREF(ret);
|
Py_DECREF(ret);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
cocoa_create_global_menu();
|
cocoa_create_global_menu();
|
||||||
|
if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option);
|
||||||
|
// This needs to be done only after the first window has been created, because glfw only sets the activation policy once upon initialization.
|
||||||
|
if (OPT(macos_hide_from_tasks)) cocoa_set_hide_from_tasks();
|
||||||
#endif
|
#endif
|
||||||
|
is_first_window = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSWindow *w = add_os_window();
|
OSWindow *w = add_os_window();
|
||||||
@@ -372,19 +405,17 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
update_os_window_viewport(w, false);
|
update_os_window_viewport(w, false);
|
||||||
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
|
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
|
||||||
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
|
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
|
||||||
glfwSetCharModsCallback(glfw_window, char_mods_callback);
|
|
||||||
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
|
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
|
||||||
glfwSetScrollCallback(glfw_window, scroll_callback);
|
glfwSetScrollCallback(glfw_window, scroll_callback);
|
||||||
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
|
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
|
||||||
glfwSetKeyCallback(glfw_window, key_callback);
|
glfwSetKeyboardCallback(glfw_window, key_callback);
|
||||||
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
|
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
|
||||||
glfwSetDropCallback(glfw_window, drop_callback);
|
glfwSetDropCallback(glfw_window, drop_callback);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
if (OPT(macos_hide_titlebar)) {
|
if (OPT(macos_hide_titlebar)) {
|
||||||
if (glfwGetCocoaWindow) { if (!cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window))) { PyErr_Print(); } }
|
if (glfwGetCocoaWindow) { if (!cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window))) { PyErr_Print(); } }
|
||||||
else fprintf(stderr, "Failed to load glfwGetCocoaWindow\n");
|
else log_error("Failed to load glfwGetCocoaWindow");
|
||||||
}
|
}
|
||||||
cocoa_set_titlebar_color(glfwGetCocoaWindow(glfw_window));
|
|
||||||
#endif
|
#endif
|
||||||
double now = monotonic();
|
double now = monotonic();
|
||||||
w->is_focused = true;
|
w->is_focused = true;
|
||||||
@@ -394,7 +425,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (want_semi_transparent && !w->is_semi_transparent) {
|
if (want_semi_transparent && !w->is_semi_transparent) {
|
||||||
static bool warned = false;
|
static bool warned = false;
|
||||||
if (!warned) {
|
if (!warned) {
|
||||||
fprintf(stderr, "Failed to enable transparency. This happens when your desktop environment does not support compositing.\n");
|
log_error("Failed to enable transparency. This happens when your desktop environment does not support compositing.");
|
||||||
warned = true;
|
warned = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,7 +490,7 @@ destroy_os_window(OSWindow *w) {
|
|||||||
// Global functions {{{
|
// Global functions {{{
|
||||||
static void
|
static void
|
||||||
error_callback(int error, const char* description) {
|
error_callback(int error, const char* description) {
|
||||||
fprintf(stderr, "[glfw error %d]: %s\n", error, description);
|
log_error("[glfw error %d]: %s", error, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -480,31 +511,19 @@ glfw_init(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_terminate(PyObject UNUSED *self) {
|
glfw_terminate(PYNOARG) {
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_wait_events(PyObject UNUSED *self, PyObject *args) {
|
glfw_post_empty_event(PYNOARG) {
|
||||||
double time = -1;
|
|
||||||
if (PyTuple_GET_SIZE(args) > 0) {
|
|
||||||
time = PyFloat_AsDouble(PyTuple_GET_ITEM(args, 0));
|
|
||||||
if (PyErr_Occurred()) PyErr_Clear();
|
|
||||||
}
|
|
||||||
if (time < 0) glfwWaitEvents();
|
|
||||||
else glfwWaitEventsTimeout(time);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject*
|
|
||||||
glfw_post_empty_event(PyObject UNUSED *self) {
|
|
||||||
glfwPostEmptyEvent();
|
glfwPostEmptyEvent();
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_poll_events(PyObject UNUSED *self) {
|
glfw_poll_events(PYNOARG) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@@ -522,7 +541,7 @@ get_physical_dpi(GLFWmonitor *m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_get_physical_dpi(PyObject UNUSED *self) {
|
glfw_get_physical_dpi(PYNOARG) {
|
||||||
GLFWmonitor *m = glfwGetPrimaryMonitor();
|
GLFWmonitor *m = glfwGetPrimaryMonitor();
|
||||||
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
|
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
|
||||||
return get_physical_dpi(m);
|
return get_physical_dpi(m);
|
||||||
@@ -547,14 +566,14 @@ glfw_window_hint(PyObject UNUSED *self, PyObject *args) {
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_clipboard_string(PyObject UNUSED *self) {
|
get_clipboard_string(PYNOARG) {
|
||||||
OSWindow *w = current_os_window();
|
OSWindow *w = current_os_window();
|
||||||
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
|
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
|
||||||
return Py_BuildValue("s", "");
|
return Py_BuildValue("s", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_content_scale_for_window(PyObject UNUSED *self) {
|
get_content_scale_for_window(PYNOARG) {
|
||||||
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
|
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
|
||||||
float xscale, yscale;
|
float xscale, yscale;
|
||||||
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
|
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
|
||||||
@@ -571,7 +590,7 @@ set_clipboard_string(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
toggle_fullscreen(PyObject UNUSED *self) {
|
toggle_fullscreen(PYNOARG) {
|
||||||
GLFWmonitor *monitor;
|
GLFWmonitor *monitor;
|
||||||
OSWindow *w = current_os_window();
|
OSWindow *w = current_os_window();
|
||||||
if (!w) Py_RETURN_NONE;
|
if (!w) Py_RETURN_NONE;
|
||||||
@@ -601,7 +620,7 @@ ring_audio_bell(OSWindow *w) {
|
|||||||
if (now - last_bell_at <= 0.1) return;
|
if (now - last_bell_at <= 0.1) return;
|
||||||
last_bell_at = now;
|
last_bell_at = now;
|
||||||
if (w->handle) {
|
if (w->handle) {
|
||||||
glfwWindowBell(w->handle, OPT(x11_bell_volume));
|
glfwWindowBell(w->handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,7 +629,7 @@ request_window_attention(id_type kitty_window_id, bool audio_bell) {
|
|||||||
OSWindow *w = os_window_for_kitty_window(kitty_window_id);
|
OSWindow *w = os_window_for_kitty_window(kitty_window_id);
|
||||||
if (w) {
|
if (w) {
|
||||||
if (audio_bell) ring_audio_bell(w);
|
if (audio_bell) ring_audio_bell(w);
|
||||||
glfwRequestWindowAttention(w->handle);
|
if (OPT(window_alert_on_bell)) glfwRequestWindowAttention(w->handle);
|
||||||
glfwPostEmptyEvent();
|
glfwPostEmptyEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -622,9 +641,7 @@ set_os_window_title(OSWindow *w, const char *title) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
hide_mouse(OSWindow *w) {
|
hide_mouse(OSWindow *w) {
|
||||||
if (glfwGetInputMode(w->handle, GLFW_CURSOR) != GLFW_CURSOR_HIDDEN) {
|
glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||||
glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -634,8 +651,8 @@ swap_window_buffers(OSWindow *w) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
event_loop_wait(double timeout) {
|
event_loop_wait(double timeout) {
|
||||||
if (timeout < 0) glfwWaitEvents();
|
if (timeout < 0) { event_loop_blocking_with_no_timeout = true; glfwWaitEvents(); event_loop_blocking_with_no_timeout = false; }
|
||||||
else if (timeout > 0) glfwWaitEventsTimeout(timeout);
|
else glfwWaitEventsTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -661,14 +678,14 @@ should_os_window_close(OSWindow* w) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
primary_monitor_size(PyObject UNUSED *self) {
|
primary_monitor_size(PYNOARG) {
|
||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||||
return Py_BuildValue("ii", mode->width, mode->height);
|
return Py_BuildValue("ii", mode->width, mode->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
primary_monitor_content_scale(PyObject UNUSED *self) {
|
primary_monitor_content_scale(PYNOARG) {
|
||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
float xscale, yscale;
|
float xscale, yscale;
|
||||||
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
|
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
|
||||||
@@ -676,10 +693,10 @@ primary_monitor_content_scale(PyObject UNUSED *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
x11_display(PyObject UNUSED *self) {
|
x11_display(PYNOARG) {
|
||||||
if (glfwGetX11Display) {
|
if (glfwGetX11Display) {
|
||||||
return PyLong_FromVoidPtr(glfwGetX11Display());
|
return PyLong_FromVoidPtr(glfwGetX11Display());
|
||||||
} else fprintf(stderr, "Failed to load glfwGetX11Display\n");
|
} else log_error("Failed to load glfwGetX11Display");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,10 +715,10 @@ x11_window_id(PyObject UNUSED *self, PyObject *os_wid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_primary_selection(PyObject UNUSED *self) {
|
get_primary_selection(PYNOARG) {
|
||||||
if (glfwGetX11SelectionString) {
|
if (glfwGetX11SelectionString) {
|
||||||
return Py_BuildValue("y", glfwGetX11SelectionString());
|
return Py_BuildValue("y", glfwGetX11SelectionString());
|
||||||
} else fprintf(stderr, "Failed to load glfwGetX11SelectionString\n");
|
} else log_error("Failed to load glfwGetX11SelectionString");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +727,7 @@ set_primary_selection(PyObject UNUSED *self, PyObject *args) {
|
|||||||
char *text;
|
char *text;
|
||||||
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
||||||
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
|
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
|
||||||
else fprintf(stderr, "Failed to load glfwSetX11SelectionString\n");
|
else log_error("Failed to load glfwSetX11SelectionString");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,7 +785,6 @@ static PyMethodDef module_methods[] = {
|
|||||||
METHODB(glfw_poll_events, METH_NOARGS),
|
METHODB(glfw_poll_events, METH_NOARGS),
|
||||||
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
|
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
|
||||||
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
|
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
|
||||||
{"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""},
|
|
||||||
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""},
|
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""},
|
||||||
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
|
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
|
||||||
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
|
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
|
||||||
@@ -777,10 +793,19 @@ static PyMethodDef module_methods[] = {
|
|||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void cleanup_glfw(void) {
|
||||||
|
if (logo.pixels) free(logo.pixels);
|
||||||
|
logo.pixels = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// constants {{{
|
// constants {{{
|
||||||
bool
|
bool
|
||||||
init_glfw(PyObject *m) {
|
init_glfw(PyObject *m) {
|
||||||
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
|
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
|
||||||
|
if (Py_AtExit(cleanup_glfw) != 0) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Failed to register the glfw exit handler");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
||||||
ADDC(GLFW_RELEASE);
|
ADDC(GLFW_RELEASE);
|
||||||
ADDC(GLFW_PRESS);
|
ADDC(GLFW_PRESS);
|
||||||
@@ -922,6 +947,7 @@ init_glfw(PyObject *m) {
|
|||||||
ADDC(GLFW_MOD_CONTROL);
|
ADDC(GLFW_MOD_CONTROL);
|
||||||
ADDC(GLFW_MOD_ALT);
|
ADDC(GLFW_MOD_ALT);
|
||||||
ADDC(GLFW_MOD_SUPER);
|
ADDC(GLFW_MOD_SUPER);
|
||||||
|
ADDC(GLFW_MOD_KITTY);
|
||||||
|
|
||||||
// --- Mouse -------------------------------------------------------------------
|
// --- Mouse -------------------------------------------------------------------
|
||||||
ADDC(GLFW_MOUSE_BUTTON_1);
|
ADDC(GLFW_MOUSE_BUTTON_1);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ PyTypeObject GraphicsManager_Type;
|
|||||||
|
|
||||||
#define STORAGE_LIMIT (320 * (1024 * 1024))
|
#define STORAGE_LIMIT (320 * (1024 * 1024))
|
||||||
|
|
||||||
#define REPORT_ERROR(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
|
#define REPORT_ERROR(...) { log_error(__VA_ARGS__); }
|
||||||
|
|
||||||
|
|
||||||
static bool send_to_gpu = true;
|
static bool send_to_gpu = true;
|
||||||
@@ -509,7 +509,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
|
|
||||||
static inline const char*
|
static inline const char*
|
||||||
create_add_response(GraphicsManager UNUSED *self, bool data_loaded, uint32_t iid) {
|
create_add_response(GraphicsManager UNUSED *self, bool data_loaded, uint32_t iid) {
|
||||||
static char rbuf[sizeof(add_response)/sizeof(add_response[0])];
|
static char rbuf[sizeof(add_response)/sizeof(add_response[0]) + 64];
|
||||||
if (iid) {
|
if (iid) {
|
||||||
if (!has_add_respose) {
|
if (!has_add_respose) {
|
||||||
if (!data_loaded) return NULL;
|
if (!data_loaded) return NULL;
|
||||||
@@ -873,9 +873,10 @@ new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
|||||||
static inline PyObject*
|
static inline PyObject*
|
||||||
image_as_dict(Image *img) {
|
image_as_dict(Image *img) {
|
||||||
#define U(x) #x, img->x
|
#define U(x) #x, img->x
|
||||||
return Py_BuildValue("{sI sI sI sI sI sI sH sH sN}",
|
return Py_BuildValue("{sI sI sI sI sI sI sO sO sN}",
|
||||||
U(texture_id), U(client_id), U(width), U(height), U(internal_id), U(refcnt), U(data_loaded),
|
U(texture_id), U(client_id), U(width), U(height), U(internal_id), U(refcnt),
|
||||||
"is_4byte_aligned", img->load_data.is_4byte_aligned,
|
"data_loaded", img->data_loaded ? Py_True : Py_False,
|
||||||
|
"is_4byte_aligned", img->load_data.is_4byte_aligned ? Py_True : Py_False,
|
||||||
"data", Py_BuildValue("y#", img->load_data.data, img->load_data.data_sz)
|
"data", Py_BuildValue("y#", img->load_data.data, img->load_data.data_sz)
|
||||||
);
|
);
|
||||||
#undef U
|
#undef U
|
||||||
|
|||||||
127
kitty/history.c
127
kitty/history.c
@@ -10,10 +10,42 @@
|
|||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
|
|
||||||
extern PyTypeObject Line_Type;
|
extern PyTypeObject Line_Type;
|
||||||
|
#define SEGMENT_SIZE 2048
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
add_segment(HistoryBuf *self) {
|
||||||
|
self->num_segments += 1;
|
||||||
|
self->segments = PyMem_Realloc(self->segments, sizeof(HistoryBufSegment) * self->num_segments);
|
||||||
|
if (self->segments == NULL) fatal("Out of memory allocating new history buffer segment");
|
||||||
|
HistoryBufSegment *s = self->segments + self->num_segments - 1;
|
||||||
|
s->cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(Cell));
|
||||||
|
s->line_attrs = PyMem_Calloc(SEGMENT_SIZE, sizeof(line_attrs_type));
|
||||||
|
if (s->cells == NULL || s->line_attrs == NULL) fatal("Out of memory allocating new history buffer segment");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline index_type
|
||||||
|
segment_for(HistoryBuf *self, index_type y) {
|
||||||
|
index_type seg_num = y / SEGMENT_SIZE;
|
||||||
|
while (UNLIKELY(seg_num >= self->num_segments && SEGMENT_SIZE * self->num_segments < self->ynum)) add_segment(self);
|
||||||
|
if (UNLIKELY(seg_num >= self->num_segments)) fatal("Out of bounds access to history buffer line number: %u", y);
|
||||||
|
return seg_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define seg_ptr(which, stride) { \
|
||||||
|
index_type seg_num = segment_for(self, y); \
|
||||||
|
y -= seg_num * SEGMENT_SIZE; \
|
||||||
|
return self->segments[seg_num].which + y * stride; \
|
||||||
|
}
|
||||||
|
|
||||||
static inline Cell*
|
static inline Cell*
|
||||||
lineptr(HistoryBuf *linebuf, index_type y) {
|
lineptr(HistoryBuf *self, index_type y) {
|
||||||
return linebuf->buf + y * linebuf->xnum;
|
seg_ptr(cells, self->xnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline line_attrs_type*
|
||||||
|
attrptr(HistoryBuf *self, index_type y) {
|
||||||
|
seg_ptr(line_attrs, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
@@ -32,18 +64,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
if (self != NULL) {
|
if (self != NULL) {
|
||||||
self->xnum = xnum;
|
self->xnum = xnum;
|
||||||
self->ynum = ynum;
|
self->ynum = ynum;
|
||||||
self->buf = PyMem_Calloc(xnum * ynum, sizeof(Cell));
|
|
||||||
self->line_attrs = PyMem_Calloc(ynum, sizeof(line_attrs_type));
|
|
||||||
self->line = alloc_line();
|
self->line = alloc_line();
|
||||||
if (self->buf == NULL || self->line == NULL || self->line_attrs == NULL) {
|
self->num_segments = 0;
|
||||||
PyErr_NoMemory();
|
add_segment(self);
|
||||||
PyMem_Free(self->buf); Py_CLEAR(self->line); PyMem_Free(self->line_attrs);
|
self->line->xnum = xnum;
|
||||||
Py_CLEAR(self);
|
for(index_type y = 0; y < self->ynum; y++) {
|
||||||
} else {
|
clear_chars_in_line(lineptr(self, y), self->xnum, BLANK_CHAR);
|
||||||
self->line->xnum = xnum;
|
|
||||||
for(index_type y = 0; y < self->ynum; y++) {
|
|
||||||
clear_chars_in_line(lineptr(self, y), self->xnum, BLANK_CHAR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +79,11 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
static void
|
static void
|
||||||
dealloc(HistoryBuf* self) {
|
dealloc(HistoryBuf* self) {
|
||||||
Py_CLEAR(self->line);
|
Py_CLEAR(self->line);
|
||||||
PyMem_Free(self->buf);
|
for (size_t i = 0; i < self->num_segments; i++) {
|
||||||
PyMem_Free(self->line_attrs);
|
PyMem_Free(self->segments[i].cells);
|
||||||
|
PyMem_Free(self->segments[i].line_attrs);
|
||||||
|
}
|
||||||
|
PyMem_Free(self->segments);
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +100,8 @@ static inline void
|
|||||||
init_line(HistoryBuf *self, index_type num, Line *l) {
|
init_line(HistoryBuf *self, index_type num, Line *l) {
|
||||||
// Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num
|
// Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num
|
||||||
l->cells = lineptr(self, num);
|
l->cells = lineptr(self, num);
|
||||||
l->continued = self->line_attrs[num] & CONTINUED_MASK;
|
l->continued = *attrptr(self, num) & CONTINUED_MASK;
|
||||||
l->has_dirty_text = self->line_attrs[num] & TEXT_DIRTY_MASK ? true : false;
|
l->has_dirty_text = *attrptr(self, num) & TEXT_DIRTY_MASK ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -82,12 +111,14 @@ historybuf_init_line(HistoryBuf *self, index_type lnum, Line *l) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
historybuf_mark_line_clean(HistoryBuf *self, index_type y) {
|
historybuf_mark_line_clean(HistoryBuf *self, index_type y) {
|
||||||
self->line_attrs[index_of(self, y)] &= ~TEXT_DIRTY_MASK;
|
line_attrs_type *p = attrptr(self, index_of(self, y));
|
||||||
|
*p &= ~TEXT_DIRTY_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
historybuf_mark_line_dirty(HistoryBuf *self, index_type y) {
|
historybuf_mark_line_dirty(HistoryBuf *self, index_type y) {
|
||||||
self->line_attrs[index_of(self, y)] |= TEXT_DIRTY_MASK;
|
line_attrs_type *p = attrptr(self, index_of(self, y));
|
||||||
|
*p |= TEXT_DIRTY_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
@@ -105,43 +136,11 @@ historybuf_push(HistoryBuf *self) {
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
historybuf_resize(HistoryBuf *self, index_type lines) {
|
|
||||||
HistoryBuf t = {{0}};
|
|
||||||
t.xnum=self->xnum;
|
|
||||||
t.ynum=lines;
|
|
||||||
if (t.ynum > 0 && t.ynum != self->ynum) {
|
|
||||||
t.buf = PyMem_Calloc(t.xnum * t.ynum, sizeof(Cell));
|
|
||||||
if (t.buf == NULL) { PyErr_NoMemory(); return false; }
|
|
||||||
t.line_attrs = PyMem_Calloc(t.ynum, sizeof(line_attrs_type));
|
|
||||||
if (t.line_attrs == NULL) { PyMem_Free(t.buf); PyErr_NoMemory(); return false; }
|
|
||||||
t.count = MIN(self->count, t.ynum);
|
|
||||||
for (index_type s=0; s < t.count; s++) {
|
|
||||||
index_type si = index_of(self, s), ti = index_of(&t, s);
|
|
||||||
copy_cells(lineptr(self, si), lineptr(&t, ti), t.xnum);
|
|
||||||
t.line_attrs[ti] = self->line_attrs[si];
|
|
||||||
}
|
|
||||||
self->count = t.count;
|
|
||||||
self->start_of_data = t.start_of_data;
|
|
||||||
self->ynum = t.ynum;
|
|
||||||
PyMem_Free(self->buf); PyMem_Free(self->line_attrs);
|
|
||||||
self->buf = t.buf; self->line_attrs = t.line_attrs;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
historybuf_add_line(HistoryBuf *self, const Line *line) {
|
historybuf_add_line(HistoryBuf *self, const Line *line) {
|
||||||
index_type idx = historybuf_push(self);
|
index_type idx = historybuf_push(self);
|
||||||
copy_line(line, self->line);
|
copy_line(line, self->line);
|
||||||
self->line_attrs[idx] = (line->continued & CONTINUED_MASK) | (line->has_dirty_text ? TEXT_DIRTY_MASK : 0);
|
*attrptr(self, idx) = (line->continued & CONTINUED_MASK) | (line->has_dirty_text ? TEXT_DIRTY_MASK : 0);
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
change_num_of_lines(HistoryBuf *self, PyObject *val) {
|
|
||||||
#define change_num_of_lines_doc "Change the number of lines in this buffer"
|
|
||||||
if(!historybuf_resize(self, (index_type)PyLong_AsUnsignedLong(val))) return NULL;
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
@@ -161,7 +160,7 @@ __str__(HistoryBuf *self) {
|
|||||||
if (lines == NULL) return PyErr_NoMemory();
|
if (lines == NULL) return PyErr_NoMemory();
|
||||||
for (index_type i = 0; i < self->count; i++) {
|
for (index_type i = 0; i < self->count; i++) {
|
||||||
init_line(self, index_of(self, i), self->line);
|
init_line(self, index_of(self, i), self->line);
|
||||||
PyObject *t = PyObject_Str((PyObject*)self->line);
|
PyObject *t = line_as_unicode(self->line);
|
||||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||||
PyTuple_SET_ITEM(lines, i, t);
|
PyTuple_SET_ITEM(lines, i, t);
|
||||||
}
|
}
|
||||||
@@ -188,7 +187,7 @@ as_ansi(HistoryBuf *self, PyObject *callback) {
|
|||||||
for(unsigned int i = 0; i < self->count; i++) {
|
for(unsigned int i = 0; i < self->count; i++) {
|
||||||
init_line(self, i, &l);
|
init_line(self, i, &l);
|
||||||
if (i < self->count - 1) {
|
if (i < self->count - 1) {
|
||||||
l.continued = self->line_attrs[index_of(self, i + 1)] & CONTINUED_MASK;
|
l.continued = *attrptr(self, index_of(self, i + 1)) & CONTINUED_MASK;
|
||||||
} else l.continued = false;
|
} else l.continued = false;
|
||||||
index_type num = line_as_ansi(&l, t, 5120);
|
index_type num = line_as_ansi(&l, t, 5120);
|
||||||
if (!(l.continued) && num < 5119) t[num++] = 10; // 10 = \n
|
if (!(l.continued) && num < 5119) t[num++] = 10; // 10 = \n
|
||||||
@@ -215,11 +214,11 @@ as_text(HistoryBuf *self, PyObject *args) {
|
|||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
dirty_lines(HistoryBuf *self) {
|
dirty_lines(HistoryBuf *self, PyObject *a UNUSED) {
|
||||||
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
||||||
PyObject *ans = PyList_New(0);
|
PyObject *ans = PyList_New(0);
|
||||||
for (index_type i = 0; i < self->ynum; i++) {
|
for (index_type i = 0; i < self->ynum; i++) {
|
||||||
if (self->line_attrs[i] & TEXT_DIRTY_MASK) {
|
if (*attrptr(self, i) & TEXT_DIRTY_MASK) {
|
||||||
PyList_Append(ans, PyLong_FromUnsignedLong(i));
|
PyList_Append(ans, PyLong_FromUnsignedLong(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +231,6 @@ static PyObject* rewrap(HistoryBuf *self, PyObject *args);
|
|||||||
#define rewrap_doc ""
|
#define rewrap_doc ""
|
||||||
|
|
||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
METHOD(change_num_of_lines, METH_O)
|
|
||||||
METHOD(line, METH_O)
|
METHOD(line, METH_O)
|
||||||
METHOD(as_ansi, METH_O)
|
METHOD(as_ansi, METH_O)
|
||||||
METHODB(as_text, METH_VARARGS),
|
METHODB(as_text, METH_VARARGS),
|
||||||
@@ -275,19 +273,22 @@ HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns) {
|
|||||||
|
|
||||||
#define init_src_line(src_y) init_line(src, map_src_index(src_y), src->line);
|
#define init_src_line(src_y) init_line(src, map_src_index(src_y), src->line);
|
||||||
|
|
||||||
#define is_src_line_continued(src_y) (map_src_index(src_y) < src->ynum - 1 ? (src->line_attrs[map_src_index(src_y + 1)] & CONTINUED_MASK) : false)
|
#define is_src_line_continued(src_y) (map_src_index(src_y) < src->ynum - 1 ? (*attrptr(src, map_src_index(src_y + 1)) & CONTINUED_MASK) : false)
|
||||||
|
|
||||||
#define next_dest_line(cont) dest->line_attrs[historybuf_push(dest)] = cont & CONTINUED_MASK; dest->line->continued = cont;
|
#define next_dest_line(cont) *attrptr(dest, historybuf_push(dest)) = cont & CONTINUED_MASK; dest->line->continued = cont;
|
||||||
|
|
||||||
#define first_dest_line next_dest_line(false);
|
#define first_dest_line next_dest_line(false);
|
||||||
|
|
||||||
#include "rewrap.h"
|
#include "rewrap.h"
|
||||||
|
|
||||||
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
|
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
|
||||||
// Fast path
|
while(other->num_segments < self->num_segments) add_segment(other);
|
||||||
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
||||||
memcpy(other->buf, self->buf, sizeof(Cell) * self->xnum * self->ynum);
|
// Fast path
|
||||||
memcpy(other->line_attrs, self->line_attrs, sizeof(line_attrs_type) * self->ynum);
|
for (index_type i = 0; i < self->num_segments; i++) {
|
||||||
|
memcpy(other->segments[i].cells, self->segments[i].cells, SEGMENT_SIZE * self->xnum * sizeof(Cell));
|
||||||
|
memcpy(other->segments[i].line_attrs, self->segments[i].line_attrs, SEGMENT_SIZE * sizeof(line_attrs_type));
|
||||||
|
}
|
||||||
other->count = self->count; other->start_of_data = self->start_of_data;
|
other->count = self->count; other->start_of_data = self->start_of_data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -295,7 +296,7 @@ void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
|
|||||||
index_type x = 0, y = 0;
|
index_type x = 0, y = 0;
|
||||||
if (self->count > 0) {
|
if (self->count > 0) {
|
||||||
rewrap_inner(self, other, self->count, NULL, &x, &y);
|
rewrap_inner(self, other, self->count, NULL, &x, &y);
|
||||||
for (index_type i = 0; i < other->count; i++) other->line_attrs[(other->start_of_data + i) % other->ynum] |= TEXT_DIRTY_MASK;
|
for (index_type i = 0; i < other->count; i++) *attrptr(other, (other->start_of_data + i) % other->ynum) |= TEXT_DIRTY_MASK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
115
kitty/keys.c
115
kitty/keys.c
@@ -43,22 +43,6 @@ active_window() {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
on_text_input(unsigned int codepoint, int mods) {
|
|
||||||
Window *w = active_window();
|
|
||||||
static char buf[16];
|
|
||||||
unsigned int sz = 0;
|
|
||||||
|
|
||||||
if (w != NULL) {
|
|
||||||
bool is_text = mods <= GLFW_MOD_SHIFT;
|
|
||||||
if (is_text) sz = encode_utf8(codepoint, buf);
|
|
||||||
#ifdef __APPLE__
|
|
||||||
if (!OPT(macos_option_as_alt) && IS_ALT_MODS(mods)) sz = encode_utf8(codepoint, buf);
|
|
||||||
#endif
|
|
||||||
if (sz) schedule_write_to_child(w->id, buf, sz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
is_modifier_key(int key) {
|
is_modifier_key(int key) {
|
||||||
switch(key) {
|
switch(key) {
|
||||||
@@ -76,66 +60,6 @@ is_modifier_key(int key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
|
||||||
get_localized_key(int key, int scancode) {
|
|
||||||
const char *name = glfwGetKeyName(key, scancode);
|
|
||||||
if (name == NULL || name[1] != 0) return key;
|
|
||||||
switch(name[0]) {
|
|
||||||
#define K(ch, name) case ch: return GLFW_KEY_##name
|
|
||||||
// key names {{{
|
|
||||||
K('A', A); K('a', A);
|
|
||||||
K('B', B); K('b', B);
|
|
||||||
K('C', C); K('c', C);
|
|
||||||
K('D', D); K('d', D);
|
|
||||||
K('E', E); K('e', E);
|
|
||||||
K('F', F); K('f', F);
|
|
||||||
K('G', G); K('g', G);
|
|
||||||
K('H', H); K('h', H);
|
|
||||||
K('I', I); K('i', I);
|
|
||||||
K('J', J); K('j', J);
|
|
||||||
K('K', K); K('k', K);
|
|
||||||
K('L', L); K('l', L);
|
|
||||||
K('M', M); K('m', M);
|
|
||||||
K('N', N); K('n', N);
|
|
||||||
K('O', O); K('o', O);
|
|
||||||
K('P', P); K('p', P);
|
|
||||||
K('Q', Q); K('q', Q);
|
|
||||||
K('R', R); K('r', R);
|
|
||||||
K('S', S); K('s', S);
|
|
||||||
K('T', T); K('t', T);
|
|
||||||
K('U', U); K('u', U);
|
|
||||||
K('V', V); K('v', V);
|
|
||||||
K('W', W); K('w', W);
|
|
||||||
K('X', X); K('x', X);
|
|
||||||
K('Y', Y); K('y', Y);
|
|
||||||
K('Z', Z); K('z', Z);
|
|
||||||
K('0', 0);
|
|
||||||
K('1', 1);
|
|
||||||
K('2', 2);
|
|
||||||
K('3', 3);
|
|
||||||
K('5', 5);
|
|
||||||
K('6', 6);
|
|
||||||
K('7', 7);
|
|
||||||
K('8', 8);
|
|
||||||
K('9', 9);
|
|
||||||
K('\'', APOSTROPHE);
|
|
||||||
K(',', COMMA);
|
|
||||||
K('.', PERIOD);
|
|
||||||
K('/', SLASH);
|
|
||||||
K('-', MINUS);
|
|
||||||
K(';', SEMICOLON);
|
|
||||||
K('=', EQUAL);
|
|
||||||
K('[', LEFT_BRACKET);
|
|
||||||
K(']', RIGHT_BRACKET);
|
|
||||||
K('`', GRAVE_ACCENT);
|
|
||||||
K('\\', BACKSLASH);
|
|
||||||
// }}}
|
|
||||||
#undef K
|
|
||||||
default:
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
send_key_to_child(Window *w, int key, int mods, int action) {
|
send_key_to_child(Window *w, int key, int mods, int action) {
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
@@ -152,14 +76,26 @@ send_key_to_child(Window *w, int key, int mods, int action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_ascii_control_char(char c) {
|
||||||
|
return c == 0 || (1 <= c && c <= 31) || c == 127;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
on_key_input(int key, int scancode, int action, int mods) {
|
on_key_input(int key, int scancode, int action, int mods, const char* text, int state UNUSED) {
|
||||||
Window *w = active_window();
|
Window *w = active_window();
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
if (global_state.in_sequence_mode) {
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
int lkey = get_localized_key(key, scancode);
|
bool has_text = text && !is_ascii_control_char(text[0]);
|
||||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||||
uint16_t qkey = key_map[lkey];
|
uint16_t qkey = (0 <= key && key < (ssize_t)arraysz(key_map)) ? key_map[key] : UINT8_MAX;
|
||||||
bool special = false;
|
bool special = false;
|
||||||
if (qkey != UINT8_MAX) {
|
if (qkey != UINT8_MAX) {
|
||||||
qkey = SPECIAL_INDEX(qkey);
|
qkey = SPECIAL_INDEX(qkey);
|
||||||
@@ -167,7 +103,7 @@ on_key_input(int key, int scancode, int action, int mods) {
|
|||||||
}
|
}
|
||||||
/* printf("key: %s mods: %d special: %d\n", key_name(lkey), mods, special); */
|
/* printf("key: %s mods: %d special: %d\n", key_name(lkey), mods, special); */
|
||||||
if (special) {
|
if (special) {
|
||||||
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", lkey, scancode, action, mods);
|
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", key, scancode, action, mods);
|
||||||
if (ret == NULL) { PyErr_Print(); }
|
if (ret == NULL) { PyErr_Print(); }
|
||||||
else {
|
else {
|
||||||
bool consumed = ret == Py_True;
|
bool consumed = ret == Py_True;
|
||||||
@@ -176,20 +112,17 @@ on_key_input(int key, int scancode, int action, int mods) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (action == GLFW_REPEAT && !screen->modes.mDECARM) return;
|
||||||
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
||||||
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
||||||
}
|
}
|
||||||
#ifdef __APPLE__
|
bool ok_to_send = action == GLFW_PRESS || action == GLFW_REPEAT || screen->modes.mEXTENDED_KEYBOARD;
|
||||||
if (!OPT(macos_option_as_alt) && IS_ALT_MODS(mods) && !(
|
if (ok_to_send) {
|
||||||
key == GLFW_KEY_UP || key == GLFW_KEY_DOWN || key == GLFW_KEY_LEFT || key == GLFW_KEY_RIGHT) // alt+arrow keys do not generate text on macOS
|
if (has_text) {
|
||||||
) return;
|
schedule_write_to_child(w->id, text, strlen(text));
|
||||||
#endif
|
} else {
|
||||||
if (
|
send_key_to_child(w, key, mods, action);
|
||||||
action == GLFW_PRESS ||
|
}
|
||||||
(action == GLFW_REPEAT && screen->modes.mDECARM) ||
|
|
||||||
screen->modes.mEXTENDED_KEYBOARD
|
|
||||||
) {
|
|
||||||
send_key_to_child(w, lkey, mods, action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1770
kitty/keys.h
generated
1770
kitty/keys.h
generated
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,17 @@ shift_alt_codes = alt_codes.copy()
|
|||||||
shift_alt_codes[defines.GLFW_KEY_TAB] = key_as_bytes('kcbt')
|
shift_alt_codes[defines.GLFW_KEY_TAB] = key_as_bytes('kcbt')
|
||||||
alt_mods = (defines.GLFW_MOD_ALT, defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT)
|
alt_mods = (defines.GLFW_MOD_ALT, defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT)
|
||||||
ctrl_shift_mod = defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL
|
ctrl_shift_mod = defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL
|
||||||
|
ctrl_alt_mod = defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL
|
||||||
|
SHIFTED_KEYS = {
|
||||||
|
defines.GLFW_KEY_TAB: key_as_bytes('kcbt'),
|
||||||
|
defines.GLFW_KEY_HOME: key_as_bytes('kHOM'),
|
||||||
|
defines.GLFW_KEY_END: key_as_bytes('kEND'),
|
||||||
|
defines.GLFW_KEY_LEFT: key_as_bytes('kLFT'),
|
||||||
|
defines.GLFW_KEY_RIGHT: key_as_bytes('kRIT'),
|
||||||
|
defines.GLFW_KEY_UP: key_as_bytes('kri'),
|
||||||
|
defines.GLFW_KEY_DOWN: key_as_bytes('kind'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for kf, kn in {
|
for kf, kn in {
|
||||||
defines.GLFW_KEY_UP: 'kcuu1',
|
defines.GLFW_KEY_UP: 'kcuu1',
|
||||||
@@ -57,9 +68,14 @@ for f in range(1, 13):
|
|||||||
kf = getattr(defines, 'GLFW_KEY_F{}'.format(f))
|
kf = getattr(defines, 'GLFW_KEY_F{}'.format(f))
|
||||||
kn = 'kf{}'.format(f)
|
kn = 'kf{}'.format(f)
|
||||||
smkx_key_map[kf] = key_as_bytes(kn)
|
smkx_key_map[kf] = key_as_bytes(kn)
|
||||||
|
SHIFTED_KEYS[kf] = modify_complex_key(kn, 2)
|
||||||
alt_codes[kf] = modify_complex_key(kn, 3)
|
alt_codes[kf] = modify_complex_key(kn, 3)
|
||||||
shift_alt_codes[kf] = modify_complex_key(kn, 4)
|
shift_alt_codes[kf] = modify_complex_key(kn, 4)
|
||||||
control_codes[kf] = modify_complex_key(kn, 5)
|
control_codes[kf] = modify_complex_key(kn, 5)
|
||||||
|
for f in range(13, 26):
|
||||||
|
kf = getattr(defines, 'GLFW_KEY_F{}'.format(f))
|
||||||
|
kn = 'kf{}'.format(f)
|
||||||
|
smkx_key_map[kf] = key_as_bytes(kn)
|
||||||
f = {k: k for k in '0123456789'}
|
f = {k: k for k in '0123456789'}
|
||||||
f.update({
|
f.update({
|
||||||
'COMMA': ',',
|
'COMMA': ',',
|
||||||
@@ -79,16 +95,6 @@ smkx_key_map[defines.GLFW_KEY_KP_ENTER] = b'\r'
|
|||||||
smkx_key_map[defines.GLFW_KEY_BACKSPACE] = key_as_bytes('kbs')
|
smkx_key_map[defines.GLFW_KEY_BACKSPACE] = key_as_bytes('kbs')
|
||||||
smkx_key_map[defines.GLFW_KEY_TAB] = b'\t'
|
smkx_key_map[defines.GLFW_KEY_TAB] = b'\t'
|
||||||
|
|
||||||
SHIFTED_KEYS = {
|
|
||||||
defines.GLFW_KEY_TAB: key_as_bytes('kcbt'),
|
|
||||||
defines.GLFW_KEY_HOME: key_as_bytes('kHOM'),
|
|
||||||
defines.GLFW_KEY_END: key_as_bytes('kEND'),
|
|
||||||
defines.GLFW_KEY_LEFT: key_as_bytes('kLFT'),
|
|
||||||
defines.GLFW_KEY_RIGHT: key_as_bytes('kRIT'),
|
|
||||||
defines.GLFW_KEY_UP: key_as_bytes('kri'),
|
|
||||||
defines.GLFW_KEY_DOWN: key_as_bytes('kind'),
|
|
||||||
}
|
|
||||||
|
|
||||||
control_codes.update({
|
control_codes.update({
|
||||||
k: (1 + i, )
|
k: (1 + i, )
|
||||||
for i, k in
|
for i, k in
|
||||||
@@ -204,6 +210,7 @@ ASCII_C0_SHIFTED = {
|
|||||||
'SLASH': b'\x7f',
|
'SLASH': b'\x7f',
|
||||||
}
|
}
|
||||||
CTRL_SHIFT_KEYS = {getattr(defines, 'GLFW_KEY_' + k): v for k, v in ASCII_C0_SHIFTED.items()}
|
CTRL_SHIFT_KEYS = {getattr(defines, 'GLFW_KEY_' + k): v for k, v in ASCII_C0_SHIFTED.items()}
|
||||||
|
CTRL_ALT_KEYS = {getattr(defines, 'GLFW_KEY_' + k) for k in string.ascii_uppercase}
|
||||||
|
|
||||||
|
|
||||||
def key_to_bytes(key, smkx, extended, mods, action):
|
def key_to_bytes(key, smkx, extended, mods, action):
|
||||||
@@ -222,6 +229,8 @@ def key_to_bytes(key, smkx, extended, mods, action):
|
|||||||
m = UN_SHIFTED_PRINTABLE if mods == defines.GLFW_MOD_ALT else SHIFTED_PRINTABLE
|
m = UN_SHIFTED_PRINTABLE if mods == defines.GLFW_MOD_ALT else SHIFTED_PRINTABLE
|
||||||
data.append(0o33)
|
data.append(0o33)
|
||||||
data.extend(m[key])
|
data.extend(m[key])
|
||||||
|
elif mods == ctrl_alt_mod and key in CTRL_ALT_KEYS:
|
||||||
|
data.append(0x1b), data.extend(control_codes[key])
|
||||||
else:
|
else:
|
||||||
key_map = cursor_key_mode_map[smkx]
|
key_map = cursor_key_mode_map[smkx]
|
||||||
x = key_map.get(key)
|
x = key_map.get(key)
|
||||||
@@ -247,6 +256,10 @@ def get_shortcut(keymap, mods, key, scancode):
|
|||||||
return keymap.get((mods & 0b1111, key))
|
return keymap.get((mods & 0b1111, key))
|
||||||
|
|
||||||
|
|
||||||
|
def shortcut_matches(s, mods, key, scancode):
|
||||||
|
return s[0] & 0b1111 == mods & 0b1111 and s[1] == key
|
||||||
|
|
||||||
|
|
||||||
def generate_key_table():
|
def generate_key_table():
|
||||||
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
||||||
import os
|
import os
|
||||||
@@ -332,13 +345,15 @@ def generate_key_table():
|
|||||||
ind('return NULL;')
|
ind('return NULL;')
|
||||||
i -= 2
|
i -= 2
|
||||||
i -= 1
|
i -= 1
|
||||||
ind('} // end switch(mods)\n')
|
ind('} // end switch(mods)')
|
||||||
|
ind('break;\n')
|
||||||
i -= 1
|
i -= 1
|
||||||
else:
|
else:
|
||||||
ind('return NULL;\n')
|
ind('return NULL;\n')
|
||||||
i -= 1
|
i -= 1
|
||||||
i -= 1
|
i -= 1
|
||||||
ind('}} // end switch(action) in mode {}\n\n'.format(mode))
|
ind('}} // end switch(action) in mode {}'.format(mode))
|
||||||
|
ind('break;\n\n')
|
||||||
i -= 1
|
i -= 1
|
||||||
i -= 1
|
i -= 1
|
||||||
ind('}')
|
ind('}')
|
||||||
|
|||||||
43
kitty/khrplatform.h
generated
43
kitty/khrplatform.h
generated
@@ -1,8 +1,7 @@
|
|||||||
#ifndef __khrplatform_h_
|
#ifndef __khrplatform_h_
|
||||||
#define __khrplatform_h_
|
#define __khrplatform_h_
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Copyright (c) 2008-2009 The Khronos Group Inc.
|
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||||
**
|
**
|
||||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
** copy of this software and/or associated documentation files (the
|
** copy of this software and/or associated documentation files (the
|
||||||
@@ -23,21 +22,18 @@
|
|||||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Khronos platform-specific types and definitions.
|
/* Khronos platform-specific types and definitions.
|
||||||
*
|
*
|
||||||
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
|
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||||
|
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||||
|
* The last semantic modification to khrplatform.h was at commit ID:
|
||||||
|
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||||
*
|
*
|
||||||
* Adopters may modify this file to suit their platform. Adopters are
|
* Adopters may modify this file to suit their platform. Adopters are
|
||||||
* encouraged to submit platform specific modifications to the Khronos
|
* encouraged to submit platform specific modifications to the Khronos
|
||||||
* group so that they can be included in future versions of this file.
|
* group so that they can be included in future versions of this file.
|
||||||
* Please submit changes by sending them to the public Khronos Bugzilla
|
* Please submit changes by filing pull requests or issues on
|
||||||
* (http://khronos.org/bugzilla) by filing a bug against product
|
* the EGL Registry repository linked above.
|
||||||
* "Khronos (general)" component "Registry".
|
|
||||||
*
|
|
||||||
* A predefined template which fills in some of the bug fields can be
|
|
||||||
* reached using http://tinyurl.com/khrplatform-h-bugreport, but you
|
|
||||||
* must create a Bugzilla login first.
|
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* See the Implementer's Guidelines for information about where this file
|
* See the Implementer's Guidelines for information about where this file
|
||||||
@@ -91,7 +87,6 @@
|
|||||||
* int arg1,
|
* int arg1,
|
||||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
* Definition of KHRONOS_APICALL
|
* Definition of KHRONOS_APICALL
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
@@ -106,7 +101,6 @@
|
|||||||
#else
|
#else
|
||||||
# define KHRONOS_APICALL
|
# define KHRONOS_APICALL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
* Definition of KHRONOS_APIENTRY
|
* Definition of KHRONOS_APIENTRY
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
@@ -119,7 +113,6 @@
|
|||||||
#else
|
#else
|
||||||
# define KHRONOS_APIENTRY
|
# define KHRONOS_APIENTRY
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
* Definition of KHRONOS_APIATTRIBUTES
|
* Definition of KHRONOS_APIATTRIBUTES
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
@@ -130,13 +123,10 @@
|
|||||||
#else
|
#else
|
||||||
#define KHRONOS_APIATTRIBUTES
|
#define KHRONOS_APIATTRIBUTES
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
* basic type definitions
|
* basic type definitions
|
||||||
*-----------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------*/
|
||||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Using <stdint.h>
|
* Using <stdint.h>
|
||||||
*/
|
*/
|
||||||
@@ -147,9 +137,7 @@ typedef int64_t khronos_int64_t;
|
|||||||
typedef uint64_t khronos_uint64_t;
|
typedef uint64_t khronos_uint64_t;
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
#elif defined(__VMS ) || defined(__sgi)
|
#elif defined(__VMS ) || defined(__sgi)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Using <inttypes.h>
|
* Using <inttypes.h>
|
||||||
*/
|
*/
|
||||||
@@ -160,9 +148,7 @@ typedef int64_t khronos_int64_t;
|
|||||||
typedef uint64_t khronos_uint64_t;
|
typedef uint64_t khronos_uint64_t;
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Win32
|
* Win32
|
||||||
*/
|
*/
|
||||||
@@ -172,9 +158,7 @@ typedef __int64 khronos_int64_t;
|
|||||||
typedef unsigned __int64 khronos_uint64_t;
|
typedef unsigned __int64 khronos_uint64_t;
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
#elif defined(__sun__) || defined(__digital__)
|
#elif defined(__sun__) || defined(__digital__)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sun or Digital
|
* Sun or Digital
|
||||||
*/
|
*/
|
||||||
@@ -189,9 +173,7 @@ typedef unsigned long long int khronos_uint64_t;
|
|||||||
#endif /* __arch64__ */
|
#endif /* __arch64__ */
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
#elif 0
|
#elif 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hypothetical platform with no float or int64 support
|
* Hypothetical platform with no float or int64 support
|
||||||
*/
|
*/
|
||||||
@@ -199,9 +181,7 @@ typedef int khronos_int32_t;
|
|||||||
typedef unsigned int khronos_uint32_t;
|
typedef unsigned int khronos_uint32_t;
|
||||||
#define KHRONOS_SUPPORT_INT64 0
|
#define KHRONOS_SUPPORT_INT64 0
|
||||||
#define KHRONOS_SUPPORT_FLOAT 0
|
#define KHRONOS_SUPPORT_FLOAT 0
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generic fallback
|
* Generic fallback
|
||||||
*/
|
*/
|
||||||
@@ -212,10 +192,7 @@ typedef int64_t khronos_int64_t;
|
|||||||
typedef uint64_t khronos_uint64_t;
|
typedef uint64_t khronos_uint64_t;
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Types that are (so far) the same on all platforms
|
* Types that are (so far) the same on all platforms
|
||||||
*/
|
*/
|
||||||
@@ -223,7 +200,6 @@ typedef signed char khronos_int8_t;
|
|||||||
typedef unsigned char khronos_uint8_t;
|
typedef unsigned char khronos_uint8_t;
|
||||||
typedef signed short int khronos_int16_t;
|
typedef signed short int khronos_int16_t;
|
||||||
typedef unsigned short int khronos_uint16_t;
|
typedef unsigned short int khronos_uint16_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||||
@@ -240,14 +216,12 @@ typedef unsigned long int khronos_uintptr_t;
|
|||||||
typedef signed long int khronos_ssize_t;
|
typedef signed long int khronos_ssize_t;
|
||||||
typedef unsigned long int khronos_usize_t;
|
typedef unsigned long int khronos_usize_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if KHRONOS_SUPPORT_FLOAT
|
#if KHRONOS_SUPPORT_FLOAT
|
||||||
/*
|
/*
|
||||||
* Float type
|
* Float type
|
||||||
*/
|
*/
|
||||||
typedef float khronos_float_t;
|
typedef float khronos_float_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if KHRONOS_SUPPORT_INT64
|
#if KHRONOS_SUPPORT_INT64
|
||||||
/* Time types
|
/* Time types
|
||||||
*
|
*
|
||||||
@@ -261,14 +235,12 @@ typedef float khronos_float_t;
|
|||||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dummy value used to pad enum types to 32 bits.
|
* Dummy value used to pad enum types to 32 bits.
|
||||||
*/
|
*/
|
||||||
#ifndef KHRONOS_MAX_ENUM
|
#ifndef KHRONOS_MAX_ENUM
|
||||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enumerated boolean type
|
* Enumerated boolean type
|
||||||
*
|
*
|
||||||
@@ -280,5 +252,4 @@ typedef enum {
|
|||||||
KHRONOS_TRUE = 1,
|
KHRONOS_TRUE = 1,
|
||||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||||
} khronos_boolean_enum_t;
|
} khronos_boolean_enum_t;
|
||||||
|
|
||||||
#endif /* __khrplatform_h_ */
|
#endif /* __khrplatform_h_ */
|
||||||
|
|||||||
590
kitty/kitty.conf
590
kitty/kitty.conf
@@ -1,5 +1,11 @@
|
|||||||
# vim:fileencoding=utf-8:ft=conf
|
# vim:fileencoding=utf-8:ft=conf
|
||||||
|
|
||||||
|
# You can include secondary config files via the "include" directive.
|
||||||
|
# If you use a relative path for include, it is resolved with respect to the
|
||||||
|
# location of the current config file. For example:
|
||||||
|
# include other.conf
|
||||||
|
|
||||||
|
# Fonts {{{
|
||||||
# Font family. You can also specify different fonts for the
|
# Font family. You can also specify different fonts for the
|
||||||
# bold/italic/bold-italic variants. By default they are derived automatically,
|
# bold/italic/bold-italic variants. By default they are derived automatically,
|
||||||
# by the OSes font system. Setting them manually is useful for font families
|
# by the OSes font system. Setting them manually is useful for font families
|
||||||
@@ -11,6 +17,7 @@
|
|||||||
#
|
#
|
||||||
# You can get a list of full family names available on your computer by running
|
# You can get a list of full family names available on your computer by running
|
||||||
# kitty list-fonts
|
# kitty list-fonts
|
||||||
|
# The default values shown below rely on your OS to choose an appropriate monospace font family.
|
||||||
font_family monospace
|
font_family monospace
|
||||||
italic_font auto
|
italic_font auto
|
||||||
bold_font auto
|
bold_font auto
|
||||||
@@ -32,12 +39,200 @@ font_size_delta 2
|
|||||||
adjust_line_height 0
|
adjust_line_height 0
|
||||||
adjust_column_width 0
|
adjust_column_width 0
|
||||||
|
|
||||||
|
# Symbol mapping (special font for specified unicode code points). Map the
|
||||||
|
# specified unicode codepoints to a particular font. Useful if you need special
|
||||||
|
# rendering for some symbols, such as for Powerline. Avoids the need for
|
||||||
|
# patched fonts. Each unicode code point is specified in the form U+<code point
|
||||||
|
# in hexadecimal>. You can specify multiple code points, separated by commas
|
||||||
|
# and ranges separated by hyphens. symbol_map itself can be specified multiple times.
|
||||||
|
# Syntax is:
|
||||||
|
#
|
||||||
|
# symbol_map codepoints Font Family Name
|
||||||
|
#
|
||||||
|
# For example:
|
||||||
|
#
|
||||||
|
# symbol_map U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols
|
||||||
|
|
||||||
# Change the sizes of the lines used for the box drawing unicode characters
|
# Change the sizes of the lines used for the box drawing unicode characters
|
||||||
# These values are in pts. They will be scaled by the monitor DPI to arrive at
|
# These values are in pts. They will be scaled by the monitor DPI to arrive at
|
||||||
# a pixel value. There must be four values corresponding to thin, normal, thick,
|
# a pixel value. There must be four values corresponding to thin, normal, thick,
|
||||||
# and very thick lines;
|
# and very thick lines;
|
||||||
box_drawing_scale 0.001, 1, 1.5, 2
|
box_drawing_scale 0.001, 1, 1.5, 2
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Cursor customization {{{
|
||||||
|
# The cursor color
|
||||||
|
cursor #cccccc
|
||||||
|
|
||||||
|
# The cursor shape can be one of (block, beam, underline)
|
||||||
|
cursor_shape block
|
||||||
|
|
||||||
|
# The interval (in seconds) at which to blink the cursor. Set to zero to
|
||||||
|
# disable blinking. Note that numbers smaller than repaint_delay will be
|
||||||
|
# limited to repaint_delay.
|
||||||
|
cursor_blink_interval 0.5
|
||||||
|
|
||||||
|
# Stop blinking cursor after the specified number of seconds of keyboard inactivity. Set to
|
||||||
|
# zero to never stop blinking.
|
||||||
|
cursor_stop_blinking_after 15.0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Scrollback {{{
|
||||||
|
# Number of lines of history to keep in memory for scrolling back
|
||||||
|
scrollback_lines 2000
|
||||||
|
|
||||||
|
# Program with which to view scrollback in a new window. The scrollback buffer is passed as
|
||||||
|
# STDIN to this program. If you change it, make sure the program you use can
|
||||||
|
# handle ANSI escape sequences for colors and text formatting.
|
||||||
|
scrollback_pager less +G -R
|
||||||
|
|
||||||
|
# Wheel scroll multiplier (modify the amount scrolled by the mouse wheel). Use negative
|
||||||
|
# numbers to change scroll direction.
|
||||||
|
wheel_scroll_multiplier 5.0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Mouse {{{
|
||||||
|
# The color and style for highlighting URLs on mouse-over. url_style can be one of:
|
||||||
|
# none, single, double, curly
|
||||||
|
url_color #0087BD
|
||||||
|
url_style curly
|
||||||
|
|
||||||
|
# The modifier keys to press when clicking with the mouse on URLs to open the URL
|
||||||
|
open_url_modifiers kitty_mod
|
||||||
|
|
||||||
|
# The program with which to open URLs that are clicked on. The special value "default" means to
|
||||||
|
# use the operating system's default URL handler.
|
||||||
|
open_url_with default
|
||||||
|
|
||||||
|
# Copy to clipboard on select. With this enabled, simply selecting text with
|
||||||
|
# the mouse will cause the text to be copied to clipboard. Useful on platforms
|
||||||
|
# such as macOS/Wayland that do not have the concept of primary selections. Note
|
||||||
|
# that this is a security risk, as all programs, including websites open in your
|
||||||
|
# browser can read the contents of the clipboard.
|
||||||
|
copy_on_select no
|
||||||
|
|
||||||
|
# The modifiers to use rectangular selection (i.e. to select text in a
|
||||||
|
# rectangular block with the mouse)
|
||||||
|
rectangle_select_modifiers ctrl+alt
|
||||||
|
|
||||||
|
# Characters considered part of a word when double clicking. In addition to these characters
|
||||||
|
# any character that is marked as an alpha-numeric character in the unicode
|
||||||
|
# database will be matched.
|
||||||
|
select_by_word_characters :@-./_~?&=%+#
|
||||||
|
|
||||||
|
# The interval between successive clicks to detect double/triple clicks (in seconds)
|
||||||
|
click_interval 0.5
|
||||||
|
|
||||||
|
# Hide mouse cursor after the specified number of seconds of the mouse not being used. Set to
|
||||||
|
# zero to disable mouse cursor hiding.
|
||||||
|
mouse_hide_wait 3.0
|
||||||
|
|
||||||
|
# Set the active window to the window under the mouse when moving the mouse around
|
||||||
|
focus_follows_mouse no
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Performance tuning {{{
|
||||||
|
# Delay (in milliseconds) between screen updates. Decreasing it, increases
|
||||||
|
# frames-per-second (FPS) at the cost of more CPU usage. The default value
|
||||||
|
# yields ~100 FPS which is more than sufficient for most uses. Note that to
|
||||||
|
# actually achieve 100FPS you have to either set sync_to_monitor to no or use a
|
||||||
|
# monitor with a high refresh rate.
|
||||||
|
repaint_delay 10
|
||||||
|
|
||||||
|
# Delay (in milliseconds) before input from the program running in the terminal
|
||||||
|
# is processed. Note that decreasing it will increase responsiveness, but also
|
||||||
|
# increase CPU usage and might cause flicker in full screen programs that
|
||||||
|
# redraw the entire screen on each loop, because kitty is so fast that partial
|
||||||
|
# screen updates will be drawn.
|
||||||
|
input_delay 3
|
||||||
|
|
||||||
|
# Sync screen updates to the refresh rate of the monitor. This prevents
|
||||||
|
# tearing (https://en.wikipedia.org/wiki/Screen_tearing) when scrolling. However,
|
||||||
|
# it limits the rendering speed to the refresh rate of your monitor. With a
|
||||||
|
# very high speed mouse/high keyboard repeat rate, you may notice some slight input latency.
|
||||||
|
# If so, set this to no.
|
||||||
|
sync_to_monitor yes
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Audio/visual bell {{{
|
||||||
|
# Visual bell duration. Flash the screen when a bell occurs for the specified number of
|
||||||
|
# seconds. Set to zero to disable.
|
||||||
|
visual_bell_duration 0.0
|
||||||
|
|
||||||
|
# Enable/disable the audio bell. Useful in environments that require silence.
|
||||||
|
enable_audio_bell yes
|
||||||
|
|
||||||
|
# Request window attention on bell.
|
||||||
|
# Makes the dock icon bounce on macOS or the taskbar flash on linux.
|
||||||
|
window_alert_on_bell yes
|
||||||
|
|
||||||
|
# Show a bell symbol on the tab if a bell occurs in one of the windows in the
|
||||||
|
# tab and the window is not the currently focused window
|
||||||
|
bell_on_tab yes
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Window layout {{{
|
||||||
|
# If enabled, the window size will be remembered so that new instances of kitty will have the same
|
||||||
|
# size as the previous instance. If disabled, the window will initially have size configured
|
||||||
|
# by initial_window_width/height, in pixels.
|
||||||
|
remember_window_size yes
|
||||||
|
initial_window_width 640
|
||||||
|
initial_window_height 400
|
||||||
|
|
||||||
|
# The enabled window layouts. A comma separated list of layout names. The special value * means
|
||||||
|
# all layouts. The first listed layout will be used as the startup layout.
|
||||||
|
# For a list of available layouts, see the README.
|
||||||
|
enabled_layouts *
|
||||||
|
|
||||||
|
# The width (in pts) of window borders. Will be rounded to the nearest number of pixels based on screen resolution.
|
||||||
|
# Note that borders are displayed only when more than one window is visible. They are meant to separate multiple windows.
|
||||||
|
window_border_width 1
|
||||||
|
|
||||||
|
# The window margin (in pts) (blank area outside the border)
|
||||||
|
window_margin_width 0
|
||||||
|
|
||||||
|
# The window padding (in pts) (blank area between the text and the window border)
|
||||||
|
window_padding_width 0
|
||||||
|
|
||||||
|
# The color for the border of the active window
|
||||||
|
active_border_color #00ff00
|
||||||
|
|
||||||
|
# The color for the border of inactive windows
|
||||||
|
inactive_border_color #cccccc
|
||||||
|
|
||||||
|
# The color for the border of inactive windows in which a bell has occurred
|
||||||
|
bell_border_color #ff5a00
|
||||||
|
|
||||||
|
# Fade the text in inactive windows by the specified amount (a number between
|
||||||
|
# zero and one, with 0 being fully faded).
|
||||||
|
inactive_text_alpha 1.0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Tab bar {{{
|
||||||
|
# Which edge to show the tab bar on, top or bottom
|
||||||
|
tab_bar_edge bottom
|
||||||
|
|
||||||
|
# The separator between tabs in the tab bar
|
||||||
|
tab_separator " ┇"
|
||||||
|
|
||||||
|
# Tab bar colors and styles
|
||||||
|
active_tab_foreground #000
|
||||||
|
active_tab_background #eee
|
||||||
|
active_tab_font_style bold-italic
|
||||||
|
inactive_tab_foreground #444
|
||||||
|
inactive_tab_background #999
|
||||||
|
inactive_tab_font_style normal
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Color scheme {{{
|
||||||
# The foreground color
|
# The foreground color
|
||||||
foreground #dddddd
|
foreground #dddddd
|
||||||
|
|
||||||
@@ -62,155 +257,9 @@ selection_foreground #000000
|
|||||||
# The background for selections
|
# The background for selections
|
||||||
selection_background #FFFACD
|
selection_background #FFFACD
|
||||||
|
|
||||||
# The color and style for highlighting URLs on mouse-over. url_style can be one of:
|
|
||||||
# none, single, double, curly
|
|
||||||
url_color #0087BD
|
|
||||||
url_style curly
|
|
||||||
|
|
||||||
# The cursor color
|
|
||||||
cursor #cccccc
|
|
||||||
|
|
||||||
# The cursor shape can be one of (block, beam, underline)
|
|
||||||
cursor_shape block
|
|
||||||
|
|
||||||
# The interval (in seconds) at which to blink the cursor. Set to zero to
|
|
||||||
# disable blinking. Note that numbers smaller than repaint_delay will be
|
|
||||||
# limited to repaint_delay.
|
|
||||||
cursor_blink_interval 0.5
|
|
||||||
|
|
||||||
# Stop blinking cursor after the specified number of seconds of keyboard inactivity. Set to
|
|
||||||
# zero to never stop blinking.
|
|
||||||
cursor_stop_blinking_after 15.0
|
|
||||||
|
|
||||||
# Number of lines of history to keep in memory for scrolling back
|
|
||||||
scrollback_lines 2000
|
|
||||||
|
|
||||||
# Program with which to view scrollback in a new window. The scrollback buffer is passed as
|
|
||||||
# STDIN to this program. If you change it, make sure the program you use can
|
|
||||||
# handle ANSI escape sequences for colors and text formatting.
|
|
||||||
scrollback_pager less +G -R
|
|
||||||
|
|
||||||
# Wheel scroll multiplier (modify the amount scrolled by the mouse wheel). Use negative
|
|
||||||
# numbers to change scroll direction.
|
|
||||||
wheel_scroll_multiplier 5.0
|
|
||||||
|
|
||||||
# The interval between successive clicks to detect double/triple clicks (in seconds)
|
|
||||||
click_interval 0.5
|
|
||||||
|
|
||||||
# Characters considered part of a word when double clicking. In addition to these characters
|
|
||||||
# any character that is marked as an alpha-numeric character in the unicode
|
|
||||||
# database will be matched.
|
|
||||||
select_by_word_characters :@-./_~?&=%+#
|
|
||||||
|
|
||||||
# The shell program to execute. The default value of . means
|
|
||||||
# to use whatever shell is set as the default shell for the current user.
|
|
||||||
shell .
|
|
||||||
|
|
||||||
# Hide mouse cursor after the specified number of seconds of the mouse not being used. Set to
|
|
||||||
# zero to disable mouse cursor hiding.
|
|
||||||
mouse_hide_wait 3.0
|
|
||||||
|
|
||||||
# Set the active window to the window under the mouse when moving the mouse around
|
|
||||||
focus_follows_mouse no
|
|
||||||
|
|
||||||
# The enabled window layouts. A comma separated list of layout names. The special value * means
|
|
||||||
# all layouts. The first listed layout will be used as the startup layout.
|
|
||||||
# For a list of available layouts, see the file layouts.py
|
|
||||||
enabled_layouts *
|
|
||||||
|
|
||||||
# If enabled, the window size will be remembered so that new instances of kitty will have the same
|
|
||||||
# size as the previous instance. If disabled, the window will initially have size configured
|
|
||||||
# by initial_window_width/height, in pixels.
|
|
||||||
remember_window_size yes
|
|
||||||
initial_window_width 640
|
|
||||||
initial_window_height 400
|
|
||||||
|
|
||||||
# Delay (in milliseconds) between screen updates. Decreasing it, increases
|
|
||||||
# frames-per-second (FPS) at the cost of more CPU usage. The default value
|
|
||||||
# yields ~100 FPS which is more than sufficient for most uses. Note that to
|
|
||||||
# actually achieve 100FPS you have to either set sync_to_monitor to no or use a
|
|
||||||
# monitor with a high refresh rate.
|
|
||||||
repaint_delay 10
|
|
||||||
|
|
||||||
# Delay (in milliseconds) before input from the program running in the terminal
|
|
||||||
# is processed. Note that decreasing it will increase responsiveness, but also
|
|
||||||
# increase CPU usage and might cause flicker in full screen programs that
|
|
||||||
# redraw the entire screen on each loop, because kitty is so fast that partial
|
|
||||||
# screen updates will be drawn.
|
|
||||||
input_delay 3
|
|
||||||
|
|
||||||
# Sync screen updates to the refresh rate of the monitor. This prevents
|
|
||||||
# tearing (https://en.wikipedia.org/wiki/Screen_tearing) when scrolling. However,
|
|
||||||
# it limits the rendering speed to the refresh rate of your monitor. With a
|
|
||||||
# very high speed mouse/high keyboard repeat rate, you may notice some slight input latency.
|
|
||||||
# If so, set this to no.
|
|
||||||
sync_to_monitor yes
|
|
||||||
|
|
||||||
# Visual bell duration. Flash the screen when a bell occurs for the specified number of
|
|
||||||
# seconds. Set to zero to disable.
|
|
||||||
visual_bell_duration 0.0
|
|
||||||
|
|
||||||
# Enable/disable the audio bell. Useful in environments that require silence.
|
|
||||||
enable_audio_bell yes
|
|
||||||
|
|
||||||
# The modifier keys to press when clicking with the mouse on URLs to open the URL
|
|
||||||
open_url_modifiers ctrl+shift
|
|
||||||
|
|
||||||
# The program with which to open URLs that are clicked on. The special value "default" means to
|
|
||||||
# use the operating system's default URL handler.
|
|
||||||
open_url_with default
|
|
||||||
|
|
||||||
# The modifiers to use rectangular selection (i.e. to select text in a
|
|
||||||
# rectangular block with the mouse)
|
|
||||||
rectangle_select_modifiers ctrl+alt
|
|
||||||
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
allow_remote_control no
|
|
||||||
|
|
||||||
# The value of the TERM environment variable to set
|
|
||||||
term xterm-kitty
|
|
||||||
|
|
||||||
# The width (in pts) of window borders. Will be rounded to the nearest number of pixels based on screen resolution.
|
|
||||||
# Note that borders are displayed only when more than one window is visible. They are meant to separate multiple windows.
|
|
||||||
window_border_width 1
|
|
||||||
|
|
||||||
# The window margin (in pts) (blank area outside the border)
|
|
||||||
window_margin_width 0
|
|
||||||
|
|
||||||
# The window padding (in pts) (blank area between the text and the window border)
|
|
||||||
window_padding_width 0
|
|
||||||
|
|
||||||
# The color for the border of the active window
|
|
||||||
active_border_color #00ff00
|
|
||||||
|
|
||||||
# The color for the border of inactive windows
|
|
||||||
inactive_border_color #cccccc
|
|
||||||
|
|
||||||
# Fade the text in inactive windows by the specified amount (a number between
|
|
||||||
# zero and one, with 0 being fully faded).
|
|
||||||
inactive_text_alpha 1.0
|
|
||||||
|
|
||||||
# Which edge to show the tab bar on, top or bottom
|
|
||||||
tab_bar_edge bottom
|
|
||||||
|
|
||||||
# The separator between tabs in the tab bar
|
|
||||||
tab_separator " ┇"
|
|
||||||
|
|
||||||
# Tab bar colors and styles
|
|
||||||
active_tab_foreground #000
|
|
||||||
active_tab_background #eee
|
|
||||||
active_tab_font_style bold-italic
|
|
||||||
inactive_tab_foreground #444
|
|
||||||
inactive_tab_background #999
|
|
||||||
inactive_tab_font_style normal
|
|
||||||
|
|
||||||
|
|
||||||
# The 16 terminal colors. There are 8 basic colors, each color has a dull and
|
# The 16 terminal colors. There are 8 basic colors, each color has a dull and
|
||||||
# bright version.
|
# bright version. You can also set the remaining colors from the 256 color table
|
||||||
|
# as color16 to color256.
|
||||||
|
|
||||||
# black
|
# black
|
||||||
color0 #000000
|
color0 #000000
|
||||||
@@ -243,11 +292,49 @@ color14 #14ffff
|
|||||||
# white
|
# white
|
||||||
color7 #dddddd
|
color7 #dddddd
|
||||||
color15 #ffffff
|
color15 #ffffff
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
# Key mapping
|
# Advanced {{{
|
||||||
|
|
||||||
|
# The shell program to execute. The default value of . means
|
||||||
|
# to use whatever shell is set as the default shell for the current user.
|
||||||
|
# Note that on macOS if you change this, you might need to add --login to
|
||||||
|
# ensure that the shell starts in interactive mode and reads its startup rc files.
|
||||||
|
shell .
|
||||||
|
|
||||||
|
# Close the window when the child process (shell) exits. If no (the default),
|
||||||
|
# the terminal will remain open when the child exits as long as there are still
|
||||||
|
# processes outputting to the terminal (for example disowned or backgrounded
|
||||||
|
# processes). If yes, the window will close as soon as the child process exits.
|
||||||
|
# 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.
|
||||||
|
close_on_child_death no
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
allow_remote_control no
|
||||||
|
|
||||||
|
# The value of the TERM environment variable to set. Changing this can break
|
||||||
|
# many terminal programs, only change it if you know what you are doing, not
|
||||||
|
# because you read some advice on Stack Overflow to change it.
|
||||||
|
term xterm-kitty
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Keyboard shortcuts {{{
|
||||||
# For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html
|
# For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html
|
||||||
# For a list of modifier names, see: http://www.glfw.org/docs/latest/group__mods.html
|
# For a list of modifier names, see: http://www.glfw.org/docs/latest/group__mods.html
|
||||||
|
|
||||||
|
# The value of kitty_mod is used as the modifier for all default shortcuts, you
|
||||||
|
# can change it in your kitty.conf to change the modifiers for all the default
|
||||||
|
# shortcuts.
|
||||||
|
kitty_mod ctrl+shift
|
||||||
#
|
#
|
||||||
# You can use the special action no_op to unmap a keyboard shortcut that is
|
# You can use the special action no_op to unmap a keyboard shortcut that is
|
||||||
# assigned in the default configuration.
|
# assigned in the default configuration.
|
||||||
@@ -256,94 +343,140 @@ color15 #ffffff
|
|||||||
# syntax below:
|
# syntax below:
|
||||||
# map key combine <separator> action1 <separator> action2 <separator> action3 ...
|
# map key combine <separator> action1 <separator> action2 <separator> action3 ...
|
||||||
# For example:
|
# For example:
|
||||||
# map ctrl+shift+e combine : new_window : next_layout
|
# map kitty_mod+e combine : new_window : next_layout
|
||||||
# this will create a new window and switch to the next available layout
|
# this will create a new window and switch to the next available layout
|
||||||
|
#
|
||||||
|
# You can use multi-key shortcuts using the syntax shown below:
|
||||||
|
# map key1>key2>key3 action
|
||||||
|
# For example:
|
||||||
|
# map ctrl+f>2 set_font_size 20
|
||||||
|
# this will change the font size to 20 points when you press ctrl+f and then 2
|
||||||
|
|
||||||
# Clipboard
|
# Clipboard {{{
|
||||||
map ctrl+shift+v paste_from_clipboard
|
map kitty_mod+v paste_from_clipboard
|
||||||
map ctrl+shift+s paste_from_selection
|
map kitty_mod+s paste_from_selection
|
||||||
map ctrl+shift+c copy_to_clipboard
|
map kitty_mod+c copy_to_clipboard
|
||||||
map shift+insert paste_from_selection
|
map shift+insert paste_from_selection
|
||||||
# You can also pass the contents of the current selection to any program using
|
# You can also pass the contents of the current selection to any program using
|
||||||
# pass_selection_to_program. By default, the system's open program is used, but
|
# pass_selection_to_program. By default, the system's open program is used, but
|
||||||
# you can specify your own, for example:
|
# you can specify your own, for example:
|
||||||
# map ctrl+shift+o pass_selection_to_program firefox
|
# map kitty_mod+o pass_selection_to_program firefox
|
||||||
map ctrl+shift+o pass_selection_to_program
|
map kitty_mod+o pass_selection_to_program
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Scrolling
|
# Scrolling {{{
|
||||||
map ctrl+shift+up scroll_line_up
|
map kitty_mod+up scroll_line_up
|
||||||
map ctrl+shift+down scroll_line_down
|
map kitty_mod+down scroll_line_down
|
||||||
map ctrl+shift+k scroll_line_up
|
map kitty_mod+k scroll_line_up
|
||||||
map ctrl+shift+j scroll_line_down
|
map kitty_mod+j scroll_line_down
|
||||||
map ctrl+shift+page_up scroll_page_up
|
map kitty_mod+page_up scroll_page_up
|
||||||
map ctrl+shift+page_down scroll_page_down
|
map kitty_mod+page_down scroll_page_down
|
||||||
map ctrl+shift+home scroll_home
|
map kitty_mod+home scroll_home
|
||||||
map ctrl+shift+end scroll_end
|
map kitty_mod+end scroll_end
|
||||||
map ctrl+shift+h show_scrollback
|
map kitty_mod+h show_scrollback
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Window management
|
# Window management {{{
|
||||||
map ctrl+shift+enter new_window
|
map kitty_mod+enter new_window
|
||||||
map ctrl+shift+n new_os_window
|
map kitty_mod+n new_os_window
|
||||||
map ctrl+shift+w close_window
|
map kitty_mod+w close_window
|
||||||
map ctrl+shift+] next_window
|
map kitty_mod+] next_window
|
||||||
map ctrl+shift+[ previous_window
|
map kitty_mod+[ previous_window
|
||||||
map ctrl+shift+f move_window_forward
|
map kitty_mod+f move_window_forward
|
||||||
map ctrl+shift+b move_window_backward
|
map kitty_mod+b move_window_backward
|
||||||
map ctrl+shift+` move_window_to_top
|
map kitty_mod+` move_window_to_top
|
||||||
map ctrl+shift+1 first_window
|
map kitty_mod+1 first_window
|
||||||
map ctrl+shift+2 second_window
|
map kitty_mod+2 second_window
|
||||||
map ctrl+shift+3 third_window
|
map kitty_mod+3 third_window
|
||||||
map ctrl+shift+4 fourth_window
|
map kitty_mod+4 fourth_window
|
||||||
map ctrl+shift+5 fifth_window
|
map kitty_mod+5 fifth_window
|
||||||
map ctrl+shift+6 sixth_window
|
map kitty_mod+6 sixth_window
|
||||||
map ctrl+shift+7 seventh_window
|
map kitty_mod+7 seventh_window
|
||||||
map ctrl+shift+8 eighth_window
|
map kitty_mod+8 eighth_window
|
||||||
map ctrl+shift+9 ninth_window
|
map kitty_mod+9 ninth_window
|
||||||
map ctrl+shift+0 tenth_window
|
map kitty_mod+0 tenth_window
|
||||||
# You can open a new window running an arbitrary program, for example:
|
# You can open a new window running an arbitrary program, for example:
|
||||||
# map ctrl+shift+y new_window mutt
|
# map kitty_mod+y new_window mutt
|
||||||
#
|
#
|
||||||
# You can pass the current selection to the new program by using the @selection placeholder
|
# You can pass the current selection to the new program by using the @selection placeholder
|
||||||
# map ctrl+shift+y new_window less @selection
|
# map kitty_mod+y new_window less @selection
|
||||||
#
|
#
|
||||||
# You can even send the contents of the current screen + history buffer as stdin using
|
# You can even send the contents of the current screen + history buffer as stdin using
|
||||||
# the placeholders @text (which is the plain text) and @ansi (which includes text styling escape codes).
|
# the placeholders @text (which is the plain text) and @ansi (which includes text styling escape codes).
|
||||||
# For only the current screen, use @screen or @ansi_screen.
|
# For only the current screen, use @screen or @ansi_screen.
|
||||||
# For example, the following command opens the scrollback buffer in less in a new window.
|
# For example, the following command opens the scrollback buffer in less in a new window.
|
||||||
# map ctrl+shift+y new_window @ansi less +G -R
|
# map kitty_mod+y new_window @ansi less +G -R
|
||||||
#
|
#
|
||||||
# You can open a new window with the current working directory set to the
|
# You can open a new window with the current working directory set to the
|
||||||
# working directory of the current window using
|
# working directory of the current window using
|
||||||
# map ctrl+alt+enter new_window_with_cwd
|
# map ctrl+alt+enter new_window_with_cwd
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Tab management {{{
|
||||||
# Tab management
|
map kitty_mod+right next_tab
|
||||||
map ctrl+shift+right next_tab
|
map kitty_mod+left previous_tab
|
||||||
map ctrl+shift+left previous_tab
|
map kitty_mod+t new_tab
|
||||||
map ctrl+shift+t new_tab
|
map kitty_mod+q close_tab
|
||||||
map ctrl+shift+q close_tab
|
map kitty_mod+l next_layout
|
||||||
map ctrl+shift+l next_layout
|
map kitty_mod+. move_tab_forward
|
||||||
map ctrl+shift+. move_tab_forward
|
map kitty_mod+, move_tab_backward
|
||||||
map ctrl+shift+, move_tab_backward
|
map kitty_mod+alt+t set_tab_title
|
||||||
# You can also create shortcuts to go to specific tabs, with 1 being the first tab
|
# You can also create shortcuts to go to specific tabs, with 1 being the first tab
|
||||||
# map ctrl+alt+1 goto_tab 1
|
# map ctrl+alt+1 goto_tab 1
|
||||||
# map ctrl+alt+2 goto_tab 2
|
# map ctrl+alt+2 goto_tab 2
|
||||||
|
|
||||||
# Just as with new_window above, you can also pass the name of arbitrary
|
# Just as with new_window above, you can also pass the name of arbitrary
|
||||||
# commands to run when using new_tab and use new_tab_with_cwd.
|
# commands to run when using new_tab and use new_tab_with_cwd.
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Layout management {{{
|
||||||
|
# You can create shortcuts to switch to specific layouts
|
||||||
|
# map ctrl+alt+1 goto_layout tall
|
||||||
|
# map ctrl+alt+2 goto_layout stack
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Miscellaneous
|
# Font sizes {{{
|
||||||
map ctrl+shift+equal increase_font_size
|
map kitty_mod+equal increase_font_size
|
||||||
map ctrl+shift+minus decrease_font_size
|
map kitty_mod+minus decrease_font_size
|
||||||
map ctrl+shift+backspace restore_font_size
|
map kitty_mod+backspace restore_font_size
|
||||||
map ctrl+shift+f11 toggle_fullscreen
|
# To setup shortcuts for specific font sizes, follow the example below:
|
||||||
map ctrl+shift+u input_unicode_character
|
# map kitty_mod+f6 set_font_size 10.0
|
||||||
# Open a currently visible URL using the keyboard. The program used ot open the URL is specified in open_url_with.
|
# map kitty_mod+f7 set_font_size 20.5
|
||||||
# You can customize how the URLs are detected and opened by specifying command line options to
|
# }}}
|
||||||
# url_hints. For example:
|
|
||||||
# map ctrl+shift+e run_simple_kitten text url_hints --program firefox --regex "http://[^ ]+"
|
# Select and act on visible text {{{
|
||||||
map ctrl+shift+e run_simple_kitten text url_hints
|
# Use the hints kitten to select text and either pass it to an external program or
|
||||||
|
# insert it into the terminal or copy it to the clipboard.
|
||||||
|
#
|
||||||
|
# Open a currently visible URL using the keyboard. The program used to open the
|
||||||
|
# URL is specified in open_url_with.
|
||||||
|
map kitty_mod+e run_kitten text hints
|
||||||
|
|
||||||
|
# Select a path/filename and insert it into the terminal. Useful, for instance to
|
||||||
|
# run git commands on a filename output from a previous git command.
|
||||||
|
map kitty_mod+p>f run_kitten text hints --type path --program -
|
||||||
|
|
||||||
|
# Select a path/filename and open it with the default open program.
|
||||||
|
map kitty_mod+p>shift+f run_kitten text hints --type path
|
||||||
|
|
||||||
|
# Select a line of text and insert it into the terminal. Use for the
|
||||||
|
# output of things like: ls -1
|
||||||
|
map kitty_mod+p>l run_kitten text hints --type line --program -
|
||||||
|
|
||||||
|
# Select words and insert into terminal.
|
||||||
|
map kitty_mod+p>w run_kitten text hints --type word --program -
|
||||||
|
|
||||||
|
# The hints kitten has many more modes of operation that you can map to different
|
||||||
|
# shortcuts. For a full description run: kitty +kitten hints --help
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Miscellaneous {{{
|
||||||
|
map kitty_mod+f11 toggle_fullscreen
|
||||||
|
map kitty_mod+u input_unicode_character
|
||||||
|
map kitty_mod+f2 edit_config_file
|
||||||
|
You can customize how the URLs are
|
||||||
|
# Open the kitty shell in a new window/tab/overlay/os_window to control kitty using commands.
|
||||||
|
map kitty_mod+escape kitty_shell window
|
||||||
|
|
||||||
# Sending arbitrary text on shortcut key presses
|
# Sending arbitrary text on shortcut key presses
|
||||||
# You can tell kitty to send arbitrary (UTF-8) encoded text to
|
# You can tell kitty to send arbitrary (UTF-8) encoded text to
|
||||||
@@ -361,35 +494,24 @@ map ctrl+shift+e run_simple_kitten text url_hints
|
|||||||
# to the start of the line (same as pressing the Home key):
|
# to the start of the line (same as pressing the Home key):
|
||||||
# map ctrl+alt+a send_text normal Word\x1b[H
|
# map ctrl+alt+a send_text normal Word\x1b[H
|
||||||
# map ctrl+alt+a send_text application Word\x1bOH
|
# map ctrl+alt+a send_text application Word\x1bOH
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Symbol mapping (special font for specified unicode code points). Map the
|
# You can have kitty remove all shortcut definition seen upto this point. Useful for
|
||||||
# specified unicode codepoints to a particular font. Useful if you need special
|
# instance, to remove the default shortcuts.
|
||||||
# rendering for some symbols, such as for Powerline. Avoids the need for
|
clear_all_shortcuts no
|
||||||
# patched fonts. Each unicode code point is specified in the form U+<code point
|
# }}}
|
||||||
# in hexadecimal>. You can specify multiple code points, separated by commas
|
|
||||||
# and ranges separated by hyphens. symbol_map itself can be specified multiple times.
|
|
||||||
# Syntax is:
|
|
||||||
#
|
|
||||||
# symbol_map codepoints Font Family Name
|
|
||||||
#
|
|
||||||
# For example:
|
|
||||||
#
|
|
||||||
# symbol_map U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols
|
|
||||||
|
|
||||||
|
|
||||||
# OS specific tweaks
|
# OS specific tweaks {{{
|
||||||
|
|
||||||
# Copy to clipboard on select. With this enabled, simply selecting text with
|
|
||||||
# the mouse will cause the text to be copied to clipboard. Useful on platforms
|
|
||||||
# such as macOS/Wayland that do not have the concept of primary selections. Note
|
|
||||||
# that this is a security risk, as all programs, including websites open in your
|
|
||||||
# browser can read the contents of the clipboard.
|
|
||||||
copy_on_select no
|
|
||||||
|
|
||||||
# Change the color of the kitty window's titlebar on macOS. A value of "system"
|
# Change the color of the kitty window's titlebar on macOS. A value of "system"
|
||||||
# means to use the default system color, a value of "background" means to use
|
# means to use the default system color, a value of "background" means to use
|
||||||
# the default background color and finally you can use an arbitrary color, such
|
# the background color of the currently active window and finally you can use
|
||||||
# as #12af59 or "red".
|
# an arbitrary color, such as #12af59 or "red". WARNING: This option works by
|
||||||
|
# using a hack, as there is no proper Cocoa API for it. It sets the background
|
||||||
|
# color of the entire window and makes the titlebar transparent. As such it is
|
||||||
|
# incompatible with background_opacity. If you want to use both, you are
|
||||||
|
# probably better off just hiding the titlebar with macos_hide_titlebar.
|
||||||
macos_titlebar_color system
|
macos_titlebar_color system
|
||||||
|
|
||||||
# Hide the kitty window's title bar on macOS.
|
# Hide the kitty window's title bar on macOS.
|
||||||
@@ -401,6 +523,6 @@ macos_hide_titlebar no
|
|||||||
# can use the macOS unicode input technique.
|
# can use the macOS unicode input technique.
|
||||||
macos_option_as_alt yes
|
macos_option_as_alt yes
|
||||||
|
|
||||||
# The number is a percentage of maximum volume.
|
# Hide the kitty window from running tasks (alt-tab) on macOS.
|
||||||
# See man XBell for details.
|
macos_hide_from_tasks no
|
||||||
x11_bell_volume 80
|
# }}}
|
||||||
|
|||||||
@@ -75,9 +75,11 @@ class Layout:
|
|||||||
# this layout, i.e. spaces that are not covered by any window
|
# this layout, i.e. spaces that are not covered by any window
|
||||||
self.blank_rects = ()
|
self.blank_rects = ()
|
||||||
|
|
||||||
def nth_window(self, all_windows, num):
|
def nth_window(self, all_windows, num, make_active=True):
|
||||||
windows = process_overlaid_windows(all_windows)[1]
|
windows = process_overlaid_windows(all_windows)[1]
|
||||||
w = windows[min(num, len(windows) - 1)]
|
w = windows[min(num, len(windows) - 1)]
|
||||||
|
if not make_active:
|
||||||
|
return w
|
||||||
active_window_idx = idx_for_id(w.id, all_windows)
|
active_window_idx = idx_for_id(w.id, all_windows)
|
||||||
return self.set_active_window(all_windows, active_window_idx)
|
return self.set_active_window(all_windows, active_window_idx)
|
||||||
|
|
||||||
@@ -252,7 +254,9 @@ def top_blank_rect(w, rects):
|
|||||||
|
|
||||||
def bottom_blank_rect(w, rects):
|
def bottom_blank_rect(w, rects):
|
||||||
b = w.geometry.bottom
|
b = w.geometry.bottom
|
||||||
if b < central.bottom:
|
# Need to use <= here as otherwise a single pixel row at the bottom of the
|
||||||
|
# window is sometimes not covered. See https://github.com/kovidgoyal/kitty/issues/506
|
||||||
|
if b <= central.bottom:
|
||||||
rects.append(Rect(central.left, b, central.right + 1, central.bottom + 1))
|
rects.append(Rect(central.left, b, central.right + 1, central.bottom + 1))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ linebuf_mark_line_clean(LineBuf *self, index_type y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
clear(LineBuf *self) {
|
clear(LineBuf *self, PyObject *a UNUSED) {
|
||||||
#define clear_doc "Clear all lines in this LineBuf"
|
#define clear_doc "Clear all lines in this LineBuf"
|
||||||
linebuf_clear(self, BLANK_CHAR);
|
linebuf_clear(self, BLANK_CHAR);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -167,7 +167,7 @@ set_continued(LineBuf *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
dirty_lines(LineBuf *self) {
|
dirty_lines(LineBuf *self, PyObject *a UNUSED) {
|
||||||
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
||||||
PyObject *ans = PyList_New(0);
|
PyObject *ans = PyList_New(0);
|
||||||
for (index_type i = 0; i < self->ynum; i++) {
|
for (index_type i = 0; i < self->ynum; i++) {
|
||||||
@@ -424,7 +424,7 @@ __str__(LineBuf *self) {
|
|||||||
if (lines == NULL) return PyErr_NoMemory();
|
if (lines == NULL) return PyErr_NoMemory();
|
||||||
for (index_type i = 0; i < self->ynum; i++) {
|
for (index_type i = 0; i < self->ynum; i++) {
|
||||||
init_line(self, self->line, self->line_map[i]);
|
init_line(self, self->line, self->line_map[i]);
|
||||||
PyObject *t = PyObject_Str((PyObject*)self->line);
|
PyObject *t = line_as_unicode(self->line);
|
||||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||||
PyTuple_SET_ITEM(lines, i, t);
|
PyTuple_SET_ITEM(lines, i, t);
|
||||||
}
|
}
|
||||||
|
|||||||
12
kitty/line.c
12
kitty/line.c
@@ -199,8 +199,8 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
|
|||||||
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
PyObject *
|
||||||
as_unicode(Line* self) {
|
line_as_unicode(Line* self) {
|
||||||
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0);
|
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
as_ansi(Line* self) {
|
as_ansi(Line* self, PyObject *a UNUSED) {
|
||||||
#define as_ansi_doc "Return the line's contents with ANSI (SGR) escape codes for formatting"
|
#define as_ansi_doc "Return the line's contents with ANSI (SGR) escape codes for formatting"
|
||||||
static Py_UCS4 t[5120] = {0};
|
static Py_UCS4 t[5120] = {0};
|
||||||
index_type num = line_as_ansi(self, t, 5120);
|
index_type num = line_as_ansi(self, t, 5120);
|
||||||
@@ -272,7 +272,7 @@ as_ansi(Line* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
is_continued(Line* self) {
|
is_continued(Line* self, PyObject *a UNUSED) {
|
||||||
#define is_continued_doc "Return the line's continued flag"
|
#define is_continued_doc "Return the line's continued flag"
|
||||||
PyObject *ans = self->continued ? Py_True : Py_False;
|
PyObject *ans = self->continued ? Py_True : Py_False;
|
||||||
Py_INCREF(ans);
|
Py_INCREF(ans);
|
||||||
@@ -281,7 +281,7 @@ is_continued(Line* self) {
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
__repr__(Line* self) {
|
__repr__(Line* self) {
|
||||||
PyObject *s = as_unicode(self);
|
PyObject *s = line_as_unicode(self);
|
||||||
if (s == NULL) return NULL;
|
if (s == NULL) return NULL;
|
||||||
PyObject *ans = PyObject_Repr(s);
|
PyObject *ans = PyObject_Repr(s);
|
||||||
Py_CLEAR(s);
|
Py_CLEAR(s);
|
||||||
@@ -570,7 +570,7 @@ PyTypeObject Line_Type = {
|
|||||||
.tp_basicsize = sizeof(Line),
|
.tp_basicsize = sizeof(Line),
|
||||||
.tp_dealloc = (destructor)dealloc,
|
.tp_dealloc = (destructor)dealloc,
|
||||||
.tp_repr = (reprfunc)__repr__,
|
.tp_repr = (reprfunc)__repr__,
|
||||||
.tp_str = (reprfunc)as_unicode,
|
.tp_str = (reprfunc)line_as_unicode,
|
||||||
.tp_as_sequence = &sequence_methods,
|
.tp_as_sequence = &sequence_methods,
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
.tp_richcompare = richcmp,
|
.tp_richcompare = richcmp,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user