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
|
||||
build
|
||||
linux-package
|
||||
kitty.app
|
||||
logo/*.iconset
|
||||
compile_commands.json
|
||||
glad/out
|
||||
|
||||
16
.travis.yml
16
.travis.yml
@@ -6,7 +6,7 @@ matrix:
|
||||
group: beta
|
||||
sudo: false
|
||||
env:
|
||||
- CC=gcc SANITIZE_ARG=--sanitize
|
||||
- CC=gcc CFLAGS=-funsigned-char SANITIZE_ARG=--sanitize
|
||||
language: python
|
||||
python: "3.5"
|
||||
addons:
|
||||
@@ -20,8 +20,6 @@ matrix:
|
||||
- libxcursor-dev
|
||||
- libunistring-dev
|
||||
- libxcb-xkb-dev
|
||||
- libxkbcommon-dev
|
||||
- libxkbcommon-x11-dev
|
||||
- libpng-dev
|
||||
- python3-pil
|
||||
|
||||
@@ -44,8 +42,6 @@ matrix:
|
||||
- libxcursor-dev
|
||||
- libunistring-dev
|
||||
- libxcb-xkb-dev
|
||||
- libxkbcommon-dev
|
||||
- libxkbcommon-x11-dev
|
||||
- libpng-dev
|
||||
- python3-pil
|
||||
|
||||
@@ -68,8 +64,6 @@ matrix:
|
||||
- libxcursor-dev
|
||||
- libunistring-dev
|
||||
- libxcb-xkb-dev
|
||||
- libxkbcommon-dev
|
||||
- libxkbcommon-x11-dev
|
||||
- libpng-dev
|
||||
- python3-pil
|
||||
- clang
|
||||
@@ -94,7 +88,7 @@ install: |
|
||||
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
|
||||
if [[ "$USE_BREW" == "1" ]]; then
|
||||
brew update;
|
||||
brew install python3;
|
||||
brew upgrade python;
|
||||
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
|
||||
else
|
||||
mkdir -p $SW;
|
||||
@@ -102,6 +96,12 @@ install: |
|
||||
fi
|
||||
else
|
||||
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
|
||||
tar xf harfbuzz-1.6.3.tar.bz2
|
||||
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.
|
||||
|
||||
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]
|
||||
-----------------------------
|
||||
|
||||
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
|
||||
|
||||
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_copy_to_clipboard: pass:quotes[`ctrl+shift+c`]
|
||||
: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_fifth_window: pass:quotes[`ctrl+shift+5`]
|
||||
:sc_first_window: pass:quotes[`ctrl+shift+1`]
|
||||
:sc_fourth_window: pass:quotes[`ctrl+shift+4`]
|
||||
:sc_increase_font_size: pass:quotes[`ctrl+shift+equal`]
|
||||
: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_forward: pass:quotes[`ctrl+shift+.`]
|
||||
:sc_move_window_backward: pass:quotes[`ctrl+shift+b`]
|
||||
@@ -30,7 +32,11 @@
|
||||
:sc_previous_tab: pass:quotes[`ctrl+shift+left`]
|
||||
:sc_previous_window: pass:quotes[`ctrl+shift+[`]
|
||||
: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_home: pass:quotes[`ctrl+shift+home`]
|
||||
: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_up: pass:quotes[`ctrl+shift+page_up`]
|
||||
: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_show_scrollback: pass:quotes[`ctrl+shift+h`]
|
||||
: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.
|
||||
|
||||
* 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,
|
||||
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)
|
||||
* gcc or clang (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
|
||||
|
||||
@@ -109,7 +116,7 @@ the following dependencies are installed first.
|
||||
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
|
||||
@@ -121,16 +128,32 @@ You can run kitty, as:
|
||||
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
|
||||
|
||||
* 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
|
||||
|
||||
* 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
|
||||
|
||||
kitty is available as a macOS `dmg` file for easy installation from the
|
||||
@@ -199,6 +222,7 @@ windows are:
|
||||
|Next layout | {sc_next_layout}
|
||||
|Move tab forward | {sc_move_tab_forward}
|
||||
|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}
|
||||
|Click URL using the keyboard | {sc_run_simple_kitten_text_url_hints}
|
||||
|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 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
|
||||
`kitty @`. Note that you must set `allow_remote_control yes` in your
|
||||
kitty.conf to use this feature. It even works over SSH connections. This
|
||||
feature is best illustrated with a link:remote-control.asciidoc[tutorial].
|
||||
arbitrary input/text to any specified window. Messages can be sent using `kitty
|
||||
@`. Note that you must set `allow_remote_control yes` in your
|
||||
link:kitty/kitty.conf[kitty.conf] to use this feature. It even works over SSH
|
||||
connections. This feature is best illustrated with a
|
||||
link:remote-control.asciidoc[tutorial].
|
||||
|
||||
|
||||
== 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]
|
||||
|
||||
== 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.
|
||||
Press {sc_run_simple_kitten_text_url_hints} to activate hints mode, as shown below.
|
||||
In Name mode you instead type words from the character name and use the arrow
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
You can double click to select a word and triple click to select a line. You can also
|
||||
hold down `ctrl+shift` and click on a URL to open it in a browser.
|
||||
* You can also 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
|
||||
@@ -290,7 +338,9 @@ Currently, there are five layouts available,
|
||||
* Horizontal -- All windows are shown side-by-side
|
||||
* 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
|
||||
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
|
||||
@@ -299,13 +349,18 @@ a pull request!
|
||||
|
||||
== Configuration
|
||||
|
||||
kitty is highly customizable, everything from keyboard shortcuts, to
|
||||
painting frames-per-second. See the heavily commented
|
||||
link:kitty/kitty.conf[default config file]. By default kitty looks for a
|
||||
config file in the OS config directory (usually
|
||||
`~/.config/kitty/kitty.conf` on Linux and
|
||||
kitty is highly customizable, everything from keyboard shortcuts, to painting
|
||||
frames-per-second. See the heavily commented link:kitty/kitty.conf[default
|
||||
config file] for an overview of all customization possibilities.
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
@@ -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,
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
for this is to use either use different symbols that are not square, or to use
|
||||
a font that defines ligatures with the space character for these symbols. See
|
||||
link:https://github.com/kovidgoyal/kitty/issues/182[#182] for a discussion of
|
||||
the approach using ligatures.
|
||||
standard, kitty renders them either smaller or truncated. The exception is if
|
||||
these characters are followed by a space or empty cell in which case kitty
|
||||
makes use of the extra cell to render them in two cells.
|
||||
|
||||
=== 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:
|
||||
```
|
||||
@@ -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]
|
||||
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,
|
||||
or simply copy over the terminfo files, using:
|
||||
This happens because the kitty terminfo files are not available on the server.
|
||||
Either install kitty on the server, or simply copy over the terminfo files,
|
||||
using:
|
||||
|
||||
....
|
||||
ssh myserver mkdir -p \~/.terminfo
|
||||
infocmp xterm-kitty | ssh myserver "cat > /tmp/kitty.terminfo"
|
||||
ssh myserver tic -x -o \~/.terminfo /tmp/kitty.terminfo
|
||||
infocmp xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
|
||||
....
|
||||
|
||||
Really, the correct solution for this is to convince the OpenSSH maintainers to have ssh do this
|
||||
automatically when connecting to a server.
|
||||
Really, the correct solution for this is to convince the OpenSSH maintainers to
|
||||
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
|
||||
|
||||
|
||||
@@ -31,6 +31,13 @@ def launch(args):
|
||||
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):
|
||||
func = namespaced_entry_points[args[1]]
|
||||
func(args[1:])
|
||||
@@ -42,6 +49,7 @@ entry_points = {
|
||||
'list-fonts': list_fonts,
|
||||
'runpy': runpy,
|
||||
'launch': launch,
|
||||
'kitten': run_kitten,
|
||||
|
||||
'@': remote_control,
|
||||
'+': namespaced,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/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):
|
||||
if c <= 32 or c == 127 or 128 <= c <= 159:
|
||||
return
|
||||
word_search_map[w.lower()].add(c)
|
||||
if len(w) > 1:
|
||||
word_search_map[w.lower()].add(c)
|
||||
|
||||
first = None
|
||||
for word, c in html5.items():
|
||||
@@ -54,7 +55,9 @@ def parse_ucd():
|
||||
for line in get_data('ucd/UnicodeData.txt'):
|
||||
parts = [x.strip() for x in line.split(';')]
|
||||
codepoint = int(parts[0], 16)
|
||||
name = parts[1]
|
||||
name = parts[1] or parts[10]
|
||||
if name == '<control>':
|
||||
name = parts[10]
|
||||
if name:
|
||||
name_map[codepoint] = name
|
||||
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):
|
||||
if isinstance(spec, tuple):
|
||||
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:
|
||||
p(f'\t\tcase {spec}: return {rmap[spec]};')
|
||||
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(
|
||||
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))))
|
||||
|
||||
|
||||
def gen_names():
|
||||
words = tuple(sorted(word_search_map))
|
||||
|
||||
with create_header('kittens/unicode_input/names.h') as p:
|
||||
cp_map = list(sorted(name_map))
|
||||
p(f'static const char* name_map[{len(cp_map)}] = {{' ' // {{{')
|
||||
for cp in cp_map:
|
||||
mark_to_cp = list(sorted(name_map))
|
||||
cp_to_mark = {cp: m for m, cp in enumerate(mark_to_cp)}
|
||||
# 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('"', '\\"')
|
||||
p(f'\t"{w}",')
|
||||
p("}; // }}}\n")
|
||||
|
||||
p(f'static const char* idx_to_word[{len(words)}] = ' '{ // {{{')
|
||||
for s in words:
|
||||
s = s.replace('"', '\\"')
|
||||
p(f'\t"{s}",')
|
||||
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")
|
||||
# Mapping of mark to codepoint
|
||||
p(f'static const char_type mark_to_cp[{len(mark_to_cp)}] = {{' ' // {{{')
|
||||
p(', '.join(map(str, mark_to_cp)))
|
||||
p('}; // }}}\n')
|
||||
|
||||
# Function to get mark number for codepoint
|
||||
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('static inline const char* name_for_codepoint(char_type cp) {')
|
||||
p('\tchar_type m = mark_for_codepoint(cp); if (m == 0) return NULL;')
|
||||
p('\treturn name_map[m];')
|
||||
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():
|
||||
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():
|
||||
c = open('out/src/glad.c', 'rb').read().decode('utf-8')
|
||||
functions = []
|
||||
@@ -48,12 +52,13 @@ init_glad(GLADloadproc load, int debug) {
|
||||
}
|
||||
return ret;
|
||||
}'''.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 = raw.replace('<KHR/khrplatform.h>', '"khrplatform.h"')
|
||||
raw += '\nint init_glad(GLADloadproc, int);\n'
|
||||
open('../kitty/gl-wrapper.h', 'w').write(raw)
|
||||
shutil.copy2('out/include/KHR/khrplatform.h', '../kitty')
|
||||
open('../kitty/gl-wrapper.h', 'w').write(strip_trailing_whitespace(raw))
|
||||
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__':
|
||||
|
||||
@@ -69,6 +69,7 @@ static void changeToResourcesDirectory(void)
|
||||
static void createKeyTables(void)
|
||||
{
|
||||
int scancode;
|
||||
_glfw.ns.debug_keyboard = getenv("GLFW_DEBUG_KEYBOARD") != NULL;
|
||||
|
||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
||||
@@ -207,6 +208,9 @@ static GLFWbool updateUnicodeDataNS(void)
|
||||
_glfw.ns.unicodeData = nil;
|
||||
}
|
||||
|
||||
for (_GLFWwindow *window = _glfw.windowListHead; window; window = window->next)
|
||||
window->ns.deadKeyState = 0;
|
||||
|
||||
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
if (!_glfw.ns.inputSource)
|
||||
{
|
||||
@@ -371,4 +375,3 @@ const char* _glfwPlatformGetVersionString(void)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -355,6 +355,10 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||
{
|
||||
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
|
||||
|
||||
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,int);
|
||||
|
||||
typedef struct VkMacOSSurfaceCreateInfoMVK
|
||||
{
|
||||
@@ -98,6 +99,10 @@ typedef struct _GLFWwindowNS
|
||||
// This is kept to counteract Cocoa doing the same internally
|
||||
double cursorWarpDeltaX, cursorWarpDeltaY;
|
||||
|
||||
// The text input filter callback
|
||||
GLFWcocoatextinputfilterfun textInputFilterCallback;
|
||||
// Dead key state
|
||||
UInt32 deadKeyState;
|
||||
} _GLFWwindowNS;
|
||||
|
||||
// Cocoa-specific global data
|
||||
@@ -114,6 +119,8 @@ typedef struct _GLFWlibraryNS
|
||||
id listener;
|
||||
|
||||
char keyName[64];
|
||||
char text[256];
|
||||
GLFWbool debug_keyboard;
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
char* clipboardString;
|
||||
@@ -166,4 +173,3 @@ void _glfwInitTimerNS(void);
|
||||
void _glfwPollMonitorsNS(void);
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||
|
||||
|
||||
@@ -128,6 +128,32 @@ static void updateCursorImage(_GLFWwindow* 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
|
||||
// coordinate systems
|
||||
//
|
||||
@@ -183,10 +209,112 @@ static int translateFlags(NSUInteger flags)
|
||||
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
|
||||
//
|
||||
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]))
|
||||
return GLFW_KEY_UNKNOWN;
|
||||
|
||||
@@ -321,7 +449,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
centerCursor(window);
|
||||
|
||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||
_glfwPlatformSetCursorMode(window, window->cursorMode);
|
||||
updateCursorMode(window);
|
||||
}
|
||||
|
||||
- (void)windowDidResignKey:(NSNotification *)notification
|
||||
@@ -582,6 +710,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
window->ns.xscale = xscale;
|
||||
window->ns.yscale = 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];
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
const int key = translateKey([event keyCode]);
|
||||
const int mods = translateFlags([event modifierFlags]);
|
||||
|
||||
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
|
||||
|
||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||
const unsigned int scancode = [event keyCode];
|
||||
const NSUInteger flags = [event modifierFlags];
|
||||
const int mods = translateFlags(flags);
|
||||
const int key = translateKey(scancode, GLFW_TRUE);
|
||||
const GLFWbool process_text = !window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, scancode) != 1;
|
||||
_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
|
||||
@@ -629,7 +845,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
int action;
|
||||
const unsigned int modifierFlags =
|
||||
[event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
const int key = translateKey([event keyCode]);
|
||||
const int key = translateKey([event keyCode], GLFW_FALSE);
|
||||
const int mods = translateFlags(modifierFlags);
|
||||
const NSUInteger keyFlag = translateKeyToModifierFlag(key);
|
||||
|
||||
@@ -643,14 +859,14 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
else
|
||||
action = GLFW_RELEASE;
|
||||
|
||||
_glfwInputKey(window, key, [event keyCode], action, mods);
|
||||
_glfwInputKeyboard(window, key, [event keyCode], action, mods, "", 0);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
const int key = translateKey([event keyCode]);
|
||||
const int key = translateKey([event keyCode], GLFW_TRUE);
|
||||
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
|
||||
@@ -785,25 +1001,12 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||
{
|
||||
NSString* characters;
|
||||
NSEvent* event = [NSApp currentEvent];
|
||||
const int mods = translateFlags([event modifierFlags]);
|
||||
const int plain = !(mods & GLFW_MOD_SUPER);
|
||||
|
||||
if ([string isKindOfClass:[NSAttributedString class]])
|
||||
characters = [string string];
|
||||
else
|
||||
characters = (NSString*) string;
|
||||
|
||||
NSUInteger i, length = [characters length];
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
const unichar codepoint = [characters characterAtIndex:i];
|
||||
if ((codepoint & 0xff00) == 0xf700)
|
||||
continue;
|
||||
|
||||
_glfwInputChar(window, codepoint, mods, plain);
|
||||
}
|
||||
snprintf(_glfw.ns.text, sizeof(_glfw.ns.text), "%s", [characters UTF8String]);
|
||||
_glfw.ns.text[sizeof(_glfw.ns.text) - 1] = 0;
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)selector
|
||||
@@ -1136,6 +1339,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
window->ns.deadKeyState = 0;
|
||||
if (!initializeAppKit())
|
||||
return GLFW_FALSE;
|
||||
|
||||
@@ -1358,7 +1562,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
[NSApp requestUserAttention:NSInformationalRequest];
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||
{
|
||||
NSBeep();
|
||||
return GLFW_TRUE;
|
||||
@@ -1534,6 +1738,9 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
||||
|
||||
void _glfwPlatformPollEvents(void)
|
||||
{
|
||||
if (!initializeAppKit())
|
||||
return;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
||||
@@ -1632,26 +1839,8 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
|
||||
|
||||
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
||||
{
|
||||
if (mode == 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);
|
||||
if (_glfwPlatformWindowFocused(window))
|
||||
updateCursorMode(window);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
@@ -1677,16 +1866,7 @@ const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
if (!characterCount)
|
||||
return NULL;
|
||||
|
||||
CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
|
||||
characters,
|
||||
characterCount,
|
||||
kCFAllocatorNull);
|
||||
CFStringGetCString(string,
|
||||
_glfw.ns.keyName,
|
||||
sizeof(_glfw.ns.keyName),
|
||||
kCFStringEncodingUTF8);
|
||||
CFRelease(string);
|
||||
|
||||
convert_utf16_to_utf8(characters, characterCount, _glfw.ns.keyName, sizeof(_glfw.ns.keyName));
|
||||
return _glfw.ns.keyName;
|
||||
}
|
||||
|
||||
@@ -1868,6 +2048,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
|
||||
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
|
||||
[window->ns.view setWantsLayer:YES];
|
||||
|
||||
memset(&sci, 0, sizeof(sci));
|
||||
@@ -1900,3 +2081,10 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
|
||||
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()
|
||||
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__))
|
||||
|
||||
|
||||
@@ -42,8 +46,11 @@ def init_env(env, pkg_config, at_least_version, module='x11'):
|
||||
ans.sources = sinfo['common']['sources'] + sinfo[module]['sources']
|
||||
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':
|
||||
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.ldpaths.extend(pkg_config(dep, '--libs'))
|
||||
|
||||
@@ -94,19 +101,20 @@ def collect_source_information():
|
||||
).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('.')))
|
||||
ans = {
|
||||
'common': dict(extract_sources('common')),
|
||||
'wayland_protocols': wayland_protocols,
|
||||
}
|
||||
joystick = 'null' if is_bsd else 'linux'
|
||||
for group in 'cocoa win32 x11 wayland osmesa'.split():
|
||||
m = re.search('_GLFW_' + group.upper(), raw)
|
||||
ans[group] = dict(extract_sources('glfw', m.start()))
|
||||
if group == 'x11':
|
||||
ans[group]['headers'].append('linux_joystick.h')
|
||||
ans[group]['sources'].append('linux_joystick.c')
|
||||
elif group == 'wayland':
|
||||
if group in ('x11', 'wayland'):
|
||||
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
|
||||
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
|
||||
if group == 'wayland':
|
||||
ans[group]['protocols'] = p = []
|
||||
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
|
||||
p.append(m.group(1))
|
||||
@@ -188,6 +196,7 @@ def generate_wrappers(glfw_header, glfw_native_header):
|
||||
for line in '''\
|
||||
void* glfwGetCocoaWindow(GLFWwindow* window)
|
||||
uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor)
|
||||
GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback)
|
||||
void* glfwGetX11Display(void)
|
||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||
void glfwSetX11SelectionString(const char* string)
|
||||
@@ -204,6 +213,7 @@ def generate_wrappers(glfw_header, glfw_native_header):
|
||||
#pragma once
|
||||
#include <stddef.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);
|
||||
|
||||
/*! @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] 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] mods Bit field describing which [modifier keys](@ref mods) were
|
||||
* held down.
|
||||
* @param[in] text UTF-8 encoded text generated by this key event or empty string.
|
||||
* @param[in] 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 glfwSetKeyCallback
|
||||
* @sa @ref glfwSetKeyboardCallback
|
||||
*
|
||||
* @since Added in version 1.0.
|
||||
* @glfw3 Added window handle, scancode and modifier mask parameters.
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,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);
|
||||
typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, int);
|
||||
|
||||
/*! @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
|
||||
* this.
|
||||
*
|
||||
* @remark @wayland The window frame is currently unimplemented, as if
|
||||
* [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`.
|
||||
* A compositor can still emit close, resize or maximize events, using for
|
||||
* example a keybind mechanism.
|
||||
* @remark @wayland The window frame is currently very simple, only allowing
|
||||
* window resize or move. A compositor can still emit close, maximize or
|
||||
* fullscreen events, using for example a keybind mechanism. Additionally,
|
||||
* 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,
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* @sa @ref window_size
|
||||
@@ -3191,13 +3160,9 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
|
||||
/*! @brief Sounds an audible bell associated with the window
|
||||
*
|
||||
* 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] 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
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
@@ -3212,7 +3177,7 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* 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.
|
||||
@@ -4239,121 +4204,11 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
|
||||
*/
|
||||
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
||||
|
||||
/*! @brief Sets the key callback.
|
||||
*
|
||||
* 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.
|
||||
/*! @brief Sets the callback for handling keyboard events.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun 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);
|
||||
GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboardfun cbfun);
|
||||
|
||||
/*! @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
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
* 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.
|
||||
*
|
||||
@@ -4904,7 +4759,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
|
||||
*
|
||||
* Not all devices have all the buttons or axes provided by @ref
|
||||
* 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[out] state The gamepad input state of the joystick.
|
||||
@@ -5549,4 +5404,3 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
|
||||
#endif
|
||||
|
||||
#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
|
||||
//
|
||||
static void terminate(void)
|
||||
@@ -173,7 +142,30 @@ void _glfwInputError(int code, const char* format, ...)
|
||||
description[sizeof(description) - 1] = '\0';
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
||||
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,
|
||||
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;
|
||||
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;
|
||||
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_TRUE;
|
||||
@@ -167,6 +167,10 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
|
||||
while (*c)
|
||||
{
|
||||
// TODO: Implement output modifiers
|
||||
if (*c == '+' || *c == '-')
|
||||
return GLFW_FALSE;
|
||||
|
||||
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
|
||||
{
|
||||
length = strlen(fields[i].name);
|
||||
@@ -177,23 +181,50 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
|
||||
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')
|
||||
fields[i].element->type = _GLFW_JOYSTICK_AXIS;
|
||||
e->type = _GLFW_JOYSTICK_AXIS;
|
||||
else if (*c == 'b')
|
||||
fields[i].element->type = _GLFW_JOYSTICK_BUTTON;
|
||||
e->type = _GLFW_JOYSTICK_BUTTON;
|
||||
else if (*c == 'h')
|
||||
fields[i].element->type = _GLFW_JOYSTICK_HATBIT;
|
||||
e->type = _GLFW_JOYSTICK_HATBIT;
|
||||
else
|
||||
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 bit = strtoul(c + 1, (char**) &c, 10);
|
||||
fields[i].element->value = (uint8_t) ((hat << 4) | bit);
|
||||
e->index = (uint8_t) ((hat << 4) | bit);
|
||||
}
|
||||
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
|
||||
{
|
||||
@@ -224,9 +255,9 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
////// 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)
|
||||
{
|
||||
@@ -247,31 +278,10 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m
|
||||
action = GLFW_REPEAT;
|
||||
}
|
||||
|
||||
if (!window->lockKeyMods)
|
||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
|
||||
if (window->callbacks.key)
|
||||
window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (window->callbacks.keyboard) {
|
||||
if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
window->callbacks.keyboard((GLFWwindow*) window, key, scancode, action, mods, text, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,6 +433,137 @@ void _glfwFreeJoystick(_GLFWjoystick* js)
|
||||
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 //////
|
||||
@@ -478,9 +619,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
_glfwPlatformGetCursorPos(window,
|
||||
&window->virtualCursorPosX,
|
||||
&window->virtualCursorPosY);
|
||||
|
||||
if (_glfwPlatformWindowFocused(window))
|
||||
_glfwPlatformSetCursorMode(window, value);
|
||||
_glfwPlatformSetCursorMode(window, value);
|
||||
}
|
||||
else if (mode == GLFW_STICKY_KEYS)
|
||||
{
|
||||
@@ -760,33 +899,13 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
|
||||
_glfwPlatformSetCursor(window, cursor);
|
||||
}
|
||||
|
||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
||||
GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboardfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.key, 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);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.keyboard, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
@@ -1188,35 +1307,41 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
||||
|
||||
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;
|
||||
}
|
||||
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 bit = js->mapping->buttons[i].value & 0xf;
|
||||
const unsigned int hat = e->index >> 4;
|
||||
const unsigned int bit = e->index & 0xf;
|
||||
if (js->hats[hat] & bit)
|
||||
state->buttons[i] = GLFW_PRESS;
|
||||
}
|
||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
|
||||
state->buttons[i] = js->buttons[js->mapping->buttons[i].value];
|
||||
else if (e->type == _GLFW_JOYSTICK_BUTTON)
|
||||
state->buttons[i] = js->buttons[e->index];
|
||||
}
|
||||
|
||||
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
|
||||
{
|
||||
if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS)
|
||||
state->axes[i] = js->axes[js->mapping->axes[i].value];
|
||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
|
||||
const _GLFWmapelement* e = js->mapping->axes + i;
|
||||
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||
{
|
||||
const unsigned int hat = js->mapping->axes[i].value >> 4;
|
||||
const unsigned int bit = js->mapping->axes[i].value & 0xf;
|
||||
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
|
||||
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)
|
||||
state->axes[i] = 1.f;
|
||||
}
|
||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
|
||||
state->axes[i] = (float) js->buttons[js->mapping->axes[i].value];
|
||||
else if (e->type == _GLFW_JOYSTICK_BUTTON)
|
||||
state->axes[i] = (float) js->buttons[e->index];
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
@@ -1268,4 +1393,3 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||
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_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
|
||||
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
|
||||
} VkStructureType;
|
||||
|
||||
@@ -407,9 +407,7 @@ struct _GLFWwindow
|
||||
GLFWcursorposfun cursorPos;
|
||||
GLFWcursorenterfun cursorEnter;
|
||||
GLFWscrollfun scroll;
|
||||
GLFWkeyfun key;
|
||||
GLFWcharfun character;
|
||||
GLFWcharmodsfun charmods;
|
||||
GLFWkeyboardfun keyboard;
|
||||
GLFWdropfun drop;
|
||||
} callbacks;
|
||||
|
||||
@@ -456,7 +454,9 @@ struct _GLFWcursor
|
||||
struct _GLFWmapelement
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t value;
|
||||
uint8_t index;
|
||||
int8_t axisScale;
|
||||
int8_t axisOffset;
|
||||
};
|
||||
|
||||
// Gamepad mapping structure
|
||||
@@ -605,6 +605,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
||||
const char* _glfwPlatformGetScancodeName(int scancode);
|
||||
int _glfwPlatformGetKeyScancode(int key);
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||
float* xscale, float* yscale);
|
||||
@@ -650,7 +651,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformHideWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t);
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window);
|
||||
void _glfwPlatformFocusWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
|
||||
int xpos, int ypos, int width, int height,
|
||||
@@ -708,10 +709,7 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
|
||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||
|
||||
void _glfwInputKey(_GLFWwindow* window,
|
||||
int key, int scancode, int action, int mods);
|
||||
void _glfwInputChar(_GLFWwindow* window,
|
||||
unsigned int codepoint, int mods, GLFWbool plain);
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
@@ -760,10 +758,10 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name,
|
||||
int buttonCount,
|
||||
int hatCount);
|
||||
void _glfwFreeJoystick(_GLFWjoystick* js);
|
||||
const char* _glfwGetKeyName(int key);
|
||||
|
||||
GLFWbool _glfwInitVulkan(int mode);
|
||||
void _glfwTerminateVulkan(void);
|
||||
const char* _glfwGetVulkanResultString(VkResult result);
|
||||
|
||||
char* _glfw_strdup(const char* source);
|
||||
|
||||
|
||||
624
glfw/mappings.h
vendored
624
glfw/mappings.h
vendored
@@ -60,243 +60,411 @@
|
||||
|
||||
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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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 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,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 ,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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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,",
|
||||
"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)
|
||||
return;
|
||||
|
||||
_glfwPlatformFreeMonitor(monitor);
|
||||
|
||||
_glfwFreeGammaArrays(&monitor->originalRamp);
|
||||
_glfwFreeGammaArrays(&monitor->currentRamp);
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
||||
// Info.plist for unbundled applications
|
||||
// HACK: This assumes that NSOpenGLPixelFormat will remain
|
||||
// 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);
|
||||
#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 //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -323,4 +323,3 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
// This seems like the most appropriate error to return here
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,15 +55,16 @@
|
||||
"wayland": {
|
||||
"headers": [
|
||||
"wl_platform.h",
|
||||
"linux_joystick.h",
|
||||
"posix_time.h",
|
||||
"posix_thread.h",
|
||||
"xkb_unicode.h",
|
||||
"xkb_glfw.h",
|
||||
"egl_context.h",
|
||||
"osmesa_context.h"
|
||||
"osmesa_context.h",
|
||||
"linux_joystick.h"
|
||||
],
|
||||
"protocols": [
|
||||
"stable/xdg-shell/xdg-shell.xml",
|
||||
"stable/viewporter/viewporter.xml",
|
||||
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||
@@ -72,12 +73,12 @@
|
||||
"wl_init.c",
|
||||
"wl_monitor.c",
|
||||
"wl_window.c",
|
||||
"linux_joystick.c",
|
||||
"posix_time.c",
|
||||
"posix_thread.c",
|
||||
"xkb_unicode.c",
|
||||
"xkb_glfw.c",
|
||||
"egl_context.c",
|
||||
"osmesa_context.c"
|
||||
"osmesa_context.c",
|
||||
"linux_joystick.c"
|
||||
]
|
||||
},
|
||||
"wayland_protocols": [
|
||||
@@ -107,7 +108,7 @@
|
||||
"x11": {
|
||||
"headers": [
|
||||
"x11_platform.h",
|
||||
"xkb_unicode.h",
|
||||
"xkb_glfw.h",
|
||||
"posix_time.h",
|
||||
"posix_thread.h",
|
||||
"glx_context.h",
|
||||
@@ -119,7 +120,7 @@
|
||||
"x11_init.c",
|
||||
"x11_monitor.c",
|
||||
"x11_window.c",
|
||||
"xkb_unicode.c",
|
||||
"xkb_glfw.c",
|
||||
"posix_time.c",
|
||||
"posix_thread.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)
|
||||
_glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
|
||||
#elif defined(_GLFW_COCOA)
|
||||
_glfw.vk.handle = _glfw_dlopen("libMoltenVK.dylib");
|
||||
_glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib");
|
||||
#else
|
||||
_glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
|
||||
#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 //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||
{
|
||||
DEVMODEW dm;
|
||||
|
||||
2
glfw/win32_platform.h
vendored
2
glfw/win32_platform.h
vendored
@@ -120,6 +120,8 @@ typedef struct
|
||||
HRGN hRgnBlur;
|
||||
BOOL fTransitionOnMaximized;
|
||||
} DWM_BLURBEHIND;
|
||||
#else
|
||||
#include <dwmapi.h>
|
||||
#endif /*Windows Vista*/
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
static void updateCursorImage(_GLFWwindow* window)
|
||||
@@ -286,6 +266,67 @@ static void updateClipRect(_GLFWwindow* window)
|
||||
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
|
||||
//
|
||||
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 (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
||||
disableCursor(window);
|
||||
|
||||
window->win32.frameAction = GLFW_FALSE;
|
||||
}
|
||||
@@ -593,7 +634,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
break;
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
||||
disableCursor(window);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -601,7 +642,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
case WM_KILLFOCUS:
|
||||
{
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
|
||||
enableCursor(window);
|
||||
|
||||
if (window->monitor && window->autoIconify)
|
||||
_glfwPlatformIconifyWindow(window);
|
||||
@@ -857,10 +898,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
{
|
||||
// HACK: Postpone cursor disabling while the user is moving or
|
||||
// resizing the window or using the menu
|
||||
// HACK: Enable the cursor while the user is moving or
|
||||
// resizing the window or using the window menu
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
|
||||
enableCursor(window);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -871,7 +912,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
// HACK: Disable the cursor once the user is done moving or
|
||||
// resizing the window or using the menu
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
||||
disableCursor(window);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1474,9 +1515,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
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)
|
||||
@@ -1777,39 +1818,12 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
||||
{
|
||||
if (mode == GLFW_CURSOR_DISABLED)
|
||||
{
|
||||
const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
|
||||
|
||||
_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");
|
||||
}
|
||||
if (_glfwPlatformWindowFocused(window))
|
||||
disableCursor(window);
|
||||
}
|
||||
else if (_glfw.win32.disabledCursorWindow == window)
|
||||
{
|
||||
const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
|
||||
|
||||
_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))
|
||||
enableCursor(window);
|
||||
else if (cursorInClientArea(window))
|
||||
updateCursorImage(window);
|
||||
}
|
||||
|
||||
@@ -2014,4 +2028,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwWindowBell(GLFWwindow* handle, int64_t param)
|
||||
GLFWAPI int glfwWindowBell(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
return _glfwPlatformWindowBell(window, param);
|
||||
return _glfwPlatformWindowBell(window);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwHideWindow(GLFWwindow* handle)
|
||||
@@ -1106,4 +1106,3 @@ GLFWAPI void glfwPostEmptyEvent(void)
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
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,
|
||||
struct wl_pointer* pointer,
|
||||
uint32_t serial,
|
||||
@@ -52,8 +85,16 @@ static void pointerHandleEnter(void* data,
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
int focus = 0;
|
||||
_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.pointerFocus = window;
|
||||
|
||||
@@ -80,6 +121,39 @@ static void pointerHandleLeave(void* data,
|
||||
_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,
|
||||
struct wl_pointer* pointer,
|
||||
uint32_t time,
|
||||
@@ -87,6 +161,7 @@ static void pointerHandleMotion(void* data,
|
||||
wl_fixed_t sy)
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
const char* cursorName;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
@@ -99,13 +174,47 @@ static void pointerHandleMotion(void* data,
|
||||
window->wl.cursorPosY = wl_fixed_to_double(sy);
|
||||
}
|
||||
|
||||
_glfwInputCursorPos(window,
|
||||
wl_fixed_to_double(sx),
|
||||
wl_fixed_to_double(sy));
|
||||
switch (window->wl.decorations.focus)
|
||||
{
|
||||
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,
|
||||
struct wl_pointer* wl_pointer,
|
||||
struct wl_pointer* pointer,
|
||||
uint32_t serial,
|
||||
uint32_t time,
|
||||
uint32_t button,
|
||||
@@ -114,8 +223,76 @@ static void pointerHandleButton(void* data,
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
int glfwButton;
|
||||
|
||||
// Both xdg-shell and wl_shell use the same values.
|
||||
uint32_t edges = WL_SHELL_SURFACE_RESIZE_NONE;
|
||||
|
||||
if (!window)
|
||||
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;
|
||||
|
||||
@@ -132,7 +309,7 @@ static void pointerHandleButton(void* data,
|
||||
}
|
||||
|
||||
static void pointerHandleAxis(void* data,
|
||||
struct wl_pointer* wl_pointer,
|
||||
struct wl_pointer* pointer,
|
||||
uint32_t time,
|
||||
uint32_t axis,
|
||||
wl_fixed_t value)
|
||||
@@ -172,16 +349,7 @@ static void keyboardHandleKeymap(void* data,
|
||||
int fd,
|
||||
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;
|
||||
const char* locale;
|
||||
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
||||
{
|
||||
@@ -194,78 +362,10 @@ static void keyboardHandleKeymap(void* data,
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
|
||||
mapStr,
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
0);
|
||||
glfw_xkb_compile_keymap(&_glfw.wl.xkb, mapStr);
|
||||
munmap(mapStr, size);
|
||||
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,
|
||||
@@ -279,6 +379,12 @@ static void keyboardHandleEnter(void* data,
|
||||
return;
|
||||
|
||||
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
||||
if (!window)
|
||||
{
|
||||
window = findWindowFromDecorationSurface(surface, NULL);
|
||||
if (!window)
|
||||
return;
|
||||
}
|
||||
|
||||
_glfw.wl.keyboardFocus = window;
|
||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||
@@ -298,63 +404,6 @@ static void keyboardHandleLeave(void* data,
|
||||
_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,
|
||||
struct wl_keyboard* keyboard,
|
||||
uint32_t serial,
|
||||
@@ -362,22 +411,19 @@ static void keyboardHandleKey(void* data,
|
||||
uint32_t key,
|
||||
uint32_t state)
|
||||
{
|
||||
int keyCode;
|
||||
int action;
|
||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||
|
||||
if (!window)
|
||||
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);
|
||||
action = state == WL_KEYBOARD_KEY_STATE_PRESSED
|
||||
? GLFW_PRESS : GLFW_RELEASE;
|
||||
|
||||
_glfwInputKey(window, keyCode, key, action,
|
||||
_glfw.wl.xkb.modifiers);
|
||||
|
||||
if (action == GLFW_PRESS)
|
||||
inputChar(window, key);
|
||||
if (action == GLFW_PRESS && _glfw.wl.keyboardRepeatRate > 0 && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))
|
||||
{
|
||||
_glfw.wl.keyRepeatInfo.key = key;
|
||||
_glfw.wl.keyRepeatInfo.nextRepeatAt = glfwGetTime() + (double)(_glfw.wl.keyboardRepeatDelay) / 1000.0;
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboardHandleModifiers(void* data,
|
||||
@@ -388,38 +434,19 @@ static void keyboardHandleModifiers(void* data,
|
||||
uint32_t modsLocked,
|
||||
uint32_t group)
|
||||
{
|
||||
xkb_mod_mask_t mask;
|
||||
unsigned int modifiers = 0;
|
||||
glfw_xkb_update_modifiers(&_glfw.wl.xkb, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
xkb_state_update_mask(_glfw.wl.xkb.state,
|
||||
modsDepressed,
|
||||
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;
|
||||
_glfw.wl.keyboardRepeatRate = rate;
|
||||
_glfw.wl.keyboardRepeatDelay = delay;
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboardListener = {
|
||||
@@ -428,6 +455,7 @@ static const struct wl_keyboard_listener keyboardListener = {
|
||||
keyboardHandleLeave,
|
||||
keyboardHandleKey,
|
||||
keyboardHandleModifiers,
|
||||
keyboardHandleRepeatInfo,
|
||||
};
|
||||
|
||||
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 = {
|
||||
seatHandleCapabilities
|
||||
seatHandleCapabilities,
|
||||
seatHandleName,
|
||||
};
|
||||
|
||||
static void wmBaseHandlePing(void* data,
|
||||
@@ -485,6 +520,11 @@ static void registryHandleGlobal(void* data,
|
||||
wl_registry_bind(registry, name, &wl_compositor_interface,
|
||||
_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)
|
||||
{
|
||||
_glfw.wl.shm =
|
||||
@@ -503,8 +543,10 @@ static void registryHandleGlobal(void* data,
|
||||
{
|
||||
if (!_glfw.wl.seat)
|
||||
{
|
||||
_glfw.wl.seatVersion = min(4, version);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
@@ -514,6 +556,11 @@ static void registryHandleGlobal(void* data,
|
||||
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
||||
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)
|
||||
{
|
||||
_glfw.wl.relativePointerManager =
|
||||
@@ -541,6 +588,18 @@ static void registryHandleGlobalRemove(void *data,
|
||||
struct wl_registry *registry,
|
||||
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
|
||||
};
|
||||
|
||||
// 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 //////
|
||||
@@ -722,52 +647,6 @@ int _glfwPlatformInit(void)
|
||||
_glfw.wl.egl.window_resize = (PFN_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);
|
||||
if (!_glfw.wl.display)
|
||||
{
|
||||
@@ -779,15 +658,7 @@ int _glfwPlatformInit(void)
|
||||
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
|
||||
wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL);
|
||||
|
||||
createKeyTables();
|
||||
|
||||
_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;
|
||||
}
|
||||
if (!glfw_xkb_create_context(&_glfw.wl.xkb)) return GLFW_FALSE;
|
||||
|
||||
// Sync so we got all registry objects
|
||||
wl_display_roundtrip(_glfw.wl.display);
|
||||
@@ -795,8 +666,10 @@ int _glfwPlatformInit(void)
|
||||
// Sync so we got all initial output events
|
||||
wl_display_roundtrip(_glfw.wl.display);
|
||||
|
||||
#ifdef __linux__
|
||||
if (!_glfwInitJoysticksLinux())
|
||||
return GLFW_FALSE;
|
||||
#endif
|
||||
|
||||
_glfwInitTimerPOSIX();
|
||||
|
||||
@@ -818,7 +691,9 @@ int _glfwPlatformInit(void)
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
_glfwTerminateJoysticksLinux();
|
||||
#endif
|
||||
_glfwTerminateEGL();
|
||||
if (_glfw.wl.egl.handle)
|
||||
{
|
||||
@@ -826,21 +701,7 @@ void _glfwPlatformTerminate(void)
|
||||
_glfw.wl.egl.handle = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
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;
|
||||
}
|
||||
glfw_xkb_release(&_glfw.wl.xkb);
|
||||
|
||||
if (_glfw.wl.cursorTheme)
|
||||
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
|
||||
@@ -852,12 +713,16 @@ void _glfwPlatformTerminate(void)
|
||||
|
||||
if (_glfw.wl.cursorSurface)
|
||||
wl_surface_destroy(_glfw.wl.cursorSurface);
|
||||
if (_glfw.wl.subcompositor)
|
||||
wl_subcompositor_destroy(_glfw.wl.subcompositor);
|
||||
if (_glfw.wl.compositor)
|
||||
wl_compositor_destroy(_glfw.wl.compositor);
|
||||
if (_glfw.wl.shm)
|
||||
wl_shm_destroy(_glfw.wl.shm);
|
||||
if (_glfw.wl.shell)
|
||||
wl_shell_destroy(_glfw.wl.shell);
|
||||
if (_glfw.wl.viewporter)
|
||||
wp_viewporter_destroy(_glfw.wl.viewporter);
|
||||
if (_glfw.wl.wmBase)
|
||||
xdg_wm_base_destroy(_glfw.wl.wmBase);
|
||||
if (_glfw.wl.pointer)
|
||||
@@ -895,4 +760,3 @@ const char* _glfwPlatformGetVersionString(void)
|
||||
#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.output = output;
|
||||
monitor->wl.name = name;
|
||||
|
||||
wl_output_add_listener(output, &outputListener, monitor);
|
||||
}
|
||||
@@ -145,6 +146,12 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
|
||||
////// 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)
|
||||
{
|
||||
if (xpos)
|
||||
|
||||
126
glfw/wl_platform.h
vendored
126
glfw/wl_platform.h
vendored
@@ -25,10 +25,6 @@
|
||||
//========================================================================
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
|
||||
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
||||
@@ -47,12 +43,17 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
||||
|
||||
#include "posix_thread.h"
|
||||
#include "posix_time.h"
|
||||
#ifdef __linux__
|
||||
#include "linux_joystick.h"
|
||||
#include "xkb_unicode.h"
|
||||
#else
|
||||
#include "null_joystick.h"
|
||||
#endif
|
||||
#include "xkb_glfw.h"
|
||||
#include "egl_context.h"
|
||||
#include "osmesa_context.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-pointer-constraints-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_resize _glfw.wl.egl.window_resize
|
||||
|
||||
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
|
||||
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
|
||||
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
|
||||
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
|
||||
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
|
||||
#define _GLFW_DECORATION_WIDTH 4
|
||||
#define _GLFW_DECORATION_TOP 24
|
||||
#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH)
|
||||
#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH)
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
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*);
|
||||
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
|
||||
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
|
||||
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
|
||||
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
|
||||
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
|
||||
typedef enum _GLFWdecorationSideWayland
|
||||
{
|
||||
mainWindow,
|
||||
topDecoration,
|
||||
leftDecoration,
|
||||
rightDecoration,
|
||||
bottomDecoration,
|
||||
|
||||
} _GLFWdecorationSideWayland;
|
||||
|
||||
typedef struct _GLFWdecorationWayland
|
||||
{
|
||||
struct wl_surface* surface;
|
||||
struct wl_subsurface* subsurface;
|
||||
struct wp_viewport* viewport;
|
||||
|
||||
} _GLFWdecorationWayland;
|
||||
|
||||
// Wayland-specific per-window data
|
||||
//
|
||||
@@ -180,6 +165,12 @@ typedef struct _GLFWwindowWayland
|
||||
// This is a hack to prevent auto-iconification on creation.
|
||||
GLFWbool justCreated;
|
||||
|
||||
struct {
|
||||
struct wl_buffer* buffer;
|
||||
_GLFWdecorationWayland top, left, right, bottom;
|
||||
int focus;
|
||||
} decorations;
|
||||
|
||||
} _GLFWwindowWayland;
|
||||
|
||||
// Wayland-specific global data
|
||||
@@ -189,64 +180,33 @@ typedef struct _GLFWlibraryWayland
|
||||
struct wl_display* display;
|
||||
struct wl_registry* registry;
|
||||
struct wl_compositor* compositor;
|
||||
struct wl_subcompositor* subcompositor;
|
||||
struct wl_shell* shell;
|
||||
struct wl_shm* shm;
|
||||
struct wl_seat* seat;
|
||||
struct wl_pointer* pointer;
|
||||
struct wl_keyboard* keyboard;
|
||||
struct xdg_wm_base* wmBase;
|
||||
struct wp_viewporter* viewporter;
|
||||
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
||||
struct zwp_pointer_constraints_v1* pointerConstraints;
|
||||
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
||||
|
||||
int compositorVersion;
|
||||
int seatVersion;
|
||||
|
||||
struct wl_cursor_theme* cursorTheme;
|
||||
struct wl_surface* cursorSurface;
|
||||
uint32_t pointerSerial;
|
||||
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
|
||||
int32_t keyboardRepeatRate;
|
||||
int32_t keyboardRepeatDelay;
|
||||
struct {
|
||||
void* handle;
|
||||
struct xkb_context* context;
|
||||
struct xkb_keymap* keymap;
|
||||
struct xkb_state* state;
|
||||
|
||||
#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;
|
||||
uint32_t key;
|
||||
double nextRepeatAt;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
} keyRepeatInfo;
|
||||
_GLFWXKBData xkb;
|
||||
|
||||
_GLFWwindow* pointerFocus;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
@@ -275,6 +235,7 @@ typedef struct _GLFWlibraryWayland
|
||||
typedef struct _GLFWmonitorWayland
|
||||
{
|
||||
struct wl_output* output;
|
||||
int name;
|
||||
int currentMode;
|
||||
|
||||
int x;
|
||||
@@ -295,4 +256,3 @@ typedef struct _GLFWcursorWayland
|
||||
|
||||
|
||||
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 (_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)
|
||||
{
|
||||
aspectRatio = (float)width / (float)height;
|
||||
@@ -94,9 +104,265 @@ static const struct wl_shell_surface_listener shellSurfaceListener = {
|
||||
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)
|
||||
{
|
||||
int scaledWidth, scaledHeight;
|
||||
int scale = 1;
|
||||
int i;
|
||||
int monitorScale;
|
||||
@@ -117,12 +383,8 @@ static void checkScaleChange(_GLFWwindow* window)
|
||||
if (scale != window->wl.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_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
||||
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
||||
_glfwInputWindowContentScale(window, scale, scale);
|
||||
resizeWindow(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,21 +434,6 @@ static const struct wl_surface_listener surfaceListener = {
|
||||
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)
|
||||
{
|
||||
if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
|
||||
@@ -231,9 +478,32 @@ static GLFWbool createSurface(_GLFWwindow* window,
|
||||
if (!window->wl.transparent)
|
||||
setOpaqueRegion(window);
|
||||
|
||||
if (window->decorated && !window->monitor)
|
||||
createDecorations(window);
|
||||
|
||||
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)
|
||||
{
|
||||
if (!_glfw.wl.shell)
|
||||
@@ -261,12 +531,7 @@ static GLFWbool createShellSurface(_GLFWwindow* window)
|
||||
|
||||
if (window->monitor)
|
||||
{
|
||||
wl_shell_surface_set_fullscreen(
|
||||
window->wl.shellSurface,
|
||||
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
|
||||
0,
|
||||
window->monitor->wl.output);
|
||||
setIdleInhibitor(window, GLFW_TRUE);
|
||||
setFullscreen(window, window->monitor, 0);
|
||||
}
|
||||
else if (window->wl.maximized)
|
||||
{
|
||||
@@ -424,25 +689,32 @@ static GLFWbool createXdgSurface(_GLFWwindow* window)
|
||||
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
|
||||
createTmpfileCloexec(char* tmpname)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = mkostemp(tmpname, O_CLOEXEC);
|
||||
if (fd >= 0)
|
||||
unlink(tmpname);
|
||||
|
||||
return fd;
|
||||
adjustTimeoutForKeyRepeat(int timeout) {
|
||||
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return timeout;
|
||||
double now = glfwGetTime();
|
||||
if (timeout < 0 || now + timeout / 1000. > _glfw.wl.keyRepeatInfo.nextRepeatAt) {
|
||||
timeout = _glfw.wl.keyRepeatInfo.nextRepeatAt <= now ? 0 : ( (_glfw.wl.keyRepeatInfo.nextRepeatAt - now) * 1000 + 1 );
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static void
|
||||
handleEvents(int timeout)
|
||||
{
|
||||
struct wl_display* display = _glfw.wl.display;
|
||||
struct pollfd fds[] = {
|
||||
{ wl_display_get_fd(display), POLLIN },
|
||||
};
|
||||
struct pollfd pfd = { wl_display_get_fd(display), POLLIN };
|
||||
|
||||
while (wl_display_prepare_read(display) != 0)
|
||||
wl_display_dispatch_pending(display);
|
||||
@@ -462,71 +734,27 @@ handleEvents(int timeout)
|
||||
return;
|
||||
}
|
||||
|
||||
if (poll(fds, 1, timeout) > 0)
|
||||
dispatchPendingKeyRepeats();
|
||||
timeout = adjustTimeoutForKeyRepeat(timeout);
|
||||
|
||||
if (poll(&pfd, 1, timeout) > 0)
|
||||
{
|
||||
wl_display_read_events(display);
|
||||
wl_display_dispatch_pending(display);
|
||||
if (pfd.revents & POLLIN)
|
||||
{
|
||||
wl_display_read_events(display);
|
||||
wl_display_dispatch_pending(display);
|
||||
}
|
||||
else
|
||||
{
|
||||
wl_display_cancel_read(display);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
wl_display_cancel_read(display);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
dispatchPendingKeyRepeats();
|
||||
}
|
||||
|
||||
// Translates a GLFW standard cursor to a theme cursor name
|
||||
@@ -639,6 +867,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
if (window->context.destroy)
|
||||
window->context.destroy(window);
|
||||
|
||||
destroyDecorations(window);
|
||||
if (window->wl.decorations.buffer)
|
||||
wl_buffer_destroy(window->wl.decorations.buffer);
|
||||
|
||||
if (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)
|
||||
{
|
||||
int scaledWidth = width * window->wl.scale;
|
||||
int scaledHeight = height * window->wl.scale;
|
||||
window->wl.width = width;
|
||||
window->wl.height = height;
|
||||
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
||||
if (!window->wl.transparent)
|
||||
setOpaqueRegion(window);
|
||||
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
||||
resizeWindow(window);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
||||
@@ -754,8 +981,17 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
int* left, int* top,
|
||||
int* right, int* bottom)
|
||||
{
|
||||
// TODO: will need a proper implementation once decorations are
|
||||
// implemented, but for now just leave everything as 0.
|
||||
if (window->decorated && !window->monitor)
|
||||
{
|
||||
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,
|
||||
@@ -851,11 +1087,15 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
"Wayland: Window attention request not implemented yet");
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Window bell request not implemented yet");
|
||||
// TODO: Use an actual Wayland API to implement this when one becomes available
|
||||
int fd = open("/dev/tty", O_WRONLY | O_CLOEXEC);
|
||||
if (fd > -1) {
|
||||
int ret = write(fd, "\x07", 1) == 1 ? GLFW_TRUE : GLFW_FALSE;
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
@@ -871,35 +1111,20 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
||||
int width, int height,
|
||||
int refreshRate)
|
||||
{
|
||||
if (window->wl.xdg.toplevel)
|
||||
if (monitor)
|
||||
{
|
||||
if (monitor)
|
||||
{
|
||||
xdg_toplevel_set_fullscreen(
|
||||
window->wl.xdg.toplevel,
|
||||
monitor->wl.output);
|
||||
}
|
||||
else
|
||||
{
|
||||
setFullscreen(window, monitor, refreshRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (window->wl.xdg.toplevel)
|
||||
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
else if (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);
|
||||
}
|
||||
|
||||
@@ -944,9 +1169,13 @@ void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
|
||||
|
||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Window attribute setting not implemented yet");
|
||||
if (!window->monitor)
|
||||
{
|
||||
if (enabled)
|
||||
createDecorations(window);
|
||||
else
|
||||
destroyDecorations(window);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
|
||||
@@ -1013,66 +1242,19 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
{
|
||||
// TODO
|
||||
return NULL;
|
||||
return glfw_xkb_keysym_name(scancode);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetKeyScancode(int key)
|
||||
{
|
||||
return _glfw.wl.scancodes[key];
|
||||
return glfw_xkb_sym_for_key(key);
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image,
|
||||
int xhot, int yhot)
|
||||
{
|
||||
struct wl_shm_pool* pool;
|
||||
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.buffer = createShmBuffer(image);
|
||||
cursor->wl.width = image->width;
|
||||
cursor->wl.height = image->height;
|
||||
cursor->wl.xhot = xhot;
|
||||
@@ -1376,4 +1558,3 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return window->wl.surface;
|
||||
}
|
||||
|
||||
|
||||
447
glfw/x11_init.c
vendored
447
glfw/x11_init.c
vendored
@@ -36,319 +36,6 @@
|
||||
#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
|
||||
//
|
||||
static Atom getSupportedAtom(Atom* supportedAtoms,
|
||||
@@ -479,7 +166,11 @@ static GLFWbool initExtensions(void)
|
||||
&_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");
|
||||
#endif
|
||||
if (_glfw.x11.xi.handle)
|
||||
{
|
||||
_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");
|
||||
#endif
|
||||
if (_glfw.x11.randr.handle)
|
||||
{
|
||||
_glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma)
|
||||
@@ -593,7 +288,11 @@ static GLFWbool initExtensions(void)
|
||||
RROutputChangeNotifyMask);
|
||||
}
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
_glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so");
|
||||
#else
|
||||
_glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1");
|
||||
#endif
|
||||
if (_glfw.x11.xcursor.handle)
|
||||
{
|
||||
_glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate)
|
||||
@@ -604,7 +303,11 @@ static GLFWbool initExtensions(void)
|
||||
_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");
|
||||
#endif
|
||||
if (_glfw.x11.xinerama.handle)
|
||||
{
|
||||
_glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive)
|
||||
@@ -623,35 +326,11 @@ static GLFWbool initExtensions(void)
|
||||
}
|
||||
}
|
||||
|
||||
_glfw.x11.xkb.major = 1;
|
||||
_glfw.x11.xkb.minor = 0;
|
||||
_glfw.x11.xkb.available =
|
||||
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");
|
||||
}
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so");
|
||||
#else
|
||||
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
|
||||
#endif
|
||||
if (_glfw.x11.xrender.handle)
|
||||
{
|
||||
_glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension)
|
||||
@@ -674,10 +353,31 @@ static GLFWbool initExtensions(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the key code LUT
|
||||
// FIXME: We should listen to XkbMapNotify events to track changes to
|
||||
// the keyboard mapping.
|
||||
createKeyTables();
|
||||
_glfw.x11.xkb.major = 1;
|
||||
_glfw.x11.xkb.minor = 0;
|
||||
_glfw.x11.xkb.available = 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)
|
||||
{
|
||||
_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
|
||||
detectEWMH();
|
||||
@@ -896,16 +596,6 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
|
||||
|
||||
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();
|
||||
XrmInitialize();
|
||||
|
||||
@@ -939,21 +629,6 @@ int _glfwPlatformInit(void)
|
||||
_glfw.x11.helperWindowHandle = createHelperWindow();
|
||||
_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 (!_glfwInitJoysticksLinux())
|
||||
return GLFW_FALSE;
|
||||
@@ -985,27 +660,16 @@ void _glfwPlatformTerminate(void)
|
||||
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
||||
}
|
||||
|
||||
glfw_xkb_release(&_glfw.x11.xkb);
|
||||
free(_glfw.x11.primarySelectionString);
|
||||
free(_glfw.x11.clipboardString);
|
||||
|
||||
if (_glfw.x11.im)
|
||||
{
|
||||
XCloseIM(_glfw.x11.im);
|
||||
_glfw.x11.im = NULL;
|
||||
}
|
||||
|
||||
if (_glfw.x11.display)
|
||||
{
|
||||
XCloseDisplay(_glfw.x11.display);
|
||||
_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)
|
||||
{
|
||||
_glfw_dlclose(_glfw.x11.xcursor.handle);
|
||||
@@ -1024,6 +688,24 @@ void _glfwPlatformTerminate(void)
|
||||
_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
|
||||
// cleanup callbacks that get called by that function
|
||||
_glfwTerminateEGL();
|
||||
@@ -1050,4 +732,3 @@ const char* _glfwPlatformGetVersionString(void)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
4
glfw/x11_monitor.c
vendored
4
glfw/x11_monitor.c
vendored
@@ -308,6 +308,10 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||
{
|
||||
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/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
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
|
||||
@@ -47,6 +50,9 @@
|
||||
// The XInput extension provides raw mouse motion input
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
// The libxkb library is used for improved keyboard support
|
||||
#include "xkb_glfw.h"
|
||||
|
||||
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
|
||||
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
|
||||
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
|
||||
@@ -96,11 +102,7 @@ typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*);
|
||||
#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension
|
||||
#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 xcb_connection_t* (* PFN_XGetXCBConnection)(Display*);
|
||||
#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection
|
||||
|
||||
typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*);
|
||||
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_time.h"
|
||||
#include "xkb_unicode.h"
|
||||
#include "glx_context.h"
|
||||
#include "egl_context.h"
|
||||
#include "osmesa_context.h"
|
||||
@@ -180,7 +181,6 @@ typedef struct _GLFWwindowX11
|
||||
{
|
||||
Colormap colormap;
|
||||
Window handle;
|
||||
XIC ic;
|
||||
|
||||
GLFWbool overrideRedirect;
|
||||
GLFWbool iconified;
|
||||
@@ -219,20 +219,12 @@ typedef struct _GLFWlibraryX11
|
||||
Cursor hiddenCursorHandle;
|
||||
// Context for mapping window XIDs to _GLFWwindow pointers
|
||||
XContext context;
|
||||
// XIM input method
|
||||
XIM im;
|
||||
// Most recent error code received by X error handler
|
||||
int errorCode;
|
||||
// Primary selection string (while the primary selection is owned)
|
||||
char* primarySelectionString;
|
||||
// Clipboard string (while the selection is owned)
|
||||
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
|
||||
double restoreCursorPosX, restoreCursorPosY;
|
||||
// The window whose disabled cursor mode is active
|
||||
@@ -318,15 +310,7 @@ typedef struct _GLFWlibraryX11
|
||||
PFN_XRRUpdateConfiguration UpdateConfiguration;
|
||||
} randr;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
GLFWbool detectable;
|
||||
int majorOpcode;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
int major;
|
||||
int minor;
|
||||
} xkb;
|
||||
_GLFWXKBData xkb;
|
||||
|
||||
struct {
|
||||
int count;
|
||||
@@ -359,11 +343,6 @@ typedef struct _GLFWlibraryX11
|
||||
PFN_XineramaQueryScreens QueryScreens;
|
||||
} xinerama;
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
PFN_XGetXCBConnection GetXCBConnection;
|
||||
} x11xcb;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
void* handle;
|
||||
@@ -441,4 +420,3 @@ void _glfwReleaseErrorHandlerX11(void);
|
||||
void _glfwInputErrorX11(int error, const char* message);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
static _GLFWwindow* findWindowByHandle(Window handle)
|
||||
@@ -497,31 +486,6 @@ static size_t encodeUTF8(char* s, unsigned int ch)
|
||||
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
|
||||
//
|
||||
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)
|
||||
//
|
||||
static GLFWbool createNativeWindow(_GLFWwindow* window,
|
||||
@@ -746,19 +765,6 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
||||
}
|
||||
|
||||
_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);
|
||||
_glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
|
||||
|
||||
@@ -1141,16 +1147,8 @@ static void releaseMonitor(_GLFWwindow* window)
|
||||
static void processEvent(XEvent *event)
|
||||
{
|
||||
_GLFWwindow* window = NULL;
|
||||
int keycode = 0;
|
||||
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 (event->type == _glfw.x11.randr.eventBase + RRNotify)
|
||||
@@ -1208,6 +1206,30 @@ static void processEvent(XEvent *event)
|
||||
handleSelectionRequest(event);
|
||||
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);
|
||||
if (window == NULL)
|
||||
@@ -1220,105 +1242,12 @@ static void processEvent(XEvent *event)
|
||||
{
|
||||
case KeyPress:
|
||||
{
|
||||
const int key = translateKey(keycode);
|
||||
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);
|
||||
}
|
||||
|
||||
glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_PRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
case KeyRelease:
|
||||
{
|
||||
const int key = translateKey(keycode);
|
||||
const int mods = translateState(event->xkey.state);
|
||||
|
||||
if (!_glfw.x11.xkb.detectable)
|
||||
{
|
||||
// HACK: Key repeat events will arrive as KeyRelease/KeyPress
|
||||
@@ -1332,7 +1261,7 @@ static void processEvent(XEvent *event)
|
||||
|
||||
if (next.type == KeyPress &&
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -1432,7 +1361,7 @@ static void processEvent(XEvent *event)
|
||||
// HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
|
||||
// ignore the defined cursor for hidden cursor mode
|
||||
if (window->cursorMode == GLFW_CURSOR_HIDDEN)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN);
|
||||
updateCursorImage(window);
|
||||
|
||||
_glfwInputCursorEnter(window, GLFW_TRUE);
|
||||
return;
|
||||
@@ -1725,7 +1654,7 @@ static void processEvent(XEvent *event)
|
||||
case FocusIn:
|
||||
{
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
|
||||
disableCursor(window);
|
||||
|
||||
if (event->xfocus.mode == NotifyGrab ||
|
||||
event->xfocus.mode == NotifyUngrab)
|
||||
@@ -1735,9 +1664,6 @@ static void processEvent(XEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
if (window->x11.ic)
|
||||
XSetICFocus(window->x11.ic);
|
||||
|
||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||
return;
|
||||
}
|
||||
@@ -1745,7 +1671,7 @@ static void processEvent(XEvent *event)
|
||||
case FocusOut:
|
||||
{
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
|
||||
enableCursor(window);
|
||||
|
||||
if (event->xfocus.mode == NotifyGrab ||
|
||||
event->xfocus.mode == NotifyUngrab)
|
||||
@@ -1755,9 +1681,6 @@ static void processEvent(XEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
if (window->x11.ic)
|
||||
XUnsetICFocus(window->x11.ic);
|
||||
|
||||
if (window->monitor && window->autoIconify)
|
||||
_glfwPlatformIconifyWindow(window);
|
||||
|
||||
@@ -1989,12 +1912,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
if (window->monitor)
|
||||
releaseMonitor(window);
|
||||
|
||||
if (window->x11.ic)
|
||||
{
|
||||
XDestroyIC(window->x11.ic);
|
||||
window->x11.ic = NULL;
|
||||
}
|
||||
|
||||
if (window->context.destroy)
|
||||
window->context.destroy(window);
|
||||
|
||||
@@ -2340,9 +2257,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
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)
|
||||
@@ -2713,80 +2630,26 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
||||
{
|
||||
if (mode == GLFW_CURSOR_DISABLED)
|
||||
{
|
||||
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);
|
||||
centerCursor(window);
|
||||
XGrabPointer(_glfw.x11.display, window->x11.handle, True,
|
||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
window->x11.handle,
|
||||
_glfw.x11.hiddenCursorHandle,
|
||||
CurrentTime);
|
||||
if (_glfwPlatformWindowFocused(window))
|
||||
disableCursor(window);
|
||||
}
|
||||
else if (_glfw.x11.disabledCursorWindow == window)
|
||||
{
|
||||
if (_glfw.x11.xi.available)
|
||||
{
|
||||
XIEventMask em;
|
||||
unsigned char mask[] = { 0 };
|
||||
enableCursor(window);
|
||||
else
|
||||
updateCursorImage(window);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
{
|
||||
if (!_glfw.x11.xkb.available)
|
||||
return NULL;
|
||||
|
||||
const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
|
||||
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;
|
||||
return glfw_xkb_keysym_name(scancode);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetKeyScancode(int key)
|
||||
{
|
||||
return _glfw.x11.scancodes[key];
|
||||
return glfw_xkb_sym_for_key(key);
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
@@ -2857,7 +2720,7 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
||||
if (!_glfw.vk.KHR_surface)
|
||||
return;
|
||||
|
||||
if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
|
||||
if (!_glfw.vk.KHR_xcb_surface)
|
||||
{
|
||||
if (!_glfw.vk.KHR_xlib_surface)
|
||||
return;
|
||||
@@ -2867,7 +2730,7 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
||||
|
||||
// NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
|
||||
// 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";
|
||||
else
|
||||
extensions[1] = "VK_KHR_xlib_surface";
|
||||
@@ -2880,7 +2743,7 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
|
||||
VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
|
||||
_glfw.x11.screen));
|
||||
|
||||
if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
|
||||
if (_glfw.vk.KHR_xcb_surface)
|
||||
{
|
||||
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
|
||||
vkGetPhysicalDeviceXcbPresentationSupportKHR =
|
||||
@@ -2931,7 +2794,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
const VkAllocationCallbacks* allocator,
|
||||
VkSurfaceKHR* surface)
|
||||
{
|
||||
if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
|
||||
if (_glfw.vk.KHR_xcb_surface)
|
||||
{
|
||||
VkResult err;
|
||||
VkXcbSurfaceCreateInfoKHR sci;
|
||||
@@ -3044,4 +2907,3 @@ GLFWAPI const char* glfwGetX11SelectionString(void)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
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:
|
||||
|
||||
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.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):
|
||||
self.screen_size = screen_size
|
||||
@@ -30,6 +44,12 @@ class Handler:
|
||||
def on_eot(self):
|
||||
pass
|
||||
|
||||
def on_wakeup(self):
|
||||
pass
|
||||
|
||||
def on_job_done(self, job_id, job_result):
|
||||
pass
|
||||
|
||||
def write(self, data):
|
||||
if isinstance(data, str):
|
||||
data = data.encode('utf-8')
|
||||
|
||||
@@ -15,8 +15,9 @@ import tty
|
||||
from collections import namedtuple
|
||||
from contextlib import closing, contextmanager
|
||||
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.key_encoding import (
|
||||
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, C, D, backspace_key,
|
||||
@@ -24,7 +25,7 @@ from kitty.key_encoding import (
|
||||
)
|
||||
|
||||
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):
|
||||
@@ -120,8 +121,7 @@ class UnhandledException(Handler):
|
||||
def __init__(self, tb):
|
||||
self.tb = tb
|
||||
|
||||
def initialize(self, screen_size, quit_loop, wakeup):
|
||||
Handler.initialize(self, screen_size, quit_loop, wakeup)
|
||||
def initialize(self):
|
||||
self.write(clear_screen())
|
||||
self.write(self.tb.replace('\n', '\r\n'))
|
||||
self.write('\r\n')
|
||||
@@ -142,10 +142,12 @@ class Loop:
|
||||
|
||||
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]'):
|
||||
self.input_fd = input_fd or sys.stdin.fileno()
|
||||
self.output_fd = output_fd or sys.stdout.fileno()
|
||||
self.wakeup_read_fd, self.wakeup_write_fd = os.pipe()
|
||||
self.sel = s = selectors.DefaultSelector()
|
||||
self.input_fd = sys.stdin.fileno() if input_fd is None else input_fd
|
||||
self.output_fd = sys.stdout.fileno() if output_fd is None else output_fd
|
||||
self.wakeup_read_fd, self.wakeup_write_fd = safe_pipe()
|
||||
# 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.wakeup_read_fd, selectors.EVENT_READ, self._wakeup_ready
|
||||
@@ -165,11 +167,32 @@ class Loop:
|
||||
self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)
|
||||
if self.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):
|
||||
if not self.read_allowed:
|
||||
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:
|
||||
raise EOFError('The input stream is closed')
|
||||
data = self.decoder.decode(data)
|
||||
@@ -278,6 +301,17 @@ class Loop:
|
||||
handler.on_term()
|
||||
if b'i' in data:
|
||||
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):
|
||||
while not os.write(self.wakeup_write_fd, val):
|
||||
@@ -324,13 +358,40 @@ class Loop:
|
||||
handler._term_manager = term_manager
|
||||
keep_going = True
|
||||
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:
|
||||
import traceback
|
||||
tb = traceback.format_exc()
|
||||
self.return_code = 1
|
||||
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)
|
||||
if not has_data_to_write and not self.read_allowed:
|
||||
break
|
||||
@@ -339,31 +400,4 @@ class Loop:
|
||||
self._modify_output_selector(waiting_for_write)
|
||||
events = select()
|
||||
for key, mask in events:
|
||||
try:
|
||||
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)
|
||||
key.data(handler)
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
from kitty.rgb import Color, color_as_sharp, to_color
|
||||
from kitty.terminfo import string_capabilities
|
||||
|
||||
S7C1T = '\033 F'
|
||||
@@ -151,3 +153,24 @@ def cursor(write):
|
||||
write(SAVE_CURSOR)
|
||||
yield
|
||||
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>
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from gettext import gettext as _
|
||||
|
||||
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.key_encoding import (
|
||||
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)
|
||||
def points_for_word(w):
|
||||
from .unicode_names import codepoints_for_word
|
||||
return codepoints_for_word(w)
|
||||
return codepoints_for_word(w.lower())
|
||||
|
||||
|
||||
@lru_cache(maxsize=4096)
|
||||
@@ -58,7 +56,7 @@ def name(cp):
|
||||
@lru_cache(maxsize=256)
|
||||
def codepoints_matching_search(parts):
|
||||
ans = []
|
||||
if parts and parts[0]:
|
||||
if parts and parts[0] and len(parts[0]) > 1:
|
||||
codepoints = points_for_word(parts[0])
|
||||
for word in parts[1:]:
|
||||
pts = points_for_word(word)
|
||||
@@ -207,7 +205,7 @@ class Table:
|
||||
col_width = longest + 2
|
||||
col_width = min(col_width, 40)
|
||||
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 = []
|
||||
a = buf.append
|
||||
rows_left = rows
|
||||
@@ -224,6 +222,8 @@ class Table:
|
||||
return self.text
|
||||
|
||||
def move_current(self, rows=0, cols=0):
|
||||
if len(self.codepoints) == 0:
|
||||
return
|
||||
if cols:
|
||||
self.current_idx = (self.current_idx + len(self.codepoints) + cols) % len(self.codepoints)
|
||||
self.layout_dirty = True
|
||||
@@ -327,8 +327,7 @@ class UnicodeInput(Handler):
|
||||
self.write(set_line_wrapping(False))
|
||||
self.write(set_window_title(_('Unicode input')))
|
||||
|
||||
def initialize(self, *args):
|
||||
Handler.initialize(self, *args)
|
||||
def initialize(self):
|
||||
self.init_terminal_state()
|
||||
self.draw_screen()
|
||||
|
||||
@@ -361,9 +360,9 @@ class UnicodeInput(Handler):
|
||||
y += 1
|
||||
|
||||
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'))
|
||||
elif self.mode is HEX:
|
||||
writeln(_('Enter the hex code for the character'))
|
||||
else:
|
||||
writeln(_('Enter the index for the character you want from the list below'))
|
||||
self.write(self.prompt)
|
||||
@@ -426,7 +425,6 @@ class UnicodeInput(Handler):
|
||||
if not os.path.exists(favorites_path):
|
||||
with open(favorites_path, 'wb') as f:
|
||||
f.write(serialize_favorites(load_favorites()).encode('utf-8'))
|
||||
editor = shlex.split(os.environ.get('EDITOR', 'vim'))
|
||||
with self.suspend():
|
||||
p = subprocess.Popen(editor + [favorites_path])
|
||||
if p.wait() == 0:
|
||||
@@ -454,17 +452,34 @@ class UnicodeInput(Handler):
|
||||
self.refresh()
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
def main(args):
|
||||
loop = Loop()
|
||||
with cached_values_for('unicode-input') as cached_values:
|
||||
handler = UnicodeInput(cached_values)
|
||||
loop.loop(handler)
|
||||
if handler.current_char and loop.return_code == 0:
|
||||
print('OK:', hex(ord(handler.current_char))[2:])
|
||||
try:
|
||||
handler.recent.remove(ord(handler.current_char))
|
||||
except Exception:
|
||||
pass
|
||||
recent = [ord(handler.current_char)] + handler.recent
|
||||
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"
|
||||
|
||||
static PyObject*
|
||||
all_words(PyObject *self UNUSED) {
|
||||
PyObject *ans = PyTuple_New(arraysz(idx_to_word));
|
||||
all_words(PYNOARG) {
|
||||
PyObject *ans = PyTuple_New(arraysz(all_words_map));
|
||||
if (!ans) return NULL;
|
||||
for (size_t i = 0; i < arraysz(idx_to_word); i++) {
|
||||
PyObject *w = PyUnicode_FromString(idx_to_word[i]);
|
||||
for (size_t i = 0; i < arraysz(all_words_map); i++) {
|
||||
PyObject *w = PyUnicode_FromString(all_words_map[i]);
|
||||
if (w == NULL) { Py_DECREF(ans); return NULL; }
|
||||
PyTuple_SET_ITEM(ans, i, w);
|
||||
}
|
||||
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*
|
||||
codepoints_for_word(const char *word, size_t len) {
|
||||
PyObject *ans = PyFrozenSet_New(NULL); if (ans == NULL) return NULL;
|
||||
const unsigned short *words = words_for_first_letter[(unsigned)*word];
|
||||
if (words == NULL) return ans;
|
||||
for (unsigned short i = 1; i <= words[0]; i++) {
|
||||
unsigned short word_idx = words[i];
|
||||
const char *w = idx_to_word[word_idx];
|
||||
if (strncmp(word, w, len) == 0 && strlen(w) == len) {
|
||||
const char_type* codepoints = codepoints_for_word_idx[word_idx];
|
||||
for (char_type i = 1; i <= codepoints[0]; 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; }
|
||||
const word_trie *wt = all_trie_nodes;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
unsigned char ch = word[i];
|
||||
size_t num_children = children_array[wt->children_offset];
|
||||
if (!num_children) return PyFrozenSet_New(NULL);
|
||||
bool found = false;
|
||||
for (size_t c = wt->children_offset + 1; c < wt->children_offset + 1 + num_children; c++) {
|
||||
uint32_t x = children_array[c];
|
||||
if ((x & 0xff) == ch) {
|
||||
found = true;
|
||||
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;
|
||||
}
|
||||
@@ -56,12 +85,13 @@ nfc(PyObject *self UNUSED, PyObject *args) {
|
||||
}
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(all_words, METH_NOARGS),
|
||||
{"all_words", (PyCFunction)all_words, METH_NOARGS, ""},
|
||||
{"codepoints_for_word", (PyCFunction)cfw, METH_VARARGS, ""},
|
||||
{"name_for_codepoint", (PyCFunction)nfc, METH_VARARGS, ""},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
static struct PyModuleDef module = {
|
||||
.m_base = PyModuleDef_HEAD_INIT,
|
||||
.m_name = "unicode_names", /* name of module */
|
||||
@@ -79,3 +109,13 @@ PyInit_unicode_names(void) {
|
||||
if (m == NULL) return NULL;
|
||||
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 active_border_color;
|
||||
uniform vec3 inactive_border_color;
|
||||
uniform vec3 bell_border_color;
|
||||
in uvec4 rect; // left, top, right, bottom
|
||||
in uint rect_color;
|
||||
out vec3 color;
|
||||
@@ -37,5 +38,5 @@ void main() {
|
||||
gl_Position = vec4(to_opengl(rect[pos.x], rect[pos.y]), 0, 1);
|
||||
int rc = int(rect_color);
|
||||
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
|
||||
if bw > 0 and draw_window_borders:
|
||||
# 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(
|
||||
self.os_window_id, self.tab_id,
|
||||
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
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import atexit
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from functools import partial
|
||||
from gettext import gettext as _
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from .cli import create_opts, parse_args
|
||||
from .config import MINIMUM_FONT_SIZE, initial_window_size
|
||||
from .constants import appname, set_boss, wakeup
|
||||
from .config import (
|
||||
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 (
|
||||
ChildMonitor, create_os_window, current_os_window, destroy_global_data,
|
||||
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
|
||||
layout_sprite_map, mark_os_window_for_close, set_clipboard_string,
|
||||
set_dpi_from_os_window, show_window, toggle_fullscreen,
|
||||
viewport_for_window
|
||||
set_dpi_from_os_window, set_in_sequence_mode, show_window,
|
||||
toggle_fullscreen, viewport_for_window
|
||||
)
|
||||
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 .rgb import Color, color_from_int
|
||||
from .session import create_session
|
||||
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
|
||||
from .utils import (
|
||||
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()
|
||||
|
||||
|
||||
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: # {{{
|
||||
|
||||
def __init__(self, args):
|
||||
@@ -64,16 +82,21 @@ class Boss:
|
||||
|
||||
def __init__(self, os_window_id, opts, args, cached_values):
|
||||
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.os_window_map = {}
|
||||
self.cursor_blinking = True
|
||||
self.shutting_down = False
|
||||
talk_fd = getattr(single_instance, 'socket', None)
|
||||
talk_fd = -1 if talk_fd is None else talk_fd.fileno()
|
||||
listen_fd = -1
|
||||
if opts.allow_remote_control and args.listen_on:
|
||||
listen_fd = listen_on(args.listen_on)
|
||||
self.child_monitor = ChildMonitor(
|
||||
self.on_child_death,
|
||||
DumpCommands(args) if args.dump_commands or args.dump_bytes else None,
|
||||
talk_fd
|
||||
talk_fd, listen_fd
|
||||
)
|
||||
set_boss(self)
|
||||
self.current_font_size = opts.font_size
|
||||
@@ -98,6 +121,7 @@ class Boss:
|
||||
self.os_window_map[os_window_id] = tm
|
||||
if dpi_changed:
|
||||
self.on_dpi_change(os_window_id)
|
||||
return os_window_id
|
||||
|
||||
def list_os_windows(self):
|
||||
for os_window_id, tm in self.os_window_map.items():
|
||||
@@ -106,33 +130,58 @@ class Boss:
|
||||
'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):
|
||||
field, exp = match.split(':', 1)
|
||||
pat = re.compile(exp)
|
||||
for tm in self.os_window_map.values():
|
||||
for tab in tm:
|
||||
for window in tab:
|
||||
if window.matches(field, pat):
|
||||
yield window
|
||||
try:
|
||||
field, exp = match.split(':', 1)
|
||||
except ValueError:
|
||||
return
|
||||
if field == 'num':
|
||||
tab = self.active_tab
|
||||
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):
|
||||
for tm in self.os_window_map.values():
|
||||
for tab in tm:
|
||||
for w in tab:
|
||||
if w.id == window.id:
|
||||
return tab
|
||||
for tab in self.all_tabs:
|
||||
for w in tab:
|
||||
if w.id == window.id:
|
||||
return tab
|
||||
|
||||
def match_tabs(self, match):
|
||||
field, exp = match.split(':', 1)
|
||||
try:
|
||||
field, exp = match.split(':', 1)
|
||||
except ValueError:
|
||||
return
|
||||
pat = re.compile(exp)
|
||||
tms = tuple(self.os_window_map.values())
|
||||
found = False
|
||||
if field in ('title', 'id'):
|
||||
for tm in tms:
|
||||
for tab in tm:
|
||||
if tab.matches(field, pat):
|
||||
yield tab
|
||||
found = True
|
||||
for tab in self.all_tabs:
|
||||
if tab.matches(field, pat):
|
||||
yield tab
|
||||
found = True
|
||||
if not found:
|
||||
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
||||
for tab in tabs:
|
||||
@@ -152,7 +201,7 @@ class Boss:
|
||||
def _new_os_window(self, args, cwd_from=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)
|
||||
self.add_os_window(startup_session)
|
||||
return self.add_os_window(startup_session)
|
||||
|
||||
def new_os_window(self, *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.window_id_map[window.id] = window
|
||||
|
||||
def peer_messages_received(self, messages):
|
||||
import json
|
||||
for msg in messages:
|
||||
msg = json.loads(msg.decode('utf-8'))
|
||||
def _handle_remote_command(self, cmd, window=None):
|
||||
response = None
|
||||
if self.opts.allow_remote_control or getattr(window, 'allow_remote_control', False):
|
||||
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':
|
||||
startup_id = msg.get('startup_id')
|
||||
args, rest = parse_args(msg['args'][1:])
|
||||
args.args = rest
|
||||
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)
|
||||
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):
|
||||
response = None
|
||||
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'}
|
||||
response = self._handle_remote_command(cmd, window)
|
||||
if response is not None:
|
||||
if window is not None:
|
||||
window.send_cmd_response(response)
|
||||
@@ -259,19 +323,16 @@ class Boss:
|
||||
tm.resize()
|
||||
|
||||
def increase_font_size(self):
|
||||
self.change_font_size(
|
||||
self.set_font_size(
|
||||
min(
|
||||
self.opts.font_size * 5, self.current_font_size +
|
||||
self.opts.font_size_delta))
|
||||
|
||||
def decrease_font_size(self):
|
||||
self.change_font_size(
|
||||
max(
|
||||
MINIMUM_FONT_SIZE, self.current_font_size -
|
||||
self.opts.font_size_delta))
|
||||
self.set_font_size(self.current_font_size - self.opts.font_size_delta)
|
||||
|
||||
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):
|
||||
if new_size is not None:
|
||||
@@ -289,7 +350,8 @@ class Boss:
|
||||
tm.refresh_sprite_positions()
|
||||
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:
|
||||
return
|
||||
self._change_font_size(new_size)
|
||||
@@ -317,8 +379,37 @@ class Boss:
|
||||
def dispatch_special_key(self, key, scancode, action, mods):
|
||||
# Handles shortcuts, return True if the key was consumed
|
||||
key_action = get_shortcut(self.opts.keymap, mods, key, scancode)
|
||||
self.current_key_press_info = key, scancode, action, mods
|
||||
return self.dispatch_action(key_action)
|
||||
if key_action is None:
|
||||
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):
|
||||
w = self.window_id_map.get(window_id)
|
||||
@@ -360,6 +451,12 @@ class Boss:
|
||||
w = tm.active_window
|
||||
if w is not None:
|
||||
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):
|
||||
tm = self.os_window_map.get(os_window_id)
|
||||
@@ -383,70 +480,111 @@ class Boss:
|
||||
SpecialWindow(
|
||||
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
|
||||
tab = self.active_tab
|
||||
if w is not None and tab is not None and w.overlay_for is None:
|
||||
overlay_window = tab.new_special_window(
|
||||
SpecialWindow(
|
||||
['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))
|
||||
orig_args, args = list(args), list(args)
|
||||
args[0:0] = [config_dir, kitten]
|
||||
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')
|
||||
elif type_of_input == 'none':
|
||||
data = None
|
||||
else:
|
||||
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(
|
||||
['kitty', '+runpy', 'from kittens.{}.main import main; main()'.format(kitten)] + args,
|
||||
['kitty', '+runpy', 'from kittens.runner import main; main()'] + args,
|
||||
stdin=data,
|
||||
env={'KITTY_COMMON_OPTS': json.dumps(copts), 'PYTHONWARNINGS': 'ignore'},
|
||||
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):
|
||||
tab = self.active_tab
|
||||
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:
|
||||
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):
|
||||
self.open_url(''.join(lines))
|
||||
def open_url_lines(self, lines, program=None):
|
||||
self.open_url(''.join(lines), program)
|
||||
|
||||
def destroy(self):
|
||||
self.shutting_down = True
|
||||
self.child_monitor.shutdown_monitor()
|
||||
wakeup()
|
||||
self.child_monitor.join()
|
||||
del self.child_monitor
|
||||
for tm in self.os_window_map.values():
|
||||
tm.destroy()
|
||||
@@ -482,6 +620,11 @@ class Boss:
|
||||
if tm is not None:
|
||||
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):
|
||||
tm = self.active_tab_manager
|
||||
if tm is not None:
|
||||
@@ -533,7 +676,7 @@ class Boss:
|
||||
special_window = self.args_to_special_window(args, cwd_from=cwd_from)
|
||||
tm = self.active_tab_manager
|
||||
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):
|
||||
self._new_tab(args)
|
||||
@@ -547,9 +690,9 @@ class Boss:
|
||||
tab = self.active_tab
|
||||
if tab is not None:
|
||||
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:
|
||||
tab.new_window(cwd_from=cwd_from)
|
||||
return tab.new_window(cwd_from=cwd_from)
|
||||
|
||||
def new_window(self, *args):
|
||||
self._new_window(args)
|
||||
@@ -570,3 +713,11 @@ class Boss:
|
||||
tm = self.active_tab_manager
|
||||
if tm is not None:
|
||||
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 {
|
||||
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;
|
||||
|
||||
int color1, color2;
|
||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
const int fg_index_map[] = int[3](0, 1, 0);
|
||||
const uvec2 cell_pos_map[] = uvec2[4](
|
||||
uvec2(1, 0), // right, top
|
||||
uvec2(1, 1), // right, bottom
|
||||
@@ -135,10 +133,10 @@ void main() {
|
||||
|
||||
// set cell color indices {{{
|
||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||
ivec2 color_indices = ivec2(color1, color2);
|
||||
uint text_attrs = sprite_coords[3];
|
||||
int fg_index = color_indices[(text_attrs >> 6) & REVERSE_MASK];
|
||||
int bg_index = color_indices[1 - fg_index];
|
||||
uint is_inverted = ((text_attrs >> 6) & REVERSE_MASK) + inverted;
|
||||
int fg_index = fg_index_map[is_inverted];
|
||||
int bg_index = 1 - fg_index;
|
||||
float cursor = is_cursor(c, r);
|
||||
vec3 bg = to_color(colors[bg_index], default_colors[bg_index]);
|
||||
// }}}
|
||||
|
||||
@@ -5,16 +5,7 @@
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#ifdef __APPLE__
|
||||
#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 "threading.h"
|
||||
#include "state.h"
|
||||
#include "screen.h"
|
||||
#include <termios.h>
|
||||
@@ -28,12 +19,17 @@ extern int pthread_setname_np(const char *name);
|
||||
extern PyTypeObject Screen_Type;
|
||||
|
||||
#define EXTRA_FDS 2
|
||||
#ifndef MSG_NOSIGNAL
|
||||
// Apple does not implement MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
static void (*parse_func)(Screen*, PyObject*);
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
size_t sz;
|
||||
int fd;
|
||||
} Message;
|
||||
|
||||
typedef struct {
|
||||
@@ -44,7 +40,7 @@ typedef struct {
|
||||
bool shutting_down;
|
||||
pthread_t io_thread, talk_thread;
|
||||
|
||||
int talk_fd;
|
||||
int talk_fd, listen_fd;
|
||||
Message *messages;
|
||||
size_t messages_capacity, messages_count;
|
||||
} ChildMonitor;
|
||||
@@ -63,6 +59,9 @@ static const Child EMPTY_CHILD = {0};
|
||||
pthread_mutex_##op(&screen->which##_buf_lock);
|
||||
#define children_mutex(op) \
|
||||
pthread_mutex_##op(&children_lock);
|
||||
#define peer_mutex(op) \
|
||||
pthread_mutex_##op(&talk_data.peer_lock);
|
||||
|
||||
|
||||
|
||||
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 struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
|
||||
static pthread_mutex_t children_lock;
|
||||
static bool signal_received = false;
|
||||
static bool kill_signal_received = false;
|
||||
static ChildMonitor *the_monitor = NULL;
|
||||
static uint8_t drain_buf[1024];
|
||||
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 {{{
|
||||
|
||||
@@ -139,11 +127,11 @@ static PyObject *
|
||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
ChildMonitor *self;
|
||||
PyObject *dump_callback, *death_notify;
|
||||
int talk_fd = -1;
|
||||
int talk_fd = -1, listen_fd = -1;
|
||||
int ret;
|
||||
|
||||
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) {
|
||||
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
|
||||
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(signal_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
struct sigaction act = {.sa_handler=handle_signal};
|
||||
if (sigaction(SIGINT, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
if (sigaction(SIGTERM, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
if (siginterrupt(SIGINT, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
if (siginterrupt(SIGTERM, false) != 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);}
|
||||
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD);
|
||||
#undef SA
|
||||
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
||||
self->talk_fd = talk_fd;
|
||||
self->listen_fd = listen_fd;
|
||||
if (self == NULL) return PyErr_NoMemory();
|
||||
self->death_notify = death_notify; Py_INCREF(death_notify);
|
||||
if (dump_callback != Py_None) {
|
||||
@@ -205,12 +193,17 @@ wakeup_io_loop(bool in_signal_handler) {
|
||||
|
||||
static void* io_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 *
|
||||
start(ChildMonitor *self) {
|
||||
start(PyObject *s, PyObject *a UNUSED) {
|
||||
#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);
|
||||
talk_thread_started = true;
|
||||
}
|
||||
int ret = pthread_create(&self->io_thread, NULL, io_loop, self);
|
||||
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
@@ -218,17 +211,9 @@ start(ChildMonitor *self) {
|
||||
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 *
|
||||
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."
|
||||
wakeup_io_loop(false);
|
||||
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;
|
||||
if (space_left < sz) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@@ -301,11 +286,21 @@ needs_write(ChildMonitor UNUSED *self, PyObject *args) {
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
shutdown_monitor(ChildMonitor *self) {
|
||||
shutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {
|
||||
#define shutdown_monitor_doc "shutdown_monitor() -> Shutdown the monitor loop."
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -343,14 +338,14 @@ parse_input(ChildMonitor *self) {
|
||||
if (msg) {
|
||||
for (size_t i = 0; i < self->messages_count; 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;
|
||||
}
|
||||
self->messages_count = 0;
|
||||
} else fatal("Out of memory");
|
||||
}
|
||||
|
||||
if (UNLIKELY(signal_received)) {
|
||||
if (UNLIKELY(kill_signal_received)) {
|
||||
global_state.close_all_windows = true;
|
||||
} else {
|
||||
count = self->count;
|
||||
@@ -361,7 +356,13 @@ parse_input(ChildMonitor *self) {
|
||||
}
|
||||
children_mutex(unlock);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -411,7 +412,7 @@ pty_resize(int fd, struct winsize *dim) {
|
||||
if (ioctl(fd, TIOCSWINSZ, dim) == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -439,7 +440,7 @@ resize_pty(ChildMonitor *self, PyObject *args) {
|
||||
if (fd == -1) FIND(add_queue, add_queue_count);
|
||||
if (fd != -1) {
|
||||
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);
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
Py_RETURN_NONE;
|
||||
@@ -568,8 +569,13 @@ simple_render_screen(PyObject UNUSED *self, PyObject *args) {
|
||||
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) {
|
||||
#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 (!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 (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");
|
||||
FILE *f = fdopen(data->fd, "w");
|
||||
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);
|
||||
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*
|
||||
main_loop(ChildMonitor *self) {
|
||||
main_loop(ChildMonitor *self, PyObject *a UNUSED) {
|
||||
#define main_loop_doc "The main thread loop"
|
||||
bool has_open_windows = true;
|
||||
|
||||
@@ -744,23 +774,8 @@ main_loop(ChildMonitor *self) {
|
||||
render(now);
|
||||
wait_for_events();
|
||||
parse_input(self);
|
||||
if (global_state.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;
|
||||
}
|
||||
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 (global_state.close_all_windows) close_all_windows();
|
||||
has_open_windows = process_pending_closes(self);
|
||||
}
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
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
|
||||
write_to_child(int fd, Screen *screen) {
|
||||
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[1].revents && POLLIN) {
|
||||
data_received = true;
|
||||
drain_fd(fds[1].fd);
|
||||
children_mutex(lock);
|
||||
signal_received = true;
|
||||
children_mutex(unlock);
|
||||
bool kill_signal = false, child_died = false;
|
||||
read_signals(fds[1].fd, &kill_signal, &child_died);
|
||||
if (kill_signal) { children_mutex(lock); kill_signal_received = true; children_mutex(unlock); }
|
||||
if (child_died) reap_children(self, OPT(close_on_child_death));
|
||||
}
|
||||
for (i = 0; i < self->count; i++) {
|
||||
if (fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {
|
||||
@@ -948,7 +1015,7 @@ io_loop(void *data) {
|
||||
children_mutex(lock);
|
||||
children[i].needs_removal = true;
|
||||
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
|
||||
@@ -975,58 +1042,258 @@ io_loop(void *data) {
|
||||
|
||||
// {{{ Talk thread functions
|
||||
|
||||
static inline void
|
||||
handle_peer(ChildMonitor *self, int s) {
|
||||
size_t bufsz = 0;
|
||||
char *buf = NULL;
|
||||
size_t buf_used = 0;
|
||||
#define MAX_PEERS 256
|
||||
#define MAX_LISTENERS 2
|
||||
|
||||
while(true) {
|
||||
if (buf_used >= bufsz) {
|
||||
bufsz = MAX(1024, bufsz) * 2;
|
||||
if (bufsz > 1024 * 1024) return;
|
||||
buf = realloc(buf, bufsz);
|
||||
if (buf == NULL) return;
|
||||
}
|
||||
ssize_t n = recv(s, buf + buf_used, bufsz - buf_used, 0);
|
||||
if (n == 0) break;
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("Error reading from talk peer");
|
||||
typedef struct {
|
||||
char *data;
|
||||
size_t capacity, used;
|
||||
int fd;
|
||||
bool finished, close_socket;
|
||||
} PeerReadData;
|
||||
static PeerReadData empty_prd = {.fd = -1, 0};
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
size_t sz, pos;
|
||||
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;
|
||||
}
|
||||
buf_used += n;
|
||||
}
|
||||
if (buf_used) {
|
||||
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 = buf; m->sz = buf_used;
|
||||
children_mutex(unlock);
|
||||
wakeup_main_loop();
|
||||
} else free(buf);
|
||||
#undef failed
|
||||
return read_finished;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
write_to_peer(int fd) {
|
||||
bool write_finished = false;
|
||||
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*
|
||||
talk_loop(void *data) {
|
||||
// The I/O thread loop
|
||||
// The talk thread loop
|
||||
|
||||
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)) {
|
||||
int peer = accept(self->talk_fd, NULL, NULL);
|
||||
if (peer == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
if (!self->shutting_down) perror("accept() on talk socket failed!");
|
||||
break;
|
||||
}
|
||||
handle_peer(self, peer);
|
||||
shutdown(peer, SHUT_RDWR);
|
||||
close(peer);
|
||||
for (size_t i = 0; i < talk_data.num_listen_fds + talk_data.num_talk_fds; i++) { talk_data.fds[i].revents = 0; }
|
||||
int ret = poll(talk_data.fds, talk_data.num_listen_fds + talk_data.num_talk_fds, -1);
|
||||
if (ret > 0) {
|
||||
bool has_finished_reads = false, has_finished_writes = false;
|
||||
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; }
|
||||
}
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
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 {{{
|
||||
@@ -1034,7 +1301,6 @@ static PyMethodDef methods[] = {
|
||||
METHOD(add_child, METH_VARARGS)
|
||||
METHOD(needs_write, METH_VARARGS)
|
||||
METHOD(start, METH_NOARGS)
|
||||
METHOD(join, METH_NOARGS)
|
||||
METHOD(wakeup, METH_NOARGS)
|
||||
METHOD(shutdown_monitor, METH_NOARGS)
|
||||
METHOD(main_loop, METH_NOARGS)
|
||||
@@ -1056,8 +1322,16 @@ PyTypeObject ChildMonitor_Type = {
|
||||
.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[] = {
|
||||
METHOD(simple_render_screen, METH_VARARGS)
|
||||
METHODB(safe_pipe, METH_NOARGS),
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ static PyObject*
|
||||
spawn(PyObject *self UNUSED, PyObject *args) {
|
||||
PyObject *argv_p, *env_p;
|
||||
int master, slave, stdin_read_fd, stdin_write_fd;
|
||||
char* cwd;
|
||||
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;
|
||||
char *cwd, *exe;
|
||||
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};
|
||||
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||
char **argv = serialize_string_tuple(argv_p);
|
||||
@@ -76,7 +76,7 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
||||
close(tfd);
|
||||
|
||||
environ = env;
|
||||
execvp(argv[0], argv);
|
||||
execvp(exe, argv);
|
||||
// Report the failure and exec a shell instead, so that we are not left
|
||||
// with a forked but not exec'ed process
|
||||
write_to_stderr("Failed to launch child: ");
|
||||
|
||||
@@ -7,7 +7,7 @@ import os
|
||||
|
||||
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):
|
||||
@@ -70,7 +70,13 @@ class Child:
|
||||
if os.path.isdir(terminfo_dir):
|
||||
env['TERMINFO'] = terminfo_dir
|
||||
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)
|
||||
self.pid = pid
|
||||
self.child_fd = master
|
||||
|
||||
186
kitty/cli.py
186
kitty/cli.py
@@ -2,16 +2,36 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import deque
|
||||
|
||||
from .config import load_config
|
||||
from .constants import appname, defconf, is_macos, str_version
|
||||
from .config import defaults, load_config
|
||||
from .config_utils import resolve_config
|
||||
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||
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 = '''
|
||||
--class
|
||||
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.
|
||||
|
||||
|
||||
--config
|
||||
--config -c
|
||||
type=list
|
||||
default={config_path}
|
||||
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.
|
||||
|
||||
{config_help}
|
||||
|
||||
--override -o
|
||||
type=list
|
||||
@@ -43,10 +60,6 @@ Override individual configuration options, can be specified multiple times.
|
||||
Syntax: |_ name=value|. For example: |_ -o font_size=20|
|
||||
|
||||
|
||||
--cmd -c
|
||||
Run python code in the kitty context
|
||||
|
||||
|
||||
--directory -d
|
||||
default=.
|
||||
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
|
||||
|
||||
|
||||
--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
|
||||
|
||||
--version -v
|
||||
@@ -107,6 +130,21 @@ Path to file in which to store the raw bytes received from the child process
|
||||
--debug-gl
|
||||
type=bool-set
|
||||
Debug OpenGL commands. This will cause all OpenGL calls to check for errors instead of ignoring them. Useful when debugging rendering problems
|
||||
|
||||
|
||||
--debug-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(',')}
|
||||
elif state is HELP:
|
||||
if line:
|
||||
current_cmd['help'] += ' ' + line
|
||||
current_cmd['help'] += ' ' + line.lstrip()
|
||||
else:
|
||||
if prev_line:
|
||||
current_cmd['help'] += '\n'
|
||||
current_cmd['help'] += '\n\n\t'
|
||||
else:
|
||||
state = NORMAL
|
||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||
@@ -213,8 +251,12 @@ def prettify(text):
|
||||
return text
|
||||
|
||||
|
||||
def version():
|
||||
return '{} {} created by {}'.format(italic(appname), green(str_version), title('Kovid Goyal'))
|
||||
def version(add_rev=False):
|
||||
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):
|
||||
@@ -252,6 +294,7 @@ def wrap(text, limit=80):
|
||||
|
||||
def print_help_for_seq(seq, usage, message, appname):
|
||||
from kitty.icat import screen_size
|
||||
screen_size.changed = True
|
||||
try:
|
||||
linesz = min(screen_size().cols, 76)
|
||||
except EnvironmentError:
|
||||
@@ -271,8 +314,9 @@ def print_help_for_seq(seq, usage, message, appname):
|
||||
lines.append('')
|
||||
a((' ' * leading_indent) + j.join(lines))
|
||||
|
||||
usage = usage or '[program-to-run ...]'
|
||||
a('{}: {} [options] {}'.format(title('Usage'), bold(yellow(appname)), usage))
|
||||
usage = '[program-to-run ...]' if usage is None else usage
|
||||
optstring = '[options] ' if seq else ''
|
||||
a('{}: {} {}{}'.format(title('Usage'), bold(yellow(appname)), optstring, usage))
|
||||
a('')
|
||||
message = message or (
|
||||
'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):
|
||||
a('{}:'.format(title(opt)))
|
||||
continue
|
||||
help_text = opt['help']
|
||||
if help_text == '!':
|
||||
continue # hidden option
|
||||
a(' ' + ', '.join(map(green, sorted(opt['aliases']))))
|
||||
if not opt.get('type', '').startswith('bool-'):
|
||||
blocks[-1] += '={}'.format(italic(opt['dest'].upper()))
|
||||
if opt.get('help'):
|
||||
defval = opt.get('default')
|
||||
t = opt['help'].replace('%default', str(defval))
|
||||
t = help_text.replace('%default', str(defval))
|
||||
wa(prettify(t.strip()), indent=4)
|
||||
if defval is not None:
|
||||
wa('Default: {}'.format(defval), indent=4)
|
||||
@@ -300,7 +347,8 @@ def print_help_for_seq(seq, usage, message, appname):
|
||||
a('')
|
||||
|
||||
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.communicate(text.encode('utf-8'))
|
||||
raise SystemExit(p.wait())
|
||||
@@ -308,6 +356,9 @@ def print_help_for_seq(seq, usage, message, appname):
|
||||
print(text)
|
||||
|
||||
|
||||
print_help_for_seq.allow_pager = True
|
||||
|
||||
|
||||
def defval_for_opt(opt):
|
||||
dv = opt.get('default')
|
||||
typ = opt.get('type', '')
|
||||
@@ -437,7 +488,7 @@ def parse_cmdline(oc, disabled, args=None):
|
||||
def options_spec():
|
||||
if not hasattr(options_spec, 'ans'):
|
||||
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)
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
def create_opts(args):
|
||||
config = args.config or (defconf, )
|
||||
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
||||
|
||||
|
||||
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 ())
|
||||
opts = load_config(*config, overrides=overrides)
|
||||
if debug_config:
|
||||
compare_opts(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
|
||||
|
||||
static NSMenuItem* title_menu = NULL;
|
||||
static bool change_titlebar_color = false;
|
||||
static color_type titlebar_color = 0;
|
||||
|
||||
|
||||
static NSString*
|
||||
@@ -59,11 +57,35 @@ find_app_name(void) {
|
||||
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
|
||||
cocoa_create_global_menu(void) {
|
||||
NSString* app_name = find_app_name();
|
||||
NSMenu* bar = [[NSMenu alloc] init];
|
||||
GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];
|
||||
[NSApp setMainMenu:bar];
|
||||
|
||||
NSMenuItem* appMenuItem =
|
||||
@@ -75,6 +97,9 @@ cocoa_create_global_menu(void) {
|
||||
action:@selector(orderFrontStandardAboutPanel:)
|
||||
keyEquivalent:@""];
|
||||
[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]
|
||||
action:@selector(hide:)
|
||||
keyEquivalent:@"h"];
|
||||
@@ -124,7 +149,7 @@ cocoa_create_global_menu(void) {
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
[windowMenu release];
|
||||
|
||||
[preferences_menu_item release];
|
||||
|
||||
[bar release];
|
||||
}
|
||||
@@ -180,32 +205,14 @@ cwd_of_process(PyObject *self UNUSED, PyObject *pid_) {
|
||||
return PyUnicode_FromString(vpi.pvi_cdir.vip_path);
|
||||
}
|
||||
|
||||
static inline color_type
|
||||
color_as_int(PyObject *color) {
|
||||
#define I(n, s) ((PyLong_AsUnsignedLong(PyTuple_GET_ITEM(color, n)) & 0xff) << s)
|
||||
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
|
||||
cocoa_set_hide_from_tasks(void) {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
double red = ((titlebar_color >> 16) & 0xFF) / 255.0;
|
||||
@@ -232,7 +239,6 @@ cocoa_set_titlebar_color(void *w)
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
||||
{"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 */
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#define EXTRA_INIT if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||
#include "data-types.h"
|
||||
#include <structmember.h>
|
||||
|
||||
@@ -29,23 +30,29 @@ static uint32_t FG_BG_256[256] = {
|
||||
0xffffff, // 15
|
||||
};
|
||||
|
||||
PyObject* create_256_color_table() {
|
||||
// colors 16..232: the 6x6x6 color cube
|
||||
const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||
uint8_t i, j=16;
|
||||
for(i = 0; i < 217; i++, j++) {
|
||||
uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];
|
||||
FG_BG_256[j] = (r << 16) | (g << 8) | b;
|
||||
}
|
||||
// colors 233..255: grayscale
|
||||
for(i = 1; i < 24; i++, j++) {
|
||||
uint8_t v = 8 + i * 10;
|
||||
FG_BG_256[j] = (v << 16) | (v << 8) | v;
|
||||
static inline void
|
||||
init_FG_BG_table() {
|
||||
if (UNLIKELY(FG_BG_256[255] == 0)) {
|
||||
// colors 16..232: the 6x6x6 color cube
|
||||
const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||
uint8_t i, j=16;
|
||||
for(i = 0; i < 217; i++, j++) {
|
||||
uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];
|
||||
FG_BG_256[j] = (r << 16) | (g << 8) | b;
|
||||
}
|
||||
// colors 233..255: grayscale
|
||||
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();
|
||||
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]);
|
||||
if (temp == NULL) { Py_CLEAR(ans); return NULL; }
|
||||
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);
|
||||
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->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
|
||||
self->dirty = true;
|
||||
@@ -80,12 +87,10 @@ alloc_color_profile() {
|
||||
|
||||
static PyObject*
|
||||
update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
||||
#define update_ansi_color_table_doc "Update the 16 basic colors"
|
||||
index_type i;
|
||||
|
||||
#define update_ansi_color_table_doc "Update the 256 basic colors"
|
||||
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; }
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (PyList_GET_SIZE(val) != arraysz(FG_BG_256)) { PyErr_SetString(PyExc_TypeError, "color table must have 256 items"); return NULL; }
|
||||
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||
self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, 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;
|
||||
}
|
||||
|
||||
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
|
||||
colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) {
|
||||
color_type t = entry & 0xFF, r;
|
||||
@@ -133,7 +175,7 @@ as_color(ColorProfile *self, PyObject *val) {
|
||||
}
|
||||
|
||||
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"
|
||||
memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));
|
||||
self->dirty = true;
|
||||
@@ -179,11 +221,15 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size
|
||||
}
|
||||
|
||||
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"
|
||||
return PyLong_FromVoidPtr((void*)self->color_table);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
|
||||
return create_256_color_table();
|
||||
}
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
@@ -236,5 +282,12 @@ PyTypeObject ColorProfile_Type = {
|
||||
.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)
|
||||
// }}}
|
||||
|
||||
311
kitty/config.py
311
kitty/config.py
@@ -6,21 +6,20 @@ import ast
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
|
||||
from . import fast_data_types as defines
|
||||
from .config_utils import (
|
||||
init_config, parse_config_base, positive_float, positive_int, to_bool,
|
||||
to_color, unit_float
|
||||
init_config, load_config as _load_config, merge_dicts, parse_config_base,
|
||||
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 .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
|
||||
|
||||
@@ -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):
|
||||
return {'CTRL': 'CONTROL', 'CMD': 'SUPER', 'OPTION': 'ALT'}.get(m, m)
|
||||
return mod_map.get(m, m)
|
||||
|
||||
mods = 0
|
||||
for m in parts:
|
||||
try:
|
||||
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
|
||||
except AttributeError:
|
||||
safe_print(
|
||||
'Shortcut: {} has an unknown modifier, ignoring'.format(
|
||||
parts.join('+')
|
||||
),
|
||||
file=sys.stderr
|
||||
)
|
||||
log_error('Shortcut: {} has unknown modifier, ignoring'.format(sc))
|
||||
return
|
||||
|
||||
return mods
|
||||
@@ -84,12 +81,14 @@ named_keys = {
|
||||
|
||||
def parse_shortcut(sc):
|
||||
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 = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
||||
if key is not None:
|
||||
return mods, key
|
||||
return None, None
|
||||
return mods, None
|
||||
|
||||
|
||||
KeyAction = namedtuple('KeyAction', 'func args')
|
||||
@@ -111,37 +110,82 @@ def parse_key_action(action):
|
||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||
elif func == 'send_text':
|
||||
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)
|
||||
elif func == 'goto_tab':
|
||||
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:
|
||||
args = shlex.split(rest)
|
||||
args = to_cmdline(rest)
|
||||
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 = sc.strip(), action.strip()
|
||||
sc, action = sc.strip().strip(sequence_sep), action.strip()
|
||||
if not sc or not action:
|
||||
return
|
||||
mods, key = parse_shortcut(sc)
|
||||
if key is None:
|
||||
safe_print(
|
||||
'Shortcut: {} has an unknown key, ignoring'.format(val),
|
||||
file=sys.stderr
|
||||
)
|
||||
return
|
||||
is_sequence = sequence_sep in sc
|
||||
if is_sequence:
|
||||
trigger = None
|
||||
rest = []
|
||||
for part in sc.split(sequence_sep):
|
||||
mods, key = parse_shortcut(part)
|
||||
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:
|
||||
paction = parse_key_action(action)
|
||||
except Exception:
|
||||
safe_print(
|
||||
'Invalid shortcut action: {}. Ignoring.'.format(action),
|
||||
file=sys.stderr
|
||||
)
|
||||
log_error('Invalid shortcut action: {}. Ignoring.'.format(
|
||||
action))
|
||||
else:
|
||||
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):
|
||||
@@ -149,9 +193,8 @@ def parse_symbol_map(val):
|
||||
symbol_map = {}
|
||||
|
||||
def abort():
|
||||
safe_print(
|
||||
'Symbol map: {} is invalid, ignoring'.format(val), file=sys.stderr
|
||||
)
|
||||
log_error('Symbol map: {} is invalid, ignoring'.format(
|
||||
val))
|
||||
return {}
|
||||
|
||||
if len(parts) < 2:
|
||||
@@ -182,14 +225,12 @@ def parse_send_text_bytes(text):
|
||||
).encode('utf-8')
|
||||
|
||||
|
||||
def parse_send_text(val, keymap):
|
||||
def parse_send_text(val, key_definitions):
|
||||
parts = val.split(' ')
|
||||
|
||||
def abort(msg):
|
||||
safe_print(
|
||||
'Send text: {} is invalid ({}), ignoring'.format(val, msg),
|
||||
file=sys.stderr
|
||||
)
|
||||
log_error('Send text: {} is invalid ({}), ignoring'.format(
|
||||
val, msg))
|
||||
return {}
|
||||
|
||||
if len(parts) < 3:
|
||||
@@ -197,11 +238,11 @@ def parse_send_text(val, keymap):
|
||||
mode, sc = parts[:2]
|
||||
text = ' '.join(parts[2:])
|
||||
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):
|
||||
return parse_mods(val.split('+'))
|
||||
return parse_mods(val.split('+'), val) or 0
|
||||
|
||||
|
||||
def to_layout_names(raw):
|
||||
@@ -223,10 +264,10 @@ def adjust_line_height(x):
|
||||
def macos_titlebar_color(x):
|
||||
x = x.strip('"')
|
||||
if x == 'system':
|
||||
return
|
||||
return 0
|
||||
if x == 'background':
|
||||
return True
|
||||
return to_color(x)
|
||||
return 1
|
||||
return (color_as_int(to_color(x)) << 8) | 2
|
||||
|
||||
|
||||
def box_drawing_scale(x):
|
||||
@@ -271,7 +312,8 @@ type_map = {
|
||||
'adjust_line_height': adjust_line_height,
|
||||
'adjust_column_width': adjust_line_height,
|
||||
'scrollback_lines': positive_int,
|
||||
'scrollback_pager': shlex.split,
|
||||
'scrollback_pager': to_cmdline,
|
||||
'open_url_with': to_cmdline,
|
||||
'font_size': to_font_size,
|
||||
'font_size_delta': positive_float,
|
||||
'focus_follows_mouse': to_bool,
|
||||
@@ -281,6 +323,7 @@ type_map = {
|
||||
'repaint_delay': positive_int,
|
||||
'input_delay': positive_int,
|
||||
'sync_to_monitor': to_bool,
|
||||
'close_on_child_death': to_bool,
|
||||
'window_border_width': positive_float,
|
||||
'window_margin_width': positive_float,
|
||||
'window_padding_width': positive_float,
|
||||
@@ -296,10 +339,10 @@ type_map = {
|
||||
'initial_window_width': positive_int,
|
||||
'initial_window_height': positive_int,
|
||||
'macos_hide_titlebar': to_bool,
|
||||
'macos_hide_from_tasks': to_bool,
|
||||
'macos_option_as_alt': to_bool,
|
||||
'macos_titlebar_color': macos_titlebar_color,
|
||||
'box_drawing_scale': box_drawing_scale,
|
||||
'x11_bell_volume': int,
|
||||
'background_opacity': unit_float,
|
||||
'tab_separator': tab_separator,
|
||||
'active_tab_font_style': tab_font_style,
|
||||
@@ -307,16 +350,20 @@ type_map = {
|
||||
'inactive_text_alpha': unit_float,
|
||||
'url_style': url_style,
|
||||
'copy_on_select': to_bool,
|
||||
'window_alert_on_bell': to_bool,
|
||||
'tab_bar_edge': tab_bar_edge,
|
||||
'bell_on_tab': to_bool,
|
||||
'kitty_mod': to_modifiers,
|
||||
'clear_all_shortcuts': to_bool,
|
||||
}
|
||||
|
||||
for name in (
|
||||
'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():
|
||||
type_map[name] = to_color
|
||||
for i in range(16):
|
||||
type_map['color%d' % i] = to_color
|
||||
for i in range(256):
|
||||
type_map['color{}'.format(i)] = to_color
|
||||
for a in ('active', 'inactive'):
|
||||
for b in ('foreground', 'background'):
|
||||
type_map['%s_tab_%s' % (a, b)] = to_color
|
||||
@@ -324,15 +371,19 @@ for a in ('active', 'inactive'):
|
||||
|
||||
def special_handling(key, val, ans):
|
||||
if key == 'map':
|
||||
parse_key(val, ans['keymap'])
|
||||
parse_key(val, ans['key_definitions'])
|
||||
return True
|
||||
if key == 'symbol_map':
|
||||
ans['symbol_map'].update(parse_symbol_map(val))
|
||||
return True
|
||||
if key == 'send_text':
|
||||
# For legacy compatibility
|
||||
parse_send_text(val, ans['keymap'])
|
||||
parse_send_text(val, ans['key_definitions'])
|
||||
return True
|
||||
if key == 'clear_all_shortcuts':
|
||||
if to_bool(val):
|
||||
ans['key_definitions'] = [None]
|
||||
return
|
||||
|
||||
|
||||
defaults = None
|
||||
@@ -342,10 +393,7 @@ default_config_path = os.path.join(
|
||||
|
||||
|
||||
def parse_config(lines, check_keys=True):
|
||||
ans = {
|
||||
'keymap': {},
|
||||
'symbol_map': {},
|
||||
}
|
||||
ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': []}
|
||||
parse_config_base(
|
||||
lines,
|
||||
defaults,
|
||||
@@ -357,65 +405,38 @@ def parse_config(lines, check_keys=True):
|
||||
return ans
|
||||
|
||||
|
||||
Options, defaults = init_config(default_config_path, parse_config)
|
||||
actions = frozenset(a.func for a in defaults.keymap.values()) | frozenset(
|
||||
'combine send_text goto_tab new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
||||
def parse_defaults(lines, check_keys=False):
|
||||
ans = parse_config(lines, check_keys)
|
||||
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()
|
||||
)
|
||||
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):
|
||||
ans = {}
|
||||
for k, v in defaults.items():
|
||||
if isinstance(v, dict):
|
||||
newvals = vals.get(k, {})
|
||||
if k == 'keymap':
|
||||
ans['keymap'] = merge_keymaps(v, newvals)
|
||||
else:
|
||||
ans[k] = merge_dicts(v, newvals)
|
||||
ans[k] = merge_dicts(v, newvals)
|
||||
elif k == 'key_definitions':
|
||||
ans['key_definitions'] = v + vals.get('key_definitions', [])
|
||||
else:
|
||||
ans[k] = vals.get(k, v)
|
||||
return ans
|
||||
|
||||
|
||||
def load_config(*paths, overrides=None) -> Options:
|
||||
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 build_ansi_color_table(opts=defaults):
|
||||
|
||||
def as_int(x):
|
||||
return (x[0] << 16) | (x[1] << 8) | x[2]
|
||||
@@ -423,10 +444,11 @@ def build_ansi_color_table(opts: Options = defaults):
|
||||
def col(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):
|
||||
import tempfile
|
||||
fd, p = tempfile.mkstemp(dir=os.path.dirname(path), suffix='.tmp')
|
||||
try:
|
||||
with os.fdopen(fd, 'wb') as f:
|
||||
@@ -438,11 +460,8 @@ def atomic_save(data, path):
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception as err:
|
||||
safe_print(
|
||||
'Failed to delete temp file {} for atomic save with error: {}'.
|
||||
format(p, err),
|
||||
file=sys.stderr
|
||||
)
|
||||
log_error('Failed to delete temp file {} for atomic save with error: {}'.format(
|
||||
p, err))
|
||||
|
||||
|
||||
@contextmanager
|
||||
@@ -455,10 +474,8 @@ def cached_values_for(name):
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception as err:
|
||||
safe_print(
|
||||
'Failed to load cached in {} values with error: {}'.format(name, err),
|
||||
file=sys.stderr
|
||||
)
|
||||
log_error('Failed to load cached in {} values with error: {}'.format(
|
||||
name, err))
|
||||
|
||||
yield cached_values
|
||||
|
||||
@@ -466,10 +483,8 @@ def cached_values_for(name):
|
||||
data = json.dumps(cached_values).encode('utf-8')
|
||||
atomic_save(data, cached_path)
|
||||
except Exception as err:
|
||||
safe_print(
|
||||
'Failed to save cached values with error: {}'.format(err),
|
||||
file=sys.stderr
|
||||
)
|
||||
log_error('Failed to save cached values with error: {}'.format(
|
||||
err))
|
||||
|
||||
|
||||
def initial_window_size(opts, cached_values):
|
||||
@@ -479,5 +494,71 @@ def initial_window_size(opts, cached_values):
|
||||
try:
|
||||
w, h = map(int, ws)
|
||||
except Exception:
|
||||
safe_print('Invalid cached window size, ignoring', file=sys.stderr)
|
||||
log_error('Invalid cached window size, ignoring')
|
||||
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
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
import shlex
|
||||
|
||||
from .utils import safe_print
|
||||
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+(.+)$')
|
||||
|
||||
@@ -32,36 +32,136 @@ def to_bool(x):
|
||||
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(
|
||||
lines, defaults, type_map, special_handling, ans, check_keys=True
|
||||
):
|
||||
if check_keys:
|
||||
all_keys = defaults._asdict()
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
all_keys = defaults._asdict() if check_keys else None
|
||||
_parse(lines, type_map, special_handling, ans, all_keys)
|
||||
|
||||
|
||||
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
|
||||
m = key_pat.match(line)
|
||||
if m is not None:
|
||||
key, val = m.groups()
|
||||
if special_handling(key, val, ans):
|
||||
continue
|
||||
if check_keys:
|
||||
if key not in all_keys:
|
||||
safe_print(
|
||||
'Ignoring unknown config key: {}'.format(key),
|
||||
file=sys.stderr
|
||||
)
|
||||
continue
|
||||
tm = type_map.get(key)
|
||||
if tm is not None:
|
||||
val = tm(val)
|
||||
ans[key] = val
|
||||
try:
|
||||
f = open(path, encoding='utf-8', errors='replace')
|
||||
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 init_config(defaults_path, parse_config):
|
||||
with open(defaults_path, encoding='utf-8') as f:
|
||||
defaults = parse_config(f.read().splitlines(), check_keys=False)
|
||||
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
||||
defaults = Options(**defaults)
|
||||
with open(defaults_path, encoding='utf-8', errors='replace') as f:
|
||||
defaults = parse_config(f, check_keys=False)
|
||||
Options = create_options_class(defaults.keys())
|
||||
defaults = Options(defaults)
|
||||
return Options, defaults
|
||||
|
||||
@@ -4,17 +4,19 @@
|
||||
|
||||
import os
|
||||
import pwd
|
||||
import shlex
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
from .fast_data_types import set_boss as set_c_boss
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 8, 0)
|
||||
version = (0, 9, 1)
|
||||
str_version = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
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')
|
||||
@@ -25,7 +27,22 @@ def _get_config_dir():
|
||||
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
|
||||
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)
|
||||
os.makedirs(ans, exist_ok=True)
|
||||
return ans
|
||||
@@ -44,10 +61,7 @@ def _get_cache_dir():
|
||||
else:
|
||||
candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')
|
||||
candidate = os.path.join(os.path.expanduser(candidate), appname)
|
||||
try:
|
||||
os.makedirs(candidate)
|
||||
except FileExistsError:
|
||||
pass
|
||||
os.makedirs(candidate, exist_ok=True)
|
||||
return candidate
|
||||
|
||||
|
||||
@@ -77,7 +91,10 @@ logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba')
|
||||
try:
|
||||
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
||||
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'
|
||||
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ cursor_as_sgr(Cursor *self, Cursor *prev) {
|
||||
}
|
||||
|
||||
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"
|
||||
cursor_reset_display_attrs(self);
|
||||
Py_RETURN_NONE;
|
||||
@@ -272,7 +272,7 @@ void cursor_copy_to(Cursor *src, Cursor *dest) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
copy(Cursor *self);
|
||||
copy(Cursor *self, PyObject*);
|
||||
#define copy_doc "Create a clone of this cursor"
|
||||
|
||||
// Boilerplate {{{
|
||||
@@ -342,7 +342,7 @@ cursor_copy(Cursor *self) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
copy(Cursor *self) {
|
||||
copy(Cursor *self, PyObject *a UNUSED) {
|
||||
return (PyObject*)cursor_copy(self);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <stddef.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef WITH_PROFILER
|
||||
#include <gperftools/profiler.h>
|
||||
#endif
|
||||
@@ -88,31 +87,6 @@ pyset_iutf8(PyObject UNUSED *self, PyObject *args) {
|
||||
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
|
||||
static PyObject*
|
||||
start_profiler(PyObject UNUSED *self, PyObject *args) {
|
||||
@@ -135,7 +109,6 @@ static PyMethodDef module_methods[] = {
|
||||
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},
|
||||
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
||||
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
||||
{"install_sigchld_handler", (PyCFunction)install_sigchld_handler, METH_NOARGS, ""},
|
||||
#ifdef __APPLE__
|
||||
METHODB(user_cache_dir, METH_NOARGS),
|
||||
#endif
|
||||
@@ -174,6 +147,7 @@ extern bool init_graphics(PyObject *module);
|
||||
extern bool init_shaders(PyObject *module);
|
||||
extern bool init_mouse(PyObject *module);
|
||||
extern bool init_kittens(PyObject *module);
|
||||
extern bool init_logging(PyObject *module);
|
||||
#ifdef __APPLE__
|
||||
extern int init_CoreText(PyObject *);
|
||||
extern bool init_cocoa(PyObject *module);
|
||||
@@ -193,6 +167,7 @@ PyInit_fast_data_types(void) {
|
||||
#endif
|
||||
|
||||
if (m != NULL) {
|
||||
if (!init_logging(m)) return NULL;
|
||||
if (!init_LineBuf(m)) return NULL;
|
||||
if (!init_HistoryBuf(m)) return NULL;
|
||||
if (!init_Line(m)) return NULL;
|
||||
@@ -218,16 +193,15 @@ PyInit_fast_data_types(void) {
|
||||
#endif
|
||||
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, "ITALIC", ITALIC_SHIFT);
|
||||
PyModule_AddIntConstant(m, "REVERSE", REVERSE_SHIFT);
|
||||
PyModule_AddIntConstant(m, "STRIKETHROUGH", STRIKE_SHIFT);
|
||||
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
|
||||
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_BEAM);
|
||||
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
// Required minimum OpenGL version
|
||||
#define OPENGL_REQUIRED_VERSION_MAJOR 3
|
||||
#define OPENGL_REQUIRED_VERSION_MINOR 3
|
||||
#define GLFW_MOD_KITTY 1024
|
||||
#define UNUSED __attribute__ ((unused))
|
||||
#define PYNOARG PyObject *__a1 UNUSED, PyObject *__a2 UNUSED
|
||||
#define EXPORTED __attribute__ ((visibility ("default")))
|
||||
#define LIKELY(x) __builtin_expect (!!(x), 1)
|
||||
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
|
||||
@@ -26,7 +28,8 @@
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
#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 uint32_t char_type;
|
||||
@@ -158,15 +161,18 @@ typedef struct {
|
||||
Line *line;
|
||||
} LineBuf;
|
||||
|
||||
typedef struct {
|
||||
Cell *cells;
|
||||
line_attrs_type *line_attrs;
|
||||
} HistoryBufSegment;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
Cell *buf;
|
||||
index_type xnum, ynum;
|
||||
index_type xnum, ynum, num_segments;
|
||||
HistoryBufSegment* segments;
|
||||
Line *line;
|
||||
index_type start_of_data, count;
|
||||
line_attrs_type *line_attrs;
|
||||
} HistoryBuf;
|
||||
|
||||
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 left_shift_line(line, at, num) \
|
||||
#define left_shift_line(line, at, num) { \
|
||||
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
|
||||
COPY_CELL(line, __i__ + (num), line, __i__) \
|
||||
} \
|
||||
@@ -215,7 +221,8 @@ typedef struct {
|
||||
(line)->cells[(at)].ch = BLANK_CHAR; \
|
||||
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
|
||||
clear_sprite_position((line)->cells[(at)]); \
|
||||
}
|
||||
}\
|
||||
}
|
||||
|
||||
#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \
|
||||
if ((base)->capacity < num) { \
|
||||
@@ -262,6 +269,5 @@ void wakeup_io_loop(bool);
|
||||
void scroll_event(double, double);
|
||||
void fake_scroll(int, bool);
|
||||
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);
|
||||
void on_key_input(int key, int scancode, int action, int mods, const char*, int);
|
||||
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
|
||||
#pragma once
|
||||
#include "data-types.h"
|
||||
|
||||
@@ -330,19 +330,39 @@ face_has_codepoint(PyObject* face, char_type cp) {
|
||||
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
|
||||
has_cell_text(Font *self, Cell *cell) {
|
||||
if (!face_has_codepoint(self->face, cell->ch)) return false;
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
|
||||
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);
|
||||
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
|
||||
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
|
||||
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face, true);
|
||||
set_size_for_face(face, cell_height, 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;
|
||||
}
|
||||
|
||||
|
||||
static inline ssize_t
|
||||
fallback_font(Cell *cell) {
|
||||
bool bold = (cell->attrs >> BOLD_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
|
||||
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
|
||||
Font *ff = fonts.fonts +j;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -418,7 +439,7 @@ START_ALLOW_CASE_RANGE
|
||||
ans = fonts.bi_font_idx; break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 > MAX_GLYPHS_IN_GROUP) {
|
||||
// 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--;
|
||||
while (num_cells_consumed) {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
render_groups(Font *font) {
|
||||
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", */
|
||||
/* 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;
|
||||
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);
|
||||
idx++;
|
||||
}
|
||||
@@ -817,7 +852,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
||||
|
||||
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));
|
||||
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++;
|
||||
}
|
||||
if (face) { Py_CLEAR(face); free(font); }
|
||||
@@ -826,10 +861,11 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
||||
#undef G
|
||||
|
||||
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) {
|
||||
default:
|
||||
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]);
|
||||
break;
|
||||
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
|
||||
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;
|
||||
index_type first_cell_in_run, i;
|
||||
attrs_type prev_width = 0;
|
||||
@@ -854,6 +895,22 @@ render_line(Line *line) {
|
||||
if (prev_width == 2) { prev_width = 0; continue; }
|
||||
Cell *cell = line->cells + i;
|
||||
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;
|
||||
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
|
||||
if (run_font_idx == cell_font_idx) continue;
|
||||
@@ -931,7 +988,7 @@ static PyObject*
|
||||
test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
||||
glyph_index glyph;
|
||||
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;
|
||||
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; }
|
||||
@@ -1015,7 +1072,7 @@ concat_cells(PyObject UNUSED *self, PyObject *args) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
current_fonts(PyObject UNUSED *self) {
|
||||
current_fonts(PYNOARG) {
|
||||
PyObject *ans = PyDict_New();
|
||||
if (!ans) return NULL;
|
||||
#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)];
|
||||
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
|
||||
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 (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
||||
ssize_t ans = fallback_font(&cell);
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
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',
|
||||
(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
|
||||
# fallback to Menlo
|
||||
if q not in font_map['family_map']:
|
||||
safe_print(
|
||||
'The font {} was not found, falling back to Menlo'.format(family),
|
||||
file=sys.stderr
|
||||
)
|
||||
log_error('The font {} was not found, falling back to Menlo'.format(family))
|
||||
family = 'Menlo'
|
||||
return {
|
||||
'monospace': True,
|
||||
|
||||
@@ -214,15 +214,20 @@ ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_dat
|
||||
|
||||
PyObject*
|
||||
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; }
|
||||
char *path;
|
||||
long index;
|
||||
bool hinting;
|
||||
long hint_style;
|
||||
D(path, PyUnicode_AsUTF8);
|
||||
D(index, PyLong_AsLong);
|
||||
D(hinting, PyObject_IsTrue);
|
||||
D(hint_style, PyLong_AsLong);
|
||||
#define D(key, conv, missing_ok) { \
|
||||
PyObject *t = PyDict_GetItemString(descriptor, #key); \
|
||||
if (t == NULL) { \
|
||||
if (!missing_ok) { PyErr_SetString(PyExc_KeyError, "font descriptor is missing the key: " #key); return NULL; } \
|
||||
} else key = conv(t); \
|
||||
}
|
||||
char *path = NULL;
|
||||
long index = 0;
|
||||
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
|
||||
Face *self = (Face *)Face_Type.tp_alloc(&Face_Type, 0);
|
||||
if (self != NULL) {
|
||||
@@ -279,7 +284,7 @@ calc_cell_width(Face *self) {
|
||||
for (char_type i = 32; i < 128; i++) {
|
||||
int glyph_index = FT_Get_Char_Index(self->face, i);
|
||||
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;
|
||||
@@ -324,7 +329,6 @@ typedef struct {
|
||||
unsigned int factor, right_edge;
|
||||
} ProcessedBitmap;
|
||||
|
||||
|
||||
static inline void
|
||||
trim_borders(ProcessedBitmap *ans, size_t extra) {
|
||||
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->pixel_mode = bitmap->pixel_mode;
|
||||
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) {
|
||||
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) {
|
||||
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
|
||||
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*
|
||||
display_name(Face *self) {
|
||||
display_name(PyObject *s, PyObject *a UNUSED) {
|
||||
Face *self = (Face*)s;
|
||||
const char *psname = FT_Get_Postscript_Name(self->face);
|
||||
if (psname) return Py_BuildValue("s", psname);
|
||||
Py_INCREF(self->path);
|
||||
@@ -540,8 +549,8 @@ display_name(Face *self) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
extra_data(Face *self) {
|
||||
return PyLong_FromVoidPtr(self->extra_data);
|
||||
extra_data(PyObject *self, PyObject *a UNUSED) {
|
||||
return PyLong_FromVoidPtr(((Face*)self)->extra_data);
|
||||
}
|
||||
|
||||
// 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.15a0 on Wed Nov 8 14:58:53 2017.
|
||||
|
||||
OpenGL loader generated by glad 0.1.20a0 on Fri May 4 18:11:55 2018.
|
||||
Language/Generator: C/C++ Debug
|
||||
Specification: gl
|
||||
APIs: gl=3.3
|
||||
@@ -15,22 +13,17 @@
|
||||
Loader: True
|
||||
Local files: False
|
||||
Omit khrplatform: False
|
||||
|
||||
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"
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __glad_h_
|
||||
#define __glad_h_
|
||||
|
||||
#ifdef __gl_h_
|
||||
#error OpenGL header already included, remove this include, glad already provides it
|
||||
#endif
|
||||
#define __gl_h_
|
||||
|
||||
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
@@ -40,25 +33,20 @@
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef APIENTRY
|
||||
#define APIENTRY
|
||||
#endif
|
||||
#ifndef APIENTRYP
|
||||
#define APIENTRYP APIENTRY *
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct gladGLversionStruct {
|
||||
int major;
|
||||
int minor;
|
||||
};
|
||||
|
||||
typedef void* (* GLADloadproc)(const char *name);
|
||||
|
||||
#ifndef GLAPI
|
||||
# if defined(GLAD_GLAPI_EXPORT)
|
||||
# if defined(_WIN32) || defined(__CYGWIN__)
|
||||
@@ -84,17 +72,11 @@ typedef void* (* GLADloadproc)(const char *name);
|
||||
# define GLAPI extern
|
||||
# endif
|
||||
#endif
|
||||
|
||||
GLAPI struct gladGLversionStruct GLVersion;
|
||||
|
||||
GLAPI int gladLoadGL(void);
|
||||
|
||||
GLAPI int gladLoadGLLoader(GLADloadproc);
|
||||
|
||||
|
||||
#define GLAD_DEBUG
|
||||
typedef void (* GLADcallback)(const char *name, void *funcptr, int len_args, ...);
|
||||
|
||||
GLAPI void glad_set_pre_callback(GLADcallback cb);
|
||||
GLAPI void glad_set_post_callback(GLADcallback cb);
|
||||
#include <stddef.h>
|
||||
@@ -1112,11 +1094,11 @@ typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap);
|
||||
GLAPI PFNGLENABLEPROC glad_glEnable;
|
||||
GLAPI PFNGLENABLEPROC glad_debug_glEnable;
|
||||
#define glEnable glad_debug_glEnable
|
||||
typedef void (APIENTRYP PFNGLFINISHPROC)();
|
||||
typedef void (APIENTRYP PFNGLFINISHPROC)(void);
|
||||
GLAPI PFNGLFINISHPROC glad_glFinish;
|
||||
GLAPI PFNGLFINISHPROC glad_debug_glFinish;
|
||||
#define glFinish glad_debug_glFinish
|
||||
typedef void (APIENTRYP PFNGLFLUSHPROC)();
|
||||
typedef void (APIENTRYP PFNGLFLUSHPROC)(void);
|
||||
GLAPI PFNGLFLUSHPROC glad_glFlush;
|
||||
GLAPI PFNGLFLUSHPROC 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_debug_glGetDoublev;
|
||||
#define glGetDoublev glad_debug_glGetDoublev
|
||||
typedef GLenum (APIENTRYP PFNGLGETERRORPROC)();
|
||||
typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void);
|
||||
GLAPI PFNGLGETERRORPROC glad_glGetError;
|
||||
GLAPI PFNGLGETERRORPROC 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_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_debug_glDepthRange;
|
||||
#define glDepthRange glad_debug_glDepthRange
|
||||
@@ -1484,7 +1466,7 @@ typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);
|
||||
GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;
|
||||
GLAPI PFNGLCOMPILESHADERPROC glad_debug_glCompileShader;
|
||||
#define glCompileShader glad_debug_glCompileShader
|
||||
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)();
|
||||
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void);
|
||||
GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
|
||||
GLAPI PFNGLCREATEPROGRAMPROC 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_debug_glBeginTransformFeedback;
|
||||
#define glBeginTransformFeedback glad_debug_glBeginTransformFeedback
|
||||
typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)();
|
||||
typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void);
|
||||
GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;
|
||||
GLAPI PFNGLENDTRANSFORMFEEDBACKPROC 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_debug_glBeginConditionalRender;
|
||||
#define glBeginConditionalRender glad_debug_glBeginConditionalRender
|
||||
typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)();
|
||||
typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(void);
|
||||
GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;
|
||||
GLAPI PFNGLENDCONDITIONALRENDERPROC glad_debug_glEndConditionalRender;
|
||||
#define glEndConditionalRender glad_debug_glEndConditionalRender
|
||||
@@ -2678,7 +2660,7 @@ GLAPI PFNGLSAMPLECOVERAGEARBPROC glad_debug_glSampleCoverageARB;
|
||||
#ifndef GL_ARB_robustness
|
||||
#define GL_ARB_robustness 1
|
||||
GLAPI int GLAD_GL_ARB_robustness;
|
||||
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC)();
|
||||
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC)(void);
|
||||
GLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB;
|
||||
GLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC 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_debug_glPushDebugGroup;
|
||||
#define glPushDebugGroup glad_debug_glPushDebugGroup
|
||||
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)();
|
||||
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void);
|
||||
GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;
|
||||
GLAPI PFNGLPOPDEBUGGROUPPROC 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_debug_glPushDebugGroupKHR;
|
||||
#define glPushDebugGroupKHR glad_debug_glPushDebugGroupKHR
|
||||
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)();
|
||||
typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void);
|
||||
GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR;
|
||||
GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_debug_glPopDebugGroupKHR;
|
||||
#define glPopDebugGroupKHR glad_debug_glPopDebugGroupKHR
|
||||
@@ -2867,11 +2849,8 @@ GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR;
|
||||
GLAPI PFNGLGETPOINTERVKHRPROC glad_debug_glGetPointervKHR;
|
||||
#define glGetPointervKHR glad_debug_glGetPointervKHR
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
int init_glad(GLADloadproc, int);
|
||||
|
||||
@@ -59,6 +59,7 @@ gl_init() {
|
||||
ARB_TEST(texture_storage);
|
||||
#undef ARB_TEST
|
||||
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)) {
|
||||
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) {
|
||||
GLsizei len;
|
||||
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);
|
||||
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
||||
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");
|
||||
if (glfwSetCursor_impl == NULL) fail("Failed to load glfw function glfwSetCursor with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetKeyCallback_impl) = dlsym(handle, "glfwSetKeyCallback");
|
||||
if (glfwSetKeyCallback_impl == NULL) fail("Failed to load glfw function glfwSetKeyCallback 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 **) (&glfwSetKeyboardCallback_impl) = dlsym(handle, "glfwSetKeyboardCallback");
|
||||
if (glfwSetKeyboardCallback_impl == NULL) fail("Failed to load glfw function glfwSetKeyboardCallback with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetMouseButtonCallback_impl) = dlsym(handle, "glfwSetMouseButtonCallback");
|
||||
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 **) (&glfwSetCocoaTextInputFilter_impl) = dlsym(handle, "glfwSetCocoaTextInputFilter");
|
||||
|
||||
*(void **) (&glfwGetX11Display_impl) = dlsym(handle, "glfwGetX11Display");
|
||||
|
||||
*(void **) (&glfwGetX11Window_impl) = dlsym(handle, "glfwGetX11Window");
|
||||
|
||||
85
kitty/glfw-wrapper.h
generated
85
kitty/glfw-wrapper.h
generated
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int);
|
||||
|
||||
|
||||
/*! @name GLFW version macros
|
||||
@@ -1133,9 +1134,15 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
||||
*/
|
||||
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] 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] mods Bit field describing which [modifier keys](@ref mods) were
|
||||
* held down.
|
||||
* @param[in] text UTF-8 encoded text generated by this key event or empty string.
|
||||
* @param[in] 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 glfwSetKeyCallback
|
||||
* @sa @ref glfwSetKeyboardCallback
|
||||
*
|
||||
* @since Added in version 1.0.
|
||||
* @glfw3 Added window handle, scancode and modifier mask parameters.
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,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);
|
||||
typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, int);
|
||||
|
||||
/*! @brief The function signature for file drop callbacks.
|
||||
*
|
||||
@@ -1585,7 +1558,7 @@ typedef void (*glfwRequestWindowAttention_func)(GLFWwindow*);
|
||||
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
|
||||
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
|
||||
|
||||
typedef int (*glfwWindowBell_func)(GLFWwindow*, int64_t);
|
||||
typedef int (*glfwWindowBell_func)(GLFWwindow*);
|
||||
glfwWindowBell_func glfwWindowBell_impl;
|
||||
#define glfwWindowBell glfwWindowBell_impl
|
||||
|
||||
@@ -1713,17 +1686,9 @@ typedef void (*glfwSetCursor_func)(GLFWwindow*, GLFWcursor*);
|
||||
glfwSetCursor_func glfwSetCursor_impl;
|
||||
#define glfwSetCursor glfwSetCursor_impl
|
||||
|
||||
typedef GLFWkeyfun (*glfwSetKeyCallback_func)(GLFWwindow*, GLFWkeyfun);
|
||||
glfwSetKeyCallback_func glfwSetKeyCallback_impl;
|
||||
#define glfwSetKeyCallback glfwSetKeyCallback_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 GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboardfun);
|
||||
glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl;
|
||||
#define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl
|
||||
|
||||
typedef GLFWmousebuttonfun (*glfwSetMouseButtonCallback_func)(GLFWwindow*, GLFWmousebuttonfun);
|
||||
glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;
|
||||
@@ -1861,6 +1826,10 @@ typedef uint32_t (*glfwGetCocoaMonitor_func)(GLFWmonitor*);
|
||||
glfwGetCocoaMonitor_func glfwGetCocoaMonitor_impl;
|
||||
#define glfwGetCocoaMonitor glfwGetCocoaMonitor_impl
|
||||
|
||||
typedef GLFWcocoatextinputfilterfun (*glfwSetCocoaTextInputFilter_func)(GLFWwindow*, GLFWcocoatextinputfilterfun);
|
||||
glfwSetCocoaTextInputFilter_func glfwSetCocoaTextInputFilter_impl;
|
||||
#define glfwSetCocoaTextInputFilter glfwSetCocoaTextInputFilter_impl
|
||||
|
||||
typedef void* (*glfwGetX11Display_func)();
|
||||
glfwGetX11Display_func glfwGetX11Display_impl;
|
||||
#define glfwGetX11Display glfwGetX11Display_impl
|
||||
|
||||
142
kitty/glfw.c
142
kitty/glfw.c
@@ -9,13 +9,15 @@
|
||||
#include "glfw-wrapper.h"
|
||||
extern bool cocoa_make_window_resizable(void *w);
|
||||
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
|
||||
#error "glfw has too many keys, you should increase MAX_KEY_COUNT"
|
||||
#endif
|
||||
|
||||
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
|
||||
static bool event_loop_blocking_with_no_timeout = false;
|
||||
|
||||
void
|
||||
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->viewport_width = MAX(window->viewport_width, 100);
|
||||
window->viewport_height = MAX(window->viewport_height, 100);
|
||||
window->window_width = MAX(w, 100);
|
||||
window->window_height = MAX(h, 100);
|
||||
if (notify_boss) {
|
||||
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
|
||||
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
|
||||
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
||||
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;
|
||||
window->has_pending_resizes = true; global_state.has_pending_resizes = true;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -94,21 +110,13 @@ refresh_callback(GLFWwindow *w) {
|
||||
}
|
||||
|
||||
static void
|
||||
char_mods_callback(GLFWwindow *w, unsigned int codepoint, int mods) {
|
||||
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) {
|
||||
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 (key >= 0 && key <= GLFW_KEY_LAST) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -229,7 +237,11 @@ static GLFWimage logo = {0};
|
||||
static PyObject*
|
||||
set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -296,14 +308,31 @@ set_dpi_from_os_window(OSWindow *w) {
|
||||
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*
|
||||
create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
int width, height, x = -1, y = -1;
|
||||
char *title, *wm_class_class, *wm_class_name;
|
||||
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;
|
||||
bool is_first_window = standard_cursor == NULL;
|
||||
if (!PyArg_ParseTuple(args, "iisss|Oii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
|
||||
|
||||
if (is_first_window) {
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
@@ -336,11 +365,11 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
PyErr_SetString(PyExc_ValueError, "Too many windows");
|
||||
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);
|
||||
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : 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);
|
||||
}
|
||||
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);
|
||||
#ifdef __APPLE__
|
||||
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
|
||||
is_first_window = false;
|
||||
}
|
||||
|
||||
OSWindow *w = add_os_window();
|
||||
@@ -372,19 +405,17 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
update_os_window_viewport(w, false);
|
||||
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
|
||||
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
|
||||
glfwSetCharModsCallback(glfw_window, char_mods_callback);
|
||||
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
|
||||
glfwSetScrollCallback(glfw_window, scroll_callback);
|
||||
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
|
||||
glfwSetKeyCallback(glfw_window, key_callback);
|
||||
glfwSetKeyboardCallback(glfw_window, key_callback);
|
||||
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
|
||||
glfwSetDropCallback(glfw_window, drop_callback);
|
||||
#ifdef __APPLE__
|
||||
if (OPT(macos_hide_titlebar)) {
|
||||
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
|
||||
double now = monotonic();
|
||||
w->is_focused = true;
|
||||
@@ -394,7 +425,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
if (want_semi_transparent && !w->is_semi_transparent) {
|
||||
static bool warned = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -459,7 +490,7 @@ destroy_os_window(OSWindow *w) {
|
||||
// Global functions {{{
|
||||
static void
|
||||
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*
|
||||
glfw_terminate(PyObject UNUSED *self) {
|
||||
glfw_terminate(PYNOARG) {
|
||||
glfwTerminate();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
glfw_wait_events(PyObject UNUSED *self, PyObject *args) {
|
||||
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) {
|
||||
glfw_post_empty_event(PYNOARG) {
|
||||
glfwPostEmptyEvent();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
glfw_poll_events(PyObject UNUSED *self) {
|
||||
glfw_poll_events(PYNOARG) {
|
||||
glfwPollEvents();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
@@ -522,7 +541,7 @@ get_physical_dpi(GLFWmonitor *m) {
|
||||
}
|
||||
|
||||
PyObject*
|
||||
glfw_get_physical_dpi(PyObject UNUSED *self) {
|
||||
glfw_get_physical_dpi(PYNOARG) {
|
||||
GLFWmonitor *m = glfwGetPrimaryMonitor();
|
||||
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
|
||||
return get_physical_dpi(m);
|
||||
@@ -547,14 +566,14 @@ glfw_window_hint(PyObject UNUSED *self, PyObject *args) {
|
||||
// }}}
|
||||
|
||||
static PyObject*
|
||||
get_clipboard_string(PyObject UNUSED *self) {
|
||||
get_clipboard_string(PYNOARG) {
|
||||
OSWindow *w = current_os_window();
|
||||
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
|
||||
return Py_BuildValue("s", "");
|
||||
}
|
||||
|
||||
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;
|
||||
float xscale, yscale;
|
||||
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
|
||||
@@ -571,7 +590,7 @@ set_clipboard_string(PyObject UNUSED *self, PyObject *args) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
toggle_fullscreen(PyObject UNUSED *self) {
|
||||
toggle_fullscreen(PYNOARG) {
|
||||
GLFWmonitor *monitor;
|
||||
OSWindow *w = current_os_window();
|
||||
if (!w) Py_RETURN_NONE;
|
||||
@@ -601,7 +620,7 @@ ring_audio_bell(OSWindow *w) {
|
||||
if (now - last_bell_at <= 0.1) return;
|
||||
last_bell_at = now;
|
||||
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);
|
||||
if (w) {
|
||||
if (audio_bell) ring_audio_bell(w);
|
||||
glfwRequestWindowAttention(w->handle);
|
||||
if (OPT(window_alert_on_bell)) glfwRequestWindowAttention(w->handle);
|
||||
glfwPostEmptyEvent();
|
||||
}
|
||||
}
|
||||
@@ -622,9 +641,7 @@ set_os_window_title(OSWindow *w, const char *title) {
|
||||
|
||||
void
|
||||
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
|
||||
@@ -634,8 +651,8 @@ swap_window_buffers(OSWindow *w) {
|
||||
|
||||
void
|
||||
event_loop_wait(double timeout) {
|
||||
if (timeout < 0) glfwWaitEvents();
|
||||
else if (timeout > 0) glfwWaitEventsTimeout(timeout);
|
||||
if (timeout < 0) { event_loop_blocking_with_no_timeout = true; glfwWaitEvents(); event_loop_blocking_with_no_timeout = false; }
|
||||
else glfwWaitEventsTimeout(timeout);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -661,14 +678,14 @@ should_os_window_close(OSWindow* w) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
primary_monitor_size(PyObject UNUSED *self) {
|
||||
primary_monitor_size(PYNOARG) {
|
||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||
return Py_BuildValue("ii", mode->width, mode->height);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
primary_monitor_content_scale(PyObject UNUSED *self) {
|
||||
primary_monitor_content_scale(PYNOARG) {
|
||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||
float xscale, yscale;
|
||||
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
|
||||
@@ -676,10 +693,10 @@ primary_monitor_content_scale(PyObject UNUSED *self) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
x11_display(PyObject UNUSED *self) {
|
||||
x11_display(PYNOARG) {
|
||||
if (glfwGetX11Display) {
|
||||
return PyLong_FromVoidPtr(glfwGetX11Display());
|
||||
} else fprintf(stderr, "Failed to load glfwGetX11Display\n");
|
||||
} else log_error("Failed to load glfwGetX11Display");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@@ -698,10 +715,10 @@ x11_window_id(PyObject UNUSED *self, PyObject *os_wid) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_primary_selection(PyObject UNUSED *self) {
|
||||
get_primary_selection(PYNOARG) {
|
||||
if (glfwGetX11SelectionString) {
|
||||
return Py_BuildValue("y", glfwGetX11SelectionString());
|
||||
} else fprintf(stderr, "Failed to load glfwGetX11SelectionString\n");
|
||||
} else log_error("Failed to load glfwGetX11SelectionString");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@@ -710,7 +727,7 @@ set_primary_selection(PyObject UNUSED *self, PyObject *args) {
|
||||
char *text;
|
||||
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
||||
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
|
||||
else fprintf(stderr, "Failed to load glfwSetX11SelectionString\n");
|
||||
else log_error("Failed to load glfwSetX11SelectionString");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@@ -768,7 +785,6 @@ static PyMethodDef module_methods[] = {
|
||||
METHODB(glfw_poll_events, METH_NOARGS),
|
||||
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
|
||||
{"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_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
|
||||
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
|
||||
@@ -777,10 +793,19 @@ static PyMethodDef module_methods[] = {
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
void cleanup_glfw(void) {
|
||||
if (logo.pixels) free(logo.pixels);
|
||||
logo.pixels = NULL;
|
||||
}
|
||||
|
||||
// constants {{{
|
||||
bool
|
||||
init_glfw(PyObject *m) {
|
||||
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;
|
||||
ADDC(GLFW_RELEASE);
|
||||
ADDC(GLFW_PRESS);
|
||||
@@ -922,6 +947,7 @@ init_glfw(PyObject *m) {
|
||||
ADDC(GLFW_MOD_CONTROL);
|
||||
ADDC(GLFW_MOD_ALT);
|
||||
ADDC(GLFW_MOD_SUPER);
|
||||
ADDC(GLFW_MOD_KITTY);
|
||||
|
||||
// --- Mouse -------------------------------------------------------------------
|
||||
ADDC(GLFW_MOUSE_BUTTON_1);
|
||||
|
||||
@@ -21,7 +21,7 @@ PyTypeObject GraphicsManager_Type;
|
||||
|
||||
#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;
|
||||
@@ -509,7 +509,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
||||
|
||||
static inline const char*
|
||||
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 (!has_add_respose) {
|
||||
if (!data_loaded) return NULL;
|
||||
@@ -873,9 +873,10 @@ new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
||||
static inline PyObject*
|
||||
image_as_dict(Image *img) {
|
||||
#define U(x) #x, img->x
|
||||
return Py_BuildValue("{sI sI sI sI sI sI sH sH sN}",
|
||||
U(texture_id), U(client_id), U(width), U(height), U(internal_id), U(refcnt), U(data_loaded),
|
||||
"is_4byte_aligned", img->load_data.is_4byte_aligned,
|
||||
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),
|
||||
"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)
|
||||
);
|
||||
#undef U
|
||||
|
||||
127
kitty/history.c
127
kitty/history.c
@@ -10,10 +10,42 @@
|
||||
#include <structmember.h>
|
||||
|
||||
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*
|
||||
lineptr(HistoryBuf *linebuf, index_type y) {
|
||||
return linebuf->buf + y * linebuf->xnum;
|
||||
lineptr(HistoryBuf *self, index_type y) {
|
||||
seg_ptr(cells, self->xnum);
|
||||
}
|
||||
|
||||
|
||||
static inline line_attrs_type*
|
||||
attrptr(HistoryBuf *self, index_type y) {
|
||||
seg_ptr(line_attrs, 1);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
@@ -32,18 +64,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
if (self != NULL) {
|
||||
self->xnum = xnum;
|
||||
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();
|
||||
if (self->buf == NULL || self->line == NULL || self->line_attrs == NULL) {
|
||||
PyErr_NoMemory();
|
||||
PyMem_Free(self->buf); Py_CLEAR(self->line); PyMem_Free(self->line_attrs);
|
||||
Py_CLEAR(self);
|
||||
} else {
|
||||
self->line->xnum = xnum;
|
||||
for(index_type y = 0; y < self->ynum; y++) {
|
||||
clear_chars_in_line(lineptr(self, y), self->xnum, BLANK_CHAR);
|
||||
}
|
||||
self->num_segments = 0;
|
||||
add_segment(self);
|
||||
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
|
||||
dealloc(HistoryBuf* self) {
|
||||
Py_CLEAR(self->line);
|
||||
PyMem_Free(self->buf);
|
||||
PyMem_Free(self->line_attrs);
|
||||
for (size_t i = 0; i < self->num_segments; i++) {
|
||||
PyMem_Free(self->segments[i].cells);
|
||||
PyMem_Free(self->segments[i].line_attrs);
|
||||
}
|
||||
PyMem_Free(self->segments);
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
@@ -71,8 +100,8 @@ static inline void
|
||||
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
|
||||
l->cells = lineptr(self, num);
|
||||
l->continued = self->line_attrs[num] & CONTINUED_MASK;
|
||||
l->has_dirty_text = self->line_attrs[num] & TEXT_DIRTY_MASK ? true : false;
|
||||
l->continued = *attrptr(self, num) & CONTINUED_MASK;
|
||||
l->has_dirty_text = *attrptr(self, num) & TEXT_DIRTY_MASK ? true : false;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -82,12 +111,14 @@ historybuf_init_line(HistoryBuf *self, index_type lnum, Line *l) {
|
||||
|
||||
void
|
||||
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
|
||||
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
|
||||
@@ -105,43 +136,11 @@ historybuf_push(HistoryBuf *self) {
|
||||
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
|
||||
historybuf_add_line(HistoryBuf *self, const Line *line) {
|
||||
index_type idx = historybuf_push(self);
|
||||
copy_line(line, self->line);
|
||||
self->line_attrs[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;
|
||||
*attrptr(self, idx) = (line->continued & CONTINUED_MASK) | (line->has_dirty_text ? TEXT_DIRTY_MASK : 0);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
@@ -161,7 +160,7 @@ __str__(HistoryBuf *self) {
|
||||
if (lines == NULL) return PyErr_NoMemory();
|
||||
for (index_type i = 0; i < self->count; i++) {
|
||||
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; }
|
||||
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++) {
|
||||
init_line(self, i, &l);
|
||||
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;
|
||||
index_type num = line_as_ansi(&l, t, 5120);
|
||||
if (!(l.continued) && num < 5119) t[num++] = 10; // 10 = \n
|
||||
@@ -215,11 +214,11 @@ as_text(HistoryBuf *self, PyObject *args) {
|
||||
|
||||
|
||||
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."
|
||||
PyObject *ans = PyList_New(0);
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -232,7 +231,6 @@ static PyObject* rewrap(HistoryBuf *self, PyObject *args);
|
||||
#define rewrap_doc ""
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
METHOD(change_num_of_lines, METH_O)
|
||||
METHOD(line, METH_O)
|
||||
METHOD(as_ansi, METH_O)
|
||||
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 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);
|
||||
|
||||
#include "rewrap.h"
|
||||
|
||||
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) {
|
||||
memcpy(other->buf, self->buf, sizeof(Cell) * self->xnum * self->ynum);
|
||||
memcpy(other->line_attrs, self->line_attrs, sizeof(line_attrs_type) * self->ynum);
|
||||
// Fast path
|
||||
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;
|
||||
return;
|
||||
}
|
||||
@@ -295,7 +296,7 @@ void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
|
||||
index_type x = 0, y = 0;
|
||||
if (self->count > 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
is_modifier_key(int 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
|
||||
send_key_to_child(Window *w, int key, int mods, int action) {
|
||||
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
|
||||
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();
|
||||
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;
|
||||
int lkey = get_localized_key(key, scancode);
|
||||
bool has_text = text && !is_ascii_control_char(text[0]);
|
||||
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;
|
||||
if (qkey != UINT8_MAX) {
|
||||
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); */
|
||||
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(); }
|
||||
else {
|
||||
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)) {
|
||||
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (!OPT(macos_option_as_alt) && IS_ALT_MODS(mods) && !(
|
||||
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
|
||||
) return;
|
||||
#endif
|
||||
if (
|
||||
action == GLFW_PRESS ||
|
||||
(action == GLFW_REPEAT && screen->modes.mDECARM) ||
|
||||
screen->modes.mEXTENDED_KEYBOARD
|
||||
) {
|
||||
send_key_to_child(w, lkey, mods, action);
|
||||
bool ok_to_send = action == GLFW_PRESS || action == GLFW_REPEAT || screen->modes.mEXTENDED_KEYBOARD;
|
||||
if (ok_to_send) {
|
||||
if (has_text) {
|
||||
schedule_write_to_child(w->id, text, strlen(text));
|
||||
} else {
|
||||
send_key_to_child(w, key, 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')
|
||||
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_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 {
|
||||
defines.GLFW_KEY_UP: 'kcuu1',
|
||||
@@ -57,9 +68,14 @@ for f in range(1, 13):
|
||||
kf = getattr(defines, 'GLFW_KEY_F{}'.format(f))
|
||||
kn = 'kf{}'.format(f)
|
||||
smkx_key_map[kf] = key_as_bytes(kn)
|
||||
SHIFTED_KEYS[kf] = modify_complex_key(kn, 2)
|
||||
alt_codes[kf] = modify_complex_key(kn, 3)
|
||||
shift_alt_codes[kf] = modify_complex_key(kn, 4)
|
||||
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.update({
|
||||
'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_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({
|
||||
k: (1 + i, )
|
||||
for i, k in
|
||||
@@ -204,6 +210,7 @@ ASCII_C0_SHIFTED = {
|
||||
'SLASH': b'\x7f',
|
||||
}
|
||||
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):
|
||||
@@ -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
|
||||
data.append(0o33)
|
||||
data.extend(m[key])
|
||||
elif mods == ctrl_alt_mod and key in CTRL_ALT_KEYS:
|
||||
data.append(0x1b), data.extend(control_codes[key])
|
||||
else:
|
||||
key_map = cursor_key_mode_map[smkx]
|
||||
x = key_map.get(key)
|
||||
@@ -247,6 +256,10 @@ def get_shortcut(keymap, mods, key, scancode):
|
||||
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():
|
||||
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
||||
import os
|
||||
@@ -332,13 +345,15 @@ def generate_key_table():
|
||||
ind('return NULL;')
|
||||
i -= 2
|
||||
i -= 1
|
||||
ind('} // end switch(mods)\n')
|
||||
ind('} // end switch(mods)')
|
||||
ind('break;\n')
|
||||
i -= 1
|
||||
else:
|
||||
ind('return NULL;\n')
|
||||
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
|
||||
ind('}')
|
||||
|
||||
43
kitty/khrplatform.h
generated
43
kitty/khrplatform.h
generated
@@ -1,8 +1,7 @@
|
||||
#ifndef __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
|
||||
** 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
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* 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
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by sending them to the public Khronos Bugzilla
|
||||
* (http://khronos.org/bugzilla) by filing a bug against product
|
||||
* "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.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
@@ -91,7 +87,6 @@
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
@@ -106,7 +101,6 @@
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
@@ -119,7 +113,6 @@
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
@@ -130,13 +123,10 @@
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
@@ -147,9 +137,7 @@ typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
@@ -160,9 +148,7 @@ typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
@@ -172,9 +158,7 @@ typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
@@ -189,9 +173,7 @@ typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
@@ -199,9 +181,7 @@ typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
@@ -212,10 +192,7 @@ typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* 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 signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* 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 unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
@@ -261,14 +235,12 @@ typedef float khronos_float_t;
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
@@ -280,5 +252,4 @@ typedef enum {
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
|
||||
590
kitty/kitty.conf
590
kitty/kitty.conf
@@ -1,5 +1,11 @@
|
||||
# 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
|
||||
# bold/italic/bold-italic variants. By default they are derived automatically,
|
||||
# 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
|
||||
# kitty list-fonts
|
||||
# The default values shown below rely on your OS to choose an appropriate monospace font family.
|
||||
font_family monospace
|
||||
italic_font auto
|
||||
bold_font auto
|
||||
@@ -32,12 +39,200 @@ font_size_delta 2
|
||||
adjust_line_height 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
|
||||
# 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,
|
||||
# and very thick lines;
|
||||
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
|
||||
foreground #dddddd
|
||||
|
||||
@@ -62,155 +257,9 @@ selection_foreground #000000
|
||||
# The background for selections
|
||||
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
|
||||
# bright version.
|
||||
# bright version. You can also set the remaining colors from the 256 color table
|
||||
# as color16 to color256.
|
||||
|
||||
# black
|
||||
color0 #000000
|
||||
@@ -243,11 +292,49 @@ color14 #14ffff
|
||||
# white
|
||||
color7 #dddddd
|
||||
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 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
|
||||
# assigned in the default configuration.
|
||||
@@ -256,94 +343,140 @@ color15 #ffffff
|
||||
# syntax below:
|
||||
# map key combine <separator> action1 <separator> action2 <separator> action3 ...
|
||||
# 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
|
||||
#
|
||||
# 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
|
||||
map ctrl+shift+v paste_from_clipboard
|
||||
map ctrl+shift+s paste_from_selection
|
||||
map ctrl+shift+c copy_to_clipboard
|
||||
# Clipboard {{{
|
||||
map kitty_mod+v paste_from_clipboard
|
||||
map kitty_mod+s paste_from_selection
|
||||
map kitty_mod+c copy_to_clipboard
|
||||
map shift+insert paste_from_selection
|
||||
# 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
|
||||
# you can specify your own, for example:
|
||||
# map ctrl+shift+o pass_selection_to_program firefox
|
||||
map ctrl+shift+o pass_selection_to_program
|
||||
# map kitty_mod+o pass_selection_to_program firefox
|
||||
map kitty_mod+o pass_selection_to_program
|
||||
# }}}
|
||||
|
||||
# Scrolling
|
||||
map ctrl+shift+up scroll_line_up
|
||||
map ctrl+shift+down scroll_line_down
|
||||
map ctrl+shift+k scroll_line_up
|
||||
map ctrl+shift+j scroll_line_down
|
||||
map ctrl+shift+page_up scroll_page_up
|
||||
map ctrl+shift+page_down scroll_page_down
|
||||
map ctrl+shift+home scroll_home
|
||||
map ctrl+shift+end scroll_end
|
||||
map ctrl+shift+h show_scrollback
|
||||
# Scrolling {{{
|
||||
map kitty_mod+up scroll_line_up
|
||||
map kitty_mod+down scroll_line_down
|
||||
map kitty_mod+k scroll_line_up
|
||||
map kitty_mod+j scroll_line_down
|
||||
map kitty_mod+page_up scroll_page_up
|
||||
map kitty_mod+page_down scroll_page_down
|
||||
map kitty_mod+home scroll_home
|
||||
map kitty_mod+end scroll_end
|
||||
map kitty_mod+h show_scrollback
|
||||
# }}}
|
||||
|
||||
# Window management
|
||||
map ctrl+shift+enter new_window
|
||||
map ctrl+shift+n new_os_window
|
||||
map ctrl+shift+w close_window
|
||||
map ctrl+shift+] next_window
|
||||
map ctrl+shift+[ previous_window
|
||||
map ctrl+shift+f move_window_forward
|
||||
map ctrl+shift+b move_window_backward
|
||||
map ctrl+shift+` move_window_to_top
|
||||
map ctrl+shift+1 first_window
|
||||
map ctrl+shift+2 second_window
|
||||
map ctrl+shift+3 third_window
|
||||
map ctrl+shift+4 fourth_window
|
||||
map ctrl+shift+5 fifth_window
|
||||
map ctrl+shift+6 sixth_window
|
||||
map ctrl+shift+7 seventh_window
|
||||
map ctrl+shift+8 eighth_window
|
||||
map ctrl+shift+9 ninth_window
|
||||
map ctrl+shift+0 tenth_window
|
||||
# Window management {{{
|
||||
map kitty_mod+enter new_window
|
||||
map kitty_mod+n new_os_window
|
||||
map kitty_mod+w close_window
|
||||
map kitty_mod+] next_window
|
||||
map kitty_mod+[ previous_window
|
||||
map kitty_mod+f move_window_forward
|
||||
map kitty_mod+b move_window_backward
|
||||
map kitty_mod+` move_window_to_top
|
||||
map kitty_mod+1 first_window
|
||||
map kitty_mod+2 second_window
|
||||
map kitty_mod+3 third_window
|
||||
map kitty_mod+4 fourth_window
|
||||
map kitty_mod+5 fifth_window
|
||||
map kitty_mod+6 sixth_window
|
||||
map kitty_mod+7 seventh_window
|
||||
map kitty_mod+8 eighth_window
|
||||
map kitty_mod+9 ninth_window
|
||||
map kitty_mod+0 tenth_window
|
||||
# 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
|
||||
# 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
|
||||
# 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 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
|
||||
# working directory of the current window using
|
||||
# map ctrl+alt+enter new_window_with_cwd
|
||||
# }}}
|
||||
|
||||
|
||||
# Tab management
|
||||
map ctrl+shift+right next_tab
|
||||
map ctrl+shift+left previous_tab
|
||||
map ctrl+shift+t new_tab
|
||||
map ctrl+shift+q close_tab
|
||||
map ctrl+shift+l next_layout
|
||||
map ctrl+shift+. move_tab_forward
|
||||
map ctrl+shift+, move_tab_backward
|
||||
# Tab management {{{
|
||||
map kitty_mod+right next_tab
|
||||
map kitty_mod+left previous_tab
|
||||
map kitty_mod+t new_tab
|
||||
map kitty_mod+q close_tab
|
||||
map kitty_mod+l next_layout
|
||||
map kitty_mod+. move_tab_forward
|
||||
map kitty_mod+, 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
|
||||
# map ctrl+alt+1 goto_tab 1
|
||||
# map ctrl+alt+2 goto_tab 2
|
||||
|
||||
# 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.
|
||||
# }}}
|
||||
|
||||
# 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
|
||||
map ctrl+shift+equal increase_font_size
|
||||
map ctrl+shift+minus decrease_font_size
|
||||
map ctrl+shift+backspace restore_font_size
|
||||
map ctrl+shift+f11 toggle_fullscreen
|
||||
map ctrl+shift+u input_unicode_character
|
||||
# Open a currently visible URL using the keyboard. The program used ot open the URL is specified in open_url_with.
|
||||
# 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://[^ ]+"
|
||||
map ctrl+shift+e run_simple_kitten text url_hints
|
||||
# Font sizes {{{
|
||||
map kitty_mod+equal increase_font_size
|
||||
map kitty_mod+minus decrease_font_size
|
||||
map kitty_mod+backspace restore_font_size
|
||||
# To setup shortcuts for specific font sizes, follow the example below:
|
||||
# map kitty_mod+f6 set_font_size 10.0
|
||||
# map kitty_mod+f7 set_font_size 20.5
|
||||
# }}}
|
||||
|
||||
# Select and act on visible text {{{
|
||||
# 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
|
||||
# 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):
|
||||
# map ctrl+alt+a send_text normal Word\x1b[H
|
||||
# map ctrl+alt+a send_text application Word\x1bOH
|
||||
# }}}
|
||||
|
||||
# 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
|
||||
# You can have kitty remove all shortcut definition seen upto this point. Useful for
|
||||
# instance, to remove the default shortcuts.
|
||||
clear_all_shortcuts no
|
||||
# }}}
|
||||
|
||||
|
||||
# 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
|
||||
# OS specific tweaks {{{
|
||||
|
||||
# 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
|
||||
# the default background color and finally you can use an arbitrary color, such
|
||||
# as #12af59 or "red".
|
||||
# the background color of the currently active window and finally you can use
|
||||
# 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
|
||||
|
||||
# Hide the kitty window's title bar on macOS.
|
||||
@@ -401,6 +523,6 @@ macos_hide_titlebar no
|
||||
# can use the macOS unicode input technique.
|
||||
macos_option_as_alt yes
|
||||
|
||||
# The number is a percentage of maximum volume.
|
||||
# See man XBell for details.
|
||||
x11_bell_volume 80
|
||||
# Hide the kitty window from running tasks (alt-tab) on macOS.
|
||||
macos_hide_from_tasks no
|
||||
# }}}
|
||||
|
||||
@@ -75,9 +75,11 @@ class Layout:
|
||||
# this layout, i.e. spaces that are not covered by any window
|
||||
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]
|
||||
w = windows[min(num, len(windows) - 1)]
|
||||
if not make_active:
|
||||
return w
|
||||
active_window_idx = idx_for_id(w.id, all_windows)
|
||||
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):
|
||||
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))
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ linebuf_mark_line_clean(LineBuf *self, index_type y) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
clear(LineBuf *self) {
|
||||
clear(LineBuf *self, PyObject *a UNUSED) {
|
||||
#define clear_doc "Clear all lines in this LineBuf"
|
||||
linebuf_clear(self, BLANK_CHAR);
|
||||
Py_RETURN_NONE;
|
||||
@@ -167,7 +167,7 @@ set_continued(LineBuf *self, PyObject *args) {
|
||||
}
|
||||
|
||||
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."
|
||||
PyObject *ans = PyList_New(0);
|
||||
for (index_type i = 0; i < self->ynum; i++) {
|
||||
@@ -424,7 +424,7 @@ __str__(LineBuf *self) {
|
||||
if (lines == NULL) return PyErr_NoMemory();
|
||||
for (index_type i = 0; i < self->ynum; 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; }
|
||||
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);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
as_unicode(Line* self) {
|
||||
PyObject *
|
||||
line_as_unicode(Line* self) {
|
||||
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*
|
||||
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"
|
||||
static Py_UCS4 t[5120] = {0};
|
||||
index_type num = line_as_ansi(self, t, 5120);
|
||||
@@ -272,7 +272,7 @@ as_ansi(Line* self) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
is_continued(Line* self) {
|
||||
is_continued(Line* self, PyObject *a UNUSED) {
|
||||
#define is_continued_doc "Return the line's continued flag"
|
||||
PyObject *ans = self->continued ? Py_True : Py_False;
|
||||
Py_INCREF(ans);
|
||||
@@ -281,7 +281,7 @@ is_continued(Line* self) {
|
||||
|
||||
static PyObject*
|
||||
__repr__(Line* self) {
|
||||
PyObject *s = as_unicode(self);
|
||||
PyObject *s = line_as_unicode(self);
|
||||
if (s == NULL) return NULL;
|
||||
PyObject *ans = PyObject_Repr(s);
|
||||
Py_CLEAR(s);
|
||||
@@ -570,7 +570,7 @@ PyTypeObject Line_Type = {
|
||||
.tp_basicsize = sizeof(Line),
|
||||
.tp_dealloc = (destructor)dealloc,
|
||||
.tp_repr = (reprfunc)__repr__,
|
||||
.tp_str = (reprfunc)as_unicode,
|
||||
.tp_str = (reprfunc)line_as_unicode,
|
||||
.tp_as_sequence = &sequence_methods,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.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