Compare commits
214 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
458f10be10 | ||
|
|
c1667d9fc1 | ||
|
|
52f4e81d59 | ||
|
|
1056ab1b81 | ||
|
|
3f9d04b616 | ||
|
|
d9cc065c99 | ||
|
|
612fb3b596 | ||
|
|
532d51c411 | ||
|
|
5630c0e434 | ||
|
|
5ba8ca2745 | ||
|
|
e9ffb8d185 | ||
|
|
339a2e0800 | ||
|
|
98c04fb7da | ||
|
|
c95d1393cc | ||
|
|
1779cdd353 | ||
|
|
6a090ac740 | ||
|
|
3c7236557c | ||
|
|
83a3d99824 | ||
|
|
808750a76a | ||
|
|
32caea4197 | ||
|
|
038248f55d | ||
|
|
21b7bf6009 | ||
|
|
9dc571d8a9 | ||
|
|
805baad9cd | ||
|
|
cc910f6b79 | ||
|
|
56dd442e2c | ||
|
|
3192b61385 | ||
|
|
eef3a215a1 | ||
|
|
cb7fff6986 | ||
|
|
75b5130d96 | ||
|
|
2cf8c6aea7 | ||
|
|
5755ba72b1 | ||
|
|
785937ecfc | ||
|
|
955ead05be | ||
|
|
3830261384 | ||
|
|
662ab0df45 | ||
|
|
1ce50fb6f0 | ||
|
|
74dcf8800d | ||
|
|
e90df0ca95 | ||
|
|
49c375ae0f | ||
|
|
6c4d1a4951 | ||
|
|
f99e368831 | ||
|
|
8468ad60d5 | ||
|
|
a1ce29aab9 | ||
|
|
20b5084904 | ||
|
|
2fde0ea86b | ||
|
|
b64bceac7f | ||
|
|
a97174a350 | ||
|
|
6141c9fdda | ||
|
|
a32e010f89 | ||
|
|
4847fb52cc | ||
|
|
58e33e2512 | ||
|
|
584b59c7a1 | ||
|
|
2e657f0020 | ||
|
|
ae112294c8 | ||
|
|
8b4c7ea23f | ||
|
|
83e225a50f | ||
|
|
6256b9027a | ||
|
|
1fd84612a8 | ||
|
|
2efa83bc4d | ||
|
|
fa064bf9cf | ||
|
|
325d2bc14e | ||
|
|
90a4b4f859 | ||
|
|
ce0db16479 | ||
|
|
43ce3ce4b0 | ||
|
|
910cebeeb5 | ||
|
|
1c78633d1a | ||
|
|
530fd61125 | ||
|
|
9edfafcac2 | ||
|
|
677c47b9dd | ||
|
|
b59d7dda11 | ||
|
|
7a3534baf8 | ||
|
|
3bfcadcd81 | ||
|
|
529bb5c6a0 | ||
|
|
caf9a12b34 | ||
|
|
da8e2d3d4a | ||
|
|
582500d3e9 | ||
|
|
7926a140b8 | ||
|
|
023487a1d7 | ||
|
|
71f84b1023 | ||
|
|
ce85382c68 | ||
|
|
94d248d812 | ||
|
|
62db7639a8 | ||
|
|
80f3fe6e9f | ||
|
|
9ba2c4b397 | ||
|
|
bfa53d4b45 | ||
|
|
69f3ceb903 | ||
|
|
d0f839bbd8 | ||
|
|
48538c0e25 | ||
|
|
02ab22f08c | ||
|
|
b3b216a6dc | ||
|
|
431564c8bc | ||
|
|
57f1bbb71a | ||
|
|
faac8649e4 | ||
|
|
1e6a41186f | ||
|
|
eddee67130 | ||
|
|
9b834014e9 | ||
|
|
a467e79144 | ||
|
|
90cc785b55 | ||
|
|
09710425ab | ||
|
|
dd36727adf | ||
|
|
47b665a624 | ||
|
|
7011329761 | ||
|
|
ea24c23fde | ||
|
|
be8f2b8106 | ||
|
|
71db024a3b | ||
|
|
3790af6897 | ||
|
|
dc6ef63ddd | ||
|
|
ef762f7a2b | ||
|
|
4ed646e062 | ||
|
|
afbd843388 | ||
|
|
18c3598ef2 | ||
|
|
1c2623e609 | ||
|
|
e232863028 | ||
|
|
a89309153d | ||
|
|
fee7c8e573 | ||
|
|
1a52127c55 | ||
|
|
4920431c3a | ||
|
|
773f26b61d | ||
|
|
16e77d7329 | ||
|
|
2cec0908a5 | ||
|
|
b3ef62c188 | ||
|
|
3385f6fe67 | ||
|
|
0170bf636b | ||
|
|
b2561fd61e | ||
|
|
64b48945d4 | ||
|
|
bbfd463357 | ||
|
|
2179dab8ed | ||
|
|
801ead7f7f | ||
|
|
9e4834d58b | ||
|
|
f38e089de0 | ||
|
|
b252504306 | ||
|
|
81bcdaf48a | ||
|
|
44f654ff86 | ||
|
|
84cfa9382f | ||
|
|
c6da374aa9 | ||
|
|
d9fe0b2540 | ||
|
|
5a905dfe7f | ||
|
|
09b6d6e62b | ||
|
|
6aa26cf75f | ||
|
|
bae8915881 | ||
|
|
9c46de319e | ||
|
|
994eecadb7 | ||
|
|
f7f9ceec34 | ||
|
|
527255e3a1 | ||
|
|
0333da991d | ||
|
|
2bf8e1b1a3 | ||
|
|
ffc717717c | ||
|
|
2c8414db5d | ||
|
|
32473d7dc7 | ||
|
|
079f10eebd | ||
|
|
ae1f305a81 | ||
|
|
0b1f5209d5 | ||
|
|
fc9980903d | ||
|
|
ad87131926 | ||
|
|
4dfe1b664f | ||
|
|
d7b2ac97b5 | ||
|
|
fd9dac4ca4 | ||
|
|
8ca9348b07 | ||
|
|
5cc18fdb4b | ||
|
|
51b6b325b9 | ||
|
|
b3cc6f3802 | ||
|
|
1295094715 | ||
|
|
af85f58e3d | ||
|
|
3b34821c86 | ||
|
|
eaea0b16b5 | ||
|
|
964ffadc8e | ||
|
|
1b84064e5f | ||
|
|
dd128a99ac | ||
|
|
bc829a9712 | ||
|
|
3459c55ebf | ||
|
|
a631d950e1 | ||
|
|
f35e2d76ad | ||
|
|
8db838da9f | ||
|
|
7254a880dc | ||
|
|
a0df5b970b | ||
|
|
54ed08c24e | ||
|
|
97e1ecabdc | ||
|
|
e716f6d35e | ||
|
|
c7506496da | ||
|
|
1e3d3df9b8 | ||
|
|
ed75ad0d41 | ||
|
|
fd6159c0b4 | ||
|
|
976c322b83 | ||
|
|
28c1935800 | ||
|
|
1edf1524f8 | ||
|
|
0dfe101c0f | ||
|
|
a571821f73 | ||
|
|
f8e6f87984 | ||
|
|
c7338bca6f | ||
|
|
15f07f57bf | ||
|
|
ec989a45b5 | ||
|
|
e8286c50b2 | ||
|
|
bfbbe3db14 | ||
|
|
6214a87522 | ||
|
|
743ff719c8 | ||
|
|
b491eacb9c | ||
|
|
5bfc89e13f | ||
|
|
52d2b7b09e | ||
|
|
19e59d9575 | ||
|
|
6fe4baf1de | ||
|
|
bc21ec7551 | ||
|
|
aad4a1fd6a | ||
|
|
721595ef04 | ||
|
|
139838c717 | ||
|
|
c93238d8c9 | ||
|
|
5ee1fbe07d | ||
|
|
ee1287211d | ||
|
|
94e8be6b1c | ||
|
|
053ba7f082 | ||
|
|
e8f3f00792 | ||
|
|
740a89b77f | ||
|
|
1fe936dbbc | ||
|
|
11e85ac703 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
|||||||
tags
|
tags
|
||||||
build
|
build
|
||||||
linux-package
|
linux-package
|
||||||
|
kitty.app
|
||||||
logo/*.iconset
|
logo/*.iconset
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
glad/out
|
glad/out
|
||||||
|
|||||||
14
.travis.yml
14
.travis.yml
@@ -20,8 +20,6 @@ matrix:
|
|||||||
- libxcursor-dev
|
- libxcursor-dev
|
||||||
- libunistring-dev
|
- libunistring-dev
|
||||||
- libxcb-xkb-dev
|
- libxcb-xkb-dev
|
||||||
- libxkbcommon-dev
|
|
||||||
- libxkbcommon-x11-dev
|
|
||||||
- libpng-dev
|
- libpng-dev
|
||||||
- python3-pil
|
- python3-pil
|
||||||
|
|
||||||
@@ -44,8 +42,6 @@ matrix:
|
|||||||
- libxcursor-dev
|
- libxcursor-dev
|
||||||
- libunistring-dev
|
- libunistring-dev
|
||||||
- libxcb-xkb-dev
|
- libxcb-xkb-dev
|
||||||
- libxkbcommon-dev
|
|
||||||
- libxkbcommon-x11-dev
|
|
||||||
- libpng-dev
|
- libpng-dev
|
||||||
- python3-pil
|
- python3-pil
|
||||||
|
|
||||||
@@ -68,8 +64,6 @@ matrix:
|
|||||||
- libxcursor-dev
|
- libxcursor-dev
|
||||||
- libunistring-dev
|
- libunistring-dev
|
||||||
- libxcb-xkb-dev
|
- libxcb-xkb-dev
|
||||||
- libxkbcommon-dev
|
|
||||||
- libxkbcommon-x11-dev
|
|
||||||
- libpng-dev
|
- libpng-dev
|
||||||
- python3-pil
|
- python3-pil
|
||||||
- clang
|
- clang
|
||||||
@@ -94,7 +88,7 @@ install: |
|
|||||||
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
|
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
|
||||||
if [[ "$USE_BREW" == "1" ]]; then
|
if [[ "$USE_BREW" == "1" ]]; then
|
||||||
brew update;
|
brew update;
|
||||||
brew install python3;
|
brew upgrade python;
|
||||||
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
|
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
|
||||||
else
|
else
|
||||||
mkdir -p $SW;
|
mkdir -p $SW;
|
||||||
@@ -102,6 +96,12 @@ install: |
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
pushd /tmp
|
pushd /tmp
|
||||||
|
wget http://xkbcommon.org/download/libxkbcommon-0.6.1.tar.xz
|
||||||
|
tar xf libxkbcommon-0.6.1.tar.xz
|
||||||
|
cd libxkbcommon-0.6.1
|
||||||
|
./configure --prefix=$HOME/harfbuzz --disable-dependency-tracking --disable-static
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
wget https://github.com/behdad/harfbuzz/releases/download/1.6.3/harfbuzz-1.6.3.tar.bz2
|
wget https://github.com/behdad/harfbuzz/releases/download/1.6.3/harfbuzz-1.6.3.tar.bz2
|
||||||
tar xf harfbuzz-1.6.3.tar.bz2
|
tar xf harfbuzz-1.6.3.tar.bz2
|
||||||
cd harfbuzz-1.6.3
|
cd harfbuzz-1.6.3
|
||||||
|
|||||||
133
CHANGELOG.rst
133
CHANGELOG.rst
@@ -3,6 +3,139 @@ Changelog
|
|||||||
|
|
||||||
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||||
|
|
||||||
|
version 0.9.0 [2018-04-15]
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
- A new kitty command shell to allow controlling kitty via commands. Press
|
||||||
|
`ctrl+shift+escape` to run the shell.
|
||||||
|
|
||||||
|
- The hints kitten has become much more powerful. Now in addition to URLs you
|
||||||
|
can use it to select word, paths, filenames, lines, etc. from the screen.
|
||||||
|
These can be inserted into the terminal, copied to clipboard or sent to
|
||||||
|
external programs.
|
||||||
|
|
||||||
|
- Linux: Switch to libxkbcommon for keyboard handling. It allows kitty to
|
||||||
|
support XCompose and dead keys and also react to keyboard remapping/layout
|
||||||
|
change without needing a restart.
|
||||||
|
|
||||||
|
- Add support for multiple-key-sequence shortcuts
|
||||||
|
|
||||||
|
- A new remote control command `set-colors` to change the current and/or
|
||||||
|
configured colors.
|
||||||
|
|
||||||
|
- When double-clicking to select a word, select words that continue onto the
|
||||||
|
next/prev line as well.
|
||||||
|
|
||||||
|
- Add an `include` directive for the config files to read multiple config files
|
||||||
|
|
||||||
|
- Improve mouse selection for windows with padding. Moving the mouse into the
|
||||||
|
padding area now acts as if the mouse is over the nearest cell.
|
||||||
|
|
||||||
|
- Allow setting all 256 terminal colors in the config file
|
||||||
|
|
||||||
|
- Fix using `kitty --single-instance` to open a new window in a running kitty
|
||||||
|
instance, not respecting the `--directory` flag
|
||||||
|
|
||||||
|
- URL hints: Exclude trailing punctuation from URLs
|
||||||
|
|
||||||
|
- URL hints: Launch the browser from the kitty parent process rather than the
|
||||||
|
hints kitten. Fixes launching on some systems where xdg-open doesn't like
|
||||||
|
being run from a kitten.
|
||||||
|
|
||||||
|
- Allow using rectangle select mode by pressing shift in addition to the
|
||||||
|
rectangle select modifiers even when the terminal program has grabbed the
|
||||||
|
mouse.
|
||||||
|
|
||||||
|
|
||||||
|
version 0.8.4 [2018-03-31]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Fix presence of XDG_CONFIG_DIRS and absence of XDG_CONFIG_HOME preventing
|
||||||
|
kitty from starting
|
||||||
|
|
||||||
|
- Revert change in last release to cell width calculation. Instead just clip
|
||||||
|
the right edges of characters that overflow the cell by at most two pixels
|
||||||
|
|
||||||
|
|
||||||
|
version 0.8.3 [2018-03-29]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Fix a regression that broke the visual bell and invert screen colors escape
|
||||||
|
code
|
||||||
|
|
||||||
|
- Allow double-click and triple-click + drag to extend selections word at a
|
||||||
|
time or line at a time
|
||||||
|
|
||||||
|
- Add a keyboard shortcut to set the tab title
|
||||||
|
|
||||||
|
- Fix setting window title to empty via OSC escape code not working correctly
|
||||||
|
|
||||||
|
- Linux: Fix cell width calculation incorrect for some fonts (cell widths are
|
||||||
|
now calculated by actually rendering bitmaps, which is slower but more
|
||||||
|
accurate)
|
||||||
|
|
||||||
|
- Allow specifying a system wide kitty config file, for all users
|
||||||
|
|
||||||
|
- Add a --debug-config command line flag to output data about the system and
|
||||||
|
kitty configuration.
|
||||||
|
|
||||||
|
- Wayland: Fix auto-repeat of keys not working
|
||||||
|
|
||||||
|
|
||||||
|
version 0.8.2 [2018-03-17]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Allow extending existing selections by right clicking
|
||||||
|
|
||||||
|
- Add a configurable keyboard shortcut and remote command to set the font size to a specific value
|
||||||
|
|
||||||
|
- Add an option to have kitty close the window when the main processes running in it exits, even if there are still background processes writing to that terminal
|
||||||
|
|
||||||
|
- Add configurable keyboard shortcuts to switch to a specific layout
|
||||||
|
|
||||||
|
- Add a keyboard shortcut to edit the kitty config file easily
|
||||||
|
|
||||||
|
- macOS: Fix restoring of window size not correct on Retina screens
|
||||||
|
|
||||||
|
- macOS: Add a facility to specify command line arguments when running kitty from the GUI
|
||||||
|
|
||||||
|
- Add a focus-tab remote command
|
||||||
|
|
||||||
|
- Fix screen not being refreshed immediately after moving a window.
|
||||||
|
|
||||||
|
- Fix a crash when getting the contents of the scrollback buffer as text
|
||||||
|
|
||||||
|
version 0.8.1 [2018-03-09]
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Extend kitty's remote control feature to work over both UNIX and TCP sockets,
|
||||||
|
so now you can control kitty from across the internet, if you want to.
|
||||||
|
|
||||||
|
- Render private use unicode characters that are followed by a space as a two
|
||||||
|
character ligature. This fixes rendering for applications that misuse
|
||||||
|
private-use characters to display square symbols.
|
||||||
|
|
||||||
|
- Fix Unicode emoji presentation variant selector causing new a fallback font
|
||||||
|
instance to be created
|
||||||
|
|
||||||
|
- Fix a rare error that prevented the Unicode input kitten from working
|
||||||
|
sometimes
|
||||||
|
|
||||||
|
- Allow using Ctrl+Alt+letter in legacy keyboard modes by outputting them as Ctrl+letter and Alt+letter.
|
||||||
|
This matches other terminals' behavior.
|
||||||
|
|
||||||
|
- Fix cursor position off-by-one on horizontal axis when resizing the terminal
|
||||||
|
|
||||||
|
- Wayland: Fix auto-repeat of keys not working
|
||||||
|
|
||||||
|
- Wayland: Add support for window decorations provided by the Wayland shell
|
||||||
|
|
||||||
|
- macOS: Fix URL hints not working
|
||||||
|
|
||||||
|
- macOS: Fix shell not starting in login mode on some computers
|
||||||
|
|
||||||
|
- macOS: Output errors into console.app when running as a bundle
|
||||||
|
|
||||||
|
|
||||||
version 0.8.0 [2018-02-24]
|
version 0.8.0 [2018-02-24]
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|||||||
25
CONTRIBUTING.md
Normal file
25
CONTRIBUTING.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
### Reporting bugs
|
||||||
|
|
||||||
|
Please first search existing bug reports (especially closed ones) for a report
|
||||||
|
that matches your issue.
|
||||||
|
|
||||||
|
When reporting a bug, provide full details of your environment, that means, at
|
||||||
|
a minimum, kitty version, OS and OS version, kitty config (ideally a minimal
|
||||||
|
config to reproduce the issue with).
|
||||||
|
|
||||||
|
|
||||||
|
### Contributing code
|
||||||
|
|
||||||
|
Install [the dependencies](https://github.com/kovidgoyal/kitty#dependencies)
|
||||||
|
using your favorite package manager. Build and run kitty [from
|
||||||
|
source](https://github.com/kovidgoyal/kitty#install-and-run-from-source).
|
||||||
|
|
||||||
|
Make a fork, submit your Pull Request. If it's a large/controversial change, open an issue
|
||||||
|
beforehand to discuss it, so that you don't waste your time making a pull
|
||||||
|
request that gets rejected.
|
||||||
|
|
||||||
|
If the code you are submitting is reasonably easily testable, please contribute
|
||||||
|
tests as well (see the `kitty_tests/` sub-directory for existing tests, which
|
||||||
|
can be run with `./test.py`).
|
||||||
|
|
||||||
|
That's it.
|
||||||
2
Makefile
2
Makefile
@@ -31,4 +31,4 @@ logo/kitty.iconset/icon_256x256.png: logo/kitty.svg logo/make.py
|
|||||||
rendered_logo: logo/kitty.iconset/icon_256x256.png
|
rendered_logo: logo/kitty.iconset/icon_256x256.png
|
||||||
|
|
||||||
app: rendered_logo
|
app: rendered_logo
|
||||||
python3 setup.py app $(VVAL)
|
python3 setup.py kitty.app $(VVAL)
|
||||||
|
|||||||
132
README.asciidoc
132
README.asciidoc
@@ -6,12 +6,14 @@
|
|||||||
:sc_close_window: pass:quotes[`ctrl+shift+w`]
|
:sc_close_window: pass:quotes[`ctrl+shift+w`]
|
||||||
:sc_copy_to_clipboard: pass:quotes[`ctrl+shift+c`]
|
:sc_copy_to_clipboard: pass:quotes[`ctrl+shift+c`]
|
||||||
:sc_decrease_font_size: pass:quotes[`ctrl+shift+minus`]
|
:sc_decrease_font_size: pass:quotes[`ctrl+shift+minus`]
|
||||||
|
:sc_edit_config_file: pass:quotes[`ctrl+shift+f2`]
|
||||||
:sc_eighth_window: pass:quotes[`ctrl+shift+8`]
|
:sc_eighth_window: pass:quotes[`ctrl+shift+8`]
|
||||||
:sc_fifth_window: pass:quotes[`ctrl+shift+5`]
|
:sc_fifth_window: pass:quotes[`ctrl+shift+5`]
|
||||||
:sc_first_window: pass:quotes[`ctrl+shift+1`]
|
:sc_first_window: pass:quotes[`ctrl+shift+1`]
|
||||||
:sc_fourth_window: pass:quotes[`ctrl+shift+4`]
|
:sc_fourth_window: pass:quotes[`ctrl+shift+4`]
|
||||||
:sc_increase_font_size: pass:quotes[`ctrl+shift+equal`]
|
:sc_increase_font_size: pass:quotes[`ctrl+shift+equal`]
|
||||||
:sc_input_unicode_character: pass:quotes[`ctrl+shift+u`]
|
:sc_input_unicode_character: pass:quotes[`ctrl+shift+u`]
|
||||||
|
:sc_kitty_shell_window: pass:quotes[`ctrl+shift+escape`]
|
||||||
:sc_move_tab_backward: pass:quotes[`ctrl+shift+,`]
|
:sc_move_tab_backward: pass:quotes[`ctrl+shift+,`]
|
||||||
:sc_move_tab_forward: pass:quotes[`ctrl+shift+.`]
|
:sc_move_tab_forward: pass:quotes[`ctrl+shift+.`]
|
||||||
:sc_move_window_backward: pass:quotes[`ctrl+shift+b`]
|
:sc_move_window_backward: pass:quotes[`ctrl+shift+b`]
|
||||||
@@ -30,7 +32,11 @@
|
|||||||
:sc_previous_tab: pass:quotes[`ctrl+shift+left`]
|
:sc_previous_tab: pass:quotes[`ctrl+shift+left`]
|
||||||
:sc_previous_window: pass:quotes[`ctrl+shift+[`]
|
:sc_previous_window: pass:quotes[`ctrl+shift+[`]
|
||||||
:sc_restore_font_size: pass:quotes[`ctrl+shift+backspace`]
|
:sc_restore_font_size: pass:quotes[`ctrl+shift+backspace`]
|
||||||
:sc_run_simple_kitten_text_url_hints: pass:quotes[`ctrl+shift+e`]
|
:sc_run_kitten_text_hints: pass:quotes[`ctrl+shift+e`]
|
||||||
|
:sc_run_kitten_text_hints_type_line_program: pass:quotes[`ctrl+shift+p → l`]
|
||||||
|
:sc_run_kitten_text_hints_type_path: pass:quotes[`ctrl+shift+p → shift+f`]
|
||||||
|
:sc_run_kitten_text_hints_type_path_program: pass:quotes[`ctrl+shift+p → f`]
|
||||||
|
:sc_run_kitten_text_hints_type_word_program: pass:quotes[`ctrl+shift+p → w`]
|
||||||
:sc_scroll_end: pass:quotes[`ctrl+shift+end`]
|
:sc_scroll_end: pass:quotes[`ctrl+shift+end`]
|
||||||
:sc_scroll_home: pass:quotes[`ctrl+shift+home`]
|
:sc_scroll_home: pass:quotes[`ctrl+shift+home`]
|
||||||
:sc_scroll_line_down: pass:quotes[`ctrl+shift+down` or `ctrl+shift+j`]
|
:sc_scroll_line_down: pass:quotes[`ctrl+shift+down` or `ctrl+shift+j`]
|
||||||
@@ -38,6 +44,7 @@
|
|||||||
:sc_scroll_page_down: pass:quotes[`ctrl+shift+page_down`]
|
:sc_scroll_page_down: pass:quotes[`ctrl+shift+page_down`]
|
||||||
:sc_scroll_page_up: pass:quotes[`ctrl+shift+page_up`]
|
:sc_scroll_page_up: pass:quotes[`ctrl+shift+page_up`]
|
||||||
:sc_second_window: pass:quotes[`ctrl+shift+2`]
|
:sc_second_window: pass:quotes[`ctrl+shift+2`]
|
||||||
|
:sc_set_tab_title: pass:quotes[`ctrl+shift+alt+t`]
|
||||||
:sc_seventh_window: pass:quotes[`ctrl+shift+7`]
|
:sc_seventh_window: pass:quotes[`ctrl+shift+7`]
|
||||||
:sc_show_scrollback: pass:quotes[`ctrl+shift+h`]
|
:sc_show_scrollback: pass:quotes[`ctrl+shift+h`]
|
||||||
:sc_sixth_window: pass:quotes[`ctrl+shift+6`]
|
:sc_sixth_window: pass:quotes[`ctrl+shift+6`]
|
||||||
@@ -66,7 +73,7 @@ link:#layouts[layouts] without needing to use an extra program like tmux
|
|||||||
* Can be link:remote-control.asciidoc[controlled from scripts or the shell prompt], even over SSH.
|
* Can be link:remote-control.asciidoc[controlled from scripts or the shell prompt], even over SSH.
|
||||||
|
|
||||||
* Has a framework for _kittens_, small terminal programs that can be used to extend kitty's functionality.
|
* Has a framework for _kittens_, small terminal programs that can be used to extend kitty's functionality.
|
||||||
For example, they are used for link:#unicode-input[Unicode input] and link:#url-hints[URL hints].
|
For example, they are used for link:#unicode-input[Unicode input] and link:#url-hints[Hints].
|
||||||
|
|
||||||
* Supports link:#startup-sessions[startup sessions] which allow you to specify the window/tab layout,
|
* Supports link:#startup-sessions[startup sessions] which allow you to specify the window/tab layout,
|
||||||
working directories and programs to run on startup.
|
working directories and programs to run on startup.
|
||||||
@@ -101,7 +108,7 @@ the following dependencies are installed first.
|
|||||||
* ImageMagick (optional, needed to use the `kitty icat` tool to display images in the terminal)
|
* ImageMagick (optional, needed to use the `kitty icat` tool to display images in the terminal)
|
||||||
* gcc or clang (required only for building)
|
* gcc or clang (required only for building)
|
||||||
* pkg-config (required only for building)
|
* pkg-config (required only for building)
|
||||||
* For building on Linux in addition to the above dependencies you might also need to install the `-dev` packages for `xcursor`, `xrandr`, `xinerama` and `xkbcommon-x11`, if they are not already installed by your distro.
|
* For building on Linux in addition to the above dependencies you might also need to install the `-dev` packages for `xcursor`, `xrandr`, `xinerama`, `libgl1-mesa` and `xkbcommon-x11`, if they are not already installed by your distro.
|
||||||
|
|
||||||
=== Install and run from source
|
=== Install and run from source
|
||||||
|
|
||||||
@@ -131,6 +138,8 @@ python3 .
|
|||||||
|
|
||||||
* Gentoo: https://packages.gentoo.org/packages/x11-terms/kitty
|
* Gentoo: https://packages.gentoo.org/packages/x11-terms/kitty
|
||||||
|
|
||||||
|
* openSUSE: https://build.opensuse.org/package/show/X11:terminals/kitty
|
||||||
|
|
||||||
=== macOS packages
|
=== macOS packages
|
||||||
|
|
||||||
kitty is available as a macOS `dmg` file for easy installation from the
|
kitty is available as a macOS `dmg` file for easy installation from the
|
||||||
@@ -199,6 +208,7 @@ windows are:
|
|||||||
|Next layout | {sc_next_layout}
|
|Next layout | {sc_next_layout}
|
||||||
|Move tab forward | {sc_move_tab_forward}
|
|Move tab forward | {sc_move_tab_forward}
|
||||||
|Move tab backward | {sc_move_tab_backward}
|
|Move tab backward | {sc_move_tab_backward}
|
||||||
|
|Set tab title | {sc_set_tab_title}
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|
|
||||||
@@ -234,6 +244,8 @@ windows are:
|
|||||||
|Input unicode character | {sc_input_unicode_character}
|
|Input unicode character | {sc_input_unicode_character}
|
||||||
|Click URL using the keyboard | {sc_run_simple_kitten_text_url_hints}
|
|Click URL using the keyboard | {sc_run_simple_kitten_text_url_hints}
|
||||||
|Pass current selection to program | {sc_pass_selection_to_program}
|
|Pass current selection to program | {sc_pass_selection_to_program}
|
||||||
|
|Edit kitty config file | {sc_edit_config_file}
|
||||||
|
|Open a kitty shell | {sc_kitty_shell}
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@@ -241,10 +253,11 @@ windows are:
|
|||||||
|
|
||||||
You can control kitty from the command line/scripts by sending it messages.
|
You can control kitty from the command line/scripts by sending it messages.
|
||||||
You can tell kitty to open/close/rename tabs and windows. You can even send
|
You can tell kitty to open/close/rename tabs and windows. You can even send
|
||||||
arbitrary input/text to any specified window. Messages can be sent using
|
arbitrary input/text to any specified window. Messages can be sent using `kitty
|
||||||
`kitty @`. Note that you must set `allow_remote_control yes` in your
|
@`. Note that you must set `allow_remote_control yes` in your
|
||||||
kitty.conf to use this feature. It even works over SSH connections. This
|
link:kitty/kitty.conf[kitty.conf] to use this feature. It even works over SSH
|
||||||
feature is best illustrated with a link:remote-control.asciidoc[tutorial].
|
connections. This feature is best illustrated with a
|
||||||
|
link:remote-control.asciidoc[tutorial].
|
||||||
|
|
||||||
|
|
||||||
== The scrollback buffer
|
== The scrollback buffer
|
||||||
@@ -267,17 +280,38 @@ Press {sc_input_unicode_character} to start the unicode input widget, shown belo
|
|||||||
|
|
||||||
image::unicode.png?raw=true[Unicode input widget, showing selection of unicode characters by name]
|
image::unicode.png?raw=true[Unicode input widget, showing selection of unicode characters by name]
|
||||||
|
|
||||||
== URL hints
|
In Code mode, you enter a unicode character by typing in the hex code for the
|
||||||
|
character and pressing enter, for example, type in 2716 and press enter to get
|
||||||
|
✖. You can also choose a character from the list of recently used characters by
|
||||||
|
typing a leading period and then the two character index and pressing Enter.
|
||||||
|
|
||||||
kitty has a _hints mode_ to click URLs visible on the screen by using only the keyboard.
|
In Name mode you instead type words from the character name and use the arrow
|
||||||
Press {sc_run_simple_kitten_text_url_hints} to activate hints mode, as shown below.
|
keys/tab to select the character from the displayed matches. You can also type
|
||||||
|
a leading period and the index for the match if you dont like to use arrow
|
||||||
|
keys.
|
||||||
|
|
||||||
|
== Hints
|
||||||
|
|
||||||
|
kitty has a _hints mode_ to select and act on arbitrary text snippets currently
|
||||||
|
visible on the screen. For example, you can press {sc_run_kitten_text_hints}
|
||||||
|
to choose any URL visible on the screen and then open it using your system
|
||||||
|
browser.
|
||||||
|
|
||||||
image::hints_mode.png?raw=true[URL hints mode]
|
image::hints_mode.png?raw=true[URL hints mode]
|
||||||
|
|
||||||
|
Similarly, you can press {sc_run_kitten_text_hints_type_path_program} to
|
||||||
|
select anything that looks like a path or filename and then insert it into the
|
||||||
|
terminal, very useful for picking files from the output of a git or ls command and
|
||||||
|
adding them to the command line for the next command.
|
||||||
|
|
||||||
|
The hints kitten is very powerful to see more detailed help on its various
|
||||||
|
options and modes of operation, use: `kitty +kitten hints --help`.
|
||||||
|
|
||||||
== Miscellaneous features
|
== Miscellaneous features
|
||||||
|
|
||||||
You can double click to select a word and triple click to select a line. You can also
|
* You can also hold down `ctrl+shift` and click on a URL to open it in a browser.
|
||||||
hold down `ctrl+shift` and click on a URL to open it in a browser.
|
* You can double click to select a word and triple click to select a line.
|
||||||
|
* You can right click to extend a previous selection
|
||||||
|
|
||||||
|
|
||||||
== Layouts
|
== Layouts
|
||||||
@@ -290,7 +324,9 @@ Currently, there are five layouts available,
|
|||||||
* Horizontal -- All windows are shown side-by-side
|
* Horizontal -- All windows are shown side-by-side
|
||||||
* Vertical -- All windows are shown one below the other
|
* Vertical -- All windows are shown one below the other
|
||||||
|
|
||||||
You can switch between layouts using the {sc_next_layout} key combination.
|
You can switch between layouts using the {sc_next_layout} key combination. You can
|
||||||
|
also create shortcuts to select particular layouts, and choose which layouts
|
||||||
|
you want to enable/disable, see link:kitty/kitty.conf[kitty.conf] for examples.
|
||||||
|
|
||||||
Writing a new layout only requires about fifty lines of code, so if there is
|
Writing a new layout only requires about fifty lines of code, so if there is
|
||||||
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
|
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
|
||||||
@@ -299,13 +335,18 @@ a pull request!
|
|||||||
|
|
||||||
== Configuration
|
== Configuration
|
||||||
|
|
||||||
kitty is highly customizable, everything from keyboard shortcuts, to
|
kitty is highly customizable, everything from keyboard shortcuts, to painting
|
||||||
painting frames-per-second. See the heavily commented
|
frames-per-second. See the heavily commented link:kitty/kitty.conf[default
|
||||||
link:kitty/kitty.conf[default config file]. By default kitty looks for a
|
config file] for an overview of all customization possibilities.
|
||||||
config file in the OS config directory (usually
|
|
||||||
`~/.config/kitty/kitty.conf` on Linux and
|
By default kitty looks for a config file in the OS config directories (usually
|
||||||
|
`~/.config/kitty/kitty.conf` and additionally
|
||||||
`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a specific
|
`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a specific
|
||||||
path via the `--config` option.
|
path via the `--config` option or use the `KITTY_CONFIG_DIRECTORY` environment
|
||||||
|
variable. See the help for the `--config` option in `kitty --help` for full
|
||||||
|
details. You can also dump the current configuration using the `--debug-config`
|
||||||
|
option.
|
||||||
|
|
||||||
|
|
||||||
== Startup Sessions
|
== Startup Sessions
|
||||||
|
|
||||||
@@ -422,6 +463,8 @@ installs the terminfo file and `kitty` that installs the main program.
|
|||||||
This allows users to install the terminfo file on servers into which they ssh,
|
This allows users to install the terminfo file on servers into which they ssh,
|
||||||
without needing to install all of kitty.
|
without needing to install all of kitty.
|
||||||
|
|
||||||
|
You also need `tic` to compile the terminfo files, it is usually found in the development package of `ncurses`
|
||||||
|
|
||||||
This applies to creating packages for kitty for macOS package managers such as
|
This applies to creating packages for kitty for macOS package managers such as
|
||||||
brew or MacPorts as well.
|
brew or MacPorts as well.
|
||||||
|
|
||||||
@@ -439,18 +482,16 @@ let the character overflow into neighboring cells, which is fine if the
|
|||||||
neighboring cell is empty, but looks terrible if it is not.
|
neighboring cell is empty, but looks terrible if it is not.
|
||||||
|
|
||||||
Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc.
|
Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc.
|
||||||
use unicode characters from the private use area to represent symbols. Often
|
misuse unicode characters from the private use area to represent symbols. Often
|
||||||
these symbols are square and should be rendered in two cells. However, since
|
these symbols are square and should be rendered in two cells. However, since
|
||||||
private use area symbols all have their width set to one in the unicode
|
private use area symbols all have their width set to one in the unicode
|
||||||
standard, kitty renders them either smaller or truncated. The correct solution
|
standard, kitty renders them either smaller or truncated. The exception is if
|
||||||
for this is to use either use different symbols that are not square, or to use
|
these characters are followed by a space or empty cell in which case kitty
|
||||||
a font that defines ligatures with the space character for these symbols. See
|
makes use of the extra cell to render them in two cells.
|
||||||
link:https://github.com/kovidgoyal/kitty/issues/182[#182] for a discussion of
|
|
||||||
the approach using ligatures.
|
|
||||||
|
|
||||||
=== How do I build kitty.app on macOS?
|
=== How do I build kitty.app on macOS?
|
||||||
|
|
||||||
Install `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
|
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
|
||||||
|
|
||||||
And run:
|
And run:
|
||||||
```
|
```
|
||||||
@@ -481,19 +522,42 @@ let &t_ut=''
|
|||||||
See link:https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc#setting-text-styles-colors-in-arbitrary-regions-of-the-screen[here]
|
See link:https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc#setting-text-styles-colors-in-arbitrary-regions-of-the-screen[here]
|
||||||
for why kitty does not support background color erase.
|
for why kitty does not support background color erase.
|
||||||
|
|
||||||
== I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
|
=== I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
|
||||||
|
|
||||||
This happens because the kitty terminfo files are not available on the server. Either install kitty on the server,
|
This happens because the kitty terminfo files are not available on the server.
|
||||||
or simply copy over the terminfo files, using:
|
Either install kitty on the server, or simply copy over the terminfo files,
|
||||||
|
using:
|
||||||
|
|
||||||
....
|
....
|
||||||
ssh myserver mkdir -p \~/.terminfo
|
infocmp xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
|
||||||
infocmp xterm-kitty | ssh myserver "cat > /tmp/kitty.terminfo"
|
|
||||||
ssh myserver tic -x -o \~/.terminfo /tmp/kitty.terminfo
|
|
||||||
....
|
....
|
||||||
|
|
||||||
Really, the correct solution for this is to convince the OpenSSH maintainers to have ssh do this
|
Really, the correct solution for this is to convince the OpenSSH maintainers to
|
||||||
automatically when connecting to a server.
|
have ssh do this automatically when connecting to a server, so that all
|
||||||
|
terminals work transparently.
|
||||||
|
|
||||||
|
|
||||||
|
=== How do I change the colors in a running kitty instance?
|
||||||
|
|
||||||
|
You can either use the
|
||||||
|
link:http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands[OSC
|
||||||
|
terminal escape codes] to set colors or you can enable link:remote-control.asciidoc[remote control]
|
||||||
|
for kitty and use `kitty @ set-colors --help`.
|
||||||
|
|
||||||
|
|
||||||
|
=== How do I specify command line options for kitty on macOS?
|
||||||
|
|
||||||
|
Apple does not want you to use command line options with GUI applications. To
|
||||||
|
workaround that limitation, kitty will read command line options from the file
|
||||||
|
`<kitty config dir>/macos-launch-services-cmdline` when it is launched
|
||||||
|
from the GUI, i.e. by clicking the kitty application icon or using `open -a kitty`.
|
||||||
|
Note that this file is *only read* when running via the GUI.
|
||||||
|
|
||||||
|
You can, of course, also run kitty from a terminal with command line options, using:
|
||||||
|
`/Applications/kitty.app/Contents/MacOS/kitty`.
|
||||||
|
|
||||||
|
And within kitty itself, you can always run kitty using just `kitty` as it
|
||||||
|
cleverly adds itself to the PATH.
|
||||||
|
|
||||||
== Resources on terminal behavior
|
== Resources on terminal behavior
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ def launch(args):
|
|||||||
runpy.run_path(args[1], run_name='__main__')
|
runpy.run_path(args[1], run_name='__main__')
|
||||||
|
|
||||||
|
|
||||||
|
def run_kitten(args):
|
||||||
|
kitten = args[1]
|
||||||
|
sys.argv = args[1:]
|
||||||
|
from kittens.runner import run_kitten
|
||||||
|
run_kitten(kitten)
|
||||||
|
|
||||||
|
|
||||||
def namespaced(args):
|
def namespaced(args):
|
||||||
func = namespaced_entry_points[args[1]]
|
func = namespaced_entry_points[args[1]]
|
||||||
func(args[1:])
|
func(args[1:])
|
||||||
@@ -42,6 +49,7 @@ entry_points = {
|
|||||||
'list-fonts': list_fonts,
|
'list-fonts': list_fonts,
|
||||||
'runpy': runpy,
|
'runpy': runpy,
|
||||||
'launch': launch,
|
'launch': launch,
|
||||||
|
'kitten': run_kitten,
|
||||||
|
|
||||||
'@': remote_control,
|
'@': remote_control,
|
||||||
'+': namespaced,
|
'+': namespaced,
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
cloc --exclude-list-file <(echo -e 'kitty/wcwidth-std.h\nkitty/glfw.c\nkitty/keys.h\nkitty/charsets.c\nkitty/unicode-data.c\nkitty/key_encoding.py\nkitty/rgb.py\nkitty/gl.h\nkitty/gl-wrapper.h\nkitty/gl-wrapper.c\nkitty/khrplatform.h\nkitty/glfw-wrapper.h\nkitty/glfw-wrapper.c\nkitty/emoji.h') kitty
|
cloc --exclude-list-file <(echo -e 'kitty/wcwidth-std.h\nkitty/glfw.c\nkitty/keys.h\nkitty/charsets.c\nkitty/unicode-data.c\nkitty/key_encoding.py\nkitty/rgb.py\nkitty/gl.h\nkitty/gl-wrapper.h\nkitty/gl-wrapper.c\nkitty/khrplatform.h\nkitty/glfw-wrapper.h\nkitty/glfw-wrapper.c\nkitty/emoji.h\nkittens/unicode_input/names.h') kitty kittens
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ static void changeToResourcesDirectory(void)
|
|||||||
static void createKeyTables(void)
|
static void createKeyTables(void)
|
||||||
{
|
{
|
||||||
int scancode;
|
int scancode;
|
||||||
|
_glfw.ns.debug_keyboard = getenv("GLFW_DEBUG_KEYBOARD") != NULL;
|
||||||
|
|
||||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||||
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
||||||
@@ -371,4 +372,3 @@ const char* _glfwPlatformGetVersionString(void)
|
|||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,10 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
|
const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
|
||||||
|
|||||||
3
glfw/cocoa_platform.h
vendored
3
glfw/cocoa_platform.h
vendored
@@ -114,6 +114,8 @@ typedef struct _GLFWlibraryNS
|
|||||||
id listener;
|
id listener;
|
||||||
|
|
||||||
char keyName[64];
|
char keyName[64];
|
||||||
|
char text[256];
|
||||||
|
GLFWbool debug_keyboard;
|
||||||
short int keycodes[256];
|
short int keycodes[256];
|
||||||
short int scancodes[GLFW_KEY_LAST + 1];
|
short int scancodes[GLFW_KEY_LAST + 1];
|
||||||
char* clipboardString;
|
char* clipboardString;
|
||||||
@@ -166,4 +168,3 @@ void _glfwInitTimerNS(void);
|
|||||||
void _glfwPollMonitorsNS(void);
|
void _glfwPollMonitorsNS(void);
|
||||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||||
|
|
||||||
|
|||||||
@@ -183,10 +183,112 @@ static int translateFlags(NSUInteger flags)
|
|||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define debug_key(...) if (_glfw.ns.debug_keyboard) NSLog(__VA_ARGS__)
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
format_mods(int mods) {
|
||||||
|
static char buf[128];
|
||||||
|
char *p = buf, *s;
|
||||||
|
#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, x)
|
||||||
|
pr("mods: ");
|
||||||
|
s = p;
|
||||||
|
if (mods & GLFW_MOD_CONTROL) pr("ctrl+");
|
||||||
|
if (mods & GLFW_MOD_ALT) pr("alt+");
|
||||||
|
if (mods & GLFW_MOD_SHIFT) pr("shift+");
|
||||||
|
if (mods & GLFW_MOD_SUPER) pr("super+");
|
||||||
|
if (mods & GLFW_MOD_CAPS_LOCK) pr("capslock+");
|
||||||
|
if (mods & GLFW_MOD_NUM_LOCK) pr("numlock+");
|
||||||
|
if (p == s) pr("none");
|
||||||
|
else p--;
|
||||||
|
pr(" ");
|
||||||
|
#undef pr
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
format_text(const char *src) {
|
||||||
|
static char buf[256];
|
||||||
|
char *p = buf;
|
||||||
|
if (!src[0]) return "<none>";
|
||||||
|
while (*src) {
|
||||||
|
p += snprintf(p, sizeof(buf) - (p - buf), "%x ", (unsigned char)*(src++));
|
||||||
|
}
|
||||||
|
if (p != buf) *(--p) = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
safe_name_for_scancode(unsigned int scancode) {
|
||||||
|
const char *ans = _glfwPlatformGetScancodeName(scancode);
|
||||||
|
if (!ans) return "<noname>";
|
||||||
|
if ((1 <= ans[0] && ans[0] <= 31) || ans[0] == 127) ans = "<cc>";
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Translates a macOS keycode to a GLFW keycode
|
// Translates a macOS keycode to a GLFW keycode
|
||||||
//
|
//
|
||||||
static int translateKey(unsigned int key)
|
static int translateKey(unsigned int key, GLFWbool apply_keymap)
|
||||||
{
|
{
|
||||||
|
if (apply_keymap) {
|
||||||
|
// Look for the effective key name after applying any keyboard layouts/mappings
|
||||||
|
const char *name = _glfwPlatformGetScancodeName(key);
|
||||||
|
if (name && name[1] == 0) {
|
||||||
|
// Single letter key name
|
||||||
|
switch(name[0]) {
|
||||||
|
#define K(ch, name) case ch: return GLFW_KEY_##name
|
||||||
|
K('A', A); K('a', A);
|
||||||
|
K('B', B); K('b', B);
|
||||||
|
K('C', C); K('c', C);
|
||||||
|
K('D', D); K('d', D);
|
||||||
|
K('E', E); K('e', E);
|
||||||
|
K('F', F); K('f', F);
|
||||||
|
K('G', G); K('g', G);
|
||||||
|
K('H', H); K('h', H);
|
||||||
|
K('I', I); K('i', I);
|
||||||
|
K('J', J); K('j', J);
|
||||||
|
K('K', K); K('k', K);
|
||||||
|
K('L', L); K('l', L);
|
||||||
|
K('M', M); K('m', M);
|
||||||
|
K('N', N); K('n', N);
|
||||||
|
K('O', O); K('o', O);
|
||||||
|
K('P', P); K('p', P);
|
||||||
|
K('Q', Q); K('q', Q);
|
||||||
|
K('R', R); K('r', R);
|
||||||
|
K('S', S); K('s', S);
|
||||||
|
K('T', T); K('t', T);
|
||||||
|
K('U', U); K('u', U);
|
||||||
|
K('V', V); K('v', V);
|
||||||
|
K('W', W); K('w', W);
|
||||||
|
K('X', X); K('x', X);
|
||||||
|
K('Y', Y); K('y', Y);
|
||||||
|
K('Z', Z); K('z', Z);
|
||||||
|
K('0', 0);
|
||||||
|
K('1', 1);
|
||||||
|
K('2', 2);
|
||||||
|
K('3', 3);
|
||||||
|
K('5', 5);
|
||||||
|
K('6', 6);
|
||||||
|
K('7', 7);
|
||||||
|
K('8', 8);
|
||||||
|
K('9', 9);
|
||||||
|
K('\'', APOSTROPHE);
|
||||||
|
K(',', COMMA);
|
||||||
|
K('.', PERIOD);
|
||||||
|
K('/', SLASH);
|
||||||
|
K('-', MINUS);
|
||||||
|
K('=', EQUAL);
|
||||||
|
K(';', SEMICOLON);
|
||||||
|
K('[', LEFT_BRACKET);
|
||||||
|
K(']', RIGHT_BRACKET);
|
||||||
|
K('`', GRAVE_ACCENT);
|
||||||
|
K('\\', BACKSLASH);
|
||||||
|
#undef K
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0]))
|
if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0]))
|
||||||
return GLFW_KEY_UNKNOWN;
|
return GLFW_KEY_UNKNOWN;
|
||||||
|
|
||||||
@@ -582,6 +684,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
window->ns.xscale = xscale;
|
window->ns.xscale = xscale;
|
||||||
window->ns.yscale = yscale;
|
window->ns.yscale = yscale;
|
||||||
_glfwInputWindowContentScale(window, xscale, yscale);
|
_glfwInputWindowContentScale(window, xscale, yscale);
|
||||||
|
|
||||||
|
if (window->ns.layer)
|
||||||
|
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,12 +721,17 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
|
|
||||||
- (void)keyDown:(NSEvent *)event
|
- (void)keyDown:(NSEvent *)event
|
||||||
{
|
{
|
||||||
const int key = translateKey([event keyCode]);
|
const unsigned int scancode = [event keyCode];
|
||||||
|
const int key = translateKey(scancode, GLFW_TRUE);
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
const int mods = translateFlags([event modifierFlags]);
|
||||||
|
_glfw.ns.text[0] = 0;
|
||||||
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
|
// this will call insertText with the text for this event, if any
|
||||||
|
|
||||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
|
if ((1 <= _glfw.ns.text[0] && _glfw.ns.text[0] <= 31) || (unsigned)_glfw.ns.text[0] == 127) _glfw.ns.text[0] = 0; // dont send text for ascii control codes
|
||||||
|
debug_key(@"scancode: 0x%x (%s) mods: %stext: %s glfw_key: %s\n",
|
||||||
|
scancode, safe_name_for_scancode(scancode), format_mods(mods),
|
||||||
|
format_text(_glfw.ns.text), _glfwGetKeyName(key));
|
||||||
|
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods, _glfw.ns.text, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)flagsChanged:(NSEvent *)event
|
- (void)flagsChanged:(NSEvent *)event
|
||||||
@@ -629,7 +739,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
int action;
|
int action;
|
||||||
const unsigned int modifierFlags =
|
const unsigned int modifierFlags =
|
||||||
[event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
[event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||||
const int key = translateKey([event keyCode]);
|
const int key = translateKey([event keyCode], GLFW_FALSE);
|
||||||
const int mods = translateFlags(modifierFlags);
|
const int mods = translateFlags(modifierFlags);
|
||||||
const NSUInteger keyFlag = translateKeyToModifierFlag(key);
|
const NSUInteger keyFlag = translateKeyToModifierFlag(key);
|
||||||
|
|
||||||
@@ -643,14 +753,14 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
else
|
else
|
||||||
action = GLFW_RELEASE;
|
action = GLFW_RELEASE;
|
||||||
|
|
||||||
_glfwInputKey(window, key, [event keyCode], action, mods);
|
_glfwInputKeyboard(window, key, [event keyCode], action, mods, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)keyUp:(NSEvent *)event
|
- (void)keyUp:(NSEvent *)event
|
||||||
{
|
{
|
||||||
const int key = translateKey([event keyCode]);
|
const int key = translateKey([event keyCode], GLFW_TRUE);
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
const int mods = translateFlags([event modifierFlags]);
|
||||||
_glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);
|
_glfwInputKeyboard(window, key, [event keyCode], GLFW_RELEASE, mods, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollWheel:(NSEvent *)event
|
- (void)scrollWheel:(NSEvent *)event
|
||||||
@@ -785,25 +895,12 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||||
{
|
{
|
||||||
NSString* characters;
|
NSString* characters;
|
||||||
NSEvent* event = [NSApp currentEvent];
|
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
|
||||||
const int plain = !(mods & GLFW_MOD_SUPER);
|
|
||||||
|
|
||||||
if ([string isKindOfClass:[NSAttributedString class]])
|
if ([string isKindOfClass:[NSAttributedString class]])
|
||||||
characters = [string string];
|
characters = [string string];
|
||||||
else
|
else
|
||||||
characters = (NSString*) string;
|
characters = (NSString*) string;
|
||||||
|
snprintf(_glfw.ns.text, sizeof(_glfw.ns.text), "%s", [characters UTF8String]);
|
||||||
NSUInteger i, length = [characters length];
|
_glfw.ns.text[sizeof(_glfw.ns.text) - 1] = 0;
|
||||||
|
|
||||||
for (i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
const unichar codepoint = [characters characterAtIndex:i];
|
|
||||||
if ((codepoint & 0xff00) == 0xf700)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_glfwInputChar(window, codepoint, mods, plain);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)doCommandBySelector:(SEL)selector
|
- (void)doCommandBySelector:(SEL)selector
|
||||||
@@ -1358,7 +1455,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
[NSApp requestUserAttention:NSInformationalRequest];
|
[NSApp requestUserAttention:NSInformationalRequest];
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
NSBeep();
|
NSBeep();
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
@@ -1534,6 +1631,9 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
|||||||
|
|
||||||
void _glfwPlatformPollEvents(void)
|
void _glfwPlatformPollEvents(void)
|
||||||
{
|
{
|
||||||
|
if (!initializeAppKit())
|
||||||
|
return;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
||||||
@@ -1868,6 +1968,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
|
||||||
[window->ns.view setWantsLayer:YES];
|
[window->ns.view setWantsLayer:YES];
|
||||||
|
|
||||||
memset(&sci, 0, sizeof(sci));
|
memset(&sci, 0, sizeof(sci));
|
||||||
@@ -1899,4 +2000,3 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
|
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
|
||||||
return window->ns.object;
|
return window->ns.object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
glfw/glfw.py
16
glfw/glfw.py
@@ -10,6 +10,10 @@ import sys
|
|||||||
|
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
is_macos = 'darwin' in _plat
|
is_macos = 'darwin' in _plat
|
||||||
|
is_freebsd = 'freebsd' in _plat
|
||||||
|
is_netbsd = 'netbsd' in _plat
|
||||||
|
is_dragonflybsd = 'dragonfly' in _plat
|
||||||
|
is_bsd = is_freebsd or is_netbsd or is_dragonflybsd
|
||||||
base = os.path.dirname(os.path.abspath(__file__))
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
@@ -42,6 +46,9 @@ def init_env(env, pkg_config, at_least_version, module='x11'):
|
|||||||
ans.sources = sinfo['common']['sources'] + sinfo[module]['sources']
|
ans.sources = sinfo['common']['sources'] + sinfo[module]['sources']
|
||||||
ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]
|
ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]
|
||||||
|
|
||||||
|
if module in ('x11', 'wayland'):
|
||||||
|
at_least_version('xkbcommon', 0, 5)
|
||||||
|
|
||||||
if module == 'x11':
|
if module == 'x11':
|
||||||
for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11'.split():
|
for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11'.split():
|
||||||
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
|
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
|
||||||
@@ -100,13 +107,14 @@ def collect_source_information():
|
|||||||
'common': dict(extract_sources('common')),
|
'common': dict(extract_sources('common')),
|
||||||
'wayland_protocols': wayland_protocols,
|
'wayland_protocols': wayland_protocols,
|
||||||
}
|
}
|
||||||
|
joystick = 'null' if is_bsd else 'linux'
|
||||||
for group in 'cocoa win32 x11 wayland osmesa'.split():
|
for group in 'cocoa win32 x11 wayland osmesa'.split():
|
||||||
m = re.search('_GLFW_' + group.upper(), raw)
|
m = re.search('_GLFW_' + group.upper(), raw)
|
||||||
ans[group] = dict(extract_sources('glfw', m.start()))
|
ans[group] = dict(extract_sources('glfw', m.start()))
|
||||||
if group == 'x11':
|
if group in ('x11', 'wayland'):
|
||||||
ans[group]['headers'].append('linux_joystick.h')
|
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
|
||||||
ans[group]['sources'].append('linux_joystick.c')
|
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
|
||||||
elif group == 'wayland':
|
if group == 'wayland':
|
||||||
ans[group]['protocols'] = p = []
|
ans[group]['protocols'] = p = []
|
||||||
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
|
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
|
||||||
p.append(m.group(1))
|
p.append(m.group(1))
|
||||||
|
|||||||
202
glfw/glfw3.h
vendored
202
glfw/glfw3.h
vendored
@@ -1375,9 +1375,15 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
|||||||
*/
|
*/
|
||||||
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
||||||
|
|
||||||
/*! @brief The function signature for keyboard key callbacks.
|
/*! @brief The function signature for key callbacks.
|
||||||
*
|
*
|
||||||
* This is the function signature for keyboard key callback functions.
|
* This is the function signature for key callback functions.
|
||||||
|
* The semantics of this function are that the key that is interacted with on the
|
||||||
|
* keyboard is reported, and the text, if any generated by the key is reported.
|
||||||
|
* So, for example, if on a US-ASCII keyboard the user presses Shift+= GLFW
|
||||||
|
* will report the text "+" and the key as GLFW_KEY_EQUAL. The reported key takes into
|
||||||
|
* account any current keyboard maps defined in the OS. So with a dvorak mapping, pressing
|
||||||
|
* the "s" key will generate text "o" and GLFW_KEY_O.
|
||||||
*
|
*
|
||||||
* @param[in] window The window that received the event.
|
* @param[in] window The window that received the event.
|
||||||
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
||||||
@@ -1385,56 +1391,22 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
|||||||
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
||||||
* held down.
|
* held down.
|
||||||
|
* @param[in] text UTF-8 encoded text generated by this key event or empty string.
|
||||||
|
* @param[in] reserved Reserved for future use.
|
||||||
|
*
|
||||||
|
* @note On X11/Wayland if a modifier other than the modifiers GLFW reports
|
||||||
|
* (ctrl/shift/alt/super) is used, GLFW will report the shifted key rather
|
||||||
|
* than the unshifted key. So for example, if ISO_Shift_Level_5 is used to
|
||||||
|
* convert the key A into UP GLFW will report the key as UP with no modifiers.
|
||||||
*
|
*
|
||||||
* @sa @ref input_key
|
* @sa @ref input_key
|
||||||
* @sa @ref glfwSetKeyCallback
|
* @sa @ref glfwSetKeyboardCallback
|
||||||
*
|
*
|
||||||
* @since Added in version 1.0.
|
* @since Added in version 4.0.
|
||||||
* @glfw3 Added window handle, scancode and modifier mask parameters.
|
|
||||||
*
|
*
|
||||||
* @ingroup input
|
* @ingroup input
|
||||||
*/
|
*/
|
||||||
typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int);
|
typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, int);
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character callback functions.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharCallback
|
|
||||||
*
|
|
||||||
* @since Added in version 2.4.
|
|
||||||
* @glfw3 Added window handle parameter.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character with modifiers
|
|
||||||
* callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character with modifiers callback
|
|
||||||
* functions. It is called for each input character, regardless of what
|
|
||||||
* modifier keys are held down.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
|
||||||
* held down.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharModsCallback
|
|
||||||
*
|
|
||||||
* @deprecated Scheduled for removal in version 4.0.
|
|
||||||
*
|
|
||||||
* @since Added in version 3.1.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for file drop callbacks.
|
/*! @brief The function signature for file drop callbacks.
|
||||||
*
|
*
|
||||||
@@ -2453,10 +2425,11 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
|
|||||||
* GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override
|
* GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override
|
||||||
* this.
|
* this.
|
||||||
*
|
*
|
||||||
* @remark @wayland The window frame is currently unimplemented, as if
|
* @remark @wayland The window frame is currently very simple, only allowing
|
||||||
* [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`.
|
* window resize or move. A compositor can still emit close, maximize or
|
||||||
* A compositor can still emit close, resize or maximize events, using for
|
* fullscreen events, using for example a keybind mechanism. Additionally,
|
||||||
* example a keybind mechanism.
|
* the wp_viewporter protocol is required for this feature, otherwise the
|
||||||
|
* window will not be decorated.
|
||||||
*
|
*
|
||||||
* @remark @wayland A full screen window will not attempt to change the mode,
|
* @remark @wayland A full screen window will not attempt to change the mode,
|
||||||
* no matter what the requested size or refresh rate.
|
* no matter what the requested size or refresh rate.
|
||||||
@@ -2898,10 +2871,6 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height)
|
|||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||||
* GLFW_PLATFORM_ERROR.
|
* GLFW_PLATFORM_ERROR.
|
||||||
*
|
*
|
||||||
* @remark @wayland The window frame is currently unimplemented, as if
|
|
||||||
* [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`,
|
|
||||||
* so the returned values will always be zero.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
* @thread_safety This function must only be called from the main thread.
|
||||||
*
|
*
|
||||||
* @sa @ref window_size
|
* @sa @ref window_size
|
||||||
@@ -3191,13 +3160,9 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
|
|||||||
/*! @brief Sounds an audible bell associated with the window
|
/*! @brief Sounds an audible bell associated with the window
|
||||||
*
|
*
|
||||||
* This function sounds an audible bell, on platforms where it is
|
* This function sounds an audible bell, on platforms where it is
|
||||||
* supported. Currently (macOS, Windows and X11).
|
* supported. Currently (macOS, Windows, X11 and Wayland).
|
||||||
*
|
*
|
||||||
* @param[in] window The window with which the bell is associated.
|
* @param[in] window The window with which the bell is associated.
|
||||||
* @param[in] param The meaning of this parameter is platform dependent. On
|
|
||||||
* X11 it corresponds to the percentage controlling bell volume (see man
|
|
||||||
* XBell). On Windows it is the type of sound to make, see the MSDN docs for
|
|
||||||
* MessageBeep. On macOS, it is ignored.
|
|
||||||
* @return GLFW_TRUE if the bell succeeded otherwise GLFW_FALSE
|
* @return GLFW_TRUE if the bell succeeded otherwise GLFW_FALSE
|
||||||
*
|
*
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||||
@@ -3212,7 +3177,7 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
|
|||||||
*
|
*
|
||||||
* @ingroup window
|
* @ingroup window
|
||||||
*/
|
*/
|
||||||
GLFWAPI int glfwWindowBell(GLFWwindow* window, int64_t param);
|
GLFWAPI int glfwWindowBell(GLFWwindow* window);
|
||||||
|
|
||||||
|
|
||||||
/*! @brief Returns the monitor that the window uses for full screen mode.
|
/*! @brief Returns the monitor that the window uses for full screen mode.
|
||||||
@@ -4239,121 +4204,11 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
|
|||||||
*/
|
*/
|
||||||
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
||||||
|
|
||||||
/*! @brief Sets the key callback.
|
/*! @brief Sets the callback for handling keyboard events.
|
||||||
*
|
|
||||||
* This function sets the key callback of the specified window, which is called
|
|
||||||
* when a key is pressed, repeated or released.
|
|
||||||
*
|
|
||||||
* The key functions deal with physical keys, with layout independent
|
|
||||||
* [key tokens](@ref keys) named after their values in the standard US keyboard
|
|
||||||
* layout. If you want to input text, use the
|
|
||||||
* [character callback](@ref glfwSetCharCallback) instead.
|
|
||||||
*
|
|
||||||
* When a window loses input focus, it will generate synthetic key release
|
|
||||||
* events for all pressed keys. You can tell these events from user-generated
|
|
||||||
* events by the fact that the synthetic ones are generated after the focus
|
|
||||||
* loss event has been processed, i.e. after the
|
|
||||||
* [window focus callback](@ref glfwSetWindowFocusCallback) has been called.
|
|
||||||
*
|
|
||||||
* The scancode of a key is specific to that platform or sometimes even to that
|
|
||||||
* machine. Scancodes are intended to allow users to bind keys that don't have
|
|
||||||
* a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their
|
|
||||||
* state is not saved and so it cannot be queried with @ref glfwGetKey.
|
|
||||||
*
|
|
||||||
* Sometimes GLFW needs to generate synthetic key events, in which case the
|
|
||||||
* scancode may be zero.
|
|
||||||
*
|
|
||||||
* @param[in] window The window whose callback to set.
|
|
||||||
* @param[in] cbfun The new key callback, or `NULL` to remove the currently
|
|
||||||
* set callback.
|
|
||||||
* @return The previously set callback, or `NULL` if no callback was set or the
|
|
||||||
* library had not been [initialized](@ref intro_init).
|
|
||||||
*
|
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
|
||||||
*
|
|
||||||
* @sa @ref input_key
|
|
||||||
*
|
|
||||||
* @since Added in version 1.0.
|
|
||||||
* @glfw3 Added window handle parameter and return value.
|
|
||||||
*
|
*
|
||||||
* @ingroup input
|
* @ingroup input
|
||||||
*/
|
*/
|
||||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun);
|
GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboardfun cbfun);
|
||||||
|
|
||||||
/*! @brief Sets the Unicode character callback.
|
|
||||||
*
|
|
||||||
* This function sets the character callback of the specified window, which is
|
|
||||||
* called when a Unicode character is input.
|
|
||||||
*
|
|
||||||
* The character callback is intended for Unicode text input. As it deals with
|
|
||||||
* characters, it is keyboard layout dependent, whereas the
|
|
||||||
* [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1
|
|
||||||
* to physical keys, as a key may produce zero, one or more characters. If you
|
|
||||||
* want to know whether a specific physical key was pressed or released, see
|
|
||||||
* the key callback instead.
|
|
||||||
*
|
|
||||||
* The character callback behaves as system text input normally does and will
|
|
||||||
* not be called if modifier keys are held down that would prevent normal text
|
|
||||||
* input on that platform, for example a Super (Command) key on macOS or Alt key
|
|
||||||
* on Windows. There is a
|
|
||||||
* [character with modifiers callback](@ref glfwSetCharModsCallback) that
|
|
||||||
* receives these events.
|
|
||||||
*
|
|
||||||
* @param[in] window The window whose callback to set.
|
|
||||||
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
|
|
||||||
* callback.
|
|
||||||
* @return The previously set callback, or `NULL` if no callback was set or the
|
|
||||||
* library had not been [initialized](@ref intro_init).
|
|
||||||
*
|
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
*
|
|
||||||
* @since Added in version 2.4.
|
|
||||||
* @glfw3 Added window handle parameter and return value.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun);
|
|
||||||
|
|
||||||
/*! @brief Sets the Unicode character with modifiers callback.
|
|
||||||
*
|
|
||||||
* This function sets the character with modifiers callback of the specified
|
|
||||||
* window, which is called when a Unicode character is input regardless of what
|
|
||||||
* modifier keys are used.
|
|
||||||
*
|
|
||||||
* The character with modifiers callback is intended for implementing custom
|
|
||||||
* Unicode character input. For regular Unicode text input, see the
|
|
||||||
* [character callback](@ref glfwSetCharCallback). Like the character
|
|
||||||
* callback, the character with modifiers callback deals with characters and is
|
|
||||||
* keyboard layout dependent. Characters do not map 1:1 to physical keys, as
|
|
||||||
* a key may produce zero, one or more characters. If you want to know whether
|
|
||||||
* a specific physical key was pressed or released, see the
|
|
||||||
* [key callback](@ref glfwSetKeyCallback) instead.
|
|
||||||
*
|
|
||||||
* @param[in] window The window whose callback to set.
|
|
||||||
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
|
|
||||||
* callback.
|
|
||||||
* @return The previously set callback, or `NULL` if no callback was set or an
|
|
||||||
* [error](@ref error_handling) occurred.
|
|
||||||
*
|
|
||||||
* @deprecated Scheduled for removal in version 4.0.
|
|
||||||
*
|
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
|
||||||
*
|
|
||||||
* @thread_safety This function must only be called from the main thread.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
*
|
|
||||||
* @since Added in version 3.1.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun);
|
|
||||||
|
|
||||||
/*! @brief Sets the mouse button callback.
|
/*! @brief Sets the mouse button callback.
|
||||||
*
|
*
|
||||||
@@ -4904,7 +4759,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
|
|||||||
*
|
*
|
||||||
* Not all devices have all the buttons or axes provided by @ref
|
* Not all devices have all the buttons or axes provided by @ref
|
||||||
* GLFWgamepadstate. Unavailable buttons and axes will always report
|
* GLFWgamepadstate. Unavailable buttons and axes will always report
|
||||||
* `GLFW_RELEASE` and 1.0 respectively.
|
* `GLFW_RELEASE` and 0.0 respectively.
|
||||||
*
|
*
|
||||||
* @param[in] jid The [joystick](@ref joysticks) to query.
|
* @param[in] jid The [joystick](@ref joysticks) to query.
|
||||||
* @param[out] state The gamepad input state of the joystick.
|
* @param[out] state The gamepad input state of the joystick.
|
||||||
@@ -5549,4 +5404,3 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _glfw3_h_ */
|
#endif /* _glfw3_h_ */
|
||||||
|
|
||||||
|
|||||||
272
glfw/input.c
vendored
272
glfw/input.c
vendored
@@ -62,11 +62,11 @@ static _GLFWmapping* findMapping(const char* guid)
|
|||||||
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
|
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
|
||||||
const _GLFWjoystick* js)
|
const _GLFWjoystick* js)
|
||||||
{
|
{
|
||||||
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->value >> 4) >= js->hatCount)
|
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount)
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->value >= js->buttonCount)
|
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount)
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
else if (e->type == _GLFW_JOYSTICK_AXIS && e->value >= js->axisCount)
|
else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount)
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
@@ -167,6 +167,10 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
|||||||
|
|
||||||
while (*c)
|
while (*c)
|
||||||
{
|
{
|
||||||
|
// TODO: Implement output modifiers
|
||||||
|
if (*c == '+' || *c == '-')
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
|
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
|
||||||
{
|
{
|
||||||
length = strlen(fields[i].name);
|
length = strlen(fields[i].name);
|
||||||
@@ -177,23 +181,50 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
|||||||
|
|
||||||
if (fields[i].element)
|
if (fields[i].element)
|
||||||
{
|
{
|
||||||
|
_GLFWmapelement* e = fields[i].element;
|
||||||
|
int8_t minimum = -1;
|
||||||
|
int8_t maximum = 1;
|
||||||
|
|
||||||
|
if (*c == '+')
|
||||||
|
{
|
||||||
|
minimum = 0;
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
else if (*c == '-')
|
||||||
|
{
|
||||||
|
maximum = 0;
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (*c == 'a')
|
if (*c == 'a')
|
||||||
fields[i].element->type = _GLFW_JOYSTICK_AXIS;
|
e->type = _GLFW_JOYSTICK_AXIS;
|
||||||
else if (*c == 'b')
|
else if (*c == 'b')
|
||||||
fields[i].element->type = _GLFW_JOYSTICK_BUTTON;
|
e->type = _GLFW_JOYSTICK_BUTTON;
|
||||||
else if (*c == 'h')
|
else if (*c == 'h')
|
||||||
fields[i].element->type = _GLFW_JOYSTICK_HATBIT;
|
e->type = _GLFW_JOYSTICK_HATBIT;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (fields[i].element->type == _GLFW_JOYSTICK_HATBIT)
|
if (e->type == _GLFW_JOYSTICK_HATBIT)
|
||||||
{
|
{
|
||||||
const unsigned long hat = strtoul(c + 1, (char**) &c, 10);
|
const unsigned long hat = strtoul(c + 1, (char**) &c, 10);
|
||||||
const unsigned long bit = strtoul(c + 1, (char**) &c, 10);
|
const unsigned long bit = strtoul(c + 1, (char**) &c, 10);
|
||||||
fields[i].element->value = (uint8_t) ((hat << 4) | bit);
|
e->index = (uint8_t) ((hat << 4) | bit);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fields[i].element->value = (uint8_t) strtoul(c + 1, (char**) &c, 10);
|
e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10);
|
||||||
|
|
||||||
|
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||||
|
{
|
||||||
|
e->axisScale = 2 / (maximum - minimum);
|
||||||
|
e->axisOffset = -(maximum + minimum);
|
||||||
|
|
||||||
|
if (*c == '~')
|
||||||
|
{
|
||||||
|
e->axisScale = -e->axisScale;
|
||||||
|
e->axisOffset = -e->axisOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -224,9 +255,9 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
|||||||
////// GLFW event API //////
|
////// GLFW event API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Notifies shared code of a physical key event
|
// Notifies shared code of a key event
|
||||||
//
|
//
|
||||||
void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods)
|
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state)
|
||||||
{
|
{
|
||||||
if (key >= 0 && key <= GLFW_KEY_LAST)
|
if (key >= 0 && key <= GLFW_KEY_LAST)
|
||||||
{
|
{
|
||||||
@@ -247,31 +278,10 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m
|
|||||||
action = GLFW_REPEAT;
|
action = GLFW_REPEAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!window->lockKeyMods)
|
|
||||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
|
||||||
|
|
||||||
if (window->callbacks.key)
|
if (window->callbacks.keyboard) {
|
||||||
window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods);
|
if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||||
}
|
window->callbacks.keyboard((GLFWwindow*) window, key, scancode, action, mods, text, state);
|
||||||
|
|
||||||
// Notifies shared code of a Unicode codepoint input event
|
|
||||||
// The 'plain' parameter determines whether to emit a regular character event
|
|
||||||
//
|
|
||||||
void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain)
|
|
||||||
{
|
|
||||||
if (codepoint < 32 || (codepoint > 126 && codepoint < 160))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!window->lockKeyMods)
|
|
||||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
|
||||||
|
|
||||||
if (window->callbacks.charmods)
|
|
||||||
window->callbacks.charmods((GLFWwindow*) window, codepoint, mods);
|
|
||||||
|
|
||||||
if (plain)
|
|
||||||
{
|
|
||||||
if (window->callbacks.character)
|
|
||||||
window->callbacks.character((GLFWwindow*) window, codepoint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,6 +433,137 @@ void _glfwFreeJoystick(_GLFWjoystick* js)
|
|||||||
memset(js, 0, sizeof(_GLFWjoystick));
|
memset(js, 0, sizeof(_GLFWjoystick));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* _glfwGetKeyName(int key)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
// Printable keys
|
||||||
|
case GLFW_KEY_A: return "A";
|
||||||
|
case GLFW_KEY_B: return "B";
|
||||||
|
case GLFW_KEY_C: return "C";
|
||||||
|
case GLFW_KEY_D: return "D";
|
||||||
|
case GLFW_KEY_E: return "E";
|
||||||
|
case GLFW_KEY_F: return "F";
|
||||||
|
case GLFW_KEY_G: return "G";
|
||||||
|
case GLFW_KEY_H: return "H";
|
||||||
|
case GLFW_KEY_I: return "I";
|
||||||
|
case GLFW_KEY_J: return "J";
|
||||||
|
case GLFW_KEY_K: return "K";
|
||||||
|
case GLFW_KEY_L: return "L";
|
||||||
|
case GLFW_KEY_M: return "M";
|
||||||
|
case GLFW_KEY_N: return "N";
|
||||||
|
case GLFW_KEY_O: return "O";
|
||||||
|
case GLFW_KEY_P: return "P";
|
||||||
|
case GLFW_KEY_Q: return "Q";
|
||||||
|
case GLFW_KEY_R: return "R";
|
||||||
|
case GLFW_KEY_S: return "S";
|
||||||
|
case GLFW_KEY_T: return "T";
|
||||||
|
case GLFW_KEY_U: return "U";
|
||||||
|
case GLFW_KEY_V: return "V";
|
||||||
|
case GLFW_KEY_W: return "W";
|
||||||
|
case GLFW_KEY_X: return "X";
|
||||||
|
case GLFW_KEY_Y: return "Y";
|
||||||
|
case GLFW_KEY_Z: return "Z";
|
||||||
|
case GLFW_KEY_1: return "1";
|
||||||
|
case GLFW_KEY_2: return "2";
|
||||||
|
case GLFW_KEY_3: return "3";
|
||||||
|
case GLFW_KEY_4: return "4";
|
||||||
|
case GLFW_KEY_5: return "5";
|
||||||
|
case GLFW_KEY_6: return "6";
|
||||||
|
case GLFW_KEY_7: return "7";
|
||||||
|
case GLFW_KEY_8: return "8";
|
||||||
|
case GLFW_KEY_9: return "9";
|
||||||
|
case GLFW_KEY_0: return "0";
|
||||||
|
case GLFW_KEY_SPACE: return "SPACE";
|
||||||
|
case GLFW_KEY_MINUS: return "MINUS";
|
||||||
|
case GLFW_KEY_EQUAL: return "EQUAL";
|
||||||
|
case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET";
|
||||||
|
case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET";
|
||||||
|
case GLFW_KEY_BACKSLASH: return "BACKSLASH";
|
||||||
|
case GLFW_KEY_SEMICOLON: return "SEMICOLON";
|
||||||
|
case GLFW_KEY_APOSTROPHE: return "APOSTROPHE";
|
||||||
|
case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT";
|
||||||
|
case GLFW_KEY_COMMA: return "COMMA";
|
||||||
|
case GLFW_KEY_PERIOD: return "PERIOD";
|
||||||
|
case GLFW_KEY_SLASH: return "SLASH";
|
||||||
|
case GLFW_KEY_WORLD_1: return "WORLD 1";
|
||||||
|
case GLFW_KEY_WORLD_2: return "WORLD 2";
|
||||||
|
|
||||||
|
// Function keys
|
||||||
|
case GLFW_KEY_ESCAPE: return "ESCAPE";
|
||||||
|
case GLFW_KEY_F1: return "F1";
|
||||||
|
case GLFW_KEY_F2: return "F2";
|
||||||
|
case GLFW_KEY_F3: return "F3";
|
||||||
|
case GLFW_KEY_F4: return "F4";
|
||||||
|
case GLFW_KEY_F5: return "F5";
|
||||||
|
case GLFW_KEY_F6: return "F6";
|
||||||
|
case GLFW_KEY_F7: return "F7";
|
||||||
|
case GLFW_KEY_F8: return "F8";
|
||||||
|
case GLFW_KEY_F9: return "F9";
|
||||||
|
case GLFW_KEY_F10: return "F10";
|
||||||
|
case GLFW_KEY_F11: return "F11";
|
||||||
|
case GLFW_KEY_F12: return "F12";
|
||||||
|
case GLFW_KEY_F13: return "F13";
|
||||||
|
case GLFW_KEY_F14: return "F14";
|
||||||
|
case GLFW_KEY_F15: return "F15";
|
||||||
|
case GLFW_KEY_F16: return "F16";
|
||||||
|
case GLFW_KEY_F17: return "F17";
|
||||||
|
case GLFW_KEY_F18: return "F18";
|
||||||
|
case GLFW_KEY_F19: return "F19";
|
||||||
|
case GLFW_KEY_F20: return "F20";
|
||||||
|
case GLFW_KEY_F21: return "F21";
|
||||||
|
case GLFW_KEY_F22: return "F22";
|
||||||
|
case GLFW_KEY_F23: return "F23";
|
||||||
|
case GLFW_KEY_F24: return "F24";
|
||||||
|
case GLFW_KEY_F25: return "F25";
|
||||||
|
case GLFW_KEY_UP: return "UP";
|
||||||
|
case GLFW_KEY_DOWN: return "DOWN";
|
||||||
|
case GLFW_KEY_LEFT: return "LEFT";
|
||||||
|
case GLFW_KEY_RIGHT: return "RIGHT";
|
||||||
|
case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT";
|
||||||
|
case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT";
|
||||||
|
case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL";
|
||||||
|
case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL";
|
||||||
|
case GLFW_KEY_LEFT_ALT: return "LEFT ALT";
|
||||||
|
case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT";
|
||||||
|
case GLFW_KEY_TAB: return "TAB";
|
||||||
|
case GLFW_KEY_ENTER: return "ENTER";
|
||||||
|
case GLFW_KEY_BACKSPACE: return "BACKSPACE";
|
||||||
|
case GLFW_KEY_INSERT: return "INSERT";
|
||||||
|
case GLFW_KEY_DELETE: return "DELETE";
|
||||||
|
case GLFW_KEY_PAGE_UP: return "PAGE UP";
|
||||||
|
case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN";
|
||||||
|
case GLFW_KEY_HOME: return "HOME";
|
||||||
|
case GLFW_KEY_END: return "END";
|
||||||
|
case GLFW_KEY_KP_0: return "KEYPAD 0";
|
||||||
|
case GLFW_KEY_KP_1: return "KEYPAD 1";
|
||||||
|
case GLFW_KEY_KP_2: return "KEYPAD 2";
|
||||||
|
case GLFW_KEY_KP_3: return "KEYPAD 3";
|
||||||
|
case GLFW_KEY_KP_4: return "KEYPAD 4";
|
||||||
|
case GLFW_KEY_KP_5: return "KEYPAD 5";
|
||||||
|
case GLFW_KEY_KP_6: return "KEYPAD 6";
|
||||||
|
case GLFW_KEY_KP_7: return "KEYPAD 7";
|
||||||
|
case GLFW_KEY_KP_8: return "KEYPAD 8";
|
||||||
|
case GLFW_KEY_KP_9: return "KEYPAD 9";
|
||||||
|
case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE";
|
||||||
|
case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTPLY";
|
||||||
|
case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT";
|
||||||
|
case GLFW_KEY_KP_ADD: return "KEYPAD ADD";
|
||||||
|
case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL";
|
||||||
|
case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL";
|
||||||
|
case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER";
|
||||||
|
case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN";
|
||||||
|
case GLFW_KEY_NUM_LOCK: return "NUM LOCK";
|
||||||
|
case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK";
|
||||||
|
case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK";
|
||||||
|
case GLFW_KEY_PAUSE: return "PAUSE";
|
||||||
|
case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER";
|
||||||
|
case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER";
|
||||||
|
case GLFW_KEY_MENU: return "MENU";
|
||||||
|
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW public API //////
|
////// GLFW public API //////
|
||||||
@@ -479,7 +620,9 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
|||||||
&window->virtualCursorPosX,
|
&window->virtualCursorPosX,
|
||||||
&window->virtualCursorPosY);
|
&window->virtualCursorPosY);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
if (_glfwPlatformWindowFocused(window))
|
if (_glfwPlatformWindowFocused(window))
|
||||||
|
#endif
|
||||||
_glfwPlatformSetCursorMode(window, value);
|
_glfwPlatformSetCursorMode(window, value);
|
||||||
}
|
}
|
||||||
else if (mode == GLFW_STICKY_KEYS)
|
else if (mode == GLFW_STICKY_KEYS)
|
||||||
@@ -760,33 +903,13 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
|
|||||||
_glfwPlatformSetCursor(window, cursor);
|
_glfwPlatformSetCursor(window, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboardfun cbfun)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
assert(window != NULL);
|
assert(window != NULL);
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
_GLFW_SWAP_POINTERS(window->callbacks.key, cbfun);
|
_GLFW_SWAP_POINTERS(window->callbacks.keyboard, cbfun);
|
||||||
return cbfun;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun)
|
|
||||||
{
|
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
|
||||||
assert(window != NULL);
|
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
||||||
_GLFW_SWAP_POINTERS(window->callbacks.character, cbfun);
|
|
||||||
return cbfun;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun)
|
|
||||||
{
|
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
|
||||||
assert(window != NULL);
|
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
||||||
_GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun);
|
|
||||||
return cbfun;
|
return cbfun;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,35 +1311,41 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
|||||||
|
|
||||||
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
|
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
|
||||||
{
|
{
|
||||||
if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_AXIS)
|
const _GLFWmapelement* e = js->mapping->buttons + i;
|
||||||
|
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||||
{
|
{
|
||||||
if (fabsf(js->axes[js->mapping->buttons[i].value]) > 0.5f)
|
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
|
||||||
|
if (value > 0.f)
|
||||||
state->buttons[i] = GLFW_PRESS;
|
state->buttons[i] = GLFW_PRESS;
|
||||||
}
|
}
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
|
else if (e->type == _GLFW_JOYSTICK_HATBIT)
|
||||||
{
|
{
|
||||||
const unsigned int hat = js->mapping->buttons[i].value >> 4;
|
const unsigned int hat = e->index >> 4;
|
||||||
const unsigned int bit = js->mapping->buttons[i].value & 0xf;
|
const unsigned int bit = e->index & 0xf;
|
||||||
if (js->hats[hat] & bit)
|
if (js->hats[hat] & bit)
|
||||||
state->buttons[i] = GLFW_PRESS;
|
state->buttons[i] = GLFW_PRESS;
|
||||||
}
|
}
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
|
else if (e->type == _GLFW_JOYSTICK_BUTTON)
|
||||||
state->buttons[i] = js->buttons[js->mapping->buttons[i].value];
|
state->buttons[i] = js->buttons[e->index];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
|
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
|
||||||
{
|
{
|
||||||
if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS)
|
const _GLFWmapelement* e = js->mapping->axes + i;
|
||||||
state->axes[i] = js->axes[js->mapping->axes[i].value];
|
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
|
|
||||||
{
|
{
|
||||||
const unsigned int hat = js->mapping->axes[i].value >> 4;
|
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
|
||||||
const unsigned int bit = js->mapping->axes[i].value & 0xf;
|
state->axes[i] = fminf(fmaxf(value, -1.f), 1.f);
|
||||||
|
}
|
||||||
|
else if (e->type == _GLFW_JOYSTICK_HATBIT)
|
||||||
|
{
|
||||||
|
const unsigned int hat = e->index >> 4;
|
||||||
|
const unsigned int bit = e->index & 0xf;
|
||||||
if (js->hats[hat] & bit)
|
if (js->hats[hat] & bit)
|
||||||
state->axes[i] = 1.f;
|
state->axes[i] = 1.f;
|
||||||
}
|
}
|
||||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
|
else if (e->type == _GLFW_JOYSTICK_BUTTON)
|
||||||
state->axes[i] = (float) js->buttons[js->mapping->axes[i].value];
|
state->axes[i] = (float) js->buttons[e->index];
|
||||||
}
|
}
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
@@ -1268,4 +1397,3 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||||
return _glfwPlatformGetTimerFrequency();
|
return _glfwPlatformGetTimerFrequency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
glfw/internal.h
vendored
20
glfw/internal.h
vendored
@@ -128,7 +128,7 @@ typedef enum VkStructureType
|
|||||||
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
|
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
|
||||||
VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
|
VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
|
||||||
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
|
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
|
||||||
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000053000,
|
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
|
||||||
VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||||
} VkStructureType;
|
} VkStructureType;
|
||||||
|
|
||||||
@@ -407,9 +407,7 @@ struct _GLFWwindow
|
|||||||
GLFWcursorposfun cursorPos;
|
GLFWcursorposfun cursorPos;
|
||||||
GLFWcursorenterfun cursorEnter;
|
GLFWcursorenterfun cursorEnter;
|
||||||
GLFWscrollfun scroll;
|
GLFWscrollfun scroll;
|
||||||
GLFWkeyfun key;
|
GLFWkeyboardfun keyboard;
|
||||||
GLFWcharfun character;
|
|
||||||
GLFWcharmodsfun charmods;
|
|
||||||
GLFWdropfun drop;
|
GLFWdropfun drop;
|
||||||
} callbacks;
|
} callbacks;
|
||||||
|
|
||||||
@@ -456,7 +454,9 @@ struct _GLFWcursor
|
|||||||
struct _GLFWmapelement
|
struct _GLFWmapelement
|
||||||
{
|
{
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t value;
|
uint8_t index;
|
||||||
|
int8_t axisScale;
|
||||||
|
int8_t axisOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gamepad mapping structure
|
// Gamepad mapping structure
|
||||||
@@ -605,6 +605,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
|||||||
const char* _glfwPlatformGetScancodeName(int scancode);
|
const char* _glfwPlatformGetScancodeName(int scancode);
|
||||||
int _glfwPlatformGetKeyScancode(int key);
|
int _glfwPlatformGetKeyScancode(int key);
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||||
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||||
float* xscale, float* yscale);
|
float* xscale, float* yscale);
|
||||||
@@ -650,7 +651,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
|
|||||||
void _glfwPlatformShowWindow(_GLFWwindow* window);
|
void _glfwPlatformShowWindow(_GLFWwindow* window);
|
||||||
void _glfwPlatformHideWindow(_GLFWwindow* window);
|
void _glfwPlatformHideWindow(_GLFWwindow* window);
|
||||||
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
|
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t);
|
int _glfwPlatformWindowBell(_GLFWwindow* window);
|
||||||
void _glfwPlatformFocusWindow(_GLFWwindow* window);
|
void _glfwPlatformFocusWindow(_GLFWwindow* window);
|
||||||
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
|
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
|
||||||
int xpos, int ypos, int width, int height,
|
int xpos, int ypos, int width, int height,
|
||||||
@@ -708,10 +709,7 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
|
|||||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||||
|
|
||||||
void _glfwInputKey(_GLFWwindow* window,
|
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state);
|
||||||
int key, int scancode, int action, int mods);
|
|
||||||
void _glfwInputChar(_GLFWwindow* window,
|
|
||||||
unsigned int codepoint, int mods, GLFWbool plain);
|
|
||||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
||||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||||
@@ -760,10 +758,10 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name,
|
|||||||
int buttonCount,
|
int buttonCount,
|
||||||
int hatCount);
|
int hatCount);
|
||||||
void _glfwFreeJoystick(_GLFWjoystick* js);
|
void _glfwFreeJoystick(_GLFWjoystick* js);
|
||||||
|
const char* _glfwGetKeyName(int key);
|
||||||
|
|
||||||
GLFWbool _glfwInitVulkan(int mode);
|
GLFWbool _glfwInitVulkan(int mode);
|
||||||
void _glfwTerminateVulkan(void);
|
void _glfwTerminateVulkan(void);
|
||||||
const char* _glfwGetVulkanResultString(VkResult result);
|
const char* _glfwGetVulkanResultString(VkResult result);
|
||||||
|
|
||||||
char* _glfw_strdup(const char* source);
|
char* _glfw_strdup(const char* source);
|
||||||
|
|
||||||
|
|||||||
624
glfw/mappings.h
vendored
624
glfw/mappings.h
vendored
@@ -60,243 +60,411 @@
|
|||||||
|
|
||||||
const char* _glfwDefaultMappings[] =
|
const char* _glfwDefaultMappings[] =
|
||||||
{
|
{
|
||||||
"02200090000000000000504944564944,8Bitdo NES30 PRO USB,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,",
|
||||||
"20380900000000000000504944564944,8Bitdo NES30 PRO Wireless,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
|
||||||
"10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,y:b3,x:b4,start:b11,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,platform:Windows,",
|
"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
|
||||||
"8f0e1200000000000000504944564944,Acme,platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
|
||||||
"341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
|
||||||
"c0111352000000000000504944564944,Battalife Joystick,platform:Windows,x:b4,a:b6,b:b7,y:b5,back:b2,start:b3,leftshoulder:b0,rightshoulder:b1,leftx:a0,lefty:a1,",
|
"030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"d81d0b00000000000000504944564944,BUFFALO BSGP1601 Series ,platform:Windows,x:b4,a:b5,b:b3,y:b2,back:b12,start:b13,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b6,rightshoulder:b9,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"5e048e02000000000000504944564944,Controller (XBOX 360 For Windows),platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,righttrigger:a2,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,",
|
||||||
"4f0423b3000000000000504944564944,Dual Trigger 3-in-1,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"341a0108000000000000504944564944,EXEQ RF USB Gamepad 8206,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,leftstick:b8,rightstick:b7,back:b8,start:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
|
"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f8500000000000000504944564944,Fighting Commander 2016 PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,",
|
||||||
"0d0f5f00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f5e00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:a3,platform:Windows,",
|
"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f8400000000000000504944564944,Fighting Commander 5,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f8700000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f8800000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b9,guide:b12,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f2700000000000000504944564944,FIGHTING STICK V3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79000600000000000000504944564944,G-Shark GS-GP702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"28040140000000000000504944564944,GamePad Pro USB,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
|
"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"45130010000000000000504944564944,Generic USB Joystick,a:b0,b:b1,x:b2,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f4900000000000000504944564944,Hatsune Miku Sho Controller,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d8140862000000000000504944564944,HitBox Edition Cthulhu+,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
|
"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f4000000000000000504944564944,Hori Fighting Stick Mini 3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
|
"03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f6e00000000000000504944564944,HORIPAD 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f4d00000000000000504944564944,HORIPAD3 A,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"25090017000000000000504944564944,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b5,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b6,platform:Windows,",
|
"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d81d0f00000000000000504944564944,iBUFFALO BSGP1204 Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"d81d1000000000000000504944564944,iBUFFALO BSGP1204P Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"83056020000000000000504944564944,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6f0e2401000000000000504944564944,INJUSTICE FightStick for PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Windows",
|
"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"49190204000000000000504944564944,Ipega PG-9023,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,",
|
"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0418c2000000000000504944564944,Logitech RumblePad 2 USB,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38075032000000000000504944564944,Mad Catz FightPad PRO PS3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38075082000000000000504944564944,Mad Catz FightPad PRO PS4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b13,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078433000000000000504944564944,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078483000000000000504944564944,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:b6,platform:Windows,",
|
"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078134000000000000504944564944,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b7,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b4,platform:Windows,",
|
"030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"38078184000000000000504944564944,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:b7,platform:Windows,",
|
"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,",
|
||||||
"38078034000000000000504944564944,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"38078084000000000000504944564944,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"38078532000000000000504944564944,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"38073888000000000000504944564944,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,",
|
||||||
"38071888000000000000504944564944,MadCatz SFIV FightStick PS3,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b6,platform:Windows,",
|
"03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"25090128000000000000504944564944,Mayflash Arcade Stick,a:b1,b:b2,x:b5,y:b6,back:b8,start:b9,leftshoulder:b0,rightshoulder:b3,leftx:a0,lefty:a1,rightx:h0.4,righty:h0.0,lefttrigger:b4,righttrigger:b7,platform:Windows,",
|
"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
|
||||||
"79004318000000000000504944564944,Mayflash GameCube Controller Adapter,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b0,start:b9,guide:b0,leftshoulder:b4,rightshoulder:b7,leftstick:b0,rightstick:b0,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,",
|
||||||
"8f0e1030000000000000504944564944,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,x:b3,y:b4,start:b9,leftshoulder:b6,rightshoulder:b2,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"8f0e0d31000000000000504944564944,Multilaser JS071 USB,platform:Windows,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"bd1215d0000000000000504944564944,Nintendo Retrolink USB Super SNES Classic Controller,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,start:b9,back:b8,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftstick:a0,rightstick:a2,leftshoulder:a3,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:h0.6,lefty:h0.12,rightx:h0.9,righty:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"36280100000000000000504944564944,OUYA Controller,platform:Windows,a:b0,b:b3,y:b2,x:b1,start:b14,guide:b15,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b8,dpleft:b10,dpdown:b9,dpright:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b12,righttrigger:b13,",
|
"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"120cf60e000000000000504944564944,P4 Wired Gamepad,a:b1,b:b2,x:b0,y:b3,back:b12,guide:b8,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.0,lefttrigger:b7,righttrigger:b6,platform:Windows,",
|
"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"8f0e0300000000000000504944564944,Piranha xtreme,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d6206dca000000000000504944564944,PowerA Pro Ex,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"10080100000000000000504944564944,PS1 USB,platform:Windows,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"10080300000000000000504944564944,PS2 USB,platform:Windows,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a4,righty:a2,lefttrigger:b4,righttrigger:b5,",
|
"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
|
"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
|
"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
|
"030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"10008200000000000000504944564944,PS360+ v1.66,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,",
|
||||||
"4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"300f0011000000000000504944564944,QanBa Arcade JoyStick 1008,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b10,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
|
"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"300f1611000000000000504944564944,QanBa Arcade JoyStick 4018,a:b1,b:b2,x:b0,y:b3,back:b10,guide:b9,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,",
|
||||||
"222c0020000000000000504944564944,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
|
||||||
"300f1210000000000000504944564944,QanBa Joystick Plus,a:b0,b:b1,x:b2,y:b3,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,platform:Windows,",
|
"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"341a0104000000000000504944564944,QanBa Joystick Q4RAF,a:b5,b:b6,x:b1,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b0,rightshoulder:b3,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b7,platform:Windows,",
|
"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"222c0223000000000000504944564944,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"222c0023000000000000504944564944,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,x:b0,y:b3,back:b13,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f1100000000000000504944564944,REAL ARCADE PRO.3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"0d0f8b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,",
|
||||||
"0d0f8a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f6b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"0d0f6a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f7000000000000000504944564944,REAL ARCADE PRO.4 VLX,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f2200000000000000504944564944,REAL ARCADE Pro.V3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
|
||||||
"00f00300000000000000504944564944,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"00f0f100000000000000504944564944,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
|
"03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"6f0e1e01000000000000504944564944,Rock Candy Gamepad for PS3,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
|
||||||
"300f1201000000000000504944564944,Saitek Dual Analog Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
|
||||||
"a3060cff000000000000504944564944,Saitek P2500,a:b2,b:b3,y:b1,x:b0,start:b4,guide:b10,back:b5,leftstick:b8,rightstick:b9,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
|
"03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"300f1001000000000000504944564944,Saitek P480 Rumble Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"9b280500000000000000504944564944,Saturn_Adapter_2.0,a:b1,b:b2,x:b0,y:b3,start:b9,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,platform:Windows,",
|
"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79001100000000000000504944564944,Sega Saturn Gamepad,a:b1,b:b2,x:b4,y:b5,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a4,lefttrigger:b3,righttrigger:b0,platform:Windows,",
|
"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05cc09000000000000504944564944,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05a00b000000000000504944564944,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
|
"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,",
|
||||||
"ff113133000000000000504944564944,SVEN X-PAD,platform:Windows,a:b2,b:b3,y:b1,x:b0,start:b5,back:b4,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b8,righttrigger:b9,",
|
"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"4f0415b3000000000000504944564944,Thrustmaster Dual Analog 3.2,platform:Windows,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4f0400b3000000000000504944564944,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Windows,",
|
"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"66660488000000000000504944564944,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpdown:b14,dpleft:b15,dpright:b13,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,platform:Windows,",
|
"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,",
|
||||||
"38076652000000000000504944564944,UnKnown,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"63252305000000000000504944564944,USB Vibration Joystick (BM),platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79001b18000000000000504944564944,Venom Arcade Joystick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
|
"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"10280000000000000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"79000000000000000600000000000000,G-Shark GP-702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
|
"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"AD1B00000000000001F9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
|
"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f0000000000004d00000000000000,HORI Gem Pad 3,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"0d0f0000000000006600000000000000,HORIPAD FPS PLUS 4,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:a4,",
|
"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"83050000000000006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
|
||||||
"2509000000000000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
|
"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
|
||||||
"79000000000000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,x:b0,y:b12,back:b32,start:b36,leftstick:b40,rightstick:b44,leftshoulder:b16,rightshoulder:b20,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a4,rightx:a8,righty:a12,lefttrigger:b24,righttrigger:b28,platform:Mac OS X,",
|
"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,rightx:,righty:,lefttrigger:b6,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,righttrigger:b7,",
|
"030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"8f0e0000000000000300000000000000,Piranha xtreme,platform:Mac OS X,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
|
"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"891600000000000000fd000000000000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b8,guide:b10,back:b9,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b11,dpleft:b13,dpdown:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X,",
|
"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"79000000000000001100000000000000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a3,lefty:a4,platform:Mac OS X,",
|
"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,",
|
||||||
"81170000000000007e05000000000000,Sega Saturn,x:b0,a:b2,b:b4,y:b6,start:b13,dpleft:b15,dpdown:b16,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,lefttrigger:b10,rightshoulder:b9,righttrigger:a4,righttrigger:b11,leftx:a0,lefty:a2,platform:Mac OS X,",
|
"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"b4040000000000000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,x:b3,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05000000000000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
|
"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,",
|
||||||
"4c05000000000000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
|
"03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"4f0400000000000015b3000000000000,Thrustmaster Dual Analog 3.2,platform:Mac OS X,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"4f0400000000000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Mac OS X,",
|
"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
|
"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"10080000000000000100000000000000,Twin USB Joystick,a:b4,b:b2,x:b6,y:b0,back:b16,start:b18,leftstick:b20,rightstick:b22,leftshoulder:b12,rightshoulder:b14,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a2,rightx:a6,righty:a4,lefttrigger:b8,righttrigger:b10,platform:Mac OS X,",
|
"030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,y:b9,x:b10,start:b6,guide:b8,back:b7,dpup:b2,dpleft:b0,dpdown:b3,dpright:b1,leftx:a0,lefty:a1,lefttrigger:b12,righttrigger:,leftshoulder:b11,platform:Mac OS X,",
|
"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,",
|
||||||
"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,x:b18,y:b17,back:b7,guide:b8,start:b6,leftstick:b23,rightstick:b24,leftshoulder:b19,rightshoulder:b20,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b21,righttrigger:b22,platform:Mac OS X,",
|
"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
|
||||||
"5e04000000000000dd02000000000000,Xbox One Wired Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
|
||||||
"5e04000000000000ea02000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,",
|
||||||
"5e04000000000000e002000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b10,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
|
||||||
"05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
|
||||||
"05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
|
"03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
|
||||||
"030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
|
||||||
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux,",
|
"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
|
||||||
"03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,",
|
"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
|
||||||
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
|
||||||
"030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
"030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
"03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
|
"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
|
||||||
"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,",
|
||||||
"03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"06000000adde0000efbe000002010000,Hidromancer Game Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1,",
|
"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
"03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux,",
|
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
|
||||||
"030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5,",
|
"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7",
|
"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,",
|
"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
|
"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,",
|
"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
|
"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,",
|
||||||
"030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
|
||||||
"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
|
||||||
"030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux,",
|
"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,",
|
||||||
|
"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,",
|
||||||
|
"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
|
||||||
|
"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
|
||||||
|
"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,",
|
||||||
|
"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,",
|
||||||
|
"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,",
|
||||||
|
"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
|
||||||
|
"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,",
|
||||||
|
"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,",
|
||||||
|
"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,",
|
||||||
|
"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
|
||||||
|
"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
|
||||||
|
"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,",
|
||||||
|
"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
|
||||||
|
"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
|
||||||
|
"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,",
|
||||||
|
"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,",
|
||||||
|
"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,",
|
||||||
|
"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
|
||||||
|
"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,",
|
||||||
|
"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,",
|
||||||
|
"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
|
||||||
|
"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,",
|
||||||
"030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux,",
|
"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,",
|
||||||
"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5,",
|
"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,",
|
"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1,",
|
"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
|
"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,",
|
"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux,",
|
"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,",
|
"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
|
"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
|
"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
|
||||||
|
"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
|
||||||
|
"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
|
||||||
|
"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
|
||||||
|
"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
|
||||||
|
"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
|
"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
|
"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
|
"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
"050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
|
"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
|
||||||
"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
|
"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,platform:Linux,x:b1,a:b0,b:b4,y:b5,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
|
"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
|
"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
|
"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
|
"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
|
"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,",
|
||||||
"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,",
|
||||||
"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,platform:Linux,a:b12,b:b10,x:b13,y:b11,back:b4,start:b5,leftstick:b14,rightstick:b15,leftshoulder:b9,rightshoulder:b8,dpup:b0,dpdown:b2,dpleft:b3,dpright:b1,leftx:a1,lefty:a0,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
|
"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
|
"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
"050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
|
"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
|
||||||
"030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
|
"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
|
||||||
"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,",
|
"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
|
"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13,",
|
"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
|
"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
|
||||||
"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,",
|
"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
|
"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
|
||||||
"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
|
"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,",
|
"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
|
"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,",
|
||||||
"03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
|
"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
|
"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
|
||||||
"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
|
"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
|
"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
|
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
|
||||||
|
"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,",
|
||||||
|
"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
|
||||||
|
"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,",
|
||||||
|
"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
|
"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"030000005e040000a102000000010000,X360 Wireless Controller,platform:Linux,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
|
"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
|
"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
|
"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,",
|
||||||
|
"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
|
||||||
"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,platform:Linux,y:b0,x:b1,b:b3,a:b4,leftshoulder:b2,rightshoulder:b5,back:b6,start:b7,guide:b9,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftx:a0,lefty:a1,",
|
"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
|
||||||
|
"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,",
|
||||||
|
"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,",
|
||||||
|
"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
|
||||||
|
"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,",
|
||||||
|
"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
|
||||||
|
"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
|
||||||
|
"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
|
||||||
|
"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,",
|
||||||
|
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,",
|
||||||
|
"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,",
|
||||||
|
"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,",
|
||||||
|
"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,",
|
||||||
|
"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,",
|
||||||
|
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,",
|
||||||
|
|
||||||
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
||||||
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
|
||||||
|
|||||||
2
glfw/monitor.c
vendored
2
glfw/monitor.c
vendored
@@ -177,6 +177,8 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor)
|
|||||||
if (monitor == NULL)
|
if (monitor == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_glfwPlatformFreeMonitor(monitor);
|
||||||
|
|
||||||
_glfwFreeGammaArrays(&monitor->originalRamp);
|
_glfwFreeGammaArrays(&monitor->originalRamp);
|
||||||
_glfwFreeGammaArrays(&monitor->currentRamp);
|
_glfwFreeGammaArrays(&monitor->currentRamp);
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
|||||||
// Info.plist for unbundled applications
|
// Info.plist for unbundled applications
|
||||||
// HACK: This assumes that NSOpenGLPixelFormat will remain
|
// HACK: This assumes that NSOpenGLPixelFormat will remain
|
||||||
// a straightforward wrapper of its CGL counterpart
|
// a straightforward wrapper of its CGL counterpart
|
||||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
|
||||||
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
|
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
|
||||||
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
||||||
}
|
}
|
||||||
|
|||||||
4
glfw/null_monitor.c
vendored
4
glfw/null_monitor.c
vendored
@@ -32,6 +32,10 @@
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
3
glfw/null_window.c
vendored
3
glfw/null_window.c
vendored
@@ -205,7 +205,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
@@ -323,4 +323,3 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
// This seems like the most appropriate error to return here
|
// This seems like the most appropriate error to return here
|
||||||
return VK_ERROR_INITIALIZATION_FAILED;
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,15 +55,16 @@
|
|||||||
"wayland": {
|
"wayland": {
|
||||||
"headers": [
|
"headers": [
|
||||||
"wl_platform.h",
|
"wl_platform.h",
|
||||||
"linux_joystick.h",
|
|
||||||
"posix_time.h",
|
"posix_time.h",
|
||||||
"posix_thread.h",
|
"posix_thread.h",
|
||||||
"xkb_unicode.h",
|
"xkb_glfw.h",
|
||||||
"egl_context.h",
|
"egl_context.h",
|
||||||
"osmesa_context.h"
|
"osmesa_context.h",
|
||||||
|
"linux_joystick.h"
|
||||||
],
|
],
|
||||||
"protocols": [
|
"protocols": [
|
||||||
"stable/xdg-shell/xdg-shell.xml",
|
"stable/xdg-shell/xdg-shell.xml",
|
||||||
|
"stable/viewporter/viewporter.xml",
|
||||||
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
||||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||||
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||||
@@ -72,12 +73,12 @@
|
|||||||
"wl_init.c",
|
"wl_init.c",
|
||||||
"wl_monitor.c",
|
"wl_monitor.c",
|
||||||
"wl_window.c",
|
"wl_window.c",
|
||||||
"linux_joystick.c",
|
|
||||||
"posix_time.c",
|
"posix_time.c",
|
||||||
"posix_thread.c",
|
"posix_thread.c",
|
||||||
"xkb_unicode.c",
|
"xkb_glfw.c",
|
||||||
"egl_context.c",
|
"egl_context.c",
|
||||||
"osmesa_context.c"
|
"osmesa_context.c",
|
||||||
|
"linux_joystick.c"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"wayland_protocols": [
|
"wayland_protocols": [
|
||||||
@@ -107,7 +108,7 @@
|
|||||||
"x11": {
|
"x11": {
|
||||||
"headers": [
|
"headers": [
|
||||||
"x11_platform.h",
|
"x11_platform.h",
|
||||||
"xkb_unicode.h",
|
"xkb_glfw.h",
|
||||||
"posix_time.h",
|
"posix_time.h",
|
||||||
"posix_thread.h",
|
"posix_thread.h",
|
||||||
"glx_context.h",
|
"glx_context.h",
|
||||||
@@ -119,7 +120,7 @@
|
|||||||
"x11_init.c",
|
"x11_init.c",
|
||||||
"x11_monitor.c",
|
"x11_monitor.c",
|
||||||
"x11_window.c",
|
"x11_window.c",
|
||||||
"xkb_unicode.c",
|
"xkb_glfw.c",
|
||||||
"posix_time.c",
|
"posix_time.c",
|
||||||
"posix_thread.c",
|
"posix_thread.c",
|
||||||
"glx_context.c",
|
"glx_context.c",
|
||||||
|
|||||||
2
glfw/vulkan.c
vendored
2
glfw/vulkan.c
vendored
@@ -54,7 +54,7 @@ GLFWbool _glfwInitVulkan(int mode)
|
|||||||
#elif defined(_GLFW_WIN32)
|
#elif defined(_GLFW_WIN32)
|
||||||
_glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
|
_glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
|
||||||
#elif defined(_GLFW_COCOA)
|
#elif defined(_GLFW_COCOA)
|
||||||
_glfw.vk.handle = _glfw_dlopen("libMoltenVK.dylib");
|
_glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib");
|
||||||
#else
|
#else
|
||||||
_glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
|
_glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
4
glfw/win32_monitor.c
vendored
4
glfw/win32_monitor.c
vendored
@@ -334,6 +334,10 @@ void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* ysc
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
DEVMODEW dm;
|
DEVMODEW dm;
|
||||||
|
|||||||
5
glfw/win32_window.c
vendored
5
glfw/win32_window.c
vendored
@@ -1474,9 +1474,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
FlashWindow(window->win32.handle, TRUE);
|
FlashWindow(window->win32.handle, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return MessageBeep(0xFFFFFFFF & param) ? GLFW_TRUE : GLFW_FALSE;
|
return MessageBeep(0xFFFFFFFF) ? GLFW_TRUE : GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
||||||
@@ -2014,4 +2014,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
return window->win32.handle;
|
return window->win32.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
glfw/window.c
vendored
7
glfw/window.c
vendored
@@ -54,7 +54,7 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused)
|
|||||||
if (window->keys[key] == GLFW_PRESS)
|
if (window->keys[key] == GLFW_PRESS)
|
||||||
{
|
{
|
||||||
const int scancode = _glfwPlatformGetKeyScancode(key);
|
const int scancode = _glfwPlatformGetKeyScancode(key);
|
||||||
_glfwInputKey(window, key, scancode, GLFW_RELEASE, 0);
|
_glfwInputKeyboard(window, key, scancode, GLFW_RELEASE, 0, "", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,14 +768,14 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
|
|||||||
_glfwPlatformRequestWindowAttention(window);
|
_glfwPlatformRequestWindowAttention(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWAPI int glfwWindowBell(GLFWwindow* handle, int64_t param)
|
GLFWAPI int glfwWindowBell(GLFWwindow* handle)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
assert(window != NULL);
|
assert(window != NULL);
|
||||||
|
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||||
|
|
||||||
return _glfwPlatformWindowBell(window, param);
|
return _glfwPlatformWindowBell(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWAPI void glfwHideWindow(GLFWwindow* handle)
|
GLFWAPI void glfwHideWindow(GLFWwindow* handle)
|
||||||
@@ -1106,4 +1106,3 @@ GLFWAPI void glfwPostEmptyEvent(void)
|
|||||||
|
|
||||||
_glfwPlatformPostEmptyEvent();
|
_glfwPlatformPostEmptyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
640
glfw/wl_init.c
vendored
640
glfw/wl_init.c
vendored
@@ -41,6 +41,39 @@ static inline int min(int n1, int n2)
|
|||||||
return n1 < n2 ? n1 : n2;
|
return n1 < n2 ? n1 : n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, int* which)
|
||||||
|
{
|
||||||
|
int focus;
|
||||||
|
_GLFWwindow* window = _glfw.windowListHead;
|
||||||
|
if (!which)
|
||||||
|
which = &focus;
|
||||||
|
while (window)
|
||||||
|
{
|
||||||
|
if (surface == window->wl.decorations.top.surface)
|
||||||
|
{
|
||||||
|
*which = topDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (surface == window->wl.decorations.left.surface)
|
||||||
|
{
|
||||||
|
*which = leftDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (surface == window->wl.decorations.right.surface)
|
||||||
|
{
|
||||||
|
*which = rightDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (surface == window->wl.decorations.bottom.surface)
|
||||||
|
{
|
||||||
|
*which = bottomDecoration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
window = window->next;
|
||||||
|
}
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
static void pointerHandleEnter(void* data,
|
static void pointerHandleEnter(void* data,
|
||||||
struct wl_pointer* pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t serial,
|
uint32_t serial,
|
||||||
@@ -52,8 +85,16 @@ static void pointerHandleEnter(void* data,
|
|||||||
if (!surface)
|
if (!surface)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int focus = 0;
|
||||||
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
window = findWindowFromDecorationSurface(surface, &focus);
|
||||||
|
if (!window)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window->wl.decorations.focus = focus;
|
||||||
_glfw.wl.pointerSerial = serial;
|
_glfw.wl.pointerSerial = serial;
|
||||||
_glfw.wl.pointerFocus = window;
|
_glfw.wl.pointerFocus = window;
|
||||||
|
|
||||||
@@ -80,6 +121,39 @@ static void pointerHandleLeave(void* data,
|
|||||||
_glfwInputCursorEnter(window, GLFW_FALSE);
|
_glfwInputCursorEnter(window, GLFW_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setCursor(const char* name)
|
||||||
|
{
|
||||||
|
struct wl_buffer* buffer;
|
||||||
|
struct wl_cursor* cursor;
|
||||||
|
struct wl_cursor_image* image;
|
||||||
|
struct wl_surface* surface = _glfw.wl.cursorSurface;
|
||||||
|
|
||||||
|
cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
|
||||||
|
name);
|
||||||
|
if (!cursor)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Standard cursor not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
image = cursor->images[0];
|
||||||
|
|
||||||
|
if (!image)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buffer = wl_cursor_image_get_buffer(image);
|
||||||
|
if (!buffer)
|
||||||
|
return;
|
||||||
|
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
|
||||||
|
surface,
|
||||||
|
image->hotspot_x,
|
||||||
|
image->hotspot_y);
|
||||||
|
wl_surface_attach(surface, buffer, 0, 0);
|
||||||
|
wl_surface_damage(surface, 0, 0,
|
||||||
|
image->width, image->height);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
}
|
||||||
|
|
||||||
static void pointerHandleMotion(void* data,
|
static void pointerHandleMotion(void* data,
|
||||||
struct wl_pointer* pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
@@ -87,6 +161,7 @@ static void pointerHandleMotion(void* data,
|
|||||||
wl_fixed_t sy)
|
wl_fixed_t sy)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||||
|
const char* cursorName;
|
||||||
|
|
||||||
if (!window)
|
if (!window)
|
||||||
return;
|
return;
|
||||||
@@ -99,13 +174,47 @@ static void pointerHandleMotion(void* data,
|
|||||||
window->wl.cursorPosY = wl_fixed_to_double(sy);
|
window->wl.cursorPosY = wl_fixed_to_double(sy);
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfwInputCursorPos(window,
|
switch (window->wl.decorations.focus)
|
||||||
wl_fixed_to_double(sx),
|
{
|
||||||
wl_fixed_to_double(sy));
|
case mainWindow:
|
||||||
|
_glfwInputCursorPos(window,
|
||||||
|
wl_fixed_to_double(sx),
|
||||||
|
wl_fixed_to_double(sy));
|
||||||
|
return;
|
||||||
|
case topDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "n-resize";
|
||||||
|
else
|
||||||
|
cursorName = "left_ptr";
|
||||||
|
break;
|
||||||
|
case leftDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "nw-resize";
|
||||||
|
else
|
||||||
|
cursorName = "w-resize";
|
||||||
|
break;
|
||||||
|
case rightDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "ne-resize";
|
||||||
|
else
|
||||||
|
cursorName = "e-resize";
|
||||||
|
break;
|
||||||
|
case bottomDecoration:
|
||||||
|
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "sw-resize";
|
||||||
|
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||||
|
cursorName = "se-resize";
|
||||||
|
else
|
||||||
|
cursorName = "s-resize";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
setCursor(cursorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointerHandleButton(void* data,
|
static void pointerHandleButton(void* data,
|
||||||
struct wl_pointer* wl_pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t serial,
|
uint32_t serial,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
uint32_t button,
|
uint32_t button,
|
||||||
@@ -114,8 +223,76 @@ static void pointerHandleButton(void* data,
|
|||||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||||
int glfwButton;
|
int glfwButton;
|
||||||
|
|
||||||
|
// Both xdg-shell and wl_shell use the same values.
|
||||||
|
uint32_t edges = WL_SHELL_SURFACE_RESIZE_NONE;
|
||||||
|
|
||||||
if (!window)
|
if (!window)
|
||||||
return;
|
return;
|
||||||
|
if (button == BTN_LEFT)
|
||||||
|
{
|
||||||
|
switch (window->wl.decorations.focus)
|
||||||
|
{
|
||||||
|
case mainWindow:
|
||||||
|
break;
|
||||||
|
case topDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_TOP;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (window->wl.xdg.toplevel)
|
||||||
|
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
|
||||||
|
else
|
||||||
|
wl_shell_surface_move(window->wl.shellSurface, _glfw.wl.seat, serial);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case leftDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_TOP_LEFT;
|
||||||
|
else
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_LEFT;
|
||||||
|
break;
|
||||||
|
case rightDecoration:
|
||||||
|
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT;
|
||||||
|
else
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_RIGHT;
|
||||||
|
break;
|
||||||
|
case bottomDecoration:
|
||||||
|
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT;
|
||||||
|
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT;
|
||||||
|
else
|
||||||
|
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
if (edges != WL_SHELL_SURFACE_RESIZE_NONE)
|
||||||
|
{
|
||||||
|
if (window->wl.xdg.toplevel)
|
||||||
|
xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
|
||||||
|
serial, edges);
|
||||||
|
else
|
||||||
|
wl_shell_surface_resize(window->wl.shellSurface, _glfw.wl.seat,
|
||||||
|
serial, edges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (button == BTN_RIGHT)
|
||||||
|
{
|
||||||
|
if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel)
|
||||||
|
{
|
||||||
|
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
|
||||||
|
_glfw.wl.seat, serial,
|
||||||
|
window->wl.cursorPosX,
|
||||||
|
window->wl.cursorPosY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don’t pass the button to the user if it was related to a decoration.
|
||||||
|
if (window->wl.decorations.focus != mainWindow)
|
||||||
|
return;
|
||||||
|
|
||||||
_glfw.wl.pointerSerial = serial;
|
_glfw.wl.pointerSerial = serial;
|
||||||
|
|
||||||
@@ -132,7 +309,7 @@ static void pointerHandleButton(void* data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void pointerHandleAxis(void* data,
|
static void pointerHandleAxis(void* data,
|
||||||
struct wl_pointer* wl_pointer,
|
struct wl_pointer* pointer,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
uint32_t axis,
|
uint32_t axis,
|
||||||
wl_fixed_t value)
|
wl_fixed_t value)
|
||||||
@@ -172,16 +349,7 @@ static void keyboardHandleKeymap(void* data,
|
|||||||
int fd,
|
int fd,
|
||||||
uint32_t size)
|
uint32_t size)
|
||||||
{
|
{
|
||||||
struct xkb_keymap* keymap;
|
|
||||||
struct xkb_state* state;
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
struct xkb_compose_table* composeTable;
|
|
||||||
struct xkb_compose_state* composeState;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char* mapStr;
|
char* mapStr;
|
||||||
const char* locale;
|
|
||||||
|
|
||||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
||||||
{
|
{
|
||||||
@@ -194,78 +362,10 @@ static void keyboardHandleKeymap(void* data,
|
|||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
glfw_xkb_compile_keymap(&_glfw.wl.xkb, mapStr);
|
||||||
keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
|
|
||||||
mapStr,
|
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
|
||||||
0);
|
|
||||||
munmap(mapStr, size);
|
munmap(mapStr, size);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (!keymap)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to compile keymap");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = xkb_state_new(keymap);
|
|
||||||
if (!state)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to create XKB state");
|
|
||||||
xkb_keymap_unref(keymap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up the preferred locale, falling back to "C" as default.
|
|
||||||
locale = getenv("LC_ALL");
|
|
||||||
if (!locale)
|
|
||||||
locale = getenv("LC_CTYPE");
|
|
||||||
if (!locale)
|
|
||||||
locale = getenv("LANG");
|
|
||||||
if (!locale)
|
|
||||||
locale = "C";
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
composeTable =
|
|
||||||
xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
|
|
||||||
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
||||||
if (composeTable)
|
|
||||||
{
|
|
||||||
composeState =
|
|
||||||
xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
|
|
||||||
xkb_compose_table_unref(composeTable);
|
|
||||||
if (composeState)
|
|
||||||
_glfw.wl.xkb.composeState = composeState;
|
|
||||||
else
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to create XKB compose state");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to create XKB compose table");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
xkb_keymap_unref(_glfw.wl.xkb.keymap);
|
|
||||||
xkb_state_unref(_glfw.wl.xkb.state);
|
|
||||||
_glfw.wl.xkb.keymap = keymap;
|
|
||||||
_glfw.wl.xkb.state = state;
|
|
||||||
|
|
||||||
_glfw.wl.xkb.controlMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
|
|
||||||
_glfw.wl.xkb.altMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
|
|
||||||
_glfw.wl.xkb.shiftMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
|
|
||||||
_glfw.wl.xkb.superMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
|
|
||||||
_glfw.wl.xkb.capsLockMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
|
|
||||||
_glfw.wl.xkb.numLockMask =
|
|
||||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboardHandleEnter(void* data,
|
static void keyboardHandleEnter(void* data,
|
||||||
@@ -279,6 +379,12 @@ static void keyboardHandleEnter(void* data,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
window = findWindowFromDecorationSurface(surface, NULL);
|
||||||
|
if (!window)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_glfw.wl.keyboardFocus = window;
|
_glfw.wl.keyboardFocus = window;
|
||||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||||
@@ -298,63 +404,6 @@ static void keyboardHandleLeave(void* data,
|
|||||||
_glfwInputWindowFocus(window, GLFW_FALSE);
|
_glfwInputWindowFocus(window, GLFW_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int toGLFWKeyCode(uint32_t key)
|
|
||||||
{
|
|
||||||
if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0]))
|
|
||||||
return _glfw.wl.keycodes[key];
|
|
||||||
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
|
|
||||||
{
|
|
||||||
if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
|
|
||||||
return sym;
|
|
||||||
if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym)
|
|
||||||
!= XKB_COMPOSE_FEED_ACCEPTED)
|
|
||||||
return sym;
|
|
||||||
switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState))
|
|
||||||
{
|
|
||||||
case XKB_COMPOSE_COMPOSED:
|
|
||||||
return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState);
|
|
||||||
case XKB_COMPOSE_COMPOSING:
|
|
||||||
case XKB_COMPOSE_CANCELLED:
|
|
||||||
return XKB_KEY_NoSymbol;
|
|
||||||
case XKB_COMPOSE_NOTHING:
|
|
||||||
default:
|
|
||||||
return sym;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void inputChar(_GLFWwindow* window, uint32_t key)
|
|
||||||
{
|
|
||||||
uint32_t code, numSyms;
|
|
||||||
long cp;
|
|
||||||
const xkb_keysym_t *syms;
|
|
||||||
xkb_keysym_t sym;
|
|
||||||
|
|
||||||
code = key + 8;
|
|
||||||
numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms);
|
|
||||||
|
|
||||||
if (numSyms == 1)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
sym = composeSymbol(syms[0]);
|
|
||||||
#else
|
|
||||||
sym = syms[0];
|
|
||||||
#endif
|
|
||||||
cp = _glfwKeySym2Unicode(sym);
|
|
||||||
if (cp != -1)
|
|
||||||
{
|
|
||||||
const int mods = _glfw.wl.xkb.modifiers;
|
|
||||||
const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
|
|
||||||
_glfwInputChar(window, cp, mods, plain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboardHandleKey(void* data,
|
static void keyboardHandleKey(void* data,
|
||||||
struct wl_keyboard* keyboard,
|
struct wl_keyboard* keyboard,
|
||||||
uint32_t serial,
|
uint32_t serial,
|
||||||
@@ -362,22 +411,19 @@ static void keyboardHandleKey(void* data,
|
|||||||
uint32_t key,
|
uint32_t key,
|
||||||
uint32_t state)
|
uint32_t state)
|
||||||
{
|
{
|
||||||
int keyCode;
|
|
||||||
int action;
|
|
||||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||||
|
|
||||||
if (!window)
|
if (!window)
|
||||||
return;
|
return;
|
||||||
|
int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
|
||||||
|
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);
|
||||||
|
_glfw.wl.keyRepeatInfo.nextRepeatAt = 0;
|
||||||
|
|
||||||
keyCode = toGLFWKeyCode(key);
|
if (action == GLFW_PRESS && _glfw.wl.keyboardRepeatRate > 0 && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))
|
||||||
action = state == WL_KEYBOARD_KEY_STATE_PRESSED
|
{
|
||||||
? GLFW_PRESS : GLFW_RELEASE;
|
_glfw.wl.keyRepeatInfo.key = key;
|
||||||
|
_glfw.wl.keyRepeatInfo.nextRepeatAt = glfwGetTime() + (double)(_glfw.wl.keyboardRepeatDelay) / 1000.0;
|
||||||
_glfwInputKey(window, keyCode, key, action,
|
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
|
||||||
_glfw.wl.xkb.modifiers);
|
}
|
||||||
|
|
||||||
if (action == GLFW_PRESS)
|
|
||||||
inputChar(window, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboardHandleModifiers(void* data,
|
static void keyboardHandleModifiers(void* data,
|
||||||
@@ -388,38 +434,19 @@ static void keyboardHandleModifiers(void* data,
|
|||||||
uint32_t modsLocked,
|
uint32_t modsLocked,
|
||||||
uint32_t group)
|
uint32_t group)
|
||||||
{
|
{
|
||||||
xkb_mod_mask_t mask;
|
glfw_xkb_update_modifiers(&_glfw.wl.xkb, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
||||||
unsigned int modifiers = 0;
|
}
|
||||||
|
|
||||||
if (!_glfw.wl.xkb.keymap)
|
static void keyboardHandleRepeatInfo(void* data,
|
||||||
|
struct wl_keyboard* keyboard,
|
||||||
|
int32_t rate,
|
||||||
|
int32_t delay)
|
||||||
|
{
|
||||||
|
if (keyboard != _glfw.wl.keyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
xkb_state_update_mask(_glfw.wl.xkb.state,
|
_glfw.wl.keyboardRepeatRate = rate;
|
||||||
modsDepressed,
|
_glfw.wl.keyboardRepeatDelay = delay;
|
||||||
modsLatched,
|
|
||||||
modsLocked,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
group);
|
|
||||||
|
|
||||||
mask = xkb_state_serialize_mods(_glfw.wl.xkb.state,
|
|
||||||
XKB_STATE_MODS_DEPRESSED |
|
|
||||||
XKB_STATE_LAYOUT_DEPRESSED |
|
|
||||||
XKB_STATE_MODS_LATCHED |
|
|
||||||
XKB_STATE_LAYOUT_LATCHED);
|
|
||||||
if (mask & _glfw.wl.xkb.controlMask)
|
|
||||||
modifiers |= GLFW_MOD_CONTROL;
|
|
||||||
if (mask & _glfw.wl.xkb.altMask)
|
|
||||||
modifiers |= GLFW_MOD_ALT;
|
|
||||||
if (mask & _glfw.wl.xkb.shiftMask)
|
|
||||||
modifiers |= GLFW_MOD_SHIFT;
|
|
||||||
if (mask & _glfw.wl.xkb.superMask)
|
|
||||||
modifiers |= GLFW_MOD_SUPER;
|
|
||||||
if (mask & _glfw.wl.xkb.capsLockMask)
|
|
||||||
modifiers |= GLFW_MOD_CAPS_LOCK;
|
|
||||||
if (mask & _glfw.wl.xkb.numLockMask)
|
|
||||||
modifiers |= GLFW_MOD_NUM_LOCK;
|
|
||||||
_glfw.wl.xkb.modifiers = modifiers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_keyboard_listener keyboardListener = {
|
static const struct wl_keyboard_listener keyboardListener = {
|
||||||
@@ -428,6 +455,7 @@ static const struct wl_keyboard_listener keyboardListener = {
|
|||||||
keyboardHandleLeave,
|
keyboardHandleLeave,
|
||||||
keyboardHandleKey,
|
keyboardHandleKey,
|
||||||
keyboardHandleModifiers,
|
keyboardHandleModifiers,
|
||||||
|
keyboardHandleRepeatInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void seatHandleCapabilities(void* data,
|
static void seatHandleCapabilities(void* data,
|
||||||
@@ -457,8 +485,15 @@ static void seatHandleCapabilities(void* data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void seatHandleName(void* data,
|
||||||
|
struct wl_seat* seat,
|
||||||
|
const char* name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static const struct wl_seat_listener seatListener = {
|
static const struct wl_seat_listener seatListener = {
|
||||||
seatHandleCapabilities
|
seatHandleCapabilities,
|
||||||
|
seatHandleName,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void wmBaseHandlePing(void* data,
|
static void wmBaseHandlePing(void* data,
|
||||||
@@ -485,6 +520,11 @@ static void registryHandleGlobal(void* data,
|
|||||||
wl_registry_bind(registry, name, &wl_compositor_interface,
|
wl_registry_bind(registry, name, &wl_compositor_interface,
|
||||||
_glfw.wl.compositorVersion);
|
_glfw.wl.compositorVersion);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(interface, "wl_subcompositor") == 0)
|
||||||
|
{
|
||||||
|
_glfw.wl.subcompositor =
|
||||||
|
wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
|
||||||
|
}
|
||||||
else if (strcmp(interface, "wl_shm") == 0)
|
else if (strcmp(interface, "wl_shm") == 0)
|
||||||
{
|
{
|
||||||
_glfw.wl.shm =
|
_glfw.wl.shm =
|
||||||
@@ -503,8 +543,10 @@ static void registryHandleGlobal(void* data,
|
|||||||
{
|
{
|
||||||
if (!_glfw.wl.seat)
|
if (!_glfw.wl.seat)
|
||||||
{
|
{
|
||||||
|
_glfw.wl.seatVersion = min(4, version);
|
||||||
_glfw.wl.seat =
|
_glfw.wl.seat =
|
||||||
wl_registry_bind(registry, name, &wl_seat_interface, 1);
|
wl_registry_bind(registry, name, &wl_seat_interface,
|
||||||
|
_glfw.wl.seatVersion);
|
||||||
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,6 +556,11 @@ static void registryHandleGlobal(void* data,
|
|||||||
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
||||||
xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
|
xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(interface, "wp_viewporter") == 0)
|
||||||
|
{
|
||||||
|
_glfw.wl.viewporter =
|
||||||
|
wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
|
||||||
|
}
|
||||||
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
|
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
|
||||||
{
|
{
|
||||||
_glfw.wl.relativePointerManager =
|
_glfw.wl.relativePointerManager =
|
||||||
@@ -541,6 +588,18 @@ static void registryHandleGlobalRemove(void *data,
|
|||||||
struct wl_registry *registry,
|
struct wl_registry *registry,
|
||||||
uint32_t name)
|
uint32_t name)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
_GLFWmonitor* monitor;
|
||||||
|
|
||||||
|
for (i = 0; i < _glfw.monitorCount; ++i)
|
||||||
|
{
|
||||||
|
monitor = _glfw.monitors[i];
|
||||||
|
if (monitor->wl.name == name)
|
||||||
|
{
|
||||||
|
_glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -549,140 +608,6 @@ static const struct wl_registry_listener registryListener = {
|
|||||||
registryHandleGlobalRemove
|
registryHandleGlobalRemove
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create key code translation tables
|
|
||||||
//
|
|
||||||
static void createKeyTables(void)
|
|
||||||
{
|
|
||||||
int scancode;
|
|
||||||
|
|
||||||
memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes));
|
|
||||||
memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes));
|
|
||||||
|
|
||||||
_glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT;
|
|
||||||
_glfw.wl.keycodes[KEY_1] = GLFW_KEY_1;
|
|
||||||
_glfw.wl.keycodes[KEY_2] = GLFW_KEY_2;
|
|
||||||
_glfw.wl.keycodes[KEY_3] = GLFW_KEY_3;
|
|
||||||
_glfw.wl.keycodes[KEY_4] = GLFW_KEY_4;
|
|
||||||
_glfw.wl.keycodes[KEY_5] = GLFW_KEY_5;
|
|
||||||
_glfw.wl.keycodes[KEY_6] = GLFW_KEY_6;
|
|
||||||
_glfw.wl.keycodes[KEY_7] = GLFW_KEY_7;
|
|
||||||
_glfw.wl.keycodes[KEY_8] = GLFW_KEY_8;
|
|
||||||
_glfw.wl.keycodes[KEY_9] = GLFW_KEY_9;
|
|
||||||
_glfw.wl.keycodes[KEY_0] = GLFW_KEY_0;
|
|
||||||
_glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE;
|
|
||||||
_glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS;
|
|
||||||
_glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL;
|
|
||||||
_glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q;
|
|
||||||
_glfw.wl.keycodes[KEY_W] = GLFW_KEY_W;
|
|
||||||
_glfw.wl.keycodes[KEY_E] = GLFW_KEY_E;
|
|
||||||
_glfw.wl.keycodes[KEY_R] = GLFW_KEY_R;
|
|
||||||
_glfw.wl.keycodes[KEY_T] = GLFW_KEY_T;
|
|
||||||
_glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y;
|
|
||||||
_glfw.wl.keycodes[KEY_U] = GLFW_KEY_U;
|
|
||||||
_glfw.wl.keycodes[KEY_I] = GLFW_KEY_I;
|
|
||||||
_glfw.wl.keycodes[KEY_O] = GLFW_KEY_O;
|
|
||||||
_glfw.wl.keycodes[KEY_P] = GLFW_KEY_P;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET;
|
|
||||||
_glfw.wl.keycodes[KEY_A] = GLFW_KEY_A;
|
|
||||||
_glfw.wl.keycodes[KEY_S] = GLFW_KEY_S;
|
|
||||||
_glfw.wl.keycodes[KEY_D] = GLFW_KEY_D;
|
|
||||||
_glfw.wl.keycodes[KEY_F] = GLFW_KEY_F;
|
|
||||||
_glfw.wl.keycodes[KEY_G] = GLFW_KEY_G;
|
|
||||||
_glfw.wl.keycodes[KEY_H] = GLFW_KEY_H;
|
|
||||||
_glfw.wl.keycodes[KEY_J] = GLFW_KEY_J;
|
|
||||||
_glfw.wl.keycodes[KEY_K] = GLFW_KEY_K;
|
|
||||||
_glfw.wl.keycodes[KEY_L] = GLFW_KEY_L;
|
|
||||||
_glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON;
|
|
||||||
_glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE;
|
|
||||||
_glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z;
|
|
||||||
_glfw.wl.keycodes[KEY_X] = GLFW_KEY_X;
|
|
||||||
_glfw.wl.keycodes[KEY_C] = GLFW_KEY_C;
|
|
||||||
_glfw.wl.keycodes[KEY_V] = GLFW_KEY_V;
|
|
||||||
_glfw.wl.keycodes[KEY_B] = GLFW_KEY_B;
|
|
||||||
_glfw.wl.keycodes[KEY_N] = GLFW_KEY_N;
|
|
||||||
_glfw.wl.keycodes[KEY_M] = GLFW_KEY_M;
|
|
||||||
_glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA;
|
|
||||||
_glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD;
|
|
||||||
_glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH;
|
|
||||||
_glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH;
|
|
||||||
_glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE;
|
|
||||||
_glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER;
|
|
||||||
_glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU;
|
|
||||||
_glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK;
|
|
||||||
_glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK;
|
|
||||||
_glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN;
|
|
||||||
_glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK;
|
|
||||||
_glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE;
|
|
||||||
_glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE;
|
|
||||||
_glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE;
|
|
||||||
_glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER;
|
|
||||||
_glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME;
|
|
||||||
_glfw.wl.keycodes[KEY_END] = GLFW_KEY_END;
|
|
||||||
_glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP;
|
|
||||||
_glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN;
|
|
||||||
_glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT;
|
|
||||||
_glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT;
|
|
||||||
_glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT;
|
|
||||||
_glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN;
|
|
||||||
_glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP;
|
|
||||||
_glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1;
|
|
||||||
_glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2;
|
|
||||||
_glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3;
|
|
||||||
_glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4;
|
|
||||||
_glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5;
|
|
||||||
_glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6;
|
|
||||||
_glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7;
|
|
||||||
_glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8;
|
|
||||||
_glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9;
|
|
||||||
_glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10;
|
|
||||||
_glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11;
|
|
||||||
_glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12;
|
|
||||||
_glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13;
|
|
||||||
_glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14;
|
|
||||||
_glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15;
|
|
||||||
_glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16;
|
|
||||||
_glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17;
|
|
||||||
_glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18;
|
|
||||||
_glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19;
|
|
||||||
_glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20;
|
|
||||||
_glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21;
|
|
||||||
_glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22;
|
|
||||||
_glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23;
|
|
||||||
_glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24;
|
|
||||||
_glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE;
|
|
||||||
_glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY;
|
|
||||||
_glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT;
|
|
||||||
_glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD;
|
|
||||||
_glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0;
|
|
||||||
_glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1;
|
|
||||||
_glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2;
|
|
||||||
_glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3;
|
|
||||||
_glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4;
|
|
||||||
_glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5;
|
|
||||||
_glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6;
|
|
||||||
_glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7;
|
|
||||||
_glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8;
|
|
||||||
_glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9;
|
|
||||||
_glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL;
|
|
||||||
_glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL;
|
|
||||||
_glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER;
|
|
||||||
|
|
||||||
for (scancode = 0; scancode < 256; scancode++)
|
|
||||||
{
|
|
||||||
if (_glfw.wl.keycodes[scancode] > 0)
|
|
||||||
_glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
@@ -722,52 +647,6 @@ int _glfwPlatformInit(void)
|
|||||||
_glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize)
|
_glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize)
|
||||||
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
|
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
|
||||||
|
|
||||||
_glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0");
|
|
||||||
if (!_glfw.wl.xkb.handle)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to open libxkbcommon");
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfw.wl.xkb.context_new = (PFN_xkb_context_new)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new");
|
|
||||||
_glfw.wl.xkb.context_unref = (PFN_xkb_context_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref");
|
|
||||||
_glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string");
|
|
||||||
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref");
|
|
||||||
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
|
|
||||||
_glfw.wl.xkb.state_new = (PFN_xkb_state_new)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new");
|
|
||||||
_glfw.wl.xkb.state_unref = (PFN_xkb_state_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref");
|
|
||||||
_glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms");
|
|
||||||
_glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
|
|
||||||
_glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
_glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale");
|
|
||||||
_glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref");
|
|
||||||
_glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new");
|
|
||||||
_glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref");
|
|
||||||
_glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed");
|
|
||||||
_glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status");
|
|
||||||
_glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym)
|
|
||||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_glfw.wl.display = wl_display_connect(NULL);
|
_glfw.wl.display = wl_display_connect(NULL);
|
||||||
if (!_glfw.wl.display)
|
if (!_glfw.wl.display)
|
||||||
{
|
{
|
||||||
@@ -779,15 +658,7 @@ int _glfwPlatformInit(void)
|
|||||||
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
|
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
|
||||||
wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL);
|
wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL);
|
||||||
|
|
||||||
createKeyTables();
|
if (!glfw_xkb_create_context(&_glfw.wl.xkb)) return GLFW_FALSE;
|
||||||
|
|
||||||
_glfw.wl.xkb.context = xkb_context_new(0);
|
|
||||||
if (!_glfw.wl.xkb.context)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Failed to initialize xkb context");
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync so we got all registry objects
|
// Sync so we got all registry objects
|
||||||
wl_display_roundtrip(_glfw.wl.display);
|
wl_display_roundtrip(_glfw.wl.display);
|
||||||
@@ -795,8 +666,10 @@ int _glfwPlatformInit(void)
|
|||||||
// Sync so we got all initial output events
|
// Sync so we got all initial output events
|
||||||
wl_display_roundtrip(_glfw.wl.display);
|
wl_display_roundtrip(_glfw.wl.display);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
if (!_glfwInitJoysticksLinux())
|
if (!_glfwInitJoysticksLinux())
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
_glfwInitTimerPOSIX();
|
_glfwInitTimerPOSIX();
|
||||||
|
|
||||||
@@ -818,7 +691,9 @@ int _glfwPlatformInit(void)
|
|||||||
|
|
||||||
void _glfwPlatformTerminate(void)
|
void _glfwPlatformTerminate(void)
|
||||||
{
|
{
|
||||||
|
#ifdef __linux__
|
||||||
_glfwTerminateJoysticksLinux();
|
_glfwTerminateJoysticksLinux();
|
||||||
|
#endif
|
||||||
_glfwTerminateEGL();
|
_glfwTerminateEGL();
|
||||||
if (_glfw.wl.egl.handle)
|
if (_glfw.wl.egl.handle)
|
||||||
{
|
{
|
||||||
@@ -826,21 +701,7 @@ void _glfwPlatformTerminate(void)
|
|||||||
_glfw.wl.egl.handle = NULL;
|
_glfw.wl.egl.handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
glfw_xkb_release(&_glfw.wl.xkb);
|
||||||
if (_glfw.wl.xkb.composeState)
|
|
||||||
xkb_compose_state_unref(_glfw.wl.xkb.composeState);
|
|
||||||
#endif
|
|
||||||
if (_glfw.wl.xkb.keymap)
|
|
||||||
xkb_keymap_unref(_glfw.wl.xkb.keymap);
|
|
||||||
if (_glfw.wl.xkb.state)
|
|
||||||
xkb_state_unref(_glfw.wl.xkb.state);
|
|
||||||
if (_glfw.wl.xkb.context)
|
|
||||||
xkb_context_unref(_glfw.wl.xkb.context);
|
|
||||||
if (_glfw.wl.xkb.handle)
|
|
||||||
{
|
|
||||||
_glfw_dlclose(_glfw.wl.xkb.handle);
|
|
||||||
_glfw.wl.xkb.handle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_glfw.wl.cursorTheme)
|
if (_glfw.wl.cursorTheme)
|
||||||
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
|
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
|
||||||
@@ -852,12 +713,16 @@ void _glfwPlatformTerminate(void)
|
|||||||
|
|
||||||
if (_glfw.wl.cursorSurface)
|
if (_glfw.wl.cursorSurface)
|
||||||
wl_surface_destroy(_glfw.wl.cursorSurface);
|
wl_surface_destroy(_glfw.wl.cursorSurface);
|
||||||
|
if (_glfw.wl.subcompositor)
|
||||||
|
wl_subcompositor_destroy(_glfw.wl.subcompositor);
|
||||||
if (_glfw.wl.compositor)
|
if (_glfw.wl.compositor)
|
||||||
wl_compositor_destroy(_glfw.wl.compositor);
|
wl_compositor_destroy(_glfw.wl.compositor);
|
||||||
if (_glfw.wl.shm)
|
if (_glfw.wl.shm)
|
||||||
wl_shm_destroy(_glfw.wl.shm);
|
wl_shm_destroy(_glfw.wl.shm);
|
||||||
if (_glfw.wl.shell)
|
if (_glfw.wl.shell)
|
||||||
wl_shell_destroy(_glfw.wl.shell);
|
wl_shell_destroy(_glfw.wl.shell);
|
||||||
|
if (_glfw.wl.viewporter)
|
||||||
|
wp_viewporter_destroy(_glfw.wl.viewporter);
|
||||||
if (_glfw.wl.wmBase)
|
if (_glfw.wl.wmBase)
|
||||||
xdg_wm_base_destroy(_glfw.wl.wmBase);
|
xdg_wm_base_destroy(_glfw.wl.wmBase);
|
||||||
if (_glfw.wl.pointer)
|
if (_glfw.wl.pointer)
|
||||||
@@ -895,4 +760,3 @@ const char* _glfwPlatformGetVersionString(void)
|
|||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
glfw/wl_monitor.c
vendored
7
glfw/wl_monitor.c
vendored
@@ -136,6 +136,7 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
|
|||||||
|
|
||||||
monitor->wl.scale = 1;
|
monitor->wl.scale = 1;
|
||||||
monitor->wl.output = output;
|
monitor->wl.output = output;
|
||||||
|
monitor->wl.name = name;
|
||||||
|
|
||||||
wl_output_add_listener(output, &outputListener, monitor);
|
wl_output_add_listener(output, &outputListener, monitor);
|
||||||
}
|
}
|
||||||
@@ -145,6 +146,12 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
if (monitor->wl.output)
|
||||||
|
wl_output_destroy(monitor->wl.output);
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
if (xpos)
|
if (xpos)
|
||||||
|
|||||||
126
glfw/wl_platform.h
vendored
126
glfw/wl_platform.h
vendored
@@ -25,10 +25,6 @@
|
|||||||
//========================================================================
|
//========================================================================
|
||||||
|
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
#include <xkbcommon/xkbcommon-compose.h>
|
|
||||||
#endif
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
||||||
@@ -47,12 +43,17 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
|||||||
|
|
||||||
#include "posix_thread.h"
|
#include "posix_thread.h"
|
||||||
#include "posix_time.h"
|
#include "posix_time.h"
|
||||||
|
#ifdef __linux__
|
||||||
#include "linux_joystick.h"
|
#include "linux_joystick.h"
|
||||||
#include "xkb_unicode.h"
|
#else
|
||||||
|
#include "null_joystick.h"
|
||||||
|
#endif
|
||||||
|
#include "xkb_glfw.h"
|
||||||
#include "egl_context.h"
|
#include "egl_context.h"
|
||||||
#include "osmesa_context.h"
|
#include "osmesa_context.h"
|
||||||
|
|
||||||
#include "wayland-xdg-shell-client-protocol.h"
|
#include "wayland-xdg-shell-client-protocol.h"
|
||||||
|
#include "wayland-viewporter-client-protocol.h"
|
||||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
@@ -100,44 +101,28 @@ typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int,
|
|||||||
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
|
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
|
||||||
#define wl_egl_window_resize _glfw.wl.egl.window_resize
|
#define wl_egl_window_resize _glfw.wl.egl.window_resize
|
||||||
|
|
||||||
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
|
#define _GLFW_DECORATION_WIDTH 4
|
||||||
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
|
#define _GLFW_DECORATION_TOP 24
|
||||||
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
|
#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH)
|
||||||
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
|
#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH)
|
||||||
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
|
|
||||||
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
|
|
||||||
typedef void (* PFN_xkb_state_unref)(struct xkb_state*);
|
|
||||||
typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
|
|
||||||
typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
|
|
||||||
typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component);
|
|
||||||
#define xkb_context_new _glfw.wl.xkb.context_new
|
|
||||||
#define xkb_context_unref _glfw.wl.xkb.context_unref
|
|
||||||
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
|
|
||||||
#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref
|
|
||||||
#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index
|
|
||||||
#define xkb_state_new _glfw.wl.xkb.state_new
|
|
||||||
#define xkb_state_unref _glfw.wl.xkb.state_unref
|
|
||||||
#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms
|
|
||||||
#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask
|
|
||||||
#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
typedef enum _GLFWdecorationSideWayland
|
||||||
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
|
{
|
||||||
typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
|
mainWindow,
|
||||||
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
|
topDecoration,
|
||||||
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
|
leftDecoration,
|
||||||
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
|
rightDecoration,
|
||||||
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
|
bottomDecoration,
|
||||||
typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*);
|
|
||||||
#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale
|
|
||||||
#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref
|
|
||||||
#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new
|
|
||||||
#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref
|
|
||||||
#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed
|
|
||||||
#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status
|
|
||||||
#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
} _GLFWdecorationSideWayland;
|
||||||
|
|
||||||
|
typedef struct _GLFWdecorationWayland
|
||||||
|
{
|
||||||
|
struct wl_surface* surface;
|
||||||
|
struct wl_subsurface* subsurface;
|
||||||
|
struct wp_viewport* viewport;
|
||||||
|
|
||||||
|
} _GLFWdecorationWayland;
|
||||||
|
|
||||||
// Wayland-specific per-window data
|
// Wayland-specific per-window data
|
||||||
//
|
//
|
||||||
@@ -180,6 +165,12 @@ typedef struct _GLFWwindowWayland
|
|||||||
// This is a hack to prevent auto-iconification on creation.
|
// This is a hack to prevent auto-iconification on creation.
|
||||||
GLFWbool justCreated;
|
GLFWbool justCreated;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct wl_buffer* buffer;
|
||||||
|
_GLFWdecorationWayland top, left, right, bottom;
|
||||||
|
int focus;
|
||||||
|
} decorations;
|
||||||
|
|
||||||
} _GLFWwindowWayland;
|
} _GLFWwindowWayland;
|
||||||
|
|
||||||
// Wayland-specific global data
|
// Wayland-specific global data
|
||||||
@@ -189,64 +180,33 @@ typedef struct _GLFWlibraryWayland
|
|||||||
struct wl_display* display;
|
struct wl_display* display;
|
||||||
struct wl_registry* registry;
|
struct wl_registry* registry;
|
||||||
struct wl_compositor* compositor;
|
struct wl_compositor* compositor;
|
||||||
|
struct wl_subcompositor* subcompositor;
|
||||||
struct wl_shell* shell;
|
struct wl_shell* shell;
|
||||||
struct wl_shm* shm;
|
struct wl_shm* shm;
|
||||||
struct wl_seat* seat;
|
struct wl_seat* seat;
|
||||||
struct wl_pointer* pointer;
|
struct wl_pointer* pointer;
|
||||||
struct wl_keyboard* keyboard;
|
struct wl_keyboard* keyboard;
|
||||||
struct xdg_wm_base* wmBase;
|
struct xdg_wm_base* wmBase;
|
||||||
|
struct wp_viewporter* viewporter;
|
||||||
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
||||||
struct zwp_pointer_constraints_v1* pointerConstraints;
|
struct zwp_pointer_constraints_v1* pointerConstraints;
|
||||||
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
||||||
|
|
||||||
int compositorVersion;
|
int compositorVersion;
|
||||||
|
int seatVersion;
|
||||||
|
|
||||||
struct wl_cursor_theme* cursorTheme;
|
struct wl_cursor_theme* cursorTheme;
|
||||||
struct wl_surface* cursorSurface;
|
struct wl_surface* cursorSurface;
|
||||||
uint32_t pointerSerial;
|
uint32_t pointerSerial;
|
||||||
|
|
||||||
short int keycodes[256];
|
int32_t keyboardRepeatRate;
|
||||||
short int scancodes[GLFW_KEY_LAST + 1];
|
int32_t keyboardRepeatDelay;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
void* handle;
|
uint32_t key;
|
||||||
struct xkb_context* context;
|
double nextRepeatAt;
|
||||||
struct xkb_keymap* keymap;
|
_GLFWwindow* keyboardFocus;
|
||||||
struct xkb_state* state;
|
} keyRepeatInfo;
|
||||||
|
_GLFWXKBData xkb;
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
struct xkb_compose_state* composeState;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
xkb_mod_mask_t controlMask;
|
|
||||||
xkb_mod_mask_t altMask;
|
|
||||||
xkb_mod_mask_t shiftMask;
|
|
||||||
xkb_mod_mask_t superMask;
|
|
||||||
xkb_mod_mask_t capsLockMask;
|
|
||||||
xkb_mod_mask_t numLockMask;
|
|
||||||
unsigned int modifiers;
|
|
||||||
|
|
||||||
PFN_xkb_context_new context_new;
|
|
||||||
PFN_xkb_context_unref context_unref;
|
|
||||||
PFN_xkb_keymap_new_from_string keymap_new_from_string;
|
|
||||||
PFN_xkb_keymap_unref keymap_unref;
|
|
||||||
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
|
|
||||||
PFN_xkb_state_new state_new;
|
|
||||||
PFN_xkb_state_unref state_unref;
|
|
||||||
PFN_xkb_state_key_get_syms state_key_get_syms;
|
|
||||||
PFN_xkb_state_update_mask state_update_mask;
|
|
||||||
PFN_xkb_state_serialize_mods state_serialize_mods;
|
|
||||||
|
|
||||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
|
||||||
PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
|
|
||||||
PFN_xkb_compose_table_unref compose_table_unref;
|
|
||||||
PFN_xkb_compose_state_new compose_state_new;
|
|
||||||
PFN_xkb_compose_state_unref compose_state_unref;
|
|
||||||
PFN_xkb_compose_state_feed compose_state_feed;
|
|
||||||
PFN_xkb_compose_state_get_status compose_state_get_status;
|
|
||||||
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
|
|
||||||
#endif
|
|
||||||
} xkb;
|
|
||||||
|
|
||||||
_GLFWwindow* pointerFocus;
|
_GLFWwindow* pointerFocus;
|
||||||
_GLFWwindow* keyboardFocus;
|
_GLFWwindow* keyboardFocus;
|
||||||
@@ -275,6 +235,7 @@ typedef struct _GLFWlibraryWayland
|
|||||||
typedef struct _GLFWmonitorWayland
|
typedef struct _GLFWmonitorWayland
|
||||||
{
|
{
|
||||||
struct wl_output* output;
|
struct wl_output* output;
|
||||||
|
int name;
|
||||||
int currentMode;
|
int currentMode;
|
||||||
|
|
||||||
int x;
|
int x;
|
||||||
@@ -295,4 +256,3 @@ typedef struct _GLFWcursorWayland
|
|||||||
|
|
||||||
|
|
||||||
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||||
|
|
||||||
|
|||||||
559
glfw/wl_window.c
vendored
559
glfw/wl_window.c
vendored
@@ -57,6 +57,16 @@ static void handleConfigure(void* data,
|
|||||||
|
|
||||||
if (!window->monitor)
|
if (!window->monitor)
|
||||||
{
|
{
|
||||||
|
if (_glfw.wl.viewporter && window->decorated)
|
||||||
|
{
|
||||||
|
width -= _GLFW_DECORATION_HORIZONTAL;
|
||||||
|
height -= _GLFW_DECORATION_VERTICAL;
|
||||||
|
}
|
||||||
|
if (width < 1)
|
||||||
|
width = 1;
|
||||||
|
if (height < 1)
|
||||||
|
height = 1;
|
||||||
|
|
||||||
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
|
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
|
||||||
{
|
{
|
||||||
aspectRatio = (float)width / (float)height;
|
aspectRatio = (float)width / (float)height;
|
||||||
@@ -94,9 +104,265 @@ static const struct wl_shell_surface_listener shellSurfaceListener = {
|
|||||||
handlePopupDone
|
handlePopupDone
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
createTmpfileCloexec(char* tmpname)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = mkostemp(tmpname, O_CLOEXEC);
|
||||||
|
if (fd >= 0)
|
||||||
|
unlink(tmpname);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new, unique, anonymous file of the given size, and
|
||||||
|
* return the file descriptor for it. The file descriptor is set
|
||||||
|
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
||||||
|
* the given size at offset zero.
|
||||||
|
*
|
||||||
|
* The file should not have a permanent backing store like a disk,
|
||||||
|
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
||||||
|
*
|
||||||
|
* The file name is deleted from the file system.
|
||||||
|
*
|
||||||
|
* The file is suitable for buffer sharing between processes by
|
||||||
|
* transmitting the file descriptor over Unix sockets using the
|
||||||
|
* SCM_RIGHTS methods.
|
||||||
|
*
|
||||||
|
* posix_fallocate() is used to guarantee that disk space is available
|
||||||
|
* for the file at the given size. If disk space is insufficent, errno
|
||||||
|
* is set to ENOSPC. If posix_fallocate() is not supported, program may
|
||||||
|
* receive SIGBUS on accessing mmap()'ed file contents instead.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
createAnonymousFile(off_t size)
|
||||||
|
{
|
||||||
|
static const char template[] = "/glfw-shared-XXXXXX";
|
||||||
|
const char* path;
|
||||||
|
char* name;
|
||||||
|
int fd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
path = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = calloc(strlen(path) + sizeof(template), 1);
|
||||||
|
strcpy(name, path);
|
||||||
|
strcat(name, template);
|
||||||
|
|
||||||
|
fd = createTmpfileCloexec(name);
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
ret = posix_fallocate(fd, 0, size);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
errno = ret;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wl_buffer* createShmBuffer(const GLFWimage* image)
|
||||||
|
{
|
||||||
|
struct wl_shm_pool* pool;
|
||||||
|
struct wl_buffer* buffer;
|
||||||
|
int stride = image->width * 4;
|
||||||
|
int length = image->width * image->height * 4;
|
||||||
|
void* data;
|
||||||
|
int fd, i;
|
||||||
|
|
||||||
|
fd = createAnonymousFile(length);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Creating a buffer file for %d B failed: %m",
|
||||||
|
length);
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (data == MAP_FAILED)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: mmap failed: %m");
|
||||||
|
close(fd);
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
unsigned char* source = (unsigned char*) image->pixels;
|
||||||
|
unsigned char* target = data;
|
||||||
|
for (i = 0; i < image->width * image->height; i++, source += 4)
|
||||||
|
{
|
||||||
|
unsigned int alpha = source[3];
|
||||||
|
|
||||||
|
*target++ = (unsigned char) ((source[2] * alpha) / 255);
|
||||||
|
*target++ = (unsigned char) ((source[1] * alpha) / 255);
|
||||||
|
*target++ = (unsigned char) ((source[0] * alpha) / 255);
|
||||||
|
*target++ = (unsigned char) alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer =
|
||||||
|
wl_shm_pool_create_buffer(pool, 0,
|
||||||
|
image->width,
|
||||||
|
image->height,
|
||||||
|
stride, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
munmap(data, length);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createDecoration(_GLFWdecorationWayland* decoration,
|
||||||
|
struct wl_surface* parent,
|
||||||
|
struct wl_buffer* buffer, GLFWbool opaque,
|
||||||
|
int x, int y,
|
||||||
|
int width, int height)
|
||||||
|
{
|
||||||
|
struct wl_region* region;
|
||||||
|
|
||||||
|
decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||||
|
decoration->subsurface =
|
||||||
|
wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
|
||||||
|
decoration->surface, parent);
|
||||||
|
wl_subsurface_set_position(decoration->subsurface, x, y);
|
||||||
|
decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
|
||||||
|
decoration->surface);
|
||||||
|
wp_viewport_set_destination(decoration->viewport, width, height);
|
||||||
|
wl_surface_attach(decoration->surface, buffer, 0, 0);
|
||||||
|
|
||||||
|
if (opaque)
|
||||||
|
{
|
||||||
|
region = wl_compositor_create_region(_glfw.wl.compositor);
|
||||||
|
wl_region_add(region, 0, 0, width, height);
|
||||||
|
wl_surface_set_opaque_region(decoration->surface, region);
|
||||||
|
wl_surface_commit(decoration->surface);
|
||||||
|
wl_region_destroy(region);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
wl_surface_commit(decoration->surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createDecorations(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
unsigned char data[] = { 224, 224, 224, 255 };
|
||||||
|
const GLFWimage image = { 1, 1, data };
|
||||||
|
GLFWbool opaque = (data[3] == 255);
|
||||||
|
|
||||||
|
if (!_glfw.wl.viewporter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!window->wl.decorations.buffer)
|
||||||
|
window->wl.decorations.buffer = createShmBuffer(&image);
|
||||||
|
|
||||||
|
createDecoration(&window->wl.decorations.top, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
0, -_GLFW_DECORATION_TOP,
|
||||||
|
window->wl.width, _GLFW_DECORATION_TOP);
|
||||||
|
createDecoration(&window->wl.decorations.left, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
-_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
createDecoration(&window->wl.decorations.right, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
window->wl.width, -_GLFW_DECORATION_TOP,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
createDecoration(&window->wl.decorations.bottom, window->wl.surface,
|
||||||
|
window->wl.decorations.buffer, opaque,
|
||||||
|
-_GLFW_DECORATION_WIDTH, window->wl.height,
|
||||||
|
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroyDecoration(_GLFWdecorationWayland* decoration)
|
||||||
|
{
|
||||||
|
if (decoration->surface)
|
||||||
|
wl_surface_destroy(decoration->surface);
|
||||||
|
if (decoration->subsurface)
|
||||||
|
wl_subsurface_destroy(decoration->subsurface);
|
||||||
|
if (decoration->viewport)
|
||||||
|
wp_viewport_destroy(decoration->viewport);
|
||||||
|
decoration->surface = NULL;
|
||||||
|
decoration->subsurface = NULL;
|
||||||
|
decoration->viewport = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroyDecorations(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
destroyDecoration(&window->wl.decorations.top);
|
||||||
|
destroyDecoration(&window->wl.decorations.left);
|
||||||
|
destroyDecoration(&window->wl.decorations.right);
|
||||||
|
destroyDecoration(&window->wl.decorations.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes the surface considered as XRGB instead of ARGB.
|
||||||
|
static void setOpaqueRegion(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
struct wl_region* region;
|
||||||
|
|
||||||
|
region = wl_compositor_create_region(_glfw.wl.compositor);
|
||||||
|
if (!region)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
|
||||||
|
wl_surface_set_opaque_region(window->wl.surface, region);
|
||||||
|
wl_surface_commit(window->wl.surface);
|
||||||
|
wl_region_destroy(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void resizeWindow(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
int scale = window->wl.scale;
|
||||||
|
int scaledWidth = window->wl.width * scale;
|
||||||
|
int scaledHeight = window->wl.height * scale;
|
||||||
|
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
||||||
|
if (!window->wl.transparent)
|
||||||
|
setOpaqueRegion(window);
|
||||||
|
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
||||||
|
_glfwInputWindowContentScale(window, scale, scale);
|
||||||
|
|
||||||
|
if (!window->wl.decorations.top.surface)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Top decoration.
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.top.viewport,
|
||||||
|
window->wl.width, _GLFW_DECORATION_TOP);
|
||||||
|
wl_surface_commit(window->wl.decorations.top.surface);
|
||||||
|
|
||||||
|
// Left decoration.
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.left.viewport,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
wl_surface_commit(window->wl.decorations.left.surface);
|
||||||
|
|
||||||
|
// Right decoration.
|
||||||
|
wl_subsurface_set_position(window->wl.decorations.right.subsurface,
|
||||||
|
window->wl.width, -_GLFW_DECORATION_TOP);
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.right.viewport,
|
||||||
|
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||||
|
wl_surface_commit(window->wl.decorations.right.surface);
|
||||||
|
|
||||||
|
// Bottom decoration.
|
||||||
|
wl_subsurface_set_position(window->wl.decorations.bottom.subsurface,
|
||||||
|
-_GLFW_DECORATION_WIDTH, window->wl.height);
|
||||||
|
wp_viewport_set_destination(window->wl.decorations.bottom.viewport,
|
||||||
|
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
|
||||||
|
wl_surface_commit(window->wl.decorations.bottom.surface);
|
||||||
|
}
|
||||||
|
|
||||||
static void checkScaleChange(_GLFWwindow* window)
|
static void checkScaleChange(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
int scaledWidth, scaledHeight;
|
|
||||||
int scale = 1;
|
int scale = 1;
|
||||||
int i;
|
int i;
|
||||||
int monitorScale;
|
int monitorScale;
|
||||||
@@ -117,12 +383,8 @@ static void checkScaleChange(_GLFWwindow* window)
|
|||||||
if (scale != window->wl.scale)
|
if (scale != window->wl.scale)
|
||||||
{
|
{
|
||||||
window->wl.scale = scale;
|
window->wl.scale = scale;
|
||||||
scaledWidth = window->wl.width * scale;
|
|
||||||
scaledHeight = window->wl.height * scale;
|
|
||||||
wl_surface_set_buffer_scale(window->wl.surface, scale);
|
wl_surface_set_buffer_scale(window->wl.surface, scale);
|
||||||
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
resizeWindow(window);
|
||||||
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
|
||||||
_glfwInputWindowContentScale(window, scale, scale);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,21 +434,6 @@ static const struct wl_surface_listener surfaceListener = {
|
|||||||
handleLeave
|
handleLeave
|
||||||
};
|
};
|
||||||
|
|
||||||
// Makes the surface considered as XRGB instead of ARGB.
|
|
||||||
static void setOpaqueRegion(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
struct wl_region* region;
|
|
||||||
|
|
||||||
region = wl_compositor_create_region(_glfw.wl.compositor);
|
|
||||||
if (!region)
|
|
||||||
return;
|
|
||||||
|
|
||||||
wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
|
|
||||||
wl_surface_set_opaque_region(window->wl.surface, region);
|
|
||||||
wl_surface_commit(window->wl.surface);
|
|
||||||
wl_region_destroy(region);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
|
static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
|
||||||
{
|
{
|
||||||
if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
|
if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
|
||||||
@@ -231,9 +478,32 @@ static GLFWbool createSurface(_GLFWwindow* window,
|
|||||||
if (!window->wl.transparent)
|
if (!window->wl.transparent)
|
||||||
setOpaqueRegion(window);
|
setOpaqueRegion(window);
|
||||||
|
|
||||||
|
if (window->decorated && !window->monitor)
|
||||||
|
createDecorations(window);
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refreshRate)
|
||||||
|
{
|
||||||
|
if (window->wl.xdg.toplevel)
|
||||||
|
{
|
||||||
|
xdg_toplevel_set_fullscreen(
|
||||||
|
window->wl.xdg.toplevel,
|
||||||
|
monitor->wl.output);
|
||||||
|
}
|
||||||
|
else if (window->wl.shellSurface)
|
||||||
|
{
|
||||||
|
wl_shell_surface_set_fullscreen(
|
||||||
|
window->wl.shellSurface,
|
||||||
|
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
|
||||||
|
refreshRate * 1000, // Convert Hz to mHz.
|
||||||
|
monitor->wl.output);
|
||||||
|
}
|
||||||
|
setIdleInhibitor(window, GLFW_TRUE);
|
||||||
|
destroyDecorations(window);
|
||||||
|
}
|
||||||
|
|
||||||
static GLFWbool createShellSurface(_GLFWwindow* window)
|
static GLFWbool createShellSurface(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
if (!_glfw.wl.shell)
|
if (!_glfw.wl.shell)
|
||||||
@@ -261,12 +531,7 @@ static GLFWbool createShellSurface(_GLFWwindow* window)
|
|||||||
|
|
||||||
if (window->monitor)
|
if (window->monitor)
|
||||||
{
|
{
|
||||||
wl_shell_surface_set_fullscreen(
|
setFullscreen(window, window->monitor, 0);
|
||||||
window->wl.shellSurface,
|
|
||||||
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
|
|
||||||
0,
|
|
||||||
window->monitor->wl.output);
|
|
||||||
setIdleInhibitor(window, GLFW_TRUE);
|
|
||||||
}
|
}
|
||||||
else if (window->wl.maximized)
|
else if (window->wl.maximized)
|
||||||
{
|
{
|
||||||
@@ -424,25 +689,32 @@ static GLFWbool createXdgSurface(_GLFWwindow* window)
|
|||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dispatchPendingKeyRepeats() {
|
||||||
|
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||||
|
double now = glfwGetTime();
|
||||||
|
while (_glfw.wl.keyRepeatInfo.nextRepeatAt <= now) {
|
||||||
|
glfw_xkb_handle_key_event(_glfw.wl.keyRepeatInfo.keyboardFocus, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
|
||||||
|
_glfw.wl.keyRepeatInfo.nextRepeatAt += 1.0 / _glfw.wl.keyboardRepeatRate;
|
||||||
|
now = glfwGetTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
createTmpfileCloexec(char* tmpname)
|
adjustTimeoutForKeyRepeat(int timeout) {
|
||||||
{
|
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return timeout;
|
||||||
int fd;
|
double now = glfwGetTime();
|
||||||
|
if (timeout < 0 || now + timeout / 1000. > _glfw.wl.keyRepeatInfo.nextRepeatAt) {
|
||||||
fd = mkostemp(tmpname, O_CLOEXEC);
|
timeout = _glfw.wl.keyRepeatInfo.nextRepeatAt <= now ? 0 : ( (_glfw.wl.keyRepeatInfo.nextRepeatAt - now) * 1000 + 1 );
|
||||||
if (fd >= 0)
|
}
|
||||||
unlink(tmpname);
|
return timeout;
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handleEvents(int timeout)
|
handleEvents(int timeout)
|
||||||
{
|
{
|
||||||
struct wl_display* display = _glfw.wl.display;
|
struct wl_display* display = _glfw.wl.display;
|
||||||
struct pollfd fds[] = {
|
struct pollfd pfd = { wl_display_get_fd(display), POLLIN };
|
||||||
{ wl_display_get_fd(display), POLLIN },
|
|
||||||
};
|
|
||||||
|
|
||||||
while (wl_display_prepare_read(display) != 0)
|
while (wl_display_prepare_read(display) != 0)
|
||||||
wl_display_dispatch_pending(display);
|
wl_display_dispatch_pending(display);
|
||||||
@@ -462,71 +734,27 @@ handleEvents(int timeout)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (poll(fds, 1, timeout) > 0)
|
dispatchPendingKeyRepeats();
|
||||||
|
timeout = adjustTimeoutForKeyRepeat(timeout);
|
||||||
|
|
||||||
|
if (poll(&pfd, 1, timeout) > 0)
|
||||||
{
|
{
|
||||||
wl_display_read_events(display);
|
if (pfd.revents & POLLIN)
|
||||||
wl_display_dispatch_pending(display);
|
{
|
||||||
|
wl_display_read_events(display);
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wl_display_cancel_read(display);
|
wl_display_cancel_read(display);
|
||||||
}
|
}
|
||||||
}
|
dispatchPendingKeyRepeats();
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a new, unique, anonymous file of the given size, and
|
|
||||||
* return the file descriptor for it. The file descriptor is set
|
|
||||||
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
|
||||||
* the given size at offset zero.
|
|
||||||
*
|
|
||||||
* The file should not have a permanent backing store like a disk,
|
|
||||||
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
|
||||||
*
|
|
||||||
* The file name is deleted from the file system.
|
|
||||||
*
|
|
||||||
* The file is suitable for buffer sharing between processes by
|
|
||||||
* transmitting the file descriptor over Unix sockets using the
|
|
||||||
* SCM_RIGHTS methods.
|
|
||||||
*
|
|
||||||
* posix_fallocate() is used to guarantee that disk space is available
|
|
||||||
* for the file at the given size. If disk space is insufficent, errno
|
|
||||||
* is set to ENOSPC. If posix_fallocate() is not supported, program may
|
|
||||||
* receive SIGBUS on accessing mmap()'ed file contents instead.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
createAnonymousFile(off_t size)
|
|
||||||
{
|
|
||||||
static const char template[] = "/glfw-shared-XXXXXX";
|
|
||||||
const char* path;
|
|
||||||
char* name;
|
|
||||||
int fd;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
path = getenv("XDG_RUNTIME_DIR");
|
|
||||||
if (!path)
|
|
||||||
{
|
|
||||||
errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = calloc(strlen(path) + sizeof(template), 1);
|
|
||||||
strcpy(name, path);
|
|
||||||
strcat(name, template);
|
|
||||||
|
|
||||||
fd = createTmpfileCloexec(name);
|
|
||||||
|
|
||||||
free(name);
|
|
||||||
|
|
||||||
if (fd < 0)
|
|
||||||
return -1;
|
|
||||||
ret = posix_fallocate(fd, 0, size);
|
|
||||||
if (ret != 0)
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
errno = ret;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translates a GLFW standard cursor to a theme cursor name
|
// Translates a GLFW standard cursor to a theme cursor name
|
||||||
@@ -639,6 +867,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
|||||||
if (window->context.destroy)
|
if (window->context.destroy)
|
||||||
window->context.destroy(window);
|
window->context.destroy(window);
|
||||||
|
|
||||||
|
destroyDecorations(window);
|
||||||
|
if (window->wl.decorations.buffer)
|
||||||
|
wl_buffer_destroy(window->wl.decorations.buffer);
|
||||||
|
|
||||||
if (window->wl.native)
|
if (window->wl.native)
|
||||||
wl_egl_window_destroy(window->wl.native);
|
wl_egl_window_destroy(window->wl.native);
|
||||||
|
|
||||||
@@ -703,14 +935,9 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
|
|||||||
|
|
||||||
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
||||||
{
|
{
|
||||||
int scaledWidth = width * window->wl.scale;
|
|
||||||
int scaledHeight = height * window->wl.scale;
|
|
||||||
window->wl.width = width;
|
window->wl.width = width;
|
||||||
window->wl.height = height;
|
window->wl.height = height;
|
||||||
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
resizeWindow(window);
|
||||||
if (!window->wl.transparent)
|
|
||||||
setOpaqueRegion(window);
|
|
||||||
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
||||||
@@ -754,8 +981,17 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
|||||||
int* left, int* top,
|
int* left, int* top,
|
||||||
int* right, int* bottom)
|
int* right, int* bottom)
|
||||||
{
|
{
|
||||||
// TODO: will need a proper implementation once decorations are
|
if (window->decorated && !window->monitor)
|
||||||
// implemented, but for now just leave everything as 0.
|
{
|
||||||
|
if (top)
|
||||||
|
*top = _GLFW_DECORATION_TOP;
|
||||||
|
if (left)
|
||||||
|
*left = _GLFW_DECORATION_WIDTH;
|
||||||
|
if (right)
|
||||||
|
*right = _GLFW_DECORATION_WIDTH;
|
||||||
|
if (bottom)
|
||||||
|
*bottom = _GLFW_DECORATION_WIDTH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||||
@@ -851,11 +1087,15 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
"Wayland: Window attention request not implemented yet");
|
"Wayland: Window attention request not implemented yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO: Use an actual Wayland API to implement this when one becomes available
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
int fd = open("/dev/tty", O_WRONLY | O_CLOEXEC);
|
||||||
"Wayland: Window bell request not implemented yet");
|
if (fd > -1) {
|
||||||
|
int ret = write(fd, "\x07", 1) == 1 ? GLFW_TRUE : GLFW_FALSE;
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,35 +1111,20 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
|||||||
int width, int height,
|
int width, int height,
|
||||||
int refreshRate)
|
int refreshRate)
|
||||||
{
|
{
|
||||||
if (window->wl.xdg.toplevel)
|
if (monitor)
|
||||||
{
|
{
|
||||||
if (monitor)
|
setFullscreen(window, monitor, refreshRate);
|
||||||
{
|
}
|
||||||
xdg_toplevel_set_fullscreen(
|
else
|
||||||
window->wl.xdg.toplevel,
|
{
|
||||||
monitor->wl.output);
|
if (window->wl.xdg.toplevel)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
|
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
|
||||||
}
|
else if (window->wl.shellSurface)
|
||||||
}
|
|
||||||
else if (window->wl.shellSurface)
|
|
||||||
{
|
|
||||||
if (monitor)
|
|
||||||
{
|
|
||||||
wl_shell_surface_set_fullscreen(
|
|
||||||
window->wl.shellSurface,
|
|
||||||
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
|
|
||||||
refreshRate * 1000, // Convert Hz to mHz.
|
|
||||||
monitor->wl.output);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wl_shell_surface_set_toplevel(window->wl.shellSurface);
|
wl_shell_surface_set_toplevel(window->wl.shellSurface);
|
||||||
}
|
setIdleInhibitor(window, GLFW_FALSE);
|
||||||
|
if (window->decorated)
|
||||||
|
createDecorations(window);
|
||||||
}
|
}
|
||||||
setIdleInhibitor(window, monitor ? GLFW_TRUE : GLFW_FALSE);
|
|
||||||
_glfwInputWindowMonitor(window, monitor);
|
_glfwInputWindowMonitor(window, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,9 +1169,13 @@ void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
|
|||||||
|
|
||||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
|
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
|
||||||
{
|
{
|
||||||
// TODO
|
if (!window->monitor)
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
{
|
||||||
"Wayland: Window attribute setting not implemented yet");
|
if (enabled)
|
||||||
|
createDecorations(window);
|
||||||
|
else
|
||||||
|
destroyDecorations(window);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
|
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
|
||||||
@@ -1013,66 +1242,19 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
|||||||
|
|
||||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||||
{
|
{
|
||||||
// TODO
|
return glfw_xkb_keysym_name(scancode);
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformGetKeyScancode(int key)
|
int _glfwPlatformGetKeyScancode(int key)
|
||||||
{
|
{
|
||||||
return _glfw.wl.scancodes[key];
|
return glfw_xkb_sym_for_key(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||||
const GLFWimage* image,
|
const GLFWimage* image,
|
||||||
int xhot, int yhot)
|
int xhot, int yhot)
|
||||||
{
|
{
|
||||||
struct wl_shm_pool* pool;
|
cursor->wl.buffer = createShmBuffer(image);
|
||||||
int stride = image->width * 4;
|
|
||||||
int length = image->width * image->height * 4;
|
|
||||||
void* data;
|
|
||||||
int fd, i;
|
|
||||||
|
|
||||||
fd = createAnonymousFile(length);
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Creating a buffer file for %d B failed: %m",
|
|
||||||
length);
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (data == MAP_FAILED)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Wayland: Cursor mmap failed: %m");
|
|
||||||
close(fd);
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
unsigned char* source = (unsigned char*) image->pixels;
|
|
||||||
unsigned char* target = data;
|
|
||||||
for (i = 0; i < image->width * image->height; i++, source += 4)
|
|
||||||
{
|
|
||||||
unsigned int alpha = source[3];
|
|
||||||
|
|
||||||
*target++ = (unsigned char) ((source[2] * alpha) / 255);
|
|
||||||
*target++ = (unsigned char) ((source[1] * alpha) / 255);
|
|
||||||
*target++ = (unsigned char) ((source[0] * alpha) / 255);
|
|
||||||
*target++ = (unsigned char) alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor->wl.buffer =
|
|
||||||
wl_shm_pool_create_buffer(pool, 0,
|
|
||||||
image->width,
|
|
||||||
image->height,
|
|
||||||
stride, WL_SHM_FORMAT_ARGB8888);
|
|
||||||
munmap(data, length);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
|
|
||||||
cursor->wl.width = image->width;
|
cursor->wl.width = image->width;
|
||||||
cursor->wl.height = image->height;
|
cursor->wl.height = image->height;
|
||||||
cursor->wl.xhot = xhot;
|
cursor->wl.xhot = xhot;
|
||||||
@@ -1376,4 +1558,3 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
return window->wl.surface;
|
return window->wl.surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
404
glfw/x11_init.c
vendored
404
glfw/x11_init.c
vendored
@@ -36,319 +36,6 @@
|
|||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
|
||||||
|
|
||||||
// Translate an X11 key code to a GLFW key code.
|
|
||||||
//
|
|
||||||
static int translateKeyCode(int scancode)
|
|
||||||
{
|
|
||||||
int keySym;
|
|
||||||
|
|
||||||
// Valid key code range is [8,255], according to the Xlib manual
|
|
||||||
if (scancode < 8 || scancode > 255)
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
|
|
||||||
if (_glfw.x11.xkb.available)
|
|
||||||
{
|
|
||||||
// Try secondary keysym, for numeric keypad keys
|
|
||||||
// Note: This way we always force "NumLock = ON", which is intentional
|
|
||||||
// since the returned key code should correspond to a physical
|
|
||||||
// location.
|
|
||||||
keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 1);
|
|
||||||
switch (keySym)
|
|
||||||
{
|
|
||||||
case XK_KP_0: return GLFW_KEY_KP_0;
|
|
||||||
case XK_KP_1: return GLFW_KEY_KP_1;
|
|
||||||
case XK_KP_2: return GLFW_KEY_KP_2;
|
|
||||||
case XK_KP_3: return GLFW_KEY_KP_3;
|
|
||||||
case XK_KP_4: return GLFW_KEY_KP_4;
|
|
||||||
case XK_KP_5: return GLFW_KEY_KP_5;
|
|
||||||
case XK_KP_6: return GLFW_KEY_KP_6;
|
|
||||||
case XK_KP_7: return GLFW_KEY_KP_7;
|
|
||||||
case XK_KP_8: return GLFW_KEY_KP_8;
|
|
||||||
case XK_KP_9: return GLFW_KEY_KP_9;
|
|
||||||
case XK_KP_Separator:
|
|
||||||
case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL;
|
|
||||||
case XK_KP_Equal: return GLFW_KEY_KP_EQUAL;
|
|
||||||
case XK_KP_Enter: return GLFW_KEY_KP_ENTER;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now try primary keysym for function keys (non-printable keys)
|
|
||||||
// These should not depend on the current keyboard layout
|
|
||||||
keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int dummy;
|
|
||||||
KeySym* keySyms;
|
|
||||||
|
|
||||||
keySyms = XGetKeyboardMapping(_glfw.x11.display, scancode, 1, &dummy);
|
|
||||||
keySym = keySyms[0];
|
|
||||||
XFree(keySyms);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (keySym)
|
|
||||||
{
|
|
||||||
case XK_Escape: return GLFW_KEY_ESCAPE;
|
|
||||||
case XK_Tab: return GLFW_KEY_TAB;
|
|
||||||
case XK_Shift_L: return GLFW_KEY_LEFT_SHIFT;
|
|
||||||
case XK_Shift_R: return GLFW_KEY_RIGHT_SHIFT;
|
|
||||||
case XK_Control_L: return GLFW_KEY_LEFT_CONTROL;
|
|
||||||
case XK_Control_R: return GLFW_KEY_RIGHT_CONTROL;
|
|
||||||
case XK_Meta_L:
|
|
||||||
case XK_Alt_L: return GLFW_KEY_LEFT_ALT;
|
|
||||||
case XK_Mode_switch: // Mapped to Alt_R on many keyboards
|
|
||||||
case XK_ISO_Level3_Shift: // AltGr on at least some machines
|
|
||||||
case XK_Meta_R:
|
|
||||||
case XK_Alt_R: return GLFW_KEY_RIGHT_ALT;
|
|
||||||
case XK_Super_L: return GLFW_KEY_LEFT_SUPER;
|
|
||||||
case XK_Super_R: return GLFW_KEY_RIGHT_SUPER;
|
|
||||||
case XK_Menu: return GLFW_KEY_MENU;
|
|
||||||
case XK_Num_Lock: return GLFW_KEY_NUM_LOCK;
|
|
||||||
case XK_Caps_Lock: return GLFW_KEY_CAPS_LOCK;
|
|
||||||
case XK_Print: return GLFW_KEY_PRINT_SCREEN;
|
|
||||||
case XK_Scroll_Lock: return GLFW_KEY_SCROLL_LOCK;
|
|
||||||
case XK_Pause: return GLFW_KEY_PAUSE;
|
|
||||||
case XK_Delete: return GLFW_KEY_DELETE;
|
|
||||||
case XK_BackSpace: return GLFW_KEY_BACKSPACE;
|
|
||||||
case XK_Return: return GLFW_KEY_ENTER;
|
|
||||||
case XK_Home: return GLFW_KEY_HOME;
|
|
||||||
case XK_End: return GLFW_KEY_END;
|
|
||||||
case XK_Page_Up: return GLFW_KEY_PAGE_UP;
|
|
||||||
case XK_Page_Down: return GLFW_KEY_PAGE_DOWN;
|
|
||||||
case XK_Insert: return GLFW_KEY_INSERT;
|
|
||||||
case XK_Left: return GLFW_KEY_LEFT;
|
|
||||||
case XK_Right: return GLFW_KEY_RIGHT;
|
|
||||||
case XK_Down: return GLFW_KEY_DOWN;
|
|
||||||
case XK_Up: return GLFW_KEY_UP;
|
|
||||||
case XK_F1: return GLFW_KEY_F1;
|
|
||||||
case XK_F2: return GLFW_KEY_F2;
|
|
||||||
case XK_F3: return GLFW_KEY_F3;
|
|
||||||
case XK_F4: return GLFW_KEY_F4;
|
|
||||||
case XK_F5: return GLFW_KEY_F5;
|
|
||||||
case XK_F6: return GLFW_KEY_F6;
|
|
||||||
case XK_F7: return GLFW_KEY_F7;
|
|
||||||
case XK_F8: return GLFW_KEY_F8;
|
|
||||||
case XK_F9: return GLFW_KEY_F9;
|
|
||||||
case XK_F10: return GLFW_KEY_F10;
|
|
||||||
case XK_F11: return GLFW_KEY_F11;
|
|
||||||
case XK_F12: return GLFW_KEY_F12;
|
|
||||||
case XK_F13: return GLFW_KEY_F13;
|
|
||||||
case XK_F14: return GLFW_KEY_F14;
|
|
||||||
case XK_F15: return GLFW_KEY_F15;
|
|
||||||
case XK_F16: return GLFW_KEY_F16;
|
|
||||||
case XK_F17: return GLFW_KEY_F17;
|
|
||||||
case XK_F18: return GLFW_KEY_F18;
|
|
||||||
case XK_F19: return GLFW_KEY_F19;
|
|
||||||
case XK_F20: return GLFW_KEY_F20;
|
|
||||||
case XK_F21: return GLFW_KEY_F21;
|
|
||||||
case XK_F22: return GLFW_KEY_F22;
|
|
||||||
case XK_F23: return GLFW_KEY_F23;
|
|
||||||
case XK_F24: return GLFW_KEY_F24;
|
|
||||||
case XK_F25: return GLFW_KEY_F25;
|
|
||||||
|
|
||||||
// Numeric keypad
|
|
||||||
case XK_KP_Divide: return GLFW_KEY_KP_DIVIDE;
|
|
||||||
case XK_KP_Multiply: return GLFW_KEY_KP_MULTIPLY;
|
|
||||||
case XK_KP_Subtract: return GLFW_KEY_KP_SUBTRACT;
|
|
||||||
case XK_KP_Add: return GLFW_KEY_KP_ADD;
|
|
||||||
|
|
||||||
// These should have been detected in secondary keysym test above!
|
|
||||||
case XK_KP_Insert: return GLFW_KEY_KP_0;
|
|
||||||
case XK_KP_End: return GLFW_KEY_KP_1;
|
|
||||||
case XK_KP_Down: return GLFW_KEY_KP_2;
|
|
||||||
case XK_KP_Page_Down: return GLFW_KEY_KP_3;
|
|
||||||
case XK_KP_Left: return GLFW_KEY_KP_4;
|
|
||||||
case XK_KP_Right: return GLFW_KEY_KP_6;
|
|
||||||
case XK_KP_Home: return GLFW_KEY_KP_7;
|
|
||||||
case XK_KP_Up: return GLFW_KEY_KP_8;
|
|
||||||
case XK_KP_Page_Up: return GLFW_KEY_KP_9;
|
|
||||||
case XK_KP_Delete: return GLFW_KEY_KP_DECIMAL;
|
|
||||||
case XK_KP_Equal: return GLFW_KEY_KP_EQUAL;
|
|
||||||
case XK_KP_Enter: return GLFW_KEY_KP_ENTER;
|
|
||||||
|
|
||||||
// Last resort: Check for printable keys (should not happen if the XKB
|
|
||||||
// extension is available). This will give a layout dependent mapping
|
|
||||||
// (which is wrong, and we may miss some keys, especially on non-US
|
|
||||||
// keyboards), but it's better than nothing...
|
|
||||||
case XK_a: return GLFW_KEY_A;
|
|
||||||
case XK_b: return GLFW_KEY_B;
|
|
||||||
case XK_c: return GLFW_KEY_C;
|
|
||||||
case XK_d: return GLFW_KEY_D;
|
|
||||||
case XK_e: return GLFW_KEY_E;
|
|
||||||
case XK_f: return GLFW_KEY_F;
|
|
||||||
case XK_g: return GLFW_KEY_G;
|
|
||||||
case XK_h: return GLFW_KEY_H;
|
|
||||||
case XK_i: return GLFW_KEY_I;
|
|
||||||
case XK_j: return GLFW_KEY_J;
|
|
||||||
case XK_k: return GLFW_KEY_K;
|
|
||||||
case XK_l: return GLFW_KEY_L;
|
|
||||||
case XK_m: return GLFW_KEY_M;
|
|
||||||
case XK_n: return GLFW_KEY_N;
|
|
||||||
case XK_o: return GLFW_KEY_O;
|
|
||||||
case XK_p: return GLFW_KEY_P;
|
|
||||||
case XK_q: return GLFW_KEY_Q;
|
|
||||||
case XK_r: return GLFW_KEY_R;
|
|
||||||
case XK_s: return GLFW_KEY_S;
|
|
||||||
case XK_t: return GLFW_KEY_T;
|
|
||||||
case XK_u: return GLFW_KEY_U;
|
|
||||||
case XK_v: return GLFW_KEY_V;
|
|
||||||
case XK_w: return GLFW_KEY_W;
|
|
||||||
case XK_x: return GLFW_KEY_X;
|
|
||||||
case XK_y: return GLFW_KEY_Y;
|
|
||||||
case XK_z: return GLFW_KEY_Z;
|
|
||||||
case XK_1: return GLFW_KEY_1;
|
|
||||||
case XK_2: return GLFW_KEY_2;
|
|
||||||
case XK_3: return GLFW_KEY_3;
|
|
||||||
case XK_4: return GLFW_KEY_4;
|
|
||||||
case XK_5: return GLFW_KEY_5;
|
|
||||||
case XK_6: return GLFW_KEY_6;
|
|
||||||
case XK_7: return GLFW_KEY_7;
|
|
||||||
case XK_8: return GLFW_KEY_8;
|
|
||||||
case XK_9: return GLFW_KEY_9;
|
|
||||||
case XK_0: return GLFW_KEY_0;
|
|
||||||
case XK_space: return GLFW_KEY_SPACE;
|
|
||||||
case XK_minus: return GLFW_KEY_MINUS;
|
|
||||||
case XK_equal: return GLFW_KEY_EQUAL;
|
|
||||||
case XK_bracketleft: return GLFW_KEY_LEFT_BRACKET;
|
|
||||||
case XK_bracketright: return GLFW_KEY_RIGHT_BRACKET;
|
|
||||||
case XK_backslash: return GLFW_KEY_BACKSLASH;
|
|
||||||
case XK_semicolon: return GLFW_KEY_SEMICOLON;
|
|
||||||
case XK_apostrophe: return GLFW_KEY_APOSTROPHE;
|
|
||||||
case XK_grave: return GLFW_KEY_GRAVE_ACCENT;
|
|
||||||
case XK_comma: return GLFW_KEY_COMMA;
|
|
||||||
case XK_period: return GLFW_KEY_PERIOD;
|
|
||||||
case XK_slash: return GLFW_KEY_SLASH;
|
|
||||||
case XK_less: return GLFW_KEY_WORLD_1; // At least in some layouts...
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No matching translation was found
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create key code translation tables
|
|
||||||
//
|
|
||||||
static void createKeyTables(void)
|
|
||||||
{
|
|
||||||
int scancode, key;
|
|
||||||
|
|
||||||
memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes));
|
|
||||||
memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes));
|
|
||||||
|
|
||||||
if (_glfw.x11.xkb.available)
|
|
||||||
{
|
|
||||||
// Use XKB to determine physical key locations independently of the
|
|
||||||
// current keyboard layout
|
|
||||||
|
|
||||||
char name[XkbKeyNameLength + 1];
|
|
||||||
XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd);
|
|
||||||
XkbGetNames(_glfw.x11.display, XkbKeyNamesMask, desc);
|
|
||||||
|
|
||||||
// Find the X11 key code -> GLFW key code mapping
|
|
||||||
for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++)
|
|
||||||
{
|
|
||||||
memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength);
|
|
||||||
name[XkbKeyNameLength] = '\0';
|
|
||||||
|
|
||||||
// Map the key name to a GLFW key code. Note: We only map printable
|
|
||||||
// keys here, and we use the US keyboard layout. The rest of the
|
|
||||||
// keys (function keys) are mapped using traditional KeySym
|
|
||||||
// translations.
|
|
||||||
if (strcmp(name, "TLDE") == 0) key = GLFW_KEY_GRAVE_ACCENT;
|
|
||||||
else if (strcmp(name, "AE01") == 0) key = GLFW_KEY_1;
|
|
||||||
else if (strcmp(name, "AE02") == 0) key = GLFW_KEY_2;
|
|
||||||
else if (strcmp(name, "AE03") == 0) key = GLFW_KEY_3;
|
|
||||||
else if (strcmp(name, "AE04") == 0) key = GLFW_KEY_4;
|
|
||||||
else if (strcmp(name, "AE05") == 0) key = GLFW_KEY_5;
|
|
||||||
else if (strcmp(name, "AE06") == 0) key = GLFW_KEY_6;
|
|
||||||
else if (strcmp(name, "AE07") == 0) key = GLFW_KEY_7;
|
|
||||||
else if (strcmp(name, "AE08") == 0) key = GLFW_KEY_8;
|
|
||||||
else if (strcmp(name, "AE09") == 0) key = GLFW_KEY_9;
|
|
||||||
else if (strcmp(name, "AE10") == 0) key = GLFW_KEY_0;
|
|
||||||
else if (strcmp(name, "AE11") == 0) key = GLFW_KEY_MINUS;
|
|
||||||
else if (strcmp(name, "AE12") == 0) key = GLFW_KEY_EQUAL;
|
|
||||||
else if (strcmp(name, "AD01") == 0) key = GLFW_KEY_Q;
|
|
||||||
else if (strcmp(name, "AD02") == 0) key = GLFW_KEY_W;
|
|
||||||
else if (strcmp(name, "AD03") == 0) key = GLFW_KEY_E;
|
|
||||||
else if (strcmp(name, "AD04") == 0) key = GLFW_KEY_R;
|
|
||||||
else if (strcmp(name, "AD05") == 0) key = GLFW_KEY_T;
|
|
||||||
else if (strcmp(name, "AD06") == 0) key = GLFW_KEY_Y;
|
|
||||||
else if (strcmp(name, "AD07") == 0) key = GLFW_KEY_U;
|
|
||||||
else if (strcmp(name, "AD08") == 0) key = GLFW_KEY_I;
|
|
||||||
else if (strcmp(name, "AD09") == 0) key = GLFW_KEY_O;
|
|
||||||
else if (strcmp(name, "AD10") == 0) key = GLFW_KEY_P;
|
|
||||||
else if (strcmp(name, "AD11") == 0) key = GLFW_KEY_LEFT_BRACKET;
|
|
||||||
else if (strcmp(name, "AD12") == 0) key = GLFW_KEY_RIGHT_BRACKET;
|
|
||||||
else if (strcmp(name, "AC01") == 0) key = GLFW_KEY_A;
|
|
||||||
else if (strcmp(name, "AC02") == 0) key = GLFW_KEY_S;
|
|
||||||
else if (strcmp(name, "AC03") == 0) key = GLFW_KEY_D;
|
|
||||||
else if (strcmp(name, "AC04") == 0) key = GLFW_KEY_F;
|
|
||||||
else if (strcmp(name, "AC05") == 0) key = GLFW_KEY_G;
|
|
||||||
else if (strcmp(name, "AC06") == 0) key = GLFW_KEY_H;
|
|
||||||
else if (strcmp(name, "AC07") == 0) key = GLFW_KEY_J;
|
|
||||||
else if (strcmp(name, "AC08") == 0) key = GLFW_KEY_K;
|
|
||||||
else if (strcmp(name, "AC09") == 0) key = GLFW_KEY_L;
|
|
||||||
else if (strcmp(name, "AC10") == 0) key = GLFW_KEY_SEMICOLON;
|
|
||||||
else if (strcmp(name, "AC11") == 0) key = GLFW_KEY_APOSTROPHE;
|
|
||||||
else if (strcmp(name, "AB01") == 0) key = GLFW_KEY_Z;
|
|
||||||
else if (strcmp(name, "AB02") == 0) key = GLFW_KEY_X;
|
|
||||||
else if (strcmp(name, "AB03") == 0) key = GLFW_KEY_C;
|
|
||||||
else if (strcmp(name, "AB04") == 0) key = GLFW_KEY_V;
|
|
||||||
else if (strcmp(name, "AB05") == 0) key = GLFW_KEY_B;
|
|
||||||
else if (strcmp(name, "AB06") == 0) key = GLFW_KEY_N;
|
|
||||||
else if (strcmp(name, "AB07") == 0) key = GLFW_KEY_M;
|
|
||||||
else if (strcmp(name, "AB08") == 0) key = GLFW_KEY_COMMA;
|
|
||||||
else if (strcmp(name, "AB09") == 0) key = GLFW_KEY_PERIOD;
|
|
||||||
else if (strcmp(name, "AB10") == 0) key = GLFW_KEY_SLASH;
|
|
||||||
else if (strcmp(name, "BKSL") == 0) key = GLFW_KEY_BACKSLASH;
|
|
||||||
else if (strcmp(name, "LSGT") == 0) key = GLFW_KEY_WORLD_1;
|
|
||||||
else key = GLFW_KEY_UNKNOWN;
|
|
||||||
|
|
||||||
if ((scancode >= 0) && (scancode < 256))
|
|
||||||
_glfw.x11.keycodes[scancode] = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
XkbFreeNames(desc, XkbKeyNamesMask, True);
|
|
||||||
XkbFreeKeyboard(desc, 0, True);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (scancode = 0; scancode < 256; scancode++)
|
|
||||||
{
|
|
||||||
// Translate the un-translated key codes using traditional X11 KeySym
|
|
||||||
// lookups
|
|
||||||
if (_glfw.x11.keycodes[scancode] < 0)
|
|
||||||
_glfw.x11.keycodes[scancode] = translateKeyCode(scancode);
|
|
||||||
|
|
||||||
// Store the reverse translation for faster key name lookup
|
|
||||||
if (_glfw.x11.keycodes[scancode] > 0)
|
|
||||||
_glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the IM has a usable style
|
|
||||||
//
|
|
||||||
static GLFWbool hasUsableInputMethodStyle(void)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
GLFWbool found = GLFW_FALSE;
|
|
||||||
XIMStyles* styles = NULL;
|
|
||||||
|
|
||||||
if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL)
|
|
||||||
return GLFW_FALSE;
|
|
||||||
|
|
||||||
for (i = 0; i < styles->count_styles; i++)
|
|
||||||
{
|
|
||||||
if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing))
|
|
||||||
{
|
|
||||||
found = GLFW_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XFree(styles);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the specified atom is supported
|
// Check whether the specified atom is supported
|
||||||
//
|
//
|
||||||
static Atom getSupportedAtom(Atom* supportedAtoms,
|
static Atom getSupportedAtom(Atom* supportedAtoms,
|
||||||
@@ -623,33 +310,14 @@ 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");
|
_glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1");
|
||||||
if (_glfw.x11.x11xcb.handle)
|
if (!_glfw.x11.x11xcb.handle)
|
||||||
{
|
{
|
||||||
_glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection)
|
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load libX11-xcb");
|
||||||
_glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
_glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection)
|
||||||
|
_glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
|
||||||
|
|
||||||
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
|
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
|
||||||
if (_glfw.x11.xrender.handle)
|
if (_glfw.x11.xrender.handle)
|
||||||
@@ -674,10 +342,31 @@ static GLFWbool initExtensions(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the key code LUT
|
_glfw.x11.xkb.major = 1;
|
||||||
// FIXME: We should listen to XkbMapNotify events to track changes to
|
_glfw.x11.xkb.minor = 0;
|
||||||
// the keyboard mapping.
|
_glfw.x11.xkb.available = XkbQueryExtension(_glfw.x11.display,
|
||||||
createKeyTables();
|
&_glfw.x11.xkb.majorOpcode,
|
||||||
|
&_glfw.x11.xkb.eventBase,
|
||||||
|
&_glfw.x11.xkb.errorBase,
|
||||||
|
&_glfw.x11.xkb.major,
|
||||||
|
&_glfw.x11.xkb.minor);
|
||||||
|
|
||||||
|
if (!_glfw.x11.xkb.available)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load Xkb extension");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
Bool supported;
|
||||||
|
if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))
|
||||||
|
{
|
||||||
|
if (supported)
|
||||||
|
_glfw.x11.xkb.detectable = GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!glfw_xkb_set_x11_events_mask()) return GLFW_FALSE;
|
||||||
|
if (!glfw_xkb_create_context(&_glfw.x11.xkb)) return GLFW_FALSE;
|
||||||
|
if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return GLFW_FALSE;
|
||||||
|
if (!glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL)) return GLFW_FALSE;
|
||||||
|
|
||||||
// Detect whether an EWMH-conformant window manager is running
|
// Detect whether an EWMH-conformant window manager is running
|
||||||
detectEWMH();
|
detectEWMH();
|
||||||
@@ -896,16 +585,6 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
|
|||||||
|
|
||||||
int _glfwPlatformInit(void)
|
int _glfwPlatformInit(void)
|
||||||
{
|
{
|
||||||
#if !defined(X_HAVE_UTF8_STRING)
|
|
||||||
// HACK: If the current locale is "C" and the Xlib UTF-8 functions are
|
|
||||||
// unavailable, apply the environment's locale in the hope that it's
|
|
||||||
// both available and not "C"
|
|
||||||
// This is done because the "C" locale breaks wide character input,
|
|
||||||
// which is what we fall back on when UTF-8 support is missing
|
|
||||||
if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0)
|
|
||||||
setlocale(LC_CTYPE, "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
XInitThreads();
|
XInitThreads();
|
||||||
XrmInitialize();
|
XrmInitialize();
|
||||||
|
|
||||||
@@ -939,21 +618,6 @@ int _glfwPlatformInit(void)
|
|||||||
_glfw.x11.helperWindowHandle = createHelperWindow();
|
_glfw.x11.helperWindowHandle = createHelperWindow();
|
||||||
_glfw.x11.hiddenCursorHandle = createHiddenCursor();
|
_glfw.x11.hiddenCursorHandle = createHiddenCursor();
|
||||||
|
|
||||||
if (XSupportsLocale())
|
|
||||||
{
|
|
||||||
XSetLocaleModifiers("");
|
|
||||||
|
|
||||||
_glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL);
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
{
|
|
||||||
if (!hasUsableInputMethodStyle())
|
|
||||||
{
|
|
||||||
XCloseIM(_glfw.x11.im);
|
|
||||||
_glfw.x11.im = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
if (!_glfwInitJoysticksLinux())
|
if (!_glfwInitJoysticksLinux())
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
@@ -985,15 +649,10 @@ void _glfwPlatformTerminate(void)
|
|||||||
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glfw_xkb_release(&_glfw.x11.xkb);
|
||||||
free(_glfw.x11.primarySelectionString);
|
free(_glfw.x11.primarySelectionString);
|
||||||
free(_glfw.x11.clipboardString);
|
free(_glfw.x11.clipboardString);
|
||||||
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
{
|
|
||||||
XCloseIM(_glfw.x11.im);
|
|
||||||
_glfw.x11.im = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_glfw.x11.display)
|
if (_glfw.x11.display)
|
||||||
{
|
{
|
||||||
XCloseDisplay(_glfw.x11.display);
|
XCloseDisplay(_glfw.x11.display);
|
||||||
@@ -1050,4 +709,3 @@ const char* _glfwPlatformGetVersionString(void)
|
|||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
glfw/x11_monitor.c
vendored
4
glfw/x11_monitor.c
vendored
@@ -308,6 +308,10 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
|
|||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||||
|
|||||||
25
glfw/x11_platform.h
vendored
25
glfw/x11_platform.h
vendored
@@ -47,6 +47,8 @@
|
|||||||
// The XInput extension provides raw mouse motion input
|
// The XInput extension provides raw mouse motion input
|
||||||
#include <X11/extensions/XInput2.h>
|
#include <X11/extensions/XInput2.h>
|
||||||
|
|
||||||
|
#include "xkb_glfw.h"
|
||||||
|
|
||||||
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
|
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
|
||||||
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
|
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
|
||||||
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
|
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
|
||||||
@@ -96,8 +98,6 @@ typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*);
|
|||||||
#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension
|
#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension
|
||||||
#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens
|
#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens
|
||||||
|
|
||||||
typedef XID xcb_window_t;
|
|
||||||
typedef XID xcb_visualid_t;
|
|
||||||
typedef struct xcb_connection_t xcb_connection_t;
|
typedef struct xcb_connection_t xcb_connection_t;
|
||||||
typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*);
|
typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*);
|
||||||
#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection
|
#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection
|
||||||
@@ -151,7 +151,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk
|
|||||||
|
|
||||||
#include "posix_thread.h"
|
#include "posix_thread.h"
|
||||||
#include "posix_time.h"
|
#include "posix_time.h"
|
||||||
#include "xkb_unicode.h"
|
|
||||||
#include "glx_context.h"
|
#include "glx_context.h"
|
||||||
#include "egl_context.h"
|
#include "egl_context.h"
|
||||||
#include "osmesa_context.h"
|
#include "osmesa_context.h"
|
||||||
@@ -180,7 +179,6 @@ typedef struct _GLFWwindowX11
|
|||||||
{
|
{
|
||||||
Colormap colormap;
|
Colormap colormap;
|
||||||
Window handle;
|
Window handle;
|
||||||
XIC ic;
|
|
||||||
|
|
||||||
GLFWbool overrideRedirect;
|
GLFWbool overrideRedirect;
|
||||||
GLFWbool iconified;
|
GLFWbool iconified;
|
||||||
@@ -219,20 +217,12 @@ typedef struct _GLFWlibraryX11
|
|||||||
Cursor hiddenCursorHandle;
|
Cursor hiddenCursorHandle;
|
||||||
// Context for mapping window XIDs to _GLFWwindow pointers
|
// Context for mapping window XIDs to _GLFWwindow pointers
|
||||||
XContext context;
|
XContext context;
|
||||||
// XIM input method
|
|
||||||
XIM im;
|
|
||||||
// Most recent error code received by X error handler
|
// Most recent error code received by X error handler
|
||||||
int errorCode;
|
int errorCode;
|
||||||
// Primary selection string (while the primary selection is owned)
|
// Primary selection string (while the primary selection is owned)
|
||||||
char* primarySelectionString;
|
char* primarySelectionString;
|
||||||
// Clipboard string (while the selection is owned)
|
// Clipboard string (while the selection is owned)
|
||||||
char* clipboardString;
|
char* clipboardString;
|
||||||
// Key name string
|
|
||||||
char keyName[5];
|
|
||||||
// X11 keycode to GLFW key LUT
|
|
||||||
short int keycodes[256];
|
|
||||||
// GLFW key to X11 keycode LUT
|
|
||||||
short int scancodes[GLFW_KEY_LAST + 1];
|
|
||||||
// Where to place the cursor when re-enabled
|
// Where to place the cursor when re-enabled
|
||||||
double restoreCursorPosX, restoreCursorPosY;
|
double restoreCursorPosX, restoreCursorPosY;
|
||||||
// The window whose disabled cursor mode is active
|
// The window whose disabled cursor mode is active
|
||||||
@@ -318,15 +308,7 @@ typedef struct _GLFWlibraryX11
|
|||||||
PFN_XRRUpdateConfiguration UpdateConfiguration;
|
PFN_XRRUpdateConfiguration UpdateConfiguration;
|
||||||
} randr;
|
} randr;
|
||||||
|
|
||||||
struct {
|
_GLFWXKBData xkb;
|
||||||
GLFWbool available;
|
|
||||||
GLFWbool detectable;
|
|
||||||
int majorOpcode;
|
|
||||||
int eventBase;
|
|
||||||
int errorBase;
|
|
||||||
int major;
|
|
||||||
int minor;
|
|
||||||
} xkb;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int count;
|
int count;
|
||||||
@@ -441,4 +423,3 @@ void _glfwReleaseErrorHandlerX11(void);
|
|||||||
void _glfwInputErrorX11(int error, const char* message);
|
void _glfwInputErrorX11(int error, const char* message);
|
||||||
|
|
||||||
void _glfwPushSelectionToManagerX11(void);
|
void _glfwPushSelectionToManagerX11(void);
|
||||||
|
|
||||||
|
|||||||
216
glfw/x11_window.c
vendored
216
glfw/x11_window.c
vendored
@@ -220,17 +220,6 @@ static int translateState(int state)
|
|||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translates an X11 key code to a GLFW key token
|
|
||||||
//
|
|
||||||
static int translateKey(int scancode)
|
|
||||||
{
|
|
||||||
// Use the pre-filled LUT (see createKeyTables() in x11_init.c)
|
|
||||||
if (scancode < 0 || scancode > 255)
|
|
||||||
return GLFW_KEY_UNKNOWN;
|
|
||||||
|
|
||||||
return _glfw.x11.keycodes[scancode];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the GLFW window corresponding to the specified X11 window
|
// Return the GLFW window corresponding to the specified X11 window
|
||||||
//
|
//
|
||||||
static _GLFWwindow* findWindowByHandle(Window handle)
|
static _GLFWwindow* findWindowByHandle(Window handle)
|
||||||
@@ -497,31 +486,6 @@ static size_t encodeUTF8(char* s, unsigned int ch)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode a Unicode code point from a UTF-8 stream
|
|
||||||
// Based on cutef8 by Jeff Bezanson (Public Domain)
|
|
||||||
//
|
|
||||||
#if defined(X_HAVE_UTF8_STRING)
|
|
||||||
static unsigned int decodeUTF8(const char** s)
|
|
||||||
{
|
|
||||||
unsigned int ch = 0, count = 0;
|
|
||||||
static const unsigned int offsets[] =
|
|
||||||
{
|
|
||||||
0x00000000u, 0x00003080u, 0x000e2080u,
|
|
||||||
0x03c82080u, 0xfa082080u, 0x82082080u
|
|
||||||
};
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ch = (ch << 6) + (unsigned char) **s;
|
|
||||||
(*s)++;
|
|
||||||
count++;
|
|
||||||
} while ((**s & 0xc0) == 0x80);
|
|
||||||
|
|
||||||
assert(count <= 6);
|
|
||||||
return ch - offsets[count - 1];
|
|
||||||
}
|
|
||||||
#endif /*X_HAVE_UTF8_STRING*/
|
|
||||||
|
|
||||||
// Convert the specified Latin-1 string to UTF-8
|
// Convert the specified Latin-1 string to UTF-8
|
||||||
//
|
//
|
||||||
static char* convertLatin1toUTF8(const char* source)
|
static char* convertLatin1toUTF8(const char* source)
|
||||||
@@ -746,19 +710,6 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
|||||||
}
|
}
|
||||||
|
|
||||||
_glfwPlatformSetWindowTitle(window, wndconfig->title);
|
_glfwPlatformSetWindowTitle(window, wndconfig->title);
|
||||||
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
{
|
|
||||||
window->x11.ic = XCreateIC(_glfw.x11.im,
|
|
||||||
XNInputStyle,
|
|
||||||
XIMPreeditNothing | XIMStatusNothing,
|
|
||||||
XNClientWindow,
|
|
||||||
window->x11.handle,
|
|
||||||
XNFocusWindow,
|
|
||||||
window->x11.handle,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
_glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
|
_glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
|
||||||
_glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
|
_glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
|
||||||
|
|
||||||
@@ -1141,16 +1092,8 @@ static void releaseMonitor(_GLFWwindow* window)
|
|||||||
static void processEvent(XEvent *event)
|
static void processEvent(XEvent *event)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = NULL;
|
_GLFWwindow* window = NULL;
|
||||||
int keycode = 0;
|
|
||||||
Bool filtered = False;
|
Bool filtered = False;
|
||||||
|
|
||||||
// HACK: Save scancode as some IMs clear the field in XFilterEvent
|
|
||||||
if (event->type == KeyPress || event->type == KeyRelease)
|
|
||||||
keycode = event->xkey.keycode;
|
|
||||||
|
|
||||||
if (_glfw.x11.im)
|
|
||||||
filtered = XFilterEvent(event, None);
|
|
||||||
|
|
||||||
if (_glfw.x11.randr.available)
|
if (_glfw.x11.randr.available)
|
||||||
{
|
{
|
||||||
if (event->type == _glfw.x11.randr.eventBase + RRNotify)
|
if (event->type == _glfw.x11.randr.eventBase + RRNotify)
|
||||||
@@ -1208,6 +1151,30 @@ static void processEvent(XEvent *event)
|
|||||||
handleSelectionRequest(event);
|
handleSelectionRequest(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (event->type == _glfw.x11.xkb.eventBase)
|
||||||
|
{
|
||||||
|
XkbEvent *kb_event = (XkbEvent*)event;
|
||||||
|
switch(kb_event->any.xkb_type) {
|
||||||
|
case XkbNewKeyboardNotify:
|
||||||
|
if(!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;
|
||||||
|
/* fallthrough */
|
||||||
|
case XkbMapNotify:
|
||||||
|
{
|
||||||
|
glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case XkbStateNotify:
|
||||||
|
{
|
||||||
|
XkbStateNotifyEvent *state_event = (XkbStateNotifyEvent*)kb_event;
|
||||||
|
glfw_xkb_update_modifiers(
|
||||||
|
&_glfw.x11.xkb, state_event->base_mods, state_event->latched_mods,
|
||||||
|
state_event->locked_mods, state_event->base_group, state_event->latched_group, state_event->locked_group
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
window = findWindowByHandle(event->xany.window);
|
window = findWindowByHandle(event->xany.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
@@ -1220,105 +1187,12 @@ static void processEvent(XEvent *event)
|
|||||||
{
|
{
|
||||||
case KeyPress:
|
case KeyPress:
|
||||||
{
|
{
|
||||||
const int key = translateKey(keycode);
|
glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_PRESS);
|
||||||
const int mods = translateState(event->xkey.state);
|
|
||||||
const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
|
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
{
|
|
||||||
// HACK: Ignore duplicate key press events generated by ibus
|
|
||||||
// These have the same timestamp as the original event
|
|
||||||
// Corresponding release events are filtered out
|
|
||||||
// implicitly by the GLFW key repeat logic
|
|
||||||
if (window->x11.lastKeyTime < event->xkey.time)
|
|
||||||
{
|
|
||||||
if (keycode)
|
|
||||||
_glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
|
|
||||||
|
|
||||||
window->x11.lastKeyTime = event->xkey.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filtered)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
Status status;
|
|
||||||
#if defined(X_HAVE_UTF8_STRING)
|
|
||||||
char buffer[100];
|
|
||||||
char* chars = buffer;
|
|
||||||
|
|
||||||
count = Xutf8LookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
buffer, sizeof(buffer) - 1,
|
|
||||||
NULL, &status);
|
|
||||||
|
|
||||||
if (status == XBufferOverflow)
|
|
||||||
{
|
|
||||||
chars = calloc(count + 1, 1);
|
|
||||||
count = Xutf8LookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
chars, count,
|
|
||||||
NULL, &status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == XLookupChars || status == XLookupBoth)
|
|
||||||
{
|
|
||||||
const char* c = chars;
|
|
||||||
chars[count] = '\0';
|
|
||||||
while (c - chars < count)
|
|
||||||
_glfwInputChar(window, decodeUTF8(&c), mods, plain);
|
|
||||||
}
|
|
||||||
#else /*X_HAVE_UTF8_STRING*/
|
|
||||||
wchar_t buffer[16];
|
|
||||||
wchar_t* chars = buffer;
|
|
||||||
|
|
||||||
count = XwcLookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
buffer,
|
|
||||||
sizeof(buffer) / sizeof(wchar_t),
|
|
||||||
NULL,
|
|
||||||
&status);
|
|
||||||
|
|
||||||
if (status == XBufferOverflow)
|
|
||||||
{
|
|
||||||
chars = calloc(count, sizeof(wchar_t));
|
|
||||||
count = XwcLookupString(window->x11.ic,
|
|
||||||
&event->xkey,
|
|
||||||
chars, count,
|
|
||||||
NULL, &status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == XLookupChars || status == XLookupBoth)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
_glfwInputChar(window, chars[i], mods, plain);
|
|
||||||
}
|
|
||||||
#endif /*X_HAVE_UTF8_STRING*/
|
|
||||||
|
|
||||||
if (chars != buffer)
|
|
||||||
free(chars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
KeySym keysym;
|
|
||||||
XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
|
|
||||||
|
|
||||||
_glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
|
|
||||||
|
|
||||||
const long character = _glfwKeySym2Unicode(keysym);
|
|
||||||
if (character != -1)
|
|
||||||
_glfwInputChar(window, character, mods, plain);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case KeyRelease:
|
case KeyRelease:
|
||||||
{
|
{
|
||||||
const int key = translateKey(keycode);
|
|
||||||
const int mods = translateState(event->xkey.state);
|
|
||||||
|
|
||||||
if (!_glfw.x11.xkb.detectable)
|
if (!_glfw.x11.xkb.detectable)
|
||||||
{
|
{
|
||||||
// HACK: Key repeat events will arrive as KeyRelease/KeyPress
|
// HACK: Key repeat events will arrive as KeyRelease/KeyPress
|
||||||
@@ -1332,7 +1206,7 @@ static void processEvent(XEvent *event)
|
|||||||
|
|
||||||
if (next.type == KeyPress &&
|
if (next.type == KeyPress &&
|
||||||
next.xkey.window == event->xkey.window &&
|
next.xkey.window == event->xkey.window &&
|
||||||
next.xkey.keycode == keycode)
|
next.xkey.keycode == event->xkey.keycode)
|
||||||
{
|
{
|
||||||
// HACK: The time of repeat events sometimes doesn't
|
// HACK: The time of repeat events sometimes doesn't
|
||||||
// match that of the press event, so add an
|
// match that of the press event, so add an
|
||||||
@@ -1351,7 +1225,7 @@ static void processEvent(XEvent *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
|
glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_RELEASE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1735,9 +1609,6 @@ static void processEvent(XEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
XSetICFocus(window->x11.ic);
|
|
||||||
|
|
||||||
_glfwInputWindowFocus(window, GLFW_TRUE);
|
_glfwInputWindowFocus(window, GLFW_TRUE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1755,9 +1626,6 @@ static void processEvent(XEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
XUnsetICFocus(window->x11.ic);
|
|
||||||
|
|
||||||
if (window->monitor && window->autoIconify)
|
if (window->monitor && window->autoIconify)
|
||||||
_glfwPlatformIconifyWindow(window);
|
_glfwPlatformIconifyWindow(window);
|
||||||
|
|
||||||
@@ -1989,12 +1857,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
|||||||
if (window->monitor)
|
if (window->monitor)
|
||||||
releaseMonitor(window);
|
releaseMonitor(window);
|
||||||
|
|
||||||
if (window->x11.ic)
|
|
||||||
{
|
|
||||||
XDestroyIC(window->x11.ic);
|
|
||||||
window->x11.ic = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window->context.destroy)
|
if (window->context.destroy)
|
||||||
window->context.destroy(window);
|
window->context.destroy(window);
|
||||||
|
|
||||||
@@ -2340,9 +2202,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|||||||
0, 1, 0);
|
0, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return XkbBell(_glfw.x11.display, window->x11.handle, (int)param, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
|
return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
||||||
@@ -2765,28 +2627,13 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
|||||||
|
|
||||||
const char* _glfwPlatformGetScancodeName(int scancode)
|
const char* _glfwPlatformGetScancodeName(int scancode)
|
||||||
{
|
{
|
||||||
if (!_glfw.x11.xkb.available)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
|
return glfw_xkb_keysym_name(scancode);
|
||||||
if (keysym == NoSymbol)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
const long ch = _glfwKeySym2Unicode(keysym);
|
|
||||||
if (ch == -1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
const size_t count = encodeUTF8(_glfw.x11.keyName, (unsigned int) ch);
|
|
||||||
if (count == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
_glfw.x11.keyName[count] = '\0';
|
|
||||||
return _glfw.x11.keyName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformGetKeyScancode(int key)
|
int _glfwPlatformGetKeyScancode(int key)
|
||||||
{
|
{
|
||||||
return _glfw.x11.scancodes[key];
|
return glfw_xkb_sym_for_key(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||||
@@ -3044,4 +2891,3 @@ GLFWAPI const char* glfwGetX11SelectionString(void)
|
|||||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
return getSelectionString(_glfw.x11.PRIMARY);
|
return getSelectionString(_glfw.x11.PRIMARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
422
glfw/xkb_glfw.c
vendored
Normal file
422
glfw/xkb_glfw.c
vendored
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
//========================================================================
|
||||||
|
// 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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#undef S
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
#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)
|
||||||
|
readline.parse_and_bind('tab: 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
|
||||||
|
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)
|
||||||
|
|
||||||
|
readline.read_init_file()
|
||||||
|
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/hints/__init__.py
Normal file
0
kittens/hints/__init__.py
Normal file
@@ -2,17 +2,16 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
import subprocess
|
|
||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
|
||||||
from functools import lru_cache, partial
|
from functools import lru_cache, partial
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
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 kitty.key_encoding import ESCAPE, backspace_key, enter_key
|
||||||
from kitty.utils import command_for_open
|
|
||||||
|
|
||||||
from ..tui.handler import Handler
|
from ..tui.handler import Handler
|
||||||
from ..tui.loop import Loop
|
from ..tui.loop import Loop
|
||||||
@@ -20,11 +19,19 @@ from ..tui.operations import (
|
|||||||
clear_screen, faint, set_cursor_visible, set_window_title, styled
|
clear_screen, faint, set_cursor_visible, set_window_title, styled
|
||||||
)
|
)
|
||||||
|
|
||||||
Mark = namedtuple('Mark', 'index start end text')
|
|
||||||
URL_PREFIXES = 'http https file ftp'.split()
|
URL_PREFIXES = 'http https file ftp'.split()
|
||||||
HINT_ALPHABET = string.digits + string.ascii_lowercase
|
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)
|
@lru_cache(maxsize=2048)
|
||||||
def encode_hint(num):
|
def encode_hint(num):
|
||||||
res = ''
|
res = ''
|
||||||
@@ -79,17 +86,19 @@ def render(lines, current_input):
|
|||||||
return '\r\n'.join(ans)
|
return '\r\n'.join(ans)
|
||||||
|
|
||||||
|
|
||||||
class URLHints(Handler):
|
class Hints(Handler):
|
||||||
|
|
||||||
def __init__(self, lines, index_map):
|
def __init__(self, lines, index_map, args):
|
||||||
self.lines, self.index_map = tuple(lines), index_map
|
self.lines, self.index_map = tuple(lines), index_map
|
||||||
self.current_input = ''
|
self.current_input = ''
|
||||||
self.current_text = None
|
self.current_text = None
|
||||||
|
self.args = args
|
||||||
|
self.window_title = _('Choose URL') if args.type == 'url' else _('Choose text')
|
||||||
self.chosen = None
|
self.chosen = None
|
||||||
|
|
||||||
def init_terminal_state(self):
|
def init_terminal_state(self):
|
||||||
self.write(set_cursor_visible(False))
|
self.write(set_cursor_visible(False))
|
||||||
self.write(set_window_title(_('Choose URL')))
|
self.write(set_window_title(self.window_title))
|
||||||
|
|
||||||
def initialize(self, *args):
|
def initialize(self, *args):
|
||||||
Handler.initialize(self, *args)
|
Handler.initialize(self, *args)
|
||||||
@@ -104,7 +113,7 @@ class URLHints(Handler):
|
|||||||
changed = True
|
changed = True
|
||||||
if changed:
|
if changed:
|
||||||
matches = [
|
matches = [
|
||||||
t for idx, t in self.index_map.items()
|
m.text for idx, m in self.index_map.items()
|
||||||
if encode_hint(idx).startswith(self.current_input)
|
if encode_hint(idx).startswith(self.current_input)
|
||||||
]
|
]
|
||||||
if len(matches) == 1:
|
if len(matches) == 1:
|
||||||
@@ -121,7 +130,7 @@ class URLHints(Handler):
|
|||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
elif key_event is enter_key and self.current_input:
|
elif key_event is enter_key and self.current_input:
|
||||||
idx = decode_hint(self.current_input)
|
idx = decode_hint(self.current_input)
|
||||||
self.chosen = self.index_map[idx]
|
self.chosen = self.index_map[idx].text
|
||||||
self.quit_loop(0)
|
self.quit_loop(0)
|
||||||
elif key_event.key is ESCAPE:
|
elif key_event.key is ESCAPE:
|
||||||
self.quit_loop(1)
|
self.quit_loop(1)
|
||||||
@@ -143,10 +152,10 @@ class URLHints(Handler):
|
|||||||
self.write(self.current_text)
|
self.write(self.current_text)
|
||||||
|
|
||||||
|
|
||||||
def regex_finditer(pat, line):
|
def regex_finditer(pat, minimum_match_length, line):
|
||||||
for m in pat.finditer(line):
|
for m in pat.finditer(line):
|
||||||
s, e = m.span()
|
s, e = m.span(pat.groups)
|
||||||
if e - s > 2:
|
if e - s >= minimum_match_length:
|
||||||
yield s, e
|
yield s, e
|
||||||
|
|
||||||
|
|
||||||
@@ -158,81 +167,151 @@ def find_urls(pat, line):
|
|||||||
idx = url.rfind('[')
|
idx = url.rfind('[')
|
||||||
if idx > -1:
|
if idx > -1:
|
||||||
e -= len(url) - idx
|
e -= len(url) - idx
|
||||||
|
while line[e - 1] in '.,?!' and e > 1: # remove trailing punctuation
|
||||||
|
e -= 1
|
||||||
yield s, e
|
yield s, e
|
||||||
|
|
||||||
|
|
||||||
def mark(finditer, line, index_map):
|
def mark(finditer, line, all_marks):
|
||||||
marks = []
|
marks = []
|
||||||
for s, e in finditer(line):
|
for s, e in finditer(line):
|
||||||
idx = len(index_map)
|
idx = len(all_marks)
|
||||||
text = line[s:e]
|
text = line[s:e]
|
||||||
marks.append(Mark(idx, s, e, text))
|
marks.append(Mark(idx, s, e, text))
|
||||||
index_map[idx] = text
|
all_marks.append(marks[-1])
|
||||||
return line, marks
|
return line, marks
|
||||||
|
|
||||||
|
|
||||||
def run(args, source_file=None):
|
def run_loop(args, lines, index_map):
|
||||||
if source_file is None:
|
loop = Loop()
|
||||||
text = sys.stdin.buffer.read().decode('utf-8')
|
handler = Hints(lines, index_map, args)
|
||||||
sys.stdin = open('/dev/tty')
|
loop.loop(handler)
|
||||||
else:
|
if handler.chosen and loop.return_code == 0:
|
||||||
with open(source_file, 'r') as f:
|
return {'match': handler.chosen, 'program': args.program}
|
||||||
text = f.read()
|
raise SystemExit(loop.return_code)
|
||||||
if args.regex is None:
|
|
||||||
|
|
||||||
|
def escape(chars):
|
||||||
|
return chars.replace('\\', '\\\\').replace('-', r'\-').replace(']', r'\]')
|
||||||
|
|
||||||
|
|
||||||
|
def run(args, text):
|
||||||
|
if args.type == 'url':
|
||||||
from .url_regex import url_delimiters
|
from .url_regex import url_delimiters
|
||||||
url_pat = '(?:{})://[^{}]{{3,}}'.format(
|
url_pat = '(?:{})://[^{}]{{3,}}'.format(
|
||||||
'|'.join(args.url_prefixes.split(',')), url_delimiters
|
'|'.join(args.url_prefixes.split(',')), url_delimiters
|
||||||
)
|
)
|
||||||
finditer = partial(find_urls, re.compile(url_pat))
|
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('(?u)[{}\w]{{{},}}'.format(escape(chars), args.minimum_match_length))
|
||||||
|
finditer = partial(regex_finditer, pat, args.minimum_match_length)
|
||||||
else:
|
else:
|
||||||
finditer = partial(regex_finditer, re.compile(args.regex))
|
finditer = partial(regex_finditer, re.compile(args.regex), args.minimum_match_length)
|
||||||
lines = []
|
lines = []
|
||||||
index_map = {}
|
all_marks = []
|
||||||
for line in text.splitlines():
|
for line in text.splitlines():
|
||||||
marked = mark(finditer, line, index_map)
|
marked = mark(finditer, line, all_marks)
|
||||||
lines.append(marked)
|
lines.append(marked)
|
||||||
if not index_map:
|
if not all_marks:
|
||||||
input(_('No URLs found, press Enter to abort.'))
|
input(_('No {} found, press Enter to abort.').format(
|
||||||
|
'URLs' if args.type == 'url' else 'matches'
|
||||||
|
))
|
||||||
return
|
return
|
||||||
|
|
||||||
loop = Loop()
|
largest_index = all_marks[-1].index
|
||||||
handler = URLHints(lines, index_map)
|
for m in all_marks:
|
||||||
loop.loop(handler)
|
m.index = largest_index - m.index
|
||||||
if handler.chosen and loop.return_code == 0:
|
index_map = {m.index: m for m in all_marks}
|
||||||
cmd = command_for_open(args.program)
|
|
||||||
ret = subprocess.Popen(cmd + [handler.chosen]).wait()
|
return run_loop(args, lines, index_map)
|
||||||
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('''\
|
OPTIONS = partial(r'''
|
||||||
--program
|
--program
|
||||||
default=default
|
default=default
|
||||||
What program to use to open matched URLs. Defaults
|
What program to use to open matched text. Defaults to the default open program
|
||||||
to the default URL open program for the operating system.
|
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
|
--regex
|
||||||
Instead of searching for URLs search for the specified regular
|
default=(?m)^\s*(.+)\s*$
|
||||||
expression instead.
|
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
|
--url-prefixes
|
||||||
default={0}
|
default={0}
|
||||||
Comma separeted list of recognized URL prefixes. Defaults to:
|
Comma separated list of recognized URL prefixes.
|
||||||
{0}
|
|
||||||
|
|
||||||
|
--word-characters
|
||||||
|
Characters to consider as part of a word. In addition, all chacraters 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)))
|
'''.format, ','.join(sorted(URL_PREFIXES)))
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args):
|
||||||
msg = 'Highlight URLs inside the specified text'
|
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:
|
try:
|
||||||
args, items = parse_args(args[1:], OPTIONS, '[path to file or omit to use stdin]', msg, 'url_hints')
|
args, items = parse_args(args[1:], OPTIONS, '', msg, 'hints')
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
print(e.args[0], file=sys.stderr)
|
if e.code != 0:
|
||||||
input('Press enter to quit...')
|
print(e.args[0], file=sys.stderr)
|
||||||
return 1
|
input(_('Press Enter to quit'))
|
||||||
run(args, (items or [None])[0])
|
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:
|
||||||
|
boss.open_url(data['match'], None if program == 'default' else program)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Run with kitty +kitten hints
|
||||||
|
ans = main(sys.argv)
|
||||||
|
if ans:
|
||||||
|
print(ans)
|
||||||
76
kittens/runner.py
Normal file
76
kittens/runner.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/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)
|
||||||
|
result = m['start'](args)
|
||||||
|
print(reset_mode('ALTERNATE_SCREEN') + clear_screen(), end='')
|
||||||
|
if result is not None:
|
||||||
|
import json
|
||||||
|
print('OK:', json.dumps(result))
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
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...')
|
||||||
@@ -16,7 +16,7 @@ from collections import namedtuple
|
|||||||
from contextlib import closing, contextmanager
|
from contextlib import closing, contextmanager
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from kitty.fast_data_types import parse_input_from_terminal
|
from kitty.fast_data_types import parse_input_from_terminal, safe_pipe
|
||||||
from kitty.icat import screen_size
|
from kitty.icat import screen_size
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import (
|
||||||
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, C, D, backspace_key,
|
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, C, D, backspace_key,
|
||||||
@@ -142,10 +142,12 @@ class Loop:
|
|||||||
|
|
||||||
def __init__(self, input_fd=None, output_fd=None,
|
def __init__(self, input_fd=None, output_fd=None,
|
||||||
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
|
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
|
||||||
self.input_fd = input_fd or sys.stdin.fileno()
|
self.input_fd = sys.stdin.fileno() if input_fd is None else input_fd
|
||||||
self.output_fd = output_fd or sys.stdout.fileno()
|
self.output_fd = sys.stdout.fileno() if output_fd is None else output_fd
|
||||||
self.wakeup_read_fd, self.wakeup_write_fd = os.pipe()
|
self.wakeup_read_fd, self.wakeup_write_fd = safe_pipe()
|
||||||
self.sel = s = selectors.DefaultSelector()
|
# For some reason on macOS the DefaultSelector fails when input_fd is
|
||||||
|
# open('/dev/tty')
|
||||||
|
self.sel = s = selectors.PollSelector()
|
||||||
s.register(self.input_fd, selectors.EVENT_READ, self._read_ready)
|
s.register(self.input_fd, selectors.EVENT_READ, self._read_ready)
|
||||||
s.register(
|
s.register(
|
||||||
self.wakeup_read_fd, selectors.EVENT_READ, self._wakeup_ready
|
self.wakeup_read_fd, selectors.EVENT_READ, self._wakeup_ready
|
||||||
@@ -169,7 +171,10 @@ class Loop:
|
|||||||
def _read_ready(self, handler):
|
def _read_ready(self, handler):
|
||||||
if not self.read_allowed:
|
if not self.read_allowed:
|
||||||
return
|
return
|
||||||
data = os.read(self.input_fd, io.DEFAULT_BUFFER_SIZE)
|
try:
|
||||||
|
data = os.read(self.input_fd, io.DEFAULT_BUFFER_SIZE)
|
||||||
|
except BlockingIOError:
|
||||||
|
return
|
||||||
if not data:
|
if not data:
|
||||||
raise EOFError('The input stream is closed')
|
raise EOFError('The input stream is closed')
|
||||||
data = self.decoder.decode(data)
|
data = self.decoder.decode(data)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from kitty.terminfo import string_capabilities
|
from kitty.terminfo import string_capabilities
|
||||||
@@ -151,3 +152,11 @@ def cursor(write):
|
|||||||
write(SAVE_CURSOR)
|
write(SAVE_CURSOR)
|
||||||
yield
|
yield
|
||||||
write(RESTORE_CURSOR)
|
write(RESTORE_CURSOR)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def alternate_screen(f=None):
|
||||||
|
f = f or sys.stdout
|
||||||
|
print(set_mode('ALTERNATE_SCREEN'), end='', file=f)
|
||||||
|
yield
|
||||||
|
print(reset_mode('ALTERNATE_SCREEN'), end='', file=f)
|
||||||
|
|||||||
@@ -3,15 +3,13 @@
|
|||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shlex
|
|
||||||
import string
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from kitty.config import cached_values_for
|
from kitty.config import cached_values_for
|
||||||
from kitty.constants import config_dir
|
from kitty.constants import config_dir, editor
|
||||||
from kitty.fast_data_types import wcswidth
|
from kitty.fast_data_types import wcswidth
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import (
|
||||||
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
|
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
|
||||||
@@ -44,7 +42,7 @@ def codepoint_ok(code):
|
|||||||
@lru_cache(maxsize=256)
|
@lru_cache(maxsize=256)
|
||||||
def points_for_word(w):
|
def points_for_word(w):
|
||||||
from .unicode_names import codepoints_for_word
|
from .unicode_names import codepoints_for_word
|
||||||
return codepoints_for_word(w)
|
return codepoints_for_word(w.lower())
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=4096)
|
@lru_cache(maxsize=4096)
|
||||||
@@ -207,7 +205,7 @@ class Table:
|
|||||||
col_width = longest + 2
|
col_width = longest + 2
|
||||||
col_width = min(col_width, 40)
|
col_width = min(col_width, 40)
|
||||||
space_for_desc = col_width - 2 - idx_size - 4
|
space_for_desc = col_width - 2 - idx_size - 4
|
||||||
num_cols = self.num_cols = cols // col_width
|
num_cols = self.num_cols = max(cols // col_width, 1)
|
||||||
buf = []
|
buf = []
|
||||||
a = buf.append
|
a = buf.append
|
||||||
rows_left = rows
|
rows_left = rows
|
||||||
@@ -224,6 +222,8 @@ class Table:
|
|||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
def move_current(self, rows=0, cols=0):
|
def move_current(self, rows=0, cols=0):
|
||||||
|
if len(self.codepoints) == 0:
|
||||||
|
return
|
||||||
if cols:
|
if cols:
|
||||||
self.current_idx = (self.current_idx + len(self.codepoints) + cols) % len(self.codepoints)
|
self.current_idx = (self.current_idx + len(self.codepoints) + cols) % len(self.codepoints)
|
||||||
self.layout_dirty = True
|
self.layout_dirty = True
|
||||||
@@ -361,9 +361,9 @@ class UnicodeInput(Handler):
|
|||||||
y += 1
|
y += 1
|
||||||
|
|
||||||
if self.mode is NAME:
|
if self.mode is NAME:
|
||||||
writeln(_('Enter the hex code for the character'))
|
|
||||||
elif self.mode is HEX:
|
|
||||||
writeln(_('Enter words from the name of the character'))
|
writeln(_('Enter words from the name of the character'))
|
||||||
|
elif self.mode is HEX:
|
||||||
|
writeln(_('Enter the hex code for the character'))
|
||||||
else:
|
else:
|
||||||
writeln(_('Enter the index for the character you want from the list below'))
|
writeln(_('Enter the index for the character you want from the list below'))
|
||||||
self.write(self.prompt)
|
self.write(self.prompt)
|
||||||
@@ -426,7 +426,6 @@ class UnicodeInput(Handler):
|
|||||||
if not os.path.exists(favorites_path):
|
if not os.path.exists(favorites_path):
|
||||||
with open(favorites_path, 'wb') as f:
|
with open(favorites_path, 'wb') as f:
|
||||||
f.write(serialize_favorites(load_favorites()).encode('utf-8'))
|
f.write(serialize_favorites(load_favorites()).encode('utf-8'))
|
||||||
editor = shlex.split(os.environ.get('EDITOR', 'vim'))
|
|
||||||
with self.suspend():
|
with self.suspend():
|
||||||
p = subprocess.Popen(editor + [favorites_path])
|
p = subprocess.Popen(editor + [favorites_path])
|
||||||
if p.wait() == 0:
|
if p.wait() == 0:
|
||||||
@@ -454,17 +453,34 @@ class UnicodeInput(Handler):
|
|||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args):
|
||||||
loop = Loop()
|
loop = Loop()
|
||||||
with cached_values_for('unicode-input') as cached_values:
|
with cached_values_for('unicode-input') as cached_values:
|
||||||
handler = UnicodeInput(cached_values)
|
handler = UnicodeInput(cached_values)
|
||||||
loop.loop(handler)
|
loop.loop(handler)
|
||||||
if handler.current_char and loop.return_code == 0:
|
if handler.current_char and loop.return_code == 0:
|
||||||
print('OK:', hex(ord(handler.current_char))[2:])
|
|
||||||
try:
|
try:
|
||||||
handler.recent.remove(ord(handler.current_char))
|
handler.recent.remove(ord(handler.current_char))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
recent = [ord(handler.current_char)] + handler.recent
|
recent = [ord(handler.current_char)] + handler.recent
|
||||||
cached_values['recent'] = recent[:len(DEFAULT_SET)]
|
cached_values['recent'] = recent[:len(DEFAULT_SET)]
|
||||||
raise SystemExit(loop.return_code)
|
return handler.current_char
|
||||||
|
if loop.return_code != 0:
|
||||||
|
raise SystemExit(loop.return_code)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_result(args, current_char, target_window_id, boss):
|
||||||
|
w = boss.window_id_map.get(target_window_id)
|
||||||
|
if w is not None:
|
||||||
|
w.paste(current_char)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if '-h' in sys.argv or '--help' in sys.argv:
|
||||||
|
print('Choose a unicode character to input into the terminal')
|
||||||
|
raise SystemExit(0)
|
||||||
|
ans = main(sys.argv)
|
||||||
|
if ans:
|
||||||
|
print(ans)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "names.h"
|
#include "names.h"
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
all_words(PyObject *self UNUSED) {
|
all_words(PYNOARG) {
|
||||||
PyObject *ans = PyTuple_New(arraysz(idx_to_word));
|
PyObject *ans = PyTuple_New(arraysz(idx_to_word));
|
||||||
if (!ans) return NULL;
|
if (!ans) return NULL;
|
||||||
for (size_t i = 0; i < arraysz(idx_to_word); i++) {
|
for (size_t i = 0; i < arraysz(idx_to_word); i++) {
|
||||||
|
|||||||
315
kitty/boss.py
315
kitty/boss.py
@@ -2,29 +2,37 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
import socket
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from weakref import WeakValueDictionary
|
from weakref import WeakValueDictionary
|
||||||
|
|
||||||
from .cli import create_opts, parse_args
|
from .cli import create_opts, parse_args
|
||||||
from .config import MINIMUM_FONT_SIZE, initial_window_size
|
from .config import (
|
||||||
from .constants import appname, set_boss, wakeup
|
MINIMUM_FONT_SIZE, initial_window_size, prepare_config_file_for_editing
|
||||||
|
)
|
||||||
|
from .constants import appname, editor, set_boss, config_dir
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
ChildMonitor, create_os_window, current_os_window, destroy_global_data,
|
ChildMonitor, create_os_window, current_os_window, destroy_global_data,
|
||||||
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
|
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
|
||||||
layout_sprite_map, mark_os_window_for_close, set_clipboard_string,
|
layout_sprite_map, mark_os_window_for_close, set_clipboard_string,
|
||||||
set_dpi_from_os_window, show_window, toggle_fullscreen,
|
set_dpi_from_os_window, set_in_sequence_mode, show_window,
|
||||||
viewport_for_window
|
toggle_fullscreen, viewport_for_window
|
||||||
)
|
)
|
||||||
from .fonts.render import prerender, resize_fonts, set_font_family
|
from .fonts.render import prerender, resize_fonts, set_font_family
|
||||||
from .keys import get_shortcut
|
from .keys import get_shortcut, shortcut_matches
|
||||||
from .remote_control import handle_cmd
|
from .remote_control import handle_cmd
|
||||||
|
from .rgb import Color, color_from_int
|
||||||
from .session import create_session
|
from .session import create_session
|
||||||
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
|
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
|
||||||
from .utils import (
|
from .utils import (
|
||||||
end_startup_notification, get_primary_selection, init_startup_notification,
|
end_startup_notification, get_primary_selection, init_startup_notification,
|
||||||
open_url, safe_print, set_primary_selection, single_instance
|
log_error, open_url, parse_address_spec, remove_socket_file, safe_print,
|
||||||
|
set_primary_selection, single_instance
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -33,6 +41,15 @@ def initialize_renderer():
|
|||||||
prerender()
|
prerender()
|
||||||
|
|
||||||
|
|
||||||
|
def listen_on(spec):
|
||||||
|
family, address, socket_path = parse_address_spec(spec)
|
||||||
|
s = socket.socket(family)
|
||||||
|
atexit.register(remove_socket_file, s, socket_path)
|
||||||
|
s.bind(address)
|
||||||
|
s.listen()
|
||||||
|
return s.fileno()
|
||||||
|
|
||||||
|
|
||||||
class DumpCommands: # {{{
|
class DumpCommands: # {{{
|
||||||
|
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
@@ -64,16 +81,21 @@ class Boss:
|
|||||||
|
|
||||||
def __init__(self, os_window_id, opts, args, cached_values):
|
def __init__(self, os_window_id, opts, args, cached_values):
|
||||||
self.window_id_map = WeakValueDictionary()
|
self.window_id_map = WeakValueDictionary()
|
||||||
|
self.startup_colors = {k: opts[k] for k in opts if isinstance(opts[k], Color)}
|
||||||
|
self.pending_sequences = None
|
||||||
self.cached_values = cached_values
|
self.cached_values = cached_values
|
||||||
self.os_window_map = {}
|
self.os_window_map = {}
|
||||||
self.cursor_blinking = True
|
self.cursor_blinking = True
|
||||||
self.shutting_down = False
|
self.shutting_down = False
|
||||||
talk_fd = getattr(single_instance, 'socket', None)
|
talk_fd = getattr(single_instance, 'socket', None)
|
||||||
talk_fd = -1 if talk_fd is None else talk_fd.fileno()
|
talk_fd = -1 if talk_fd is None else talk_fd.fileno()
|
||||||
|
listen_fd = -1
|
||||||
|
if opts.allow_remote_control and args.listen_on:
|
||||||
|
listen_fd = listen_on(args.listen_on)
|
||||||
self.child_monitor = ChildMonitor(
|
self.child_monitor = ChildMonitor(
|
||||||
self.on_child_death,
|
self.on_child_death,
|
||||||
DumpCommands(args) if args.dump_commands or args.dump_bytes else None,
|
DumpCommands(args) if args.dump_commands or args.dump_bytes else None,
|
||||||
talk_fd
|
talk_fd, listen_fd
|
||||||
)
|
)
|
||||||
set_boss(self)
|
set_boss(self)
|
||||||
self.current_font_size = opts.font_size
|
self.current_font_size = opts.font_size
|
||||||
@@ -98,6 +120,7 @@ class Boss:
|
|||||||
self.os_window_map[os_window_id] = tm
|
self.os_window_map[os_window_id] = tm
|
||||||
if dpi_changed:
|
if dpi_changed:
|
||||||
self.on_dpi_change(os_window_id)
|
self.on_dpi_change(os_window_id)
|
||||||
|
return os_window_id
|
||||||
|
|
||||||
def list_os_windows(self):
|
def list_os_windows(self):
|
||||||
for os_window_id, tm in self.os_window_map.items():
|
for os_window_id, tm in self.os_window_map.items():
|
||||||
@@ -106,33 +129,48 @@ class Boss:
|
|||||||
'tabs': list(tm.list_tabs()),
|
'tabs': list(tm.list_tabs()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_tab_managers(self):
|
||||||
|
yield from self.os_window_map.values()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_tabs(self):
|
||||||
|
for tm in self.all_tab_managers:
|
||||||
|
yield from tm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_windows(self):
|
||||||
|
for tab in self.all_tabs:
|
||||||
|
yield from tab
|
||||||
|
|
||||||
def match_windows(self, match):
|
def match_windows(self, match):
|
||||||
field, exp = match.split(':', 1)
|
try:
|
||||||
|
field, exp = match.split(':', 1)
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
pat = re.compile(exp)
|
pat = re.compile(exp)
|
||||||
for tm in self.os_window_map.values():
|
for window in self.all_windows:
|
||||||
for tab in tm:
|
if window.matches(field, pat):
|
||||||
for window in tab:
|
yield window
|
||||||
if window.matches(field, pat):
|
|
||||||
yield window
|
|
||||||
|
|
||||||
def tab_for_window(self, window):
|
def tab_for_window(self, window):
|
||||||
for tm in self.os_window_map.values():
|
for tab in self.all_tabs:
|
||||||
for tab in tm:
|
for w in tab:
|
||||||
for w in tab:
|
if w.id == window.id:
|
||||||
if w.id == window.id:
|
return tab
|
||||||
return tab
|
|
||||||
|
|
||||||
def match_tabs(self, match):
|
def match_tabs(self, match):
|
||||||
field, exp = match.split(':', 1)
|
try:
|
||||||
|
field, exp = match.split(':', 1)
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
pat = re.compile(exp)
|
pat = re.compile(exp)
|
||||||
tms = tuple(self.os_window_map.values())
|
|
||||||
found = False
|
found = False
|
||||||
if field in ('title', 'id'):
|
if field in ('title', 'id'):
|
||||||
for tm in tms:
|
for tab in self.all_tabs:
|
||||||
for tab in tm:
|
if tab.matches(field, pat):
|
||||||
if tab.matches(field, pat):
|
yield tab
|
||||||
yield tab
|
found = True
|
||||||
found = True
|
|
||||||
if not found:
|
if not found:
|
||||||
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
||||||
for tab in tabs:
|
for tab in tabs:
|
||||||
@@ -152,7 +190,7 @@ class Boss:
|
|||||||
def _new_os_window(self, args, cwd_from=None):
|
def _new_os_window(self, args, cwd_from=None):
|
||||||
sw = self.args_to_special_window(args, cwd_from) if args else None
|
sw = self.args_to_special_window(args, cwd_from) if args else None
|
||||||
startup_session = create_session(self.opts, special_window=sw, cwd_from=cwd_from)
|
startup_session = create_session(self.opts, special_window=sw, cwd_from=cwd_from)
|
||||||
self.add_os_window(startup_session)
|
return self.add_os_window(startup_session)
|
||||||
|
|
||||||
def new_os_window(self, *args):
|
def new_os_window(self, *args):
|
||||||
self._new_os_window(args)
|
self._new_os_window(args)
|
||||||
@@ -166,30 +204,45 @@ class Boss:
|
|||||||
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
|
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
|
||||||
self.window_id_map[window.id] = window
|
self.window_id_map[window.id] = window
|
||||||
|
|
||||||
def peer_messages_received(self, messages):
|
def _handle_remote_command(self, cmd, window=None):
|
||||||
import json
|
response = None
|
||||||
for msg in messages:
|
if self.opts.allow_remote_control or getattr(window, 'allow_remote_control', False):
|
||||||
msg = json.loads(msg.decode('utf-8'))
|
try:
|
||||||
|
response = handle_cmd(self, window, cmd)
|
||||||
|
except Exception as err:
|
||||||
|
import traceback
|
||||||
|
response = {'ok': False, 'error': str(err)}
|
||||||
|
if not getattr(err, 'hide_traceback', False):
|
||||||
|
response['tb'] = traceback.format_exc()
|
||||||
|
else:
|
||||||
|
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control yes to your kitty.conf'}
|
||||||
|
return response
|
||||||
|
|
||||||
|
def peer_message_received(self, msg):
|
||||||
|
msg = msg.decode('utf-8')
|
||||||
|
cmd_prefix = '\x1bP@kitty-cmd'
|
||||||
|
if msg.startswith(cmd_prefix):
|
||||||
|
cmd = msg[len(cmd_prefix):-2]
|
||||||
|
response = self._handle_remote_command(cmd)
|
||||||
|
if response is not None:
|
||||||
|
response = (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8')
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
msg = json.loads(msg)
|
||||||
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
|
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
|
||||||
startup_id = msg.get('startup_id')
|
startup_id = msg.get('startup_id')
|
||||||
args, rest = parse_args(msg['args'][1:])
|
args, rest = parse_args(msg['args'][1:])
|
||||||
args.args = rest
|
args.args = rest
|
||||||
opts = create_opts(args)
|
opts = create_opts(args)
|
||||||
session = create_session(opts, args)
|
if not os.path.isabs(args.directory):
|
||||||
|
args.directory = os.path.join(msg['cwd'], args.directory)
|
||||||
|
session = create_session(opts, args, respect_cwd=True)
|
||||||
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id)
|
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id)
|
||||||
else:
|
else:
|
||||||
safe_print('Unknown message received from peer, ignoring')
|
log_error('Unknown message received from peer, ignoring')
|
||||||
|
|
||||||
def handle_remote_cmd(self, cmd, window=None):
|
def handle_remote_cmd(self, cmd, window=None):
|
||||||
response = None
|
response = self._handle_remote_command(cmd, window)
|
||||||
if self.opts.allow_remote_control:
|
|
||||||
try:
|
|
||||||
response = handle_cmd(self, window, cmd)
|
|
||||||
except Exception as err:
|
|
||||||
import traceback
|
|
||||||
response = {'ok': False, 'error': str(err), 'tb': traceback.format_exc()}
|
|
||||||
else:
|
|
||||||
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control yes to your kitty.conf'}
|
|
||||||
if response is not None:
|
if response is not None:
|
||||||
if window is not None:
|
if window is not None:
|
||||||
window.send_cmd_response(response)
|
window.send_cmd_response(response)
|
||||||
@@ -259,19 +312,16 @@ class Boss:
|
|||||||
tm.resize()
|
tm.resize()
|
||||||
|
|
||||||
def increase_font_size(self):
|
def increase_font_size(self):
|
||||||
self.change_font_size(
|
self.set_font_size(
|
||||||
min(
|
min(
|
||||||
self.opts.font_size * 5, self.current_font_size +
|
self.opts.font_size * 5, self.current_font_size +
|
||||||
self.opts.font_size_delta))
|
self.opts.font_size_delta))
|
||||||
|
|
||||||
def decrease_font_size(self):
|
def decrease_font_size(self):
|
||||||
self.change_font_size(
|
self.set_font_size(self.current_font_size - self.opts.font_size_delta)
|
||||||
max(
|
|
||||||
MINIMUM_FONT_SIZE, self.current_font_size -
|
|
||||||
self.opts.font_size_delta))
|
|
||||||
|
|
||||||
def restore_font_size(self):
|
def restore_font_size(self):
|
||||||
self.change_font_size(self.opts.font_size)
|
self.set_font_size(self.opts.font_size)
|
||||||
|
|
||||||
def _change_font_size(self, new_size=None, on_dpi_change=False):
|
def _change_font_size(self, new_size=None, on_dpi_change=False):
|
||||||
if new_size is not None:
|
if new_size is not None:
|
||||||
@@ -289,7 +339,8 @@ class Boss:
|
|||||||
tm.refresh_sprite_positions()
|
tm.refresh_sprite_positions()
|
||||||
glfw_post_empty_event()
|
glfw_post_empty_event()
|
||||||
|
|
||||||
def change_font_size(self, new_size):
|
def set_font_size(self, new_size):
|
||||||
|
new_size = max(MINIMUM_FONT_SIZE, new_size)
|
||||||
if new_size == self.current_font_size:
|
if new_size == self.current_font_size:
|
||||||
return
|
return
|
||||||
self._change_font_size(new_size)
|
self._change_font_size(new_size)
|
||||||
@@ -317,8 +368,37 @@ class Boss:
|
|||||||
def dispatch_special_key(self, key, scancode, action, mods):
|
def dispatch_special_key(self, key, scancode, action, mods):
|
||||||
# Handles shortcuts, return True if the key was consumed
|
# Handles shortcuts, return True if the key was consumed
|
||||||
key_action = get_shortcut(self.opts.keymap, mods, key, scancode)
|
key_action = get_shortcut(self.opts.keymap, mods, key, scancode)
|
||||||
self.current_key_press_info = key, scancode, action, mods
|
if key_action is None:
|
||||||
return self.dispatch_action(key_action)
|
sequences = get_shortcut(self.opts.sequence_map, mods, key, scancode)
|
||||||
|
if sequences:
|
||||||
|
self.pending_sequences = sequences
|
||||||
|
set_in_sequence_mode(True)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.current_key_press_info = key, scancode, action, mods
|
||||||
|
return self.dispatch_action(key_action)
|
||||||
|
|
||||||
|
def process_sequence(self, key, scancode, action, mods):
|
||||||
|
if not self.pending_sequences:
|
||||||
|
set_in_sequence_mode(False)
|
||||||
|
|
||||||
|
remaining = {}
|
||||||
|
matched_action = None
|
||||||
|
for seq, key_action in self.pending_sequences.items():
|
||||||
|
if shortcut_matches(seq[0], mods, key, scancode):
|
||||||
|
seq = seq[1:]
|
||||||
|
if seq:
|
||||||
|
remaining[seq] = key_action
|
||||||
|
else:
|
||||||
|
matched_action = key_action
|
||||||
|
|
||||||
|
if remaining:
|
||||||
|
self.pending_sequences = remaining
|
||||||
|
else:
|
||||||
|
self.pending_sequences = None
|
||||||
|
set_in_sequence_mode(False)
|
||||||
|
if matched_action is not None:
|
||||||
|
self.dispatch_action(matched_action)
|
||||||
|
|
||||||
def default_bg_changed_for(self, window_id):
|
def default_bg_changed_for(self, window_id):
|
||||||
w = self.window_id_map.get(window_id)
|
w = self.window_id_map.get(window_id)
|
||||||
@@ -383,49 +463,93 @@ class Boss:
|
|||||||
SpecialWindow(
|
SpecialWindow(
|
||||||
self.opts.scrollback_pager, data, _('History'), overlay_for=window.id))
|
self.opts.scrollback_pager, data, _('History'), overlay_for=window.id))
|
||||||
|
|
||||||
def input_unicode_character(self):
|
def edit_config_file(self, *a):
|
||||||
|
confpath = prepare_config_file_for_editing()
|
||||||
|
# On macOS vim fails to handle SIGWINCH if it occurs early, so add a
|
||||||
|
# small delay.
|
||||||
|
cmd = ['kitty', '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + editor + [confpath]
|
||||||
|
self.new_os_window(*cmd)
|
||||||
|
|
||||||
|
def get_output(self, source_window, num_lines=1):
|
||||||
|
output = ''
|
||||||
|
s = source_window.screen
|
||||||
|
if num_lines is None:
|
||||||
|
num_lines = s.lines
|
||||||
|
for i in range(min(num_lines, s.lines)):
|
||||||
|
output += str(s.linebuf.line(i))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _run_kitten(self, kitten, args=(), type_of_input='none'):
|
||||||
w = self.active_window
|
w = self.active_window
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if w is not None and tab is not None and w.overlay_for is None:
|
if w is not None and tab is not None and w.overlay_for is None:
|
||||||
overlay_window = tab.new_special_window(
|
orig_args, args = list(args), list(args)
|
||||||
SpecialWindow(
|
args[0:0] = [config_dir, kitten]
|
||||||
['kitty', '+runpy', 'from kittens.unicode_input.main import main; main()'],
|
|
||||||
overlay_for=w.id))
|
|
||||||
overlay_window.action_on_close = partial(self.send_unicode_character, w.id)
|
|
||||||
|
|
||||||
def send_unicode_character(self, target_window_id, source_window):
|
|
||||||
w = self.window_id_map.get(target_window_id)
|
|
||||||
if w is not None:
|
|
||||||
output = str(source_window.screen.linebuf.line(0))
|
|
||||||
if output.startswith('OK: '):
|
|
||||||
try:
|
|
||||||
text = chr(int(output.partition(' ')[2], 16))
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
else:
|
|
||||||
w.paste(text)
|
|
||||||
|
|
||||||
def run_simple_kitten(self, type_of_input, kitten, *args):
|
|
||||||
import shlex
|
|
||||||
w = self.active_window
|
|
||||||
tab = self.active_tab
|
|
||||||
if w is not None and tab is not None and w.overlay_for is None:
|
|
||||||
cmdline = args[0] if args else ''
|
|
||||||
args = shlex.split(cmdline) if cmdline else []
|
|
||||||
if '--program' not in cmdline:
|
|
||||||
args.extend(('--program', self.opts.open_url_with))
|
|
||||||
if type_of_input in ('text', 'history', 'ansi', 'ansi-history'):
|
if type_of_input in ('text', 'history', 'ansi', 'ansi-history'):
|
||||||
data = w.as_text(as_ansi='ansi' in type_of_input, add_history='history' in type_of_input).encode('utf-8')
|
data = w.as_text(as_ansi='ansi' in type_of_input, add_history='history' in type_of_input).encode('utf-8')
|
||||||
elif type_of_input == 'none':
|
elif type_of_input == 'none':
|
||||||
data = None
|
data = None
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
||||||
tab.new_special_window(
|
from kittens.runner import create_kitten_handler
|
||||||
|
end_kitten = create_kitten_handler(kitten, orig_args)
|
||||||
|
copts = {k: self.opts[k] for k in ('select_by_word_characters', 'open_url_with')}
|
||||||
|
overlay_window = tab.new_special_window(
|
||||||
SpecialWindow(
|
SpecialWindow(
|
||||||
['kitty', '+runpy', 'from kittens.{}.main import main; main()'.format(kitten)] + args,
|
['kitty', '+runpy', 'from kittens.runner import main; main()'] + args,
|
||||||
stdin=data,
|
stdin=data,
|
||||||
|
env={'KITTY_COMMON_OPTS': json.dumps(copts)},
|
||||||
overlay_for=w.id))
|
overlay_for=w.id))
|
||||||
|
overlay_window.action_on_close = partial(self.on_kitten_finish, w.id, end_kitten)
|
||||||
|
|
||||||
|
def run_kitten(self, type_of_input, kitten, *args):
|
||||||
|
import shlex
|
||||||
|
cmdline = args[0] if args else ''
|
||||||
|
args = shlex.split(cmdline) if cmdline else []
|
||||||
|
self._run_kitten(kitten, args, type_of_input)
|
||||||
|
|
||||||
|
def on_kitten_finish(self, target_window_id, end_kitten, source_window):
|
||||||
|
output = self.get_output(source_window, num_lines=None)
|
||||||
|
if output.startswith('OK: '):
|
||||||
|
data = json.loads(output.partition(' ')[2].strip())
|
||||||
|
end_kitten(data, target_window_id, self)
|
||||||
|
|
||||||
|
def input_unicode_character(self):
|
||||||
|
self._run_kitten('unicode_input')
|
||||||
|
|
||||||
|
def set_tab_title(self):
|
||||||
|
tab = self.active_tab
|
||||||
|
if tab:
|
||||||
|
args = ['--name=tab-title', '--message', _('Enter the new title for this tab below.'), 'do_set_tab_title', str(tab.id)]
|
||||||
|
self._run_kitten('ask', args)
|
||||||
|
|
||||||
|
def do_set_tab_title(self, title, tab_id):
|
||||||
|
tm = self.active_tab_manager
|
||||||
|
if tm is not None and title:
|
||||||
|
tab_id = int(tab_id)
|
||||||
|
for tab in tm.tabs:
|
||||||
|
if tab.id == tab_id:
|
||||||
|
tab.set_title(title)
|
||||||
|
break
|
||||||
|
|
||||||
|
def kitty_shell(self, window_type):
|
||||||
|
cmd = ['kitty', '@']
|
||||||
|
if window_type == 'tab':
|
||||||
|
window = self._new_tab(cmd).active_window
|
||||||
|
elif window_type == 'os_window':
|
||||||
|
os_window_id = self._new_os_window(cmd)
|
||||||
|
window = self.os_window_map[os_window_id].active_window
|
||||||
|
elif window_type == 'overlay':
|
||||||
|
w = self.active_window
|
||||||
|
tab = self.active_tab
|
||||||
|
if w is not None and tab is not None and w.overlay_for is None:
|
||||||
|
window = tab.new_special_window(SpecialWindow(cmd, overlay_for=w.id))
|
||||||
|
else:
|
||||||
|
window = None
|
||||||
|
else:
|
||||||
|
window = self._new_window(cmd)
|
||||||
|
if window is not None:
|
||||||
|
window.allow_remote_control = True
|
||||||
|
|
||||||
def switch_focus_to(self, window_idx):
|
def switch_focus_to(self, window_idx):
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
@@ -435,18 +559,16 @@ class Boss:
|
|||||||
old_focus.focus_changed(False)
|
old_focus.focus_changed(False)
|
||||||
tab.active_window.focus_changed(True)
|
tab.active_window.focus_changed(True)
|
||||||
|
|
||||||
def open_url(self, url):
|
def open_url(self, url, program=None):
|
||||||
if url:
|
if url:
|
||||||
open_url(url, self.opts.open_url_with)
|
open_url(url, program or self.opts.open_url_with)
|
||||||
|
|
||||||
def open_url_lines(self, lines):
|
def open_url_lines(self, lines, program=None):
|
||||||
self.open_url(''.join(lines))
|
self.open_url(''.join(lines), program)
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self.shutting_down = True
|
self.shutting_down = True
|
||||||
self.child_monitor.shutdown_monitor()
|
self.child_monitor.shutdown_monitor()
|
||||||
wakeup()
|
|
||||||
self.child_monitor.join()
|
|
||||||
del self.child_monitor
|
del self.child_monitor
|
||||||
for tm in self.os_window_map.values():
|
for tm in self.os_window_map.values():
|
||||||
tm.destroy()
|
tm.destroy()
|
||||||
@@ -482,6 +604,11 @@ class Boss:
|
|||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.goto_tab(tab_num - 1)
|
tm.goto_tab(tab_num - 1)
|
||||||
|
|
||||||
|
def set_active_tab(self, tab):
|
||||||
|
tm = self.active_tab_manager
|
||||||
|
if tm is not None:
|
||||||
|
tm.set_active_tab(tab)
|
||||||
|
|
||||||
def next_tab(self):
|
def next_tab(self):
|
||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
@@ -533,7 +660,7 @@ class Boss:
|
|||||||
special_window = self.args_to_special_window(args, cwd_from=cwd_from)
|
special_window = self.args_to_special_window(args, cwd_from=cwd_from)
|
||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.new_tab(special_window=special_window, cwd_from=cwd_from)
|
return tm.new_tab(special_window=special_window, cwd_from=cwd_from)
|
||||||
|
|
||||||
def new_tab(self, *args):
|
def new_tab(self, *args):
|
||||||
self._new_tab(args)
|
self._new_tab(args)
|
||||||
@@ -547,9 +674,9 @@ class Boss:
|
|||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if tab is not None:
|
if tab is not None:
|
||||||
if args:
|
if args:
|
||||||
tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from))
|
return tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from))
|
||||||
else:
|
else:
|
||||||
tab.new_window(cwd_from=cwd_from)
|
return tab.new_window(cwd_from=cwd_from)
|
||||||
|
|
||||||
def new_window(self, *args):
|
def new_window(self, *args):
|
||||||
self._new_window(args)
|
self._new_window(args)
|
||||||
@@ -570,3 +697,11 @@ class Boss:
|
|||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.move_tab(-1)
|
tm.move_tab(-1)
|
||||||
|
|
||||||
|
def patch_colors(self, spec, configured=False):
|
||||||
|
if configured:
|
||||||
|
for k, v in spec.items():
|
||||||
|
if hasattr(self.opts, k):
|
||||||
|
setattr(self.opts, k, color_from_int(v))
|
||||||
|
for tm in self.all_tab_managers:
|
||||||
|
tm.tab_bar.patch_colors(spec)
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
layout(std140) uniform CellRenderData {
|
layout(std140) uniform CellRenderData {
|
||||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||||
|
|
||||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style;
|
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||||
|
|
||||||
int color1, color2;
|
|
||||||
|
|
||||||
uint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
uint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
||||||
|
|
||||||
@@ -21,7 +19,7 @@ layout(location=1) in uvec4 sprite_coords;
|
|||||||
layout(location=2) in uint is_selected;
|
layout(location=2) in uint is_selected;
|
||||||
|
|
||||||
|
|
||||||
|
const int fg_index_map[] = int[3](0, 1, 0);
|
||||||
const uvec2 cell_pos_map[] = uvec2[4](
|
const uvec2 cell_pos_map[] = uvec2[4](
|
||||||
uvec2(1, 0), // right, top
|
uvec2(1, 0), // right, top
|
||||||
uvec2(1, 1), // right, bottom
|
uvec2(1, 1), // right, bottom
|
||||||
@@ -135,10 +133,10 @@ void main() {
|
|||||||
|
|
||||||
// set cell color indices {{{
|
// set cell color indices {{{
|
||||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||||
ivec2 color_indices = ivec2(color1, color2);
|
|
||||||
uint text_attrs = sprite_coords[3];
|
uint text_attrs = sprite_coords[3];
|
||||||
int fg_index = color_indices[(text_attrs >> 6) & REVERSE_MASK];
|
uint is_inverted = ((text_attrs >> 6) & REVERSE_MASK) + inverted;
|
||||||
int bg_index = color_indices[1 - fg_index];
|
int fg_index = fg_index_map[is_inverted];
|
||||||
|
int bg_index = 1 - fg_index;
|
||||||
float cursor = is_cursor(c, r);
|
float cursor = is_cursor(c, r);
|
||||||
vec3 bg = to_color(colors[bg_index], default_colors[bg_index]);
|
vec3 bg = to_color(colors[bg_index], default_colors[bg_index]);
|
||||||
// }}}
|
// }}}
|
||||||
|
|||||||
@@ -5,16 +5,7 @@
|
|||||||
* Distributed under terms of the GPL3 license.
|
* Distributed under terms of the GPL3 license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#include "threading.h"
|
||||||
#include <pthread.h>
|
|
||||||
// I cant figure out how to get pthread.h to include this definition on macOS. MACOSX_DEPLOYMENT_TARGET does not work.
|
|
||||||
extern int pthread_setname_np(const char *name);
|
|
||||||
#else
|
|
||||||
// Need _GNU_SOURCE for pthread_setname_np on linux
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <pthread.h>
|
|
||||||
#undef _GNU_SOURCE
|
|
||||||
#endif
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
@@ -28,12 +19,17 @@ extern int pthread_setname_np(const char *name);
|
|||||||
extern PyTypeObject Screen_Type;
|
extern PyTypeObject Screen_Type;
|
||||||
|
|
||||||
#define EXTRA_FDS 2
|
#define EXTRA_FDS 2
|
||||||
|
#ifndef MSG_NOSIGNAL
|
||||||
|
// Apple does not implement MSG_NOSIGNAL
|
||||||
|
#define MSG_NOSIGNAL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static void (*parse_func)(Screen*, PyObject*);
|
static void (*parse_func)(Screen*, PyObject*);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *data;
|
char *data;
|
||||||
size_t sz;
|
size_t sz;
|
||||||
|
int fd;
|
||||||
} Message;
|
} Message;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -44,7 +40,7 @@ typedef struct {
|
|||||||
bool shutting_down;
|
bool shutting_down;
|
||||||
pthread_t io_thread, talk_thread;
|
pthread_t io_thread, talk_thread;
|
||||||
|
|
||||||
int talk_fd;
|
int talk_fd, listen_fd;
|
||||||
Message *messages;
|
Message *messages;
|
||||||
size_t messages_capacity, messages_count;
|
size_t messages_capacity, messages_count;
|
||||||
} ChildMonitor;
|
} ChildMonitor;
|
||||||
@@ -63,6 +59,9 @@ static const Child EMPTY_CHILD = {0};
|
|||||||
pthread_mutex_##op(&screen->which##_buf_lock);
|
pthread_mutex_##op(&screen->which##_buf_lock);
|
||||||
#define children_mutex(op) \
|
#define children_mutex(op) \
|
||||||
pthread_mutex_##op(&children_lock);
|
pthread_mutex_##op(&children_lock);
|
||||||
|
#define peer_mutex(op) \
|
||||||
|
pthread_mutex_##op(&talk_data.peer_lock);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static Child children[MAX_CHILDREN] = {{0}};
|
static Child children[MAX_CHILDREN] = {{0}};
|
||||||
@@ -72,23 +71,12 @@ static unsigned long remove_notify[MAX_CHILDREN] = {0};
|
|||||||
static size_t add_queue_count = 0, remove_queue_count = 0;
|
static size_t add_queue_count = 0, remove_queue_count = 0;
|
||||||
static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
|
static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
|
||||||
static pthread_mutex_t children_lock;
|
static pthread_mutex_t children_lock;
|
||||||
static bool signal_received = false;
|
static bool kill_signal_received = false;
|
||||||
static ChildMonitor *the_monitor = NULL;
|
static ChildMonitor *the_monitor = NULL;
|
||||||
static uint8_t drain_buf[1024];
|
static uint8_t drain_buf[1024];
|
||||||
static int signal_fds[2], wakeup_fds[2];
|
static int signal_fds[2], wakeup_fds[2];
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
set_thread_name(const char *name) {
|
|
||||||
int ret = 0;
|
|
||||||
#ifdef __APPLE__
|
|
||||||
ret = pthread_setname_np(name);
|
|
||||||
#else
|
|
||||||
ret = pthread_setname_np(pthread_self(), name);
|
|
||||||
#endif
|
|
||||||
if (ret != 0) perror("Failed to set thread name");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Main thread functions {{{
|
// Main thread functions {{{
|
||||||
|
|
||||||
@@ -139,11 +127,11 @@ static PyObject *
|
|||||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||||
ChildMonitor *self;
|
ChildMonitor *self;
|
||||||
PyObject *dump_callback, *death_notify;
|
PyObject *dump_callback, *death_notify;
|
||||||
int talk_fd = -1;
|
int talk_fd = -1, listen_fd = -1;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
|
if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
|
||||||
if (!PyArg_ParseTuple(args, "OO|i", &death_notify, &dump_callback, &talk_fd)) return NULL;
|
if (!PyArg_ParseTuple(args, "OO|ii", &death_notify, &dump_callback, &talk_fd, &listen_fd)) return NULL;
|
||||||
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
|
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
|
||||||
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
|
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -151,12 +139,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
if (!self_pipe(wakeup_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
if (!self_pipe(wakeup_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
if (!self_pipe(signal_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
if (!self_pipe(signal_fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
struct sigaction act = {.sa_handler=handle_signal};
|
struct sigaction act = {.sa_handler=handle_signal};
|
||||||
if (sigaction(SIGINT, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
#define SA(which) { if (sigaction(which, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError); if (siginterrupt(which, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);}
|
||||||
if (sigaction(SIGTERM, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD);
|
||||||
if (siginterrupt(SIGINT, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
#undef SA
|
||||||
if (siginterrupt(SIGTERM, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
||||||
self->talk_fd = talk_fd;
|
self->talk_fd = talk_fd;
|
||||||
|
self->listen_fd = listen_fd;
|
||||||
if (self == NULL) return PyErr_NoMemory();
|
if (self == NULL) return PyErr_NoMemory();
|
||||||
self->death_notify = death_notify; Py_INCREF(death_notify);
|
self->death_notify = death_notify; Py_INCREF(death_notify);
|
||||||
if (dump_callback != Py_None) {
|
if (dump_callback != Py_None) {
|
||||||
@@ -205,12 +193,17 @@ wakeup_io_loop(bool in_signal_handler) {
|
|||||||
|
|
||||||
static void* io_loop(void *data);
|
static void* io_loop(void *data);
|
||||||
static void* talk_loop(void *data);
|
static void* talk_loop(void *data);
|
||||||
|
static void send_response(int fd, const char *msg, size_t msg_sz);
|
||||||
|
static void wakeup_talk_loop(bool);
|
||||||
|
static bool talk_thread_started = false;
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
start(ChildMonitor *self) {
|
start(PyObject *s, PyObject *a UNUSED) {
|
||||||
#define start_doc "start() -> Start the I/O thread"
|
#define start_doc "start() -> Start the I/O thread"
|
||||||
if (self->talk_fd > -1) {
|
ChildMonitor *self = (ChildMonitor*)s;
|
||||||
|
if (self->talk_fd > -1 || self->listen_fd > -1) {
|
||||||
if (pthread_create(&self->talk_thread, NULL, talk_loop, self) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
if (pthread_create(&self->talk_thread, NULL, talk_loop, self) != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
talk_thread_started = true;
|
||||||
}
|
}
|
||||||
int ret = pthread_create(&self->io_thread, NULL, io_loop, self);
|
int ret = pthread_create(&self->io_thread, NULL, io_loop, self);
|
||||||
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
@@ -218,17 +211,9 @@ start(ChildMonitor *self) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
join(ChildMonitor *self) {
|
|
||||||
#define join_doc "join() -> Wait for the I/O thread to finish"
|
|
||||||
int ret = pthread_join(self->io_thread, NULL);
|
|
||||||
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
wakeup(ChildMonitor UNUSED *self) {
|
wakeup(PYNOARG) {
|
||||||
#define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there."
|
#define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there."
|
||||||
wakeup_io_loop(false);
|
wakeup_io_loop(false);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -266,7 +251,7 @@ schedule_write_to_child(unsigned long id, const char *data, size_t sz) {
|
|||||||
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
|
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
|
||||||
if (space_left < sz) {
|
if (space_left < sz) {
|
||||||
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
|
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
|
||||||
fprintf(stderr, "Too much data being sent to child with id: %lu, ignoring it\n", id);
|
log_error("Too much data being sent to child with id: %lu, ignoring it", id);
|
||||||
screen_mutex(unlock, write);
|
screen_mutex(unlock, write);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -301,11 +286,21 @@ needs_write(ChildMonitor UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
shutdown_monitor(ChildMonitor *self) {
|
shutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {
|
||||||
#define shutdown_monitor_doc "shutdown_monitor() -> Shutdown the monitor loop."
|
#define shutdown_monitor_doc "shutdown_monitor() -> Shutdown the monitor loop."
|
||||||
signal(SIGINT, SIG_DFL);
|
signal(SIGINT, SIG_DFL);
|
||||||
signal(SIGTERM, SIG_DFL);
|
signal(SIGTERM, SIG_DFL);
|
||||||
|
signal(SIGCHLD, SIG_DFL);
|
||||||
self->shutting_down = true;
|
self->shutting_down = true;
|
||||||
|
wakeup_talk_loop(false);
|
||||||
|
wakeup_io_loop(false);
|
||||||
|
int ret = pthread_join(self->io_thread, NULL);
|
||||||
|
if (ret != 0) return PyErr_Format(PyExc_OSError, "Failed to join() I/O thread with error: %s", strerror(ret));
|
||||||
|
if (talk_thread_started) {
|
||||||
|
ret = pthread_join(self->talk_thread, NULL);
|
||||||
|
if (ret != 0) return PyErr_Format(PyExc_OSError, "Failed to join() talk thread with error: %s", strerror(ret));
|
||||||
|
}
|
||||||
|
talk_thread_started = false;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,14 +338,14 @@ parse_input(ChildMonitor *self) {
|
|||||||
if (msg) {
|
if (msg) {
|
||||||
for (size_t i = 0; i < self->messages_count; i++) {
|
for (size_t i = 0; i < self->messages_count; i++) {
|
||||||
Message *m = self->messages + i;
|
Message *m = self->messages + i;
|
||||||
PyTuple_SET_ITEM(msg, i, PyBytes_FromStringAndSize(m->data, m->sz));
|
PyTuple_SET_ITEM(msg, i, Py_BuildValue("y#i", m->data, (int)m->sz, m->fd));
|
||||||
free(m->data); m->data = NULL; m->sz = 0;
|
free(m->data); m->data = NULL; m->sz = 0;
|
||||||
}
|
}
|
||||||
self->messages_count = 0;
|
self->messages_count = 0;
|
||||||
} else fatal("Out of memory");
|
} else fatal("Out of memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UNLIKELY(signal_received)) {
|
if (UNLIKELY(kill_signal_received)) {
|
||||||
global_state.close_all_windows = true;
|
global_state.close_all_windows = true;
|
||||||
} else {
|
} else {
|
||||||
count = self->count;
|
count = self->count;
|
||||||
@@ -361,7 +356,13 @@ parse_input(ChildMonitor *self) {
|
|||||||
}
|
}
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
if (msg) {
|
if (msg) {
|
||||||
call_boss(peer_messages_received, "(O)", msg);
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(msg); i++) {
|
||||||
|
PyObject *resp = PyObject_CallMethod(global_state.boss, "peer_message_received", "O", PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 0));
|
||||||
|
int peer_fd = (int)PyLong_AsLong(PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 1));
|
||||||
|
if (resp && PyBytes_Check(resp)) send_response(peer_fd, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
|
||||||
|
else { send_response(peer_fd, NULL, 0); if (!resp) PyErr_Print(); }
|
||||||
|
Py_CLEAR(resp);
|
||||||
|
}
|
||||||
Py_CLEAR(msg);
|
Py_CLEAR(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +412,7 @@ pty_resize(int fd, struct winsize *dim) {
|
|||||||
if (ioctl(fd, TIOCSWINSZ, dim) == -1) {
|
if (ioctl(fd, TIOCSWINSZ, dim) == -1) {
|
||||||
if (errno == EINTR) continue;
|
if (errno == EINTR) continue;
|
||||||
if (errno != EBADF && errno != ENOTTY) {
|
if (errno != EBADF && errno != ENOTTY) {
|
||||||
fprintf(stderr, "Failed to resize tty associated with fd: %d with error: %s", fd, strerror(errno));
|
log_error("Failed to resize tty associated with fd: %d with error: %s", fd, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,7 +440,7 @@ resize_pty(ChildMonitor *self, PyObject *args) {
|
|||||||
if (fd == -1) FIND(add_queue, add_queue_count);
|
if (fd == -1) FIND(add_queue, add_queue_count);
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);
|
if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);
|
||||||
} else fprintf(stderr, "Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)\n", window_id, self->count, add_queue_count);
|
} else log_error("Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)", window_id, self->count, add_queue_count);
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -568,7 +569,8 @@ simple_render_screen(PyObject UNUSED *self, PyObject *args) {
|
|||||||
static inline bool
|
static inline bool
|
||||||
prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows) {
|
prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows) {
|
||||||
#define TD os_window->tab_bar_render_data
|
#define TD os_window->tab_bar_render_data
|
||||||
bool needs_render = false;
|
bool needs_render = os_window->needs_render;
|
||||||
|
os_window->needs_render = false;
|
||||||
if (TD.screen && os_window->num_tabs > 1) {
|
if (TD.screen && os_window->num_tabs > 1) {
|
||||||
if (send_cell_data_to_gpu(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window)) needs_render = true;
|
if (send_cell_data_to_gpu(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window)) needs_render = true;
|
||||||
}
|
}
|
||||||
@@ -689,7 +691,7 @@ thread_write(void *x) {
|
|||||||
set_thread_name("KittyWriteStdin");
|
set_thread_name("KittyWriteStdin");
|
||||||
FILE *f = fdopen(data->fd, "w");
|
FILE *f = fdopen(data->fd, "w");
|
||||||
if (fwrite(data->buf, 1, data->sz, f) != data->sz) {
|
if (fwrite(data->buf, 1, data->sz, f) != data->sz) {
|
||||||
fprintf(stderr, "Failed to write all data\n");
|
log_error("Failed to write all data");
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
free_twd(data);
|
free_twd(data);
|
||||||
@@ -733,8 +735,32 @@ process_pending_resizes(double now) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
close_all_windows() {
|
||||||
|
for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true);
|
||||||
|
global_state.close_all_windows = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
process_pending_closes(ChildMonitor *self) {
|
||||||
|
bool has_open_windows = false;
|
||||||
|
for (size_t w = global_state.num_os_windows; w > 0; w--) {
|
||||||
|
OSWindow *os_window = global_state.os_windows + w - 1;
|
||||||
|
if (should_os_window_close(os_window)) {
|
||||||
|
destroy_os_window(os_window);
|
||||||
|
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height);
|
||||||
|
for (size_t t=0; t < os_window->num_tabs; t++) {
|
||||||
|
Tab *tab = os_window->tabs + t;
|
||||||
|
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);
|
||||||
|
}
|
||||||
|
remove_os_window(os_window->id);
|
||||||
|
} else has_open_windows = true;
|
||||||
|
}
|
||||||
|
return has_open_windows;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
main_loop(ChildMonitor *self) {
|
main_loop(ChildMonitor *self, PyObject *a UNUSED) {
|
||||||
#define main_loop_doc "The main thread loop"
|
#define main_loop_doc "The main thread loop"
|
||||||
bool has_open_windows = true;
|
bool has_open_windows = true;
|
||||||
|
|
||||||
@@ -744,23 +770,8 @@ main_loop(ChildMonitor *self) {
|
|||||||
render(now);
|
render(now);
|
||||||
wait_for_events();
|
wait_for_events();
|
||||||
parse_input(self);
|
parse_input(self);
|
||||||
if (global_state.close_all_windows) {
|
if (global_state.close_all_windows) close_all_windows();
|
||||||
for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true);
|
has_open_windows = process_pending_closes(self);
|
||||||
global_state.close_all_windows = false;
|
|
||||||
}
|
|
||||||
has_open_windows = false;
|
|
||||||
for (size_t w = global_state.num_os_windows; w > 0; w--) {
|
|
||||||
OSWindow *os_window = global_state.os_windows + w - 1;
|
|
||||||
if (should_os_window_close(os_window)) {
|
|
||||||
destroy_os_window(os_window);
|
|
||||||
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->viewport_width, os_window->viewport_height);
|
|
||||||
for (size_t t=0; t < os_window->num_tabs; t++) {
|
|
||||||
Tab *tab = os_window->tabs + t;
|
|
||||||
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);
|
|
||||||
}
|
|
||||||
remove_os_window(os_window->id);
|
|
||||||
} else has_open_windows = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -873,6 +884,58 @@ drain_fd(int fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
read_signals(int fd, bool *kill_signal, bool *child_died) {
|
||||||
|
static char buf[256];
|
||||||
|
while(true) {
|
||||||
|
ssize_t len = read(fd, buf, sizeof(buf));
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
if (errno != EIO) perror("Call to read() from read_signals() failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (ssize_t i = 0; i < len; i++) {
|
||||||
|
switch(buf[i]) {
|
||||||
|
case SIGCHLD:
|
||||||
|
*child_died = true; break;
|
||||||
|
case SIGINT:
|
||||||
|
case SIGTERM:
|
||||||
|
*kill_signal = true; break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
mark_child_for_removal(ChildMonitor *self, pid_t pid) {
|
||||||
|
children_mutex(lock);
|
||||||
|
for (size_t i = 0; i < self->count; i++) {
|
||||||
|
if (children[i].pid == pid) {
|
||||||
|
children[i].needs_removal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
children_mutex(unlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
reap_children(ChildMonitor *self, bool enable_close_on_child_death) {
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
(void)self;
|
||||||
|
while(true) {
|
||||||
|
pid = waitpid(-1, &status, WNOHANG);
|
||||||
|
if (pid == -1) {
|
||||||
|
if (errno != EINTR) break;
|
||||||
|
} else if (pid > 0) {
|
||||||
|
if (enable_close_on_child_death) mark_child_for_removal(self, pid);
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
write_to_child(int fd, Screen *screen) {
|
write_to_child(int fd, Screen *screen) {
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
@@ -924,10 +987,10 @@ io_loop(void *data) {
|
|||||||
if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup
|
if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup
|
||||||
if (fds[1].revents && POLLIN) {
|
if (fds[1].revents && POLLIN) {
|
||||||
data_received = true;
|
data_received = true;
|
||||||
drain_fd(fds[1].fd);
|
bool kill_signal = false, child_died = false;
|
||||||
children_mutex(lock);
|
read_signals(fds[1].fd, &kill_signal, &child_died);
|
||||||
signal_received = true;
|
if (kill_signal) { children_mutex(lock); kill_signal_received = true; children_mutex(unlock); }
|
||||||
children_mutex(unlock);
|
if (child_died) reap_children(self, OPT(close_on_child_death));
|
||||||
}
|
}
|
||||||
for (i = 0; i < self->count; i++) {
|
for (i = 0; i < self->count; i++) {
|
||||||
if (fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {
|
if (fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {
|
||||||
@@ -948,7 +1011,7 @@ io_loop(void *data) {
|
|||||||
children_mutex(lock);
|
children_mutex(lock);
|
||||||
children[i].needs_removal = true;
|
children[i].needs_removal = true;
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
fprintf(stderr, "The child %lu had its fd unexpectedly closed\n", children[i].id);
|
log_error("The child %lu had its fd unexpectedly closed", children[i].id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_POLL_EVENTS
|
#ifdef DEBUG_POLL_EVENTS
|
||||||
@@ -975,58 +1038,258 @@ io_loop(void *data) {
|
|||||||
|
|
||||||
// {{{ Talk thread functions
|
// {{{ Talk thread functions
|
||||||
|
|
||||||
static inline void
|
#define MAX_PEERS 256
|
||||||
handle_peer(ChildMonitor *self, int s) {
|
#define MAX_LISTENERS 2
|
||||||
size_t bufsz = 0;
|
|
||||||
char *buf = NULL;
|
|
||||||
size_t buf_used = 0;
|
|
||||||
|
|
||||||
while(true) {
|
typedef struct {
|
||||||
if (buf_used >= bufsz) {
|
char *data;
|
||||||
bufsz = MAX(1024, bufsz) * 2;
|
size_t capacity, used;
|
||||||
if (bufsz > 1024 * 1024) return;
|
int fd;
|
||||||
buf = realloc(buf, bufsz);
|
bool finished, close_socket;
|
||||||
if (buf == NULL) return;
|
} PeerReadData;
|
||||||
}
|
static PeerReadData empty_prd = {.fd = -1, 0};
|
||||||
ssize_t n = recv(s, buf + buf_used, bufsz - buf_used, 0);
|
|
||||||
if (n == 0) break;
|
typedef struct {
|
||||||
if (n < 0) {
|
char *data;
|
||||||
if (errno == EINTR) continue;
|
size_t sz, pos;
|
||||||
perror("Error reading from talk peer");
|
int fd;
|
||||||
|
bool finished;
|
||||||
|
} PeerWriteData;
|
||||||
|
static PeerWriteData empty_pwd = {.fd = -1, 0};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t num_listen_fds, num_talk_fds, num_reads, num_writes, num_queued_writes;
|
||||||
|
struct pollfd fds[MAX_PEERS + MAX_LISTENERS + 1];
|
||||||
|
PeerReadData reads[MAX_LISTENERS];
|
||||||
|
PeerWriteData writes[MAX_LISTENERS];
|
||||||
|
PeerWriteData queued_writes[MAX_LISTENERS];
|
||||||
|
int wakeup_fds[2];
|
||||||
|
pthread_mutex_t peer_lock;
|
||||||
|
} TalkData;
|
||||||
|
|
||||||
|
static TalkData talk_data = {0};
|
||||||
|
#define nuke_socket(s) { shutdown(s, SHUT_RDWR); close(s); }
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
accept_peer(int listen_fd, bool shutting_down) {
|
||||||
|
int peer = accept(listen_fd, NULL, NULL);
|
||||||
|
if (UNLIKELY(peer == -1)) {
|
||||||
|
if (errno == EINTR) return true;
|
||||||
|
if (!shutting_down) perror("accept() on talk socket failed!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
|
||||||
|
if (fd_idx < arraysz(talk_data.fds) && talk_data.num_reads < arraysz(talk_data.reads)) {
|
||||||
|
talk_data.fds[fd_idx].fd = peer; talk_data.fds[fd_idx].events = POLLIN;
|
||||||
|
talk_data.reads[talk_data.num_reads] = empty_prd; talk_data.reads[talk_data.num_reads++].fd = peer;
|
||||||
|
talk_data.num_talk_fds++;
|
||||||
|
} else nuke_socket(peer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
read_from_peer(ChildMonitor *self, int s) {
|
||||||
|
bool read_finished = false;
|
||||||
|
for (size_t i = 0; i < talk_data.num_reads; i++) {
|
||||||
|
PeerReadData *rd = talk_data.reads + i;
|
||||||
|
#define failed(msg) { read_finished = true; log_error("%s", msg); rd->finished = true; rd->close_socket = true; break; }
|
||||||
|
if (rd->fd == s) {
|
||||||
|
if (rd->used >= rd->capacity) {
|
||||||
|
if (rd->capacity >= 1024 * 1024) failed("Ignoring too large message from peer");
|
||||||
|
rd->capacity = MAX(8192, rd->capacity * 2);
|
||||||
|
rd->data = realloc(rd->data, rd->capacity);
|
||||||
|
if (!rd->data) failed("Out of memory");
|
||||||
|
}
|
||||||
|
ssize_t n = recv(s, rd->data + rd->used, rd->capacity - rd->used, 0);
|
||||||
|
if (n == 0) {
|
||||||
|
read_finished = true; rd->finished = true;
|
||||||
|
children_mutex(lock);
|
||||||
|
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
|
||||||
|
Message *m = self->messages + self->messages_count++;
|
||||||
|
m->data = rd->data; rd->data = NULL; m->sz = rd->used; m->fd = s;
|
||||||
|
children_mutex(unlock);
|
||||||
|
wakeup_main_loop();
|
||||||
|
} else if (n < 0) {
|
||||||
|
if (errno != EINTR) {
|
||||||
|
perror("Error reading from talk peer");
|
||||||
|
failed("");
|
||||||
|
}
|
||||||
|
} else rd->used += n;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buf_used += n;
|
|
||||||
}
|
}
|
||||||
if (buf_used) {
|
#undef failed
|
||||||
children_mutex(lock);
|
return read_finished;
|
||||||
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
|
}
|
||||||
Message *m = self->messages + self->messages_count++;
|
|
||||||
m->data = buf; m->sz = buf_used;
|
static inline bool
|
||||||
children_mutex(unlock);
|
write_to_peer(int fd) {
|
||||||
wakeup_main_loop();
|
bool write_finished = false;
|
||||||
} else free(buf);
|
for (size_t i = 0; i < talk_data.num_writes; i++) {
|
||||||
|
PeerWriteData *wd = talk_data.writes + i;
|
||||||
|
#define failed(msg) { write_finished = true; log_error("%s", msg); wd->finished = true; break; }
|
||||||
|
if (wd->fd == fd) {
|
||||||
|
ssize_t n = send(fd, wd->data + wd->pos, wd->sz - wd->pos, MSG_NOSIGNAL);
|
||||||
|
if (n == 0) { failed("send() to peer failed to send any data"); }
|
||||||
|
else if (n < 0) {
|
||||||
|
if (errno != EINTR) { perror("write() to peer socket failed with error"); failed(""); }
|
||||||
|
} else {
|
||||||
|
wd->pos += n;
|
||||||
|
if (wd->pos >= wd->sz) { write_finished = true; wd->finished = true; }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#undef failed
|
||||||
|
return write_finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
remove_poll_fd(int fd) {
|
||||||
|
size_t count = talk_data.num_talk_fds + talk_data.num_listen_fds;
|
||||||
|
for (size_t i = talk_data.num_listen_fds; i < count; i++) {
|
||||||
|
struct pollfd *pfd = talk_data.fds + i;
|
||||||
|
if (pfd->fd == fd) {
|
||||||
|
size_t num_to_right = count - 1 - i;
|
||||||
|
if (num_to_right) memmove(talk_data.fds + i, talk_data.fds + i + 1, num_to_right * sizeof(struct pollfd));
|
||||||
|
talk_data.num_talk_fds--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
prune_finished_reads() {
|
||||||
|
if (!talk_data.num_reads) return;
|
||||||
|
for (ssize_t i = talk_data.num_reads - 1; i >= 0; i--) {
|
||||||
|
PeerReadData *rd = talk_data.reads + i;
|
||||||
|
if (rd->finished) {
|
||||||
|
remove_poll_fd(rd->fd);
|
||||||
|
if (rd->close_socket) { nuke_socket(rd->fd); }
|
||||||
|
else shutdown(rd->fd, SHUT_RD);
|
||||||
|
free(rd->data);
|
||||||
|
ssize_t num_to_right = talk_data.num_reads - 1 - i;
|
||||||
|
if (num_to_right > 0) memmove(talk_data.reads + i, talk_data.reads + i + 1, num_to_right * sizeof(PeerReadData));
|
||||||
|
else talk_data.reads[i] = empty_prd;
|
||||||
|
talk_data.num_reads--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
prune_finished_writes() {
|
||||||
|
if (!talk_data.num_writes) return;
|
||||||
|
for (ssize_t i = talk_data.num_writes - 1; i >= 0; i--) {
|
||||||
|
PeerWriteData *wd = talk_data.writes + i;
|
||||||
|
if (wd->finished) {
|
||||||
|
remove_poll_fd(wd->fd);
|
||||||
|
shutdown(wd->fd, SHUT_WR); close(wd->fd);
|
||||||
|
free(wd->data);
|
||||||
|
ssize_t num_to_right = talk_data.num_writes - 1 - i;
|
||||||
|
if (num_to_right > 0) memmove(talk_data.writes + i, talk_data.writes + i + 1, num_to_right * sizeof(PeerWriteData));
|
||||||
|
else talk_data.writes[i] = empty_pwd;
|
||||||
|
talk_data.num_writes--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wakeup_talk_loop(bool in_signal_handler) {
|
||||||
|
if (talk_data.wakeup_fds[1] <= 0) return;
|
||||||
|
while(true) {
|
||||||
|
ssize_t ret = write(talk_data.wakeup_fds[1], "w", 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
if (!in_signal_handler) perror("Failed to write to talk wakeup fd with error");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
move_queued_writes() {
|
||||||
|
while (talk_data.num_queued_writes) {
|
||||||
|
PeerWriteData *src = talk_data.queued_writes + --talk_data.num_queued_writes;
|
||||||
|
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
|
||||||
|
if (fd_idx < arraysz(talk_data.fds) && talk_data.num_writes < arraysz(talk_data.writes)) {
|
||||||
|
talk_data.fds[fd_idx].fd = src->fd; talk_data.fds[fd_idx].events = POLLOUT;
|
||||||
|
talk_data.writes[talk_data.num_writes++] = *src;
|
||||||
|
talk_data.num_talk_fds++;
|
||||||
|
} else {
|
||||||
|
log_error("Cannot send response to peer, too many peers");
|
||||||
|
free(src->data); nuke_socket(src->fd);
|
||||||
|
}
|
||||||
|
*src = empty_pwd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void*
|
static void*
|
||||||
talk_loop(void *data) {
|
talk_loop(void *data) {
|
||||||
// The I/O thread loop
|
// The talk thread loop
|
||||||
|
|
||||||
ChildMonitor *self = (ChildMonitor*)data;
|
ChildMonitor *self = (ChildMonitor*)data;
|
||||||
set_thread_name("KittyTalkMon");
|
set_thread_name("KittyPeerMon");
|
||||||
|
if ((pthread_mutex_init(&talk_data.peer_lock, NULL)) != 0) { perror("Failed to create peer mutex"); return 0; }
|
||||||
|
if (!self_pipe(talk_data.wakeup_fds)) { perror("Failed to create wakeup fds for talk thread"); return 0; }
|
||||||
|
#define add_listener(which) \
|
||||||
|
if (self->which > -1) { \
|
||||||
|
talk_data.fds[talk_data.num_listen_fds].fd = self->which; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN; \
|
||||||
|
}
|
||||||
|
add_listener(talk_fd); add_listener(listen_fd);
|
||||||
|
#undef add_listener
|
||||||
|
talk_data.fds[talk_data.num_listen_fds].fd = talk_data.wakeup_fds[0]; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN;
|
||||||
|
|
||||||
while (LIKELY(!self->shutting_down)) {
|
while (LIKELY(!self->shutting_down)) {
|
||||||
int peer = accept(self->talk_fd, NULL, NULL);
|
for (size_t i = 0; i < talk_data.num_listen_fds + talk_data.num_talk_fds; i++) { talk_data.fds[i].revents = 0; }
|
||||||
if (peer == -1) {
|
int ret = poll(talk_data.fds, talk_data.num_listen_fds + talk_data.num_talk_fds, -1);
|
||||||
if (errno == EINTR) continue;
|
if (ret > 0) {
|
||||||
if (!self->shutting_down) perror("accept() on talk socket failed!");
|
bool has_finished_reads = false, has_finished_writes = false;
|
||||||
break;
|
for (size_t i = 0; i < talk_data.num_listen_fds - 1; i++) {
|
||||||
}
|
if (talk_data.fds[i].revents & POLLIN) {if (!accept_peer(talk_data.fds[i].fd, self->shutting_down)) goto end; }
|
||||||
handle_peer(self, peer);
|
}
|
||||||
shutdown(peer, SHUT_RDWR);
|
if (talk_data.fds[talk_data.num_listen_fds - 1].revents & POLLIN) drain_fd(talk_data.fds[talk_data.num_listen_fds - 1].fd); // wakeup
|
||||||
close(peer);
|
for (size_t i = talk_data.num_listen_fds; i < talk_data.num_talk_fds + talk_data.num_listen_fds; i++) {
|
||||||
|
if (talk_data.fds[i].revents & (POLLIN | POLLHUP)) { if (read_from_peer(self, talk_data.fds[i].fd)) has_finished_reads = true; }
|
||||||
|
if (talk_data.fds[i].revents & POLLOUT) { if (write_to_peer(talk_data.fds[i].fd)) has_finished_writes = true; }
|
||||||
|
}
|
||||||
|
if (has_finished_reads) prune_finished_reads();
|
||||||
|
if (has_finished_writes) prune_finished_writes();
|
||||||
|
peer_mutex(lock);
|
||||||
|
if (talk_data.num_queued_writes) move_queued_writes();
|
||||||
|
peer_mutex(unlock);
|
||||||
|
} else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror("poll() on talk fds failed"); }
|
||||||
}
|
}
|
||||||
|
end:
|
||||||
|
close(talk_data.wakeup_fds[0]); close(talk_data.wakeup_fds[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
add_peer_writer(int fd, const char* msg, size_t msg_sz) {
|
||||||
|
bool ok = false;
|
||||||
|
peer_mutex(lock);
|
||||||
|
if (talk_data.num_queued_writes < arraysz(talk_data.queued_writes)) {
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes] = empty_pwd;
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes].data = malloc(msg_sz);
|
||||||
|
if (talk_data.queued_writes[talk_data.num_queued_writes].data) {
|
||||||
|
memcpy(talk_data.queued_writes[talk_data.num_queued_writes].data, msg, msg_sz);
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes].sz = msg_sz;
|
||||||
|
talk_data.queued_writes[talk_data.num_queued_writes++].fd = fd;
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
} else log_error("Cannot send response to peer, too many peers");
|
||||||
|
peer_mutex(unlock);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_response(int fd, const char *msg, size_t msg_sz) {
|
||||||
|
if (msg == NULL) { shutdown(fd, SHUT_WR); close(fd); return; }
|
||||||
|
if (!add_peer_writer(fd, msg, msg_sz)) { shutdown(fd, SHUT_WR); close(fd); }
|
||||||
|
else wakeup_talk_loop(false);
|
||||||
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
@@ -1034,7 +1297,6 @@ static PyMethodDef methods[] = {
|
|||||||
METHOD(add_child, METH_VARARGS)
|
METHOD(add_child, METH_VARARGS)
|
||||||
METHOD(needs_write, METH_VARARGS)
|
METHOD(needs_write, METH_VARARGS)
|
||||||
METHOD(start, METH_NOARGS)
|
METHOD(start, METH_NOARGS)
|
||||||
METHOD(join, METH_NOARGS)
|
|
||||||
METHOD(wakeup, METH_NOARGS)
|
METHOD(wakeup, METH_NOARGS)
|
||||||
METHOD(shutdown_monitor, METH_NOARGS)
|
METHOD(shutdown_monitor, METH_NOARGS)
|
||||||
METHOD(main_loop, METH_NOARGS)
|
METHOD(main_loop, METH_NOARGS)
|
||||||
@@ -1056,8 +1318,16 @@ PyTypeObject ChildMonitor_Type = {
|
|||||||
.tp_new = new,
|
.tp_new = new,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
safe_pipe(PYNOARG) {
|
||||||
|
int fds[2] = {0};
|
||||||
|
if (!self_pipe(fds)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
return Py_BuildValue("ii", fds[0], fds[1]);
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
METHOD(simple_render_screen, METH_VARARGS)
|
METHOD(simple_render_screen, METH_VARARGS)
|
||||||
|
METHODB(safe_pipe, METH_NOARGS),
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ static PyObject*
|
|||||||
spawn(PyObject *self UNUSED, PyObject *args) {
|
spawn(PyObject *self UNUSED, PyObject *args) {
|
||||||
PyObject *argv_p, *env_p;
|
PyObject *argv_p, *env_p;
|
||||||
int master, slave, stdin_read_fd, stdin_write_fd;
|
int master, slave, stdin_read_fd, stdin_write_fd;
|
||||||
char* cwd;
|
char *cwd, *exe;
|
||||||
if (!PyArg_ParseTuple(args, "sO!O!iiii", &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
|
if (!PyArg_ParseTuple(args, "ssO!O!iiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
|
||||||
char name[2048] = {0};
|
char name[2048] = {0};
|
||||||
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
char **argv = serialize_string_tuple(argv_p);
|
char **argv = serialize_string_tuple(argv_p);
|
||||||
@@ -76,7 +76,7 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
|||||||
close(tfd);
|
close(tfd);
|
||||||
|
|
||||||
environ = env;
|
environ = env;
|
||||||
execvp(argv[0], argv);
|
execvp(exe, argv);
|
||||||
// Report the failure and exec a shell instead, so that we are not left
|
// Report the failure and exec a shell instead, so that we are not left
|
||||||
// with a forked but not exec'ed process
|
// with a forked but not exec'ed process
|
||||||
write_to_stderr("Failed to launch child: ");
|
write_to_stderr("Failed to launch child: ");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import os
|
|||||||
|
|
||||||
import kitty.fast_data_types as fast_data_types
|
import kitty.fast_data_types as fast_data_types
|
||||||
|
|
||||||
from .constants import terminfo_dir, is_macos
|
from .constants import is_macos, shell_path, terminfo_dir
|
||||||
|
|
||||||
|
|
||||||
def cwd_of_process(pid):
|
def cwd_of_process(pid):
|
||||||
@@ -70,7 +70,13 @@ class Child:
|
|||||||
if os.path.isdir(terminfo_dir):
|
if os.path.isdir(terminfo_dir):
|
||||||
env['TERMINFO'] = terminfo_dir
|
env['TERMINFO'] = terminfo_dir
|
||||||
env = tuple('{}={}'.format(k, v) for k, v in env.items())
|
env = tuple('{}={}'.format(k, v) for k, v in env.items())
|
||||||
pid = fast_data_types.spawn(self.cwd, tuple(self.argv), env, master, slave, stdin_read_fd, stdin_write_fd)
|
argv = list(self.argv)
|
||||||
|
exe = argv[0]
|
||||||
|
if is_macos and exe == shell_path:
|
||||||
|
# Some macOS machines need the shell to have argv[0] prefixed by
|
||||||
|
# hyphen, see https://github.com/kovidgoyal/kitty/issues/247
|
||||||
|
argv[0] = ('-' + exe.split('/')[-1])
|
||||||
|
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd)
|
||||||
os.close(slave)
|
os.close(slave)
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self.child_fd = master
|
self.child_fd = master
|
||||||
|
|||||||
152
kitty/cli.py
152
kitty/cli.py
@@ -2,16 +2,16 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
from .config import load_config
|
from .config import defaults, load_config
|
||||||
from .constants import appname, defconf, is_macos, str_version
|
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
|
|
||||||
is_macos
|
|
||||||
OPTIONS = '''
|
OPTIONS = '''
|
||||||
--class
|
--class
|
||||||
dest=cls
|
dest=cls
|
||||||
@@ -32,9 +32,23 @@ only use this if you are running a program that does not set titles.
|
|||||||
|
|
||||||
--config
|
--config
|
||||||
type=list
|
type=list
|
||||||
default={config_path}
|
Specify a path to the configuration file(s) to use. All configuration files are
|
||||||
Specify a path to the configuration file(s) to use.
|
merged onto the builtin kitty.conf, overriding the builtin values. This option
|
||||||
Can be specified multiple times to read multiple configuration files in sequence, which are merged.
|
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/kitty/kitty.conf", "~/.config/kitty/kitty.conf", {macos_confpath}
|
||||||
|
"$XDG_CONFIG_DIRS/kitty/kitty.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/kitty/kitty.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.
|
||||||
|
|
||||||
|
|
||||||
--override -o
|
--override -o
|
||||||
@@ -84,6 +98,16 @@ with the same |_ --instance-group| will result in new windows being created
|
|||||||
in the first |_ {appname}| instance within that group
|
in the first |_ {appname}| instance within that group
|
||||||
|
|
||||||
|
|
||||||
|
--listen-on
|
||||||
|
Tell kitty to listen on the specified address for control
|
||||||
|
messages. For example, |_ --listen-on=unix:/tmp/mykitty| or
|
||||||
|
|_ --listen-on=tcp:localhost:12345|. On Linux systems, you can also use abstract
|
||||||
|
UNIX sockets, not associated with a file, like this: |_ --listen-on=unix:@mykitty|.
|
||||||
|
To control kitty, you can send it commands with |_ kitty @| using the |_ --to| option
|
||||||
|
to specify this address. Note that this option will be ignored, unless you set
|
||||||
|
|_ allow_remote_control| to yes in |_ kitty.conf|
|
||||||
|
|
||||||
|
|
||||||
# Debugging options
|
# Debugging options
|
||||||
|
|
||||||
--version -v
|
--version -v
|
||||||
@@ -107,6 +131,16 @@ Path to file in which to store the raw bytes received from the child process
|
|||||||
--debug-gl
|
--debug-gl
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Debug OpenGL commands. This will cause all OpenGL calls to check for errors instead of ignoring them. Useful when debugging rendering problems
|
Debug OpenGL commands. This will cause all OpenGL calls to check for errors instead of ignoring them. Useful when debugging rendering problems
|
||||||
|
|
||||||
|
|
||||||
|
--debug-font-fallback
|
||||||
|
type=bool-set
|
||||||
|
Print out information about the selection of fallback fonts for characters not present in the main font.
|
||||||
|
|
||||||
|
|
||||||
|
--debug-config
|
||||||
|
type=bool-set
|
||||||
|
Print out information about the system and kitty configuration.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@@ -186,10 +220,10 @@ def parse_option_spec(spec=OPTIONS):
|
|||||||
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
|
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
|
||||||
elif state is HELP:
|
elif state is HELP:
|
||||||
if line:
|
if line:
|
||||||
current_cmd['help'] += ' ' + line
|
current_cmd['help'] += ' ' + line.lstrip()
|
||||||
else:
|
else:
|
||||||
if prev_line:
|
if prev_line:
|
||||||
current_cmd['help'] += '\n'
|
current_cmd['help'] += '\n\n\t'
|
||||||
else:
|
else:
|
||||||
state = NORMAL
|
state = NORMAL
|
||||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||||
@@ -213,8 +247,12 @@ def prettify(text):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def version():
|
def version(add_rev=False):
|
||||||
return '{} {} created by {}'.format(italic(appname), green(str_version), title('Kovid Goyal'))
|
rev = ''
|
||||||
|
from . import fast_data_types
|
||||||
|
if add_rev and hasattr(fast_data_types, 'KITTY_VCS_REV'):
|
||||||
|
rev = ' ({})'.format(fast_data_types.KITTY_VCS_REV[:10])
|
||||||
|
return '{} {}{} created by {}'.format(italic(appname), green(str_version), rev, title('Kovid Goyal'))
|
||||||
|
|
||||||
|
|
||||||
def wrap(text, limit=80):
|
def wrap(text, limit=80):
|
||||||
@@ -252,6 +290,7 @@ def wrap(text, limit=80):
|
|||||||
|
|
||||||
def print_help_for_seq(seq, usage, message, appname):
|
def print_help_for_seq(seq, usage, message, appname):
|
||||||
from kitty.icat import screen_size
|
from kitty.icat import screen_size
|
||||||
|
screen_size.changed = True
|
||||||
try:
|
try:
|
||||||
linesz = min(screen_size().cols, 76)
|
linesz = min(screen_size().cols, 76)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
@@ -271,8 +310,9 @@ def print_help_for_seq(seq, usage, message, appname):
|
|||||||
lines.append('')
|
lines.append('')
|
||||||
a((' ' * leading_indent) + j.join(lines))
|
a((' ' * leading_indent) + j.join(lines))
|
||||||
|
|
||||||
usage = usage or '[program-to-run ...]'
|
usage = '[program-to-run ...]' if usage is None else usage
|
||||||
a('{}: {} [options] {}'.format(title('Usage'), bold(yellow(appname)), usage))
|
optstring = '[options] ' if seq else ''
|
||||||
|
a('{}: {} {}{}'.format(title('Usage'), bold(yellow(appname)), optstring, usage))
|
||||||
a('')
|
a('')
|
||||||
message = message or (
|
message = message or (
|
||||||
'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
|
'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
|
||||||
@@ -300,7 +340,7 @@ def print_help_for_seq(seq, usage, message, appname):
|
|||||||
a('')
|
a('')
|
||||||
|
|
||||||
text = '\n'.join(blocks) + '\n\n' + version()
|
text = '\n'.join(blocks) + '\n\n' + version()
|
||||||
if sys.stdout.isatty():
|
if print_help_for_seq.allow_pager and sys.stdout.isatty():
|
||||||
p = subprocess.Popen(['less', '-isRXF'], stdin=subprocess.PIPE)
|
p = subprocess.Popen(['less', '-isRXF'], stdin=subprocess.PIPE)
|
||||||
p.communicate(text.encode('utf-8'))
|
p.communicate(text.encode('utf-8'))
|
||||||
raise SystemExit(p.wait())
|
raise SystemExit(p.wait())
|
||||||
@@ -308,6 +348,9 @@ def print_help_for_seq(seq, usage, message, appname):
|
|||||||
print(text)
|
print(text)
|
||||||
|
|
||||||
|
|
||||||
|
print_help_for_seq.allow_pager = True
|
||||||
|
|
||||||
|
|
||||||
def defval_for_opt(opt):
|
def defval_for_opt(opt):
|
||||||
dv = opt.get('default')
|
dv = opt.get('default')
|
||||||
typ = opt.get('type', '')
|
typ = opt.get('type', '')
|
||||||
@@ -437,7 +480,7 @@ def parse_cmdline(oc, disabled, args=None):
|
|||||||
def options_spec():
|
def options_spec():
|
||||||
if not hasattr(options_spec, 'ans'):
|
if not hasattr(options_spec, 'ans'):
|
||||||
options_spec.ans = OPTIONS.format(
|
options_spec.ans = OPTIONS.format(
|
||||||
appname=appname, config_path=defconf,
|
appname=appname, macos_confpath='~/Library/Preferences/kitty/kitty.conf, ' if is_macos else '',
|
||||||
window_layout_choices=', '.join(all_layouts)
|
window_layout_choices=', '.join(all_layouts)
|
||||||
)
|
)
|
||||||
return options_spec.ans
|
return options_spec.ans
|
||||||
@@ -450,8 +493,85 @@ def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=
|
|||||||
return parse_cmdline(oc, disabled, args=args)
|
return parse_cmdline(oc, disabled, args=args)
|
||||||
|
|
||||||
|
|
||||||
def create_opts(args):
|
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
||||||
config = args.config or (defconf, )
|
|
||||||
|
|
||||||
|
def resolve_config(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 print_shortcut(key, action):
|
||||||
|
if not getattr(print_shortcut, 'maps', None):
|
||||||
|
from kitty.keys import defines
|
||||||
|
v = vars(defines)
|
||||||
|
mmap = {m.split('_')[-1].lower(): x for m, x in v.items() if m.startswith('GLFW_MOD_')}
|
||||||
|
kmap = {k.split('_')[-1].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
|
||||||
|
names = []
|
||||||
|
mods, key = key
|
||||||
|
for name, val in mmap.items():
|
||||||
|
if mods & val:
|
||||||
|
names.append(name)
|
||||||
|
if key:
|
||||||
|
names.append(krmap[key])
|
||||||
|
print('\t', '+'.join(names), action)
|
||||||
|
|
||||||
|
|
||||||
|
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]}
|
||||||
|
if added:
|
||||||
|
print(title('Added shortcuts:'))
|
||||||
|
for k in added:
|
||||||
|
print_shortcut(k, final[k])
|
||||||
|
if removed:
|
||||||
|
print(title('Removed shortcuts:'))
|
||||||
|
for k in removed:
|
||||||
|
print_shortcut(k, initial[k])
|
||||||
|
if changed:
|
||||||
|
print(title('Changed shortcuts:'))
|
||||||
|
for k in changed:
|
||||||
|
print_shortcut(k, final[k])
|
||||||
|
|
||||||
|
|
||||||
|
def compare_opts(opts):
|
||||||
|
print('\nConfig options different from defaults:')
|
||||||
|
for f in sorted(defaults._fields):
|
||||||
|
if getattr(opts, f) != getattr(defaults, f):
|
||||||
|
if f == 'keymap':
|
||||||
|
compare_keymaps(opts.keymap, defaults.keymap)
|
||||||
|
else:
|
||||||
|
print(title('{:20s}'.format(f)), getattr(opts, f))
|
||||||
|
|
||||||
|
|
||||||
|
def create_opts(args, debug_config=False):
|
||||||
|
config = tuple(resolve_config(args.config))
|
||||||
|
if debug_config:
|
||||||
|
print(version(add_rev=True))
|
||||||
|
print(' '.join(os.uname()))
|
||||||
|
if is_macos:
|
||||||
|
print(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())
|
||||||
|
else:
|
||||||
|
print('Running under:', green('Wayland' if is_wayland else 'X11'))
|
||||||
|
if os.path.exists('/etc/issue'):
|
||||||
|
print(open('/etc/issue', encoding='utf-8', errors='replace').read().strip())
|
||||||
|
if os.path.exists('/etc/lsb-release'):
|
||||||
|
print(open('/etc/lsb-release', encoding='utf-8', errors='replace').read().strip())
|
||||||
|
config = tuple(x for x in config if os.path.exists(x))
|
||||||
|
if config:
|
||||||
|
print(green('Loaded config files:'), ', '.join(config))
|
||||||
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
|
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
|
||||||
opts = load_config(*config, overrides=overrides)
|
opts = load_config(*config, overrides=overrides)
|
||||||
|
if debug_config:
|
||||||
|
compare_opts(opts)
|
||||||
return opts
|
return opts
|
||||||
|
|||||||
547
kitty/cmds.py
Normal file
547
kitty/cmds.py
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
#!/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.
|
||||||
|
You can use the |_ ls| command to get a list of windows. Note that for
|
||||||
|
numeric fields such as id and pid the expression is interpreted as a number,
|
||||||
|
not a regular expression.
|
||||||
|
'''
|
||||||
|
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
|
||||||
@@ -59,11 +59,35 @@ find_app_name(void) {
|
|||||||
return @"kitty";
|
return @"kitty";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface GlobalMenuTarget : NSObject
|
||||||
|
+ (GlobalMenuTarget *) shared_instance;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GlobalMenuTarget
|
||||||
|
|
||||||
|
- (void) show_preferences : (id)sender {
|
||||||
|
(void)sender;
|
||||||
|
call_boss(edit_config_file, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (GlobalMenuTarget *) shared_instance
|
||||||
|
{
|
||||||
|
static GlobalMenuTarget *sharedGlobalMenuTarget = nil;
|
||||||
|
@synchronized(self)
|
||||||
|
{
|
||||||
|
if (!sharedGlobalMenuTarget)
|
||||||
|
sharedGlobalMenuTarget = [[GlobalMenuTarget alloc] init];
|
||||||
|
return sharedGlobalMenuTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
void
|
void
|
||||||
cocoa_create_global_menu(void) {
|
cocoa_create_global_menu(void) {
|
||||||
NSString* app_name = find_app_name();
|
NSString* app_name = find_app_name();
|
||||||
NSMenu* bar = [[NSMenu alloc] init];
|
NSMenu* bar = [[NSMenu alloc] init];
|
||||||
|
GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];
|
||||||
[NSApp setMainMenu:bar];
|
[NSApp setMainMenu:bar];
|
||||||
|
|
||||||
NSMenuItem* appMenuItem =
|
NSMenuItem* appMenuItem =
|
||||||
@@ -75,6 +99,9 @@ cocoa_create_global_menu(void) {
|
|||||||
action:@selector(orderFrontStandardAboutPanel:)
|
action:@selector(orderFrontStandardAboutPanel:)
|
||||||
keyEquivalent:@""];
|
keyEquivalent:@""];
|
||||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
NSMenuItem* preferences_menu_item = [[NSMenuItem alloc] initWithTitle:@"Preferences..." action:@selector(show_preferences:) keyEquivalent:@","];
|
||||||
|
[preferences_menu_item setTarget:global_menu_target];
|
||||||
|
[appMenu addItem:preferences_menu_item];
|
||||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name]
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name]
|
||||||
action:@selector(hide:)
|
action:@selector(hide:)
|
||||||
keyEquivalent:@"h"];
|
keyEquivalent:@"h"];
|
||||||
@@ -124,7 +151,7 @@ cocoa_create_global_menu(void) {
|
|||||||
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||||
[NSApp setWindowsMenu:windowMenu];
|
[NSApp setWindowsMenu:windowMenu];
|
||||||
[windowMenu release];
|
[windowMenu release];
|
||||||
|
[preferences_menu_item release];
|
||||||
|
|
||||||
[bar release];
|
[bar release];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
* Distributed under terms of the GPL3 license.
|
* Distributed under terms of the GPL3 license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define EXTRA_INIT if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
|
|
||||||
@@ -29,23 +30,29 @@ static uint32_t FG_BG_256[256] = {
|
|||||||
0xffffff, // 15
|
0xffffff, // 15
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject* create_256_color_table() {
|
static inline void
|
||||||
// colors 16..232: the 6x6x6 color cube
|
init_FG_BG_table() {
|
||||||
const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
if (UNLIKELY(FG_BG_256[255] == 0)) {
|
||||||
uint8_t i, j=16;
|
// colors 16..232: the 6x6x6 color cube
|
||||||
for(i = 0; i < 217; i++, j++) {
|
const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||||
uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];
|
uint8_t i, j=16;
|
||||||
FG_BG_256[j] = (r << 16) | (g << 8) | b;
|
for(i = 0; i < 217; i++, j++) {
|
||||||
}
|
uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];
|
||||||
// colors 233..255: grayscale
|
FG_BG_256[j] = (r << 16) | (g << 8) | b;
|
||||||
for(i = 1; i < 24; i++, j++) {
|
}
|
||||||
uint8_t v = 8 + i * 10;
|
// colors 233..255: grayscale
|
||||||
FG_BG_256[j] = (v << 16) | (v << 8) | v;
|
for(i = 1; i < 24; i++, j++) {
|
||||||
|
uint8_t v = 8 + i * 10;
|
||||||
|
FG_BG_256[j] = (v << 16) | (v << 8) | v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *ans = PyTuple_New(255);
|
PyObject* create_256_color_table() {
|
||||||
|
init_FG_BG_table();
|
||||||
|
PyObject *ans = PyTuple_New(arraysz(FG_BG_256));
|
||||||
if (ans == NULL) return PyErr_NoMemory();
|
if (ans == NULL) return PyErr_NoMemory();
|
||||||
for (i=0; i < 255; i++) {
|
for (size_t i=0; i < arraysz(FG_BG_256); i++) {
|
||||||
PyObject *temp = PyLong_FromUnsignedLong(FG_BG_256[i]);
|
PyObject *temp = PyLong_FromUnsignedLong(FG_BG_256[i]);
|
||||||
if (temp == NULL) { Py_CLEAR(ans); return NULL; }
|
if (temp == NULL) { Py_CLEAR(ans); return NULL; }
|
||||||
PyTuple_SET_ITEM(ans, i, temp);
|
PyTuple_SET_ITEM(ans, i, temp);
|
||||||
@@ -59,7 +66,7 @@ new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
|||||||
|
|
||||||
self = (ColorProfile *)type->tp_alloc(type, 0);
|
self = (ColorProfile *)type->tp_alloc(type, 0);
|
||||||
if (self != NULL) {
|
if (self != NULL) {
|
||||||
if (FG_BG_256[255] == 0) create_256_color_table();
|
init_FG_BG_table();
|
||||||
memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256));
|
memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256));
|
||||||
memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
|
memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
|
||||||
self->dirty = true;
|
self->dirty = true;
|
||||||
@@ -80,12 +87,10 @@ alloc_color_profile() {
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
||||||
#define update_ansi_color_table_doc "Update the 16 basic colors"
|
#define update_ansi_color_table_doc "Update the 256 basic colors"
|
||||||
index_type i;
|
|
||||||
|
|
||||||
if (!PyList_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a list"); return NULL; }
|
if (!PyList_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a list"); return NULL; }
|
||||||
if (PyList_GET_SIZE(val) != 16) { PyErr_SetString(PyExc_TypeError, "color table must have 16 items"); return NULL; }
|
if (PyList_GET_SIZE(val) != arraysz(FG_BG_256)) { PyErr_SetString(PyExc_TypeError, "color table must have 256 items"); return NULL; }
|
||||||
for (i = 0; i < 16; i++) {
|
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||||
self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, i));
|
self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, i));
|
||||||
self->orig_color_table[i] = self->color_table[i];
|
self->orig_color_table[i] = self->color_table[i];
|
||||||
}
|
}
|
||||||
@@ -93,6 +98,43 @@ update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
||||||
|
PyObject *spec, *profiles, *v; ColorProfile *self; int change_configured;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!O!p", &PyDict_Type, &spec, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
||||||
|
char key[32] = {0};
|
||||||
|
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||||
|
snprintf(key, sizeof(key) - 1, "color%zu", i);
|
||||||
|
v = PyDict_GetItemString(spec, key);
|
||||||
|
if (v) {
|
||||||
|
color_type color = PyLong_AsUnsignedLong(v);
|
||||||
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {
|
||||||
|
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);
|
||||||
|
self->color_table[i] = color;
|
||||||
|
if (change_configured) self->orig_color_table[i] = color;
|
||||||
|
self->dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define S(config_name, profile_name) { \
|
||||||
|
v = PyDict_GetItemString(spec, #config_name); \
|
||||||
|
if (v) { \
|
||||||
|
color_type color = PyLong_AsUnsignedLong(v); \
|
||||||
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) { \
|
||||||
|
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i); \
|
||||||
|
self->overridden.profile_name = (color << 8) | 2; \
|
||||||
|
if (change_configured) self->configured.profile_name = color; \
|
||||||
|
self->dirty = true; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color);
|
||||||
|
S(selection_foreground, highlight_fg); S(selection_background, highlight_bg);
|
||||||
|
#undef S
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
color_type
|
color_type
|
||||||
colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) {
|
colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) {
|
||||||
color_type t = entry & 0xFF, r;
|
color_type t = entry & 0xFF, r;
|
||||||
@@ -133,7 +175,7 @@ as_color(ColorProfile *self, PyObject *val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
reset_color_table(ColorProfile *self) {
|
reset_color_table(ColorProfile *self, PyObject *a UNUSED) {
|
||||||
#define reset_color_table_doc "Reset all customized colors back to defaults"
|
#define reset_color_table_doc "Reset all customized colors back to defaults"
|
||||||
memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));
|
memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));
|
||||||
self->dirty = true;
|
self->dirty = true;
|
||||||
@@ -179,11 +221,15 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
color_table_address(ColorProfile *self) {
|
color_table_address(ColorProfile *self, PyObject *a UNUSED) {
|
||||||
#define color_table_address_doc "Pointer address to start of color table"
|
#define color_table_address_doc "Pointer address to start of color table"
|
||||||
return PyLong_FromVoidPtr((void*)self->color_table);
|
return PyLong_FromVoidPtr((void*)self->color_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
|
||||||
|
return create_256_color_table();
|
||||||
|
}
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
|
|
||||||
@@ -236,5 +282,12 @@ PyTypeObject ColorProfile_Type = {
|
|||||||
.tp_new = new,
|
.tp_new = new,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
METHODB(default_color_table, METH_NOARGS),
|
||||||
|
METHODB(patch_color_profiles, METH_VARARGS),
|
||||||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
INIT_TYPE(ColorProfile)
|
INIT_TYPE(ColorProfile)
|
||||||
// }}}
|
// }}}
|
||||||
|
|||||||
206
kitty/config.py
206
kitty/config.py
@@ -17,10 +17,11 @@ from .config_utils import (
|
|||||||
init_config, parse_config_base, positive_float, positive_int, to_bool,
|
init_config, parse_config_base, positive_float, positive_int, to_bool,
|
||||||
to_color, unit_float
|
to_color, unit_float
|
||||||
)
|
)
|
||||||
from .constants import cache_dir
|
from .constants import cache_dir, defconf
|
||||||
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
from .utils import safe_print
|
from .rgb import color_from_int
|
||||||
|
from .utils import log_error
|
||||||
|
|
||||||
MINIMUM_FONT_SIZE = 4
|
MINIMUM_FONT_SIZE = 4
|
||||||
|
|
||||||
@@ -50,19 +51,15 @@ def to_cursor_shape(x):
|
|||||||
def parse_mods(parts):
|
def parse_mods(parts):
|
||||||
|
|
||||||
def map_mod(m):
|
def map_mod(m):
|
||||||
return {'CTRL': 'CONTROL', 'CMD': 'SUPER', 'OPTION': 'ALT'}.get(m, m)
|
return {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER', '⌥': 'ALT', 'OPTION': 'ALT'}.get(m, m)
|
||||||
|
|
||||||
mods = 0
|
mods = 0
|
||||||
for m in parts:
|
for m in parts:
|
||||||
try:
|
try:
|
||||||
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
|
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
safe_print(
|
log_error('Shortcut: {} has unknown modifier, ignoring'.format(
|
||||||
'Shortcut: {} has an unknown modifier, ignoring'.format(
|
parts.join('+')))
|
||||||
parts.join('+')
|
|
||||||
),
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
return mods
|
return mods
|
||||||
@@ -111,37 +108,64 @@ def parse_key_action(action):
|
|||||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||||
elif func == 'send_text':
|
elif func == 'send_text':
|
||||||
args = rest.split(' ', 1)
|
args = rest.split(' ', 1)
|
||||||
elif func == 'run_simple_kitten':
|
elif func in ('run_kitten', 'run_simple_kitten'):
|
||||||
|
if func == 'run_simple_kitten':
|
||||||
|
func = 'run_kitten'
|
||||||
args = rest.split(' ', 2)
|
args = rest.split(' ', 2)
|
||||||
elif func == 'goto_tab':
|
elif func == 'goto_tab':
|
||||||
args = (max(0, int(rest)), )
|
args = (max(0, int(rest)), )
|
||||||
|
elif func == 'goto_layout' or func == 'kitty_shell':
|
||||||
|
args = [rest]
|
||||||
|
elif func == 'set_font_size':
|
||||||
|
args = (float(rest),)
|
||||||
elif func in shlex_actions:
|
elif func in shlex_actions:
|
||||||
args = shlex.split(rest)
|
args = shlex.split(rest)
|
||||||
return KeyAction(func, args)
|
return KeyAction(func, args)
|
||||||
|
|
||||||
|
|
||||||
def parse_key(val, keymap):
|
all_key_actions = set()
|
||||||
|
sequence_sep = '>'
|
||||||
|
|
||||||
|
|
||||||
|
def parse_key(val, keymap, sequence_map):
|
||||||
sc, action = val.partition(' ')[::2]
|
sc, action = val.partition(' ')[::2]
|
||||||
sc, action = sc.strip(), action.strip()
|
sc, action = sc.strip().strip(sequence_sep), action.strip()
|
||||||
if not sc or not action:
|
if not sc or not action:
|
||||||
return
|
return
|
||||||
mods, key = parse_shortcut(sc)
|
is_sequence = sequence_sep in sc
|
||||||
if key is None:
|
if is_sequence:
|
||||||
safe_print(
|
trigger = None
|
||||||
'Shortcut: {} has an unknown key, ignoring'.format(val),
|
rest = []
|
||||||
file=sys.stderr
|
for part in sc.split(sequence_sep):
|
||||||
)
|
mods, key = parse_shortcut(part)
|
||||||
return
|
if key is None:
|
||||||
|
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:
|
||||||
|
log_error('Shortcut: {} has unknown key, ignoring'.format(
|
||||||
|
sc))
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
paction = parse_key_action(action)
|
paction = parse_key_action(action)
|
||||||
except Exception:
|
except Exception:
|
||||||
safe_print(
|
log_error('Invalid shortcut action: {}. Ignoring.'.format(
|
||||||
'Invalid shortcut action: {}. Ignoring.'.format(action),
|
action))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if paction is not None:
|
if paction is not None:
|
||||||
keymap[(mods, key)] = paction
|
all_key_actions.add(paction.func)
|
||||||
|
if is_sequence:
|
||||||
|
s = sequence_map.setdefault(trigger, {})
|
||||||
|
s[rest] = paction
|
||||||
|
else:
|
||||||
|
keymap[(mods, key)] = paction
|
||||||
|
|
||||||
|
|
||||||
def parse_symbol_map(val):
|
def parse_symbol_map(val):
|
||||||
@@ -149,9 +173,8 @@ def parse_symbol_map(val):
|
|||||||
symbol_map = {}
|
symbol_map = {}
|
||||||
|
|
||||||
def abort():
|
def abort():
|
||||||
safe_print(
|
log_error('Symbol map: {} is invalid, ignoring'.format(
|
||||||
'Symbol map: {} is invalid, ignoring'.format(val), file=sys.stderr
|
val))
|
||||||
)
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if len(parts) < 2:
|
if len(parts) < 2:
|
||||||
@@ -182,14 +205,12 @@ def parse_send_text_bytes(text):
|
|||||||
).encode('utf-8')
|
).encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def parse_send_text(val, keymap):
|
def parse_send_text(val, keymap, sequence_map):
|
||||||
parts = val.split(' ')
|
parts = val.split(' ')
|
||||||
|
|
||||||
def abort(msg):
|
def abort(msg):
|
||||||
safe_print(
|
log_error('Send text: {} is invalid ({}), ignoring'.format(
|
||||||
'Send text: {} is invalid ({}), ignoring'.format(val, msg),
|
val, msg))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if len(parts) < 3:
|
if len(parts) < 3:
|
||||||
@@ -197,7 +218,7 @@ def parse_send_text(val, keymap):
|
|||||||
mode, sc = parts[:2]
|
mode, sc = parts[:2]
|
||||||
text = ' '.join(parts[2:])
|
text = ' '.join(parts[2:])
|
||||||
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
||||||
return parse_key(key_str, keymap)
|
return parse_key(key_str, keymap, sequence_map)
|
||||||
|
|
||||||
|
|
||||||
def to_modifiers(val):
|
def to_modifiers(val):
|
||||||
@@ -281,6 +302,7 @@ type_map = {
|
|||||||
'repaint_delay': positive_int,
|
'repaint_delay': positive_int,
|
||||||
'input_delay': positive_int,
|
'input_delay': positive_int,
|
||||||
'sync_to_monitor': to_bool,
|
'sync_to_monitor': to_bool,
|
||||||
|
'close_on_child_death': to_bool,
|
||||||
'window_border_width': positive_float,
|
'window_border_width': positive_float,
|
||||||
'window_margin_width': positive_float,
|
'window_margin_width': positive_float,
|
||||||
'window_padding_width': positive_float,
|
'window_padding_width': positive_float,
|
||||||
@@ -299,7 +321,6 @@ type_map = {
|
|||||||
'macos_option_as_alt': to_bool,
|
'macos_option_as_alt': to_bool,
|
||||||
'macos_titlebar_color': macos_titlebar_color,
|
'macos_titlebar_color': macos_titlebar_color,
|
||||||
'box_drawing_scale': box_drawing_scale,
|
'box_drawing_scale': box_drawing_scale,
|
||||||
'x11_bell_volume': int,
|
|
||||||
'background_opacity': unit_float,
|
'background_opacity': unit_float,
|
||||||
'tab_separator': tab_separator,
|
'tab_separator': tab_separator,
|
||||||
'active_tab_font_style': tab_font_style,
|
'active_tab_font_style': tab_font_style,
|
||||||
@@ -315,8 +336,8 @@ for name in (
|
|||||||
' selection_foreground selection_background url_color'
|
' selection_foreground selection_background url_color'
|
||||||
).split():
|
).split():
|
||||||
type_map[name] = to_color
|
type_map[name] = to_color
|
||||||
for i in range(16):
|
for i in range(256):
|
||||||
type_map['color%d' % i] = to_color
|
type_map['color{}'.format(i)] = to_color
|
||||||
for a in ('active', 'inactive'):
|
for a in ('active', 'inactive'):
|
||||||
for b in ('foreground', 'background'):
|
for b in ('foreground', 'background'):
|
||||||
type_map['%s_tab_%s' % (a, b)] = to_color
|
type_map['%s_tab_%s' % (a, b)] = to_color
|
||||||
@@ -324,14 +345,14 @@ for a in ('active', 'inactive'):
|
|||||||
|
|
||||||
def special_handling(key, val, ans):
|
def special_handling(key, val, ans):
|
||||||
if key == 'map':
|
if key == 'map':
|
||||||
parse_key(val, ans['keymap'])
|
parse_key(val, ans['keymap'], ans['sequence_map'])
|
||||||
return True
|
return True
|
||||||
if key == 'symbol_map':
|
if key == 'symbol_map':
|
||||||
ans['symbol_map'].update(parse_symbol_map(val))
|
ans['symbol_map'].update(parse_symbol_map(val))
|
||||||
return True
|
return True
|
||||||
if key == 'send_text':
|
if key == 'send_text':
|
||||||
# For legacy compatibility
|
# For legacy compatibility
|
||||||
parse_send_text(val, ans['keymap'])
|
parse_send_text(val, ans['keymap'], ans['sequence_map'])
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -344,6 +365,7 @@ default_config_path = os.path.join(
|
|||||||
def parse_config(lines, check_keys=True):
|
def parse_config(lines, check_keys=True):
|
||||||
ans = {
|
ans = {
|
||||||
'keymap': {},
|
'keymap': {},
|
||||||
|
'sequence_map': {},
|
||||||
'symbol_map': {},
|
'symbol_map': {},
|
||||||
}
|
}
|
||||||
parse_config_base(
|
parse_config_base(
|
||||||
@@ -357,24 +379,48 @@ def parse_config(lines, check_keys=True):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
Options, defaults = init_config(default_config_path, parse_config)
|
def parse_defaults(lines, check_keys=False):
|
||||||
actions = frozenset(a.func for a in defaults.keymap.values()) | frozenset(
|
ans = parse_config(lines, check_keys)
|
||||||
'combine send_text goto_tab new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
dfctl = defines.default_color_table()
|
||||||
|
|
||||||
|
for i in range(16, 256):
|
||||||
|
k = 'color{}'.format(i)
|
||||||
|
ans.setdefault(k, color_from_int(dfctl[i]))
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
Options, defaults = init_config(default_config_path, parse_defaults)
|
||||||
|
actions = frozenset(all_key_actions) | frozenset(
|
||||||
|
'run_simple_kitten combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
||||||
split()
|
split()
|
||||||
)
|
)
|
||||||
no_op_actions = frozenset({'noop', 'no-op', 'no_op'})
|
no_op_actions = frozenset({'noop', 'no-op', 'no_op'})
|
||||||
|
|
||||||
|
|
||||||
def merge_keymaps(defaults, newvals):
|
def merge_keys(ans, defaults, newvals):
|
||||||
ans = defaults.copy()
|
ans['keymap'] = defaults['keymap'].copy()
|
||||||
for k, v in newvals.items():
|
ans['sequence_map'] = {t: r.copy() for t, r in defaults['sequence_map'].items()}
|
||||||
|
# Merge the keymap
|
||||||
|
for k, v in newvals['keymap'].items():
|
||||||
|
ans['sequence_map'].pop(k, None)
|
||||||
f = v.func
|
f = v.func
|
||||||
if f in no_op_actions:
|
if f in no_op_actions:
|
||||||
ans.pop(k, None)
|
ans['keymap'].pop(k, None)
|
||||||
continue
|
elif f in actions:
|
||||||
if f in actions:
|
ans['keymap'][k] = v
|
||||||
ans[k] = v
|
# Merge the sequence map
|
||||||
return ans
|
for trigger, rest_map in newvals['sequence_map'].items():
|
||||||
|
ans['keymap'].pop(trigger, None)
|
||||||
|
if trigger in newvals['keymap']:
|
||||||
|
log_error('The shortcut for {} has conflicting definitions'.format(newvals['keymap'][trigger].func))
|
||||||
|
s = ans['sequence_map'].setdefault(trigger, {})
|
||||||
|
for k, v in rest_map.items():
|
||||||
|
f = v.func
|
||||||
|
if f in no_op_actions:
|
||||||
|
s.pop(k, None)
|
||||||
|
elif f in actions:
|
||||||
|
s[k] = v
|
||||||
|
ans['sequence_map'] = {k: v for k, v in ans['sequence_map'].items() if v}
|
||||||
|
|
||||||
|
|
||||||
def merge_dicts(defaults, newvals):
|
def merge_dicts(defaults, newvals):
|
||||||
@@ -387,13 +433,16 @@ def merge_configs(defaults, vals):
|
|||||||
ans = {}
|
ans = {}
|
||||||
for k, v in defaults.items():
|
for k, v in defaults.items():
|
||||||
if isinstance(v, dict):
|
if isinstance(v, dict):
|
||||||
newvals = vals.get(k, {})
|
if k not in ('keymap', 'sequence_map'):
|
||||||
if k == 'keymap':
|
newvals = vals.get(k, {})
|
||||||
ans['keymap'] = merge_keymaps(v, newvals)
|
|
||||||
else:
|
|
||||||
ans[k] = merge_dicts(v, newvals)
|
ans[k] = merge_dicts(v, newvals)
|
||||||
else:
|
else:
|
||||||
ans[k] = vals.get(k, v)
|
ans[k] = vals.get(k, v)
|
||||||
|
merge_keys(
|
||||||
|
ans,
|
||||||
|
{'keymap': defaults.get('keymap', {}), 'sequence_map': defaults.get('sequence_map', {})},
|
||||||
|
{'keymap': vals.get('keymap', {}), 'sequence_map': vals.get('sequence_map', {})}
|
||||||
|
)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
@@ -403,7 +452,7 @@ def load_config(*paths, overrides=None) -> Options:
|
|||||||
if not path:
|
if not path:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
f = open(path)
|
f = open(path, encoding='utf-8', errors='replace')
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
continue
|
continue
|
||||||
with f:
|
with f:
|
||||||
@@ -412,7 +461,7 @@ def load_config(*paths, overrides=None) -> Options:
|
|||||||
if overrides is not None:
|
if overrides is not None:
|
||||||
vals = parse_config(overrides)
|
vals = parse_config(overrides)
|
||||||
ans = merge_configs(ans, vals)
|
ans = merge_configs(ans, vals)
|
||||||
return Options(**ans)
|
return Options(ans)
|
||||||
|
|
||||||
|
|
||||||
def build_ansi_color_table(opts: Options = defaults):
|
def build_ansi_color_table(opts: Options = defaults):
|
||||||
@@ -423,7 +472,7 @@ def build_ansi_color_table(opts: Options = defaults):
|
|||||||
def col(i):
|
def col(i):
|
||||||
return as_int(getattr(opts, 'color{}'.format(i)))
|
return as_int(getattr(opts, 'color{}'.format(i)))
|
||||||
|
|
||||||
return list(map(col, range(16)))
|
return list(map(col, range(256)))
|
||||||
|
|
||||||
|
|
||||||
def atomic_save(data, path):
|
def atomic_save(data, path):
|
||||||
@@ -438,11 +487,8 @@ def atomic_save(data, path):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
safe_print(
|
log_error('Failed to delete temp file {} for atomic save with error: {}'.format(
|
||||||
'Failed to delete temp file {} for atomic save with error: {}'.
|
p, err))
|
||||||
format(p, err),
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@@ -455,10 +501,8 @@ def cached_values_for(name):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
safe_print(
|
log_error('Failed to load cached in {} values with error: {}'.format(
|
||||||
'Failed to load cached in {} values with error: {}'.format(name, err),
|
name, err))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
|
|
||||||
yield cached_values
|
yield cached_values
|
||||||
|
|
||||||
@@ -466,10 +510,8 @@ def cached_values_for(name):
|
|||||||
data = json.dumps(cached_values).encode('utf-8')
|
data = json.dumps(cached_values).encode('utf-8')
|
||||||
atomic_save(data, cached_path)
|
atomic_save(data, cached_path)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
safe_print(
|
log_error('Failed to save cached values with error: {}'.format(
|
||||||
'Failed to save cached values with error: {}'.format(err),
|
err))
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def initial_window_size(opts, cached_values):
|
def initial_window_size(opts, cached_values):
|
||||||
@@ -479,5 +521,29 @@ def initial_window_size(opts, cached_values):
|
|||||||
try:
|
try:
|
||||||
w, h = map(int, ws)
|
w, h = map(int, ws)
|
||||||
except Exception:
|
except Exception:
|
||||||
safe_print('Invalid cached window size, ignoring', file=sys.stderr)
|
log_error('Invalid cached window size, ignoring')
|
||||||
return w, h
|
return w, h
|
||||||
|
|
||||||
|
|
||||||
|
def commented_out_default_config():
|
||||||
|
with open(default_config_path, encoding='utf-8', errors='replace') as f:
|
||||||
|
config = f.read()
|
||||||
|
lines = []
|
||||||
|
for line in config.splitlines():
|
||||||
|
if line.strip() and not line.startswith('#'):
|
||||||
|
line = '# ' + line
|
||||||
|
lines.append(line)
|
||||||
|
config = '\n'.join(lines)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_config_file_for_editing():
|
||||||
|
if not os.path.exists(defconf):
|
||||||
|
d = os.path.dirname(defconf)
|
||||||
|
try:
|
||||||
|
os.makedirs(d)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
with open(defconf, 'w') as f:
|
||||||
|
f.write(commented_out_default_config())
|
||||||
|
return defconf
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from .utils import safe_print
|
|
||||||
from .rgb import to_color as as_color
|
from .rgb import to_color as as_color
|
||||||
|
from .utils import log_error
|
||||||
|
|
||||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||||
|
|
||||||
@@ -32,36 +31,95 @@ def to_bool(x):
|
|||||||
return x.lower() in 'y yes true'.split()
|
return x.lower() in 'y yes true'.split()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes):
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith('#'):
|
||||||
|
return
|
||||||
|
m = key_pat.match(line)
|
||||||
|
if m is not None:
|
||||||
|
key, val = m.groups()
|
||||||
|
if special_handling(key, val, ans):
|
||||||
|
return
|
||||||
|
if key == 'include':
|
||||||
|
val = val.strip()
|
||||||
|
if not os.path.isabs(val):
|
||||||
|
val = os.path.join(base_path_for_includes, val)
|
||||||
|
try:
|
||||||
|
with open(val, encoding='utf-8', errors='replace') as include:
|
||||||
|
_parse(include, type_map, special_handling, ans, all_keys)
|
||||||
|
except FileNotFoundError:
|
||||||
|
log_error('Could not find included config file: {}, ignoring'.format(val))
|
||||||
|
except EnvironmentError:
|
||||||
|
log_error('Could not read from included config file: {}, ignoring'.format(val))
|
||||||
|
return
|
||||||
|
if all_keys is not None and key not in all_keys:
|
||||||
|
log_error('Ignoring unknown config key: {}'.format(key))
|
||||||
|
return
|
||||||
|
tm = type_map.get(key)
|
||||||
|
if tm is not None:
|
||||||
|
val = tm(val)
|
||||||
|
ans[key] = val
|
||||||
|
|
||||||
|
|
||||||
|
def _parse(lines, type_map, special_handling, ans, all_keys):
|
||||||
|
name = getattr(lines, 'name', None)
|
||||||
|
if name:
|
||||||
|
base_path_for_includes = os.path.dirname(os.path.abspath(name))
|
||||||
|
else:
|
||||||
|
from .constants import config_dir
|
||||||
|
base_path_for_includes = config_dir
|
||||||
|
for line in lines:
|
||||||
|
parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes)
|
||||||
|
|
||||||
|
|
||||||
def parse_config_base(
|
def parse_config_base(
|
||||||
lines, defaults, type_map, special_handling, ans, check_keys=True
|
lines, defaults, type_map, special_handling, ans, check_keys=True
|
||||||
):
|
):
|
||||||
if check_keys:
|
all_keys = defaults._asdict() if check_keys else None
|
||||||
all_keys = defaults._asdict()
|
_parse(lines, type_map, special_handling, ans, all_keys)
|
||||||
for line in lines:
|
|
||||||
line = line.strip()
|
|
||||||
if not line or line.startswith('#'):
|
def create_options_class(keys):
|
||||||
continue
|
keys = tuple(sorted(keys))
|
||||||
m = key_pat.match(line)
|
slots = keys + ('_fields',)
|
||||||
if m is not None:
|
|
||||||
key, val = m.groups()
|
def __init__(self, kw):
|
||||||
if special_handling(key, val, ans):
|
for k, v in kw.items():
|
||||||
continue
|
setattr(self, k, v)
|
||||||
if check_keys:
|
|
||||||
if key not in all_keys:
|
def __iter__(self):
|
||||||
safe_print(
|
return iter(keys)
|
||||||
'Ignoring unknown config key: {}'.format(key),
|
|
||||||
file=sys.stderr
|
def __len__(self):
|
||||||
)
|
return len(keys)
|
||||||
continue
|
|
||||||
tm = type_map.get(key)
|
def __getitem__(self, i):
|
||||||
if tm is not None:
|
if isinstance(i, int):
|
||||||
val = tm(val)
|
i = keys[i]
|
||||||
ans[key] = val
|
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 init_config(defaults_path, parse_config):
|
def init_config(defaults_path, parse_config):
|
||||||
with open(defaults_path, encoding='utf-8') as f:
|
with open(defaults_path, encoding='utf-8', errors='replace') as f:
|
||||||
defaults = parse_config(f.read().splitlines(), check_keys=False)
|
defaults = parse_config(f, check_keys=False)
|
||||||
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
Options = create_options_class(defaults.keys())
|
||||||
defaults = Options(**defaults)
|
defaults = Options(defaults)
|
||||||
return Options, defaults
|
return Options, defaults
|
||||||
|
|||||||
@@ -4,17 +4,19 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from .fast_data_types import set_boss as set_c_boss
|
from .fast_data_types import set_boss as set_c_boss
|
||||||
|
|
||||||
appname = 'kitty'
|
appname = 'kitty'
|
||||||
version = (0, 8, 0)
|
version = (0, 9, 0)
|
||||||
str_version = '.'.join(map(str, version))
|
str_version = '.'.join(map(str, version))
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
is_macos = 'darwin' in _plat
|
is_macos = 'darwin' in _plat
|
||||||
base = os.path.dirname(os.path.abspath(__file__))
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
editor = shlex.split(os.environ.get('EDITOR', 'vim'))
|
||||||
|
|
||||||
|
|
||||||
ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy')
|
ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy')
|
||||||
@@ -25,7 +27,22 @@ def _get_config_dir():
|
|||||||
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
|
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
|
||||||
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
|
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
|
||||||
|
|
||||||
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or ('~/Library/Preferences' if is_macos else '~/.config')))
|
locations = []
|
||||||
|
if 'XDG_CONFIG_HOME' in os.environ:
|
||||||
|
locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME'])))
|
||||||
|
locations.append(os.path.expanduser('~/.config'))
|
||||||
|
if is_macos:
|
||||||
|
locations.append(os.path.expanduser('~/Library/Preferences'))
|
||||||
|
if 'XDG_CONFIG_DIRS' in os.environ:
|
||||||
|
for loc in os.environ['XDG_CONFIG_DIRS'].split(os.pathsep):
|
||||||
|
locations.append(os.path.abspath(os.path.expanduser(loc)))
|
||||||
|
for loc in locations:
|
||||||
|
if loc:
|
||||||
|
q = os.path.join(loc, appname)
|
||||||
|
if os.access(q, os.W_OK) and os.path.exists(os.path.join(q, 'kitty.conf')):
|
||||||
|
return q
|
||||||
|
|
||||||
|
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or '~/.config'))
|
||||||
ans = os.path.join(candidate, appname)
|
ans = os.path.join(candidate, appname)
|
||||||
os.makedirs(ans, exist_ok=True)
|
os.makedirs(ans, exist_ok=True)
|
||||||
return ans
|
return ans
|
||||||
@@ -44,10 +61,7 @@ def _get_cache_dir():
|
|||||||
else:
|
else:
|
||||||
candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')
|
candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')
|
||||||
candidate = os.path.join(os.path.expanduser(candidate), appname)
|
candidate = os.path.join(os.path.expanduser(candidate), appname)
|
||||||
try:
|
os.makedirs(candidate, exist_ok=True)
|
||||||
os.makedirs(candidate)
|
|
||||||
except FileExistsError:
|
|
||||||
pass
|
|
||||||
return candidate
|
return candidate
|
||||||
|
|
||||||
|
|
||||||
@@ -77,7 +91,10 @@ logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba')
|
|||||||
try:
|
try:
|
||||||
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print('Failed to read login shell from /etc/passwd for current user, falling back to /bin/sh', file=sys.stderr)
|
try:
|
||||||
|
print('Failed to read login shell via getpwuid() for current user, falling back to /bin/sh', file=sys.stderr)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
shell_path = '/bin/sh'
|
shell_path = '/bin/sh'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ cursor_as_sgr(Cursor *self, Cursor *prev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
reset_display_attrs(Cursor *self) {
|
reset_display_attrs(Cursor *self, PyObject *a UNUSED) {
|
||||||
#define reset_display_attrs_doc "Reset all display attributes to unset"
|
#define reset_display_attrs_doc "Reset all display attributes to unset"
|
||||||
cursor_reset_display_attrs(self);
|
cursor_reset_display_attrs(self);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -272,7 +272,7 @@ void cursor_copy_to(Cursor *src, Cursor *dest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
copy(Cursor *self);
|
copy(Cursor *self, PyObject*);
|
||||||
#define copy_doc "Create a clone of this cursor"
|
#define copy_doc "Create a clone of this cursor"
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
@@ -342,7 +342,7 @@ cursor_copy(Cursor *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
copy(Cursor *self) {
|
copy(Cursor *self, PyObject *a UNUSED) {
|
||||||
return (PyObject*)cursor_copy(self);
|
return (PyObject*)cursor_copy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
#ifdef WITH_PROFILER
|
#ifdef WITH_PROFILER
|
||||||
#include <gperftools/profiler.h>
|
#include <gperftools/profiler.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -88,31 +87,6 @@ pyset_iutf8(PyObject UNUSED *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
handle_sigchld(int UNUSED signum, siginfo_t *sinfo, void UNUSED *unused) {
|
|
||||||
if (sinfo->si_code != CLD_EXITED) return;
|
|
||||||
int sav_errno = errno, status;
|
|
||||||
while(true) {
|
|
||||||
if (waitpid(sinfo->si_pid, &status, WNOHANG) == -1) {
|
|
||||||
if (errno != EINTR) break;
|
|
||||||
} else break;
|
|
||||||
}
|
|
||||||
// wakeup I/O loop as without this on macOS sometimes poll() does not detect the fd close, so
|
|
||||||
// kitty does not detect child death.
|
|
||||||
wakeup_io_loop(true);
|
|
||||||
errno = sav_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
install_sigchld_handler(PyObject UNUSED *self) {
|
|
||||||
struct sigaction sa;
|
|
||||||
sa.sa_flags = SA_SIGINFO;
|
|
||||||
sa.sa_sigaction = handle_sigchld;
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
if (sigaction(SIGCHLD, &sa, NULL) == -1) return PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WITH_PROFILER
|
#ifdef WITH_PROFILER
|
||||||
static PyObject*
|
static PyObject*
|
||||||
start_profiler(PyObject UNUSED *self, PyObject *args) {
|
start_profiler(PyObject UNUSED *self, PyObject *args) {
|
||||||
@@ -135,7 +109,6 @@ static PyMethodDef module_methods[] = {
|
|||||||
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},
|
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},
|
||||||
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
||||||
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
||||||
{"install_sigchld_handler", (PyCFunction)install_sigchld_handler, METH_NOARGS, ""},
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
METHODB(user_cache_dir, METH_NOARGS),
|
METHODB(user_cache_dir, METH_NOARGS),
|
||||||
#endif
|
#endif
|
||||||
@@ -174,6 +147,7 @@ extern bool init_graphics(PyObject *module);
|
|||||||
extern bool init_shaders(PyObject *module);
|
extern bool init_shaders(PyObject *module);
|
||||||
extern bool init_mouse(PyObject *module);
|
extern bool init_mouse(PyObject *module);
|
||||||
extern bool init_kittens(PyObject *module);
|
extern bool init_kittens(PyObject *module);
|
||||||
|
extern bool init_logging(PyObject *module);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
extern int init_CoreText(PyObject *);
|
extern int init_CoreText(PyObject *);
|
||||||
extern bool init_cocoa(PyObject *module);
|
extern bool init_cocoa(PyObject *module);
|
||||||
@@ -193,6 +167,7 @@ PyInit_fast_data_types(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m != NULL) {
|
if (m != NULL) {
|
||||||
|
if (!init_logging(m)) return NULL;
|
||||||
if (!init_LineBuf(m)) return NULL;
|
if (!init_LineBuf(m)) return NULL;
|
||||||
if (!init_HistoryBuf(m)) return NULL;
|
if (!init_HistoryBuf(m)) return NULL;
|
||||||
if (!init_Line(m)) return NULL;
|
if (!init_Line(m)) return NULL;
|
||||||
@@ -228,6 +203,9 @@ PyInit_fast_data_types(void) {
|
|||||||
PyModule_AddIntConstant(m, "STRIKETHROUGH", STRIKE_SHIFT);
|
PyModule_AddIntConstant(m, "STRIKETHROUGH", STRIKE_SHIFT);
|
||||||
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
|
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
|
||||||
PyModule_AddStringMacro(m, ERROR_PREFIX);
|
PyModule_AddStringMacro(m, ERROR_PREFIX);
|
||||||
|
#ifdef KITTY_VCS_REV
|
||||||
|
PyModule_AddStringMacro(m, KITTY_VCS_REV);
|
||||||
|
#endif
|
||||||
PyModule_AddIntMacro(m, CURSOR_BLOCK);
|
PyModule_AddIntMacro(m, CURSOR_BLOCK);
|
||||||
PyModule_AddIntMacro(m, CURSOR_BEAM);
|
PyModule_AddIntMacro(m, CURSOR_BEAM);
|
||||||
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
|
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define OPENGL_REQUIRED_VERSION_MAJOR 3
|
#define OPENGL_REQUIRED_VERSION_MAJOR 3
|
||||||
#define OPENGL_REQUIRED_VERSION_MINOR 3
|
#define OPENGL_REQUIRED_VERSION_MINOR 3
|
||||||
#define UNUSED __attribute__ ((unused))
|
#define UNUSED __attribute__ ((unused))
|
||||||
|
#define PYNOARG PyObject *__a1 UNUSED, PyObject *__a2 UNUSED
|
||||||
#define EXPORTED __attribute__ ((visibility ("default")))
|
#define EXPORTED __attribute__ ((visibility ("default")))
|
||||||
#define LIKELY(x) __builtin_expect (!!(x), 1)
|
#define LIKELY(x) __builtin_expect (!!(x), 1)
|
||||||
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
|
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
|
||||||
@@ -26,7 +27,8 @@
|
|||||||
#define xstr(s) str(s)
|
#define xstr(s) str(s)
|
||||||
#define str(s) #s
|
#define str(s) #s
|
||||||
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
||||||
#define fatal(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); }
|
void log_error(const char *fmt, ...);
|
||||||
|
#define fatal(...) { log_error(__VA_ARGS__); exit(EXIT_FAILURE); }
|
||||||
|
|
||||||
typedef unsigned long long id_type;
|
typedef unsigned long long id_type;
|
||||||
typedef uint32_t char_type;
|
typedef uint32_t char_type;
|
||||||
@@ -207,7 +209,7 @@ typedef struct {
|
|||||||
|
|
||||||
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
|
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
|
||||||
|
|
||||||
#define left_shift_line(line, at, num) \
|
#define left_shift_line(line, at, num) { \
|
||||||
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
|
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
|
||||||
COPY_CELL(line, __i__ + (num), line, __i__) \
|
COPY_CELL(line, __i__ + (num), line, __i__) \
|
||||||
} \
|
} \
|
||||||
@@ -215,7 +217,8 @@ typedef struct {
|
|||||||
(line)->cells[(at)].ch = BLANK_CHAR; \
|
(line)->cells[(at)].ch = BLANK_CHAR; \
|
||||||
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
|
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
|
||||||
clear_sprite_position((line)->cells[(at)]); \
|
clear_sprite_position((line)->cells[(at)]); \
|
||||||
}
|
}\
|
||||||
|
}
|
||||||
|
|
||||||
#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \
|
#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \
|
||||||
if ((base)->capacity < num) { \
|
if ((base)->capacity < num) { \
|
||||||
@@ -262,6 +265,5 @@ void wakeup_io_loop(bool);
|
|||||||
void scroll_event(double, double);
|
void scroll_event(double, double);
|
||||||
void fake_scroll(int, bool);
|
void fake_scroll(int, bool);
|
||||||
void set_special_key_combo(int glfw_key, int mods);
|
void set_special_key_combo(int glfw_key, int mods);
|
||||||
void on_text_input(unsigned int codepoint, int mods);
|
void on_key_input(int key, int scancode, int action, int mods, const char*, int);
|
||||||
void on_key_input(int key, int scancode, int action, int mods);
|
|
||||||
void request_window_attention(id_type, bool);
|
void request_window_attention(id_type, bool);
|
||||||
|
|||||||
@@ -330,19 +330,39 @@ face_has_codepoint(PyObject* face, char_type cp) {
|
|||||||
return glyph_id_for_codepoint(face, cp) > 0;
|
return glyph_id_for_codepoint(face, cp) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
has_emoji_presentation(Cell *cell) {
|
||||||
|
return (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
has_cell_text(Font *self, Cell *cell) {
|
has_cell_text(Font *self, Cell *cell) {
|
||||||
if (!face_has_codepoint(self->face, cell->ch)) return false;
|
if (!face_has_codepoint(self->face, cell->ch)) return false;
|
||||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||||
if (!face_has_codepoint(self->face, codepoint_for_mark(cell->cc_idx[i]))) return false;
|
combining_type cc_idx = cell->cc_idx[i];
|
||||||
|
if (cc_idx == VS15 || cc_idx == VS16) continue;
|
||||||
|
if (!face_has_codepoint(self->face, codepoint_for_mark(cc_idx))) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
|
||||||
|
printf("U+%x ", cell->ch);
|
||||||
|
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||||
|
printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
|
||||||
|
}
|
||||||
|
if (bold) printf("bold ");
|
||||||
|
if (italic) printf("italic ");
|
||||||
|
if (emoji_presentation) printf("emoji_presentation ");
|
||||||
|
PyObject_Print(face, stdout, 0);
|
||||||
|
if (new_face) printf(" (new face)");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
static inline ssize_t
|
static inline ssize_t
|
||||||
load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation) {
|
load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation) {
|
||||||
if (fonts.fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return MISSING_FONT; }
|
if (fonts.fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; }
|
||||||
ssize_t f;
|
ssize_t f;
|
||||||
|
|
||||||
if (bold) f = fonts.italic_font_idx > 0 ? fonts.bi_font_idx : fonts.bold_font_idx;
|
if (bold) f = fonts.italic_font_idx > 0 ? fonts.bi_font_idx : fonts.bold_font_idx;
|
||||||
@@ -352,6 +372,7 @@ load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation)
|
|||||||
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic, emoji_presentation);
|
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic, emoji_presentation);
|
||||||
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
|
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
|
||||||
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
|
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
|
||||||
|
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face, true);
|
||||||
set_size_for_face(face, cell_height, true);
|
set_size_for_face(face, cell_height, true);
|
||||||
|
|
||||||
ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true);
|
ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true);
|
||||||
@@ -364,17 +385,17 @@ load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation)
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline ssize_t
|
static inline ssize_t
|
||||||
fallback_font(Cell *cell) {
|
fallback_font(Cell *cell) {
|
||||||
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
||||||
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
||||||
bool emoji_presentation = (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15;
|
bool emoji_presentation = has_emoji_presentation(cell);
|
||||||
|
|
||||||
// Check if one of the existing fallback fonts has this text
|
// Check if one of the existing fallback fonts has this text
|
||||||
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
|
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
|
||||||
Font *ff = fonts.fonts +j;
|
Font *ff = fonts.fonts +j;
|
||||||
if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) {
|
if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) {
|
||||||
|
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, ff->face, false);
|
||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,7 +439,7 @@ START_ALLOW_CASE_RANGE
|
|||||||
ans = fonts.bi_font_idx; break;
|
ans = fonts.bi_font_idx; break;
|
||||||
}
|
}
|
||||||
if (ans < 0) ans = fonts.medium_font_idx;
|
if (ans < 0) ans = fonts.medium_font_idx;
|
||||||
if (has_cell_text(fonts.fonts + ans, cell)) return ans;
|
if (!has_emoji_presentation(cell) && has_cell_text(fonts.fonts + ans, cell)) return ans;
|
||||||
return fallback_font(cell);
|
return fallback_font(cell);
|
||||||
}
|
}
|
||||||
END_ALLOW_CASE_RANGE
|
END_ALLOW_CASE_RANGE
|
||||||
@@ -744,7 +765,7 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
|
|||||||
if (num_cells_consumed) {
|
if (num_cells_consumed) {
|
||||||
if (num_cells_consumed > MAX_GLYPHS_IN_GROUP) {
|
if (num_cells_consumed > MAX_GLYPHS_IN_GROUP) {
|
||||||
// Nasty, a single glyph used more than MAX_GLYPHS_IN_GROUP cells, we cannot render this case correctly
|
// Nasty, a single glyph used more than MAX_GLYPHS_IN_GROUP cells, we cannot render this case correctly
|
||||||
fprintf(stderr, "The glyph: %u needs more than %u cells, cannot render it\n", glyph_id, MAX_GLYPHS_IN_GROUP);
|
log_error("The glyph: %u needs more than %u cells, cannot render it", glyph_id, MAX_GLYPHS_IN_GROUP);
|
||||||
current_group->num_glyphs--;
|
current_group->num_glyphs--;
|
||||||
while (num_cells_consumed) {
|
while (num_cells_consumed) {
|
||||||
G(group_idx)++; current_group = G(groups) + G(group_idx);
|
G(group_idx)++; current_group = G(groups) + G(group_idx);
|
||||||
@@ -773,6 +794,17 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
|
|||||||
#undef MAX_GLYPHS_IN_GROUP
|
#undef MAX_GLYPHS_IN_GROUP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
merge_groups_for_pua_space_ligature() {
|
||||||
|
if (G(group_idx) == 1) {
|
||||||
|
Group *g = G(groups), *g1 = G(groups) + 1;
|
||||||
|
g->num_cells += g1->num_cells;
|
||||||
|
g->num_glyphs += g1->num_glyphs;
|
||||||
|
g->num_glyphs = MIN(g->num_glyphs, MAX_NUM_EXTRA_GLYPHS + 1);
|
||||||
|
G(group_idx) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_groups(Font *font) {
|
render_groups(Font *font) {
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
@@ -783,7 +815,10 @@ render_groups(Font *font) {
|
|||||||
/* printf("Group: idx: %u num_cells: %u num_glyphs: %u first_glyph_idx: %u first_cell_idx: %u total_num_glyphs: %zu\n", */
|
/* printf("Group: idx: %u num_cells: %u num_glyphs: %u first_glyph_idx: %u first_cell_idx: %u total_num_glyphs: %zu\n", */
|
||||||
/* idx, group->num_cells, group->num_glyphs, group->first_glyph_idx, group->first_cell_idx, group_state.num_glyphs); */
|
/* idx, group->num_cells, group->num_glyphs, group->first_glyph_idx, group->first_cell_idx, group_state.num_glyphs); */
|
||||||
glyph_index primary = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;
|
glyph_index primary = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;
|
||||||
for (unsigned i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) ed.data[i-1] = G(info)[group->first_glyph_idx + i].codepoint;
|
unsigned int i;
|
||||||
|
int last = -1;
|
||||||
|
for (i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) { last = i - 1; ed.data[last] = G(info)[group->first_glyph_idx + i].codepoint; }
|
||||||
|
if ((size_t)(last + 1) < arraysz(ed.data)) ed.data[last + 1] = 0;
|
||||||
render_group(group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed);
|
render_group(group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed);
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
@@ -826,10 +861,11 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||||||
#undef G
|
#undef G
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
|
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) {
|
||||||
switch(font_idx) {
|
switch(font_idx) {
|
||||||
default:
|
default:
|
||||||
shape_run(first_cell, num_cells, &fonts.fonts[font_idx]);
|
shape_run(first_cell, num_cells, &fonts.fonts[font_idx]);
|
||||||
|
if (pua_space_ligature) merge_groups_for_pua_space_ligature();
|
||||||
render_groups(&fonts.fonts[font_idx]);
|
render_groups(&fonts.fonts[font_idx]);
|
||||||
break;
|
break;
|
||||||
case BLANK_FONT:
|
case BLANK_FONT:
|
||||||
@@ -844,9 +880,14 @@ render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_private_use(char_type ch) {
|
||||||
|
return (0xe000 <= ch && ch <= 0xf8ff) || (0xF0000 <= ch && ch <= 0xFFFFF) || (0x100000 <= ch && ch <= 0x10FFFF);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
render_line(Line *line) {
|
render_line(Line *line) {
|
||||||
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx);
|
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false);
|
||||||
ssize_t run_font_idx = NO_FONT;
|
ssize_t run_font_idx = NO_FONT;
|
||||||
index_type first_cell_in_run, i;
|
index_type first_cell_in_run, i;
|
||||||
attrs_type prev_width = 0;
|
attrs_type prev_width = 0;
|
||||||
@@ -854,6 +895,16 @@ render_line(Line *line) {
|
|||||||
if (prev_width == 2) { prev_width = 0; continue; }
|
if (prev_width == 2) { prev_width = 0; continue; }
|
||||||
Cell *cell = line->cells + i;
|
Cell *cell = line->cells + i;
|
||||||
ssize_t cell_font_idx = font_for_cell(cell);
|
ssize_t cell_font_idx = font_for_cell(cell);
|
||||||
|
if (is_private_use(cell->ch) && i + 1 < line->xnum && (line->cells[i+1].ch == ' ' || line->cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) {
|
||||||
|
// We have a private use char followed by a space char, render it as a two cell ligature.
|
||||||
|
RENDER;
|
||||||
|
render_run(line->cells + i, 2, cell_font_idx, true);
|
||||||
|
run_font_idx = NO_FONT;
|
||||||
|
first_cell_in_run = i + 2;
|
||||||
|
prev_width = line->cells[i+1].attrs & WIDTH_MASK;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
prev_width = cell->attrs & WIDTH_MASK;
|
prev_width = cell->attrs & WIDTH_MASK;
|
||||||
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
|
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
|
||||||
if (run_font_idx == cell_font_idx) continue;
|
if (run_font_idx == cell_font_idx) continue;
|
||||||
@@ -1015,7 +1066,7 @@ concat_cells(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
current_fonts(PyObject UNUSED *self) {
|
current_fonts(PYNOARG) {
|
||||||
PyObject *ans = PyDict_New();
|
PyObject *ans = PyDict_New();
|
||||||
if (!ans) return NULL;
|
if (!ans) return NULL;
|
||||||
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fonts.fonts[val].face) != 0) { goto error; }}
|
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fonts.fonts[val].face) != 0) { goto error; }}
|
||||||
@@ -1046,7 +1097,7 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
|
|||||||
static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)];
|
static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)];
|
||||||
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
|
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
|
||||||
cell.ch = char_buf[0];
|
cell.ch = char_buf[0];
|
||||||
for (unsigned i = 0; i + 1 < PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
|
for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
|
||||||
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
||||||
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
||||||
ssize_t ans = fallback_font(&cell);
|
ssize_t ans = fallback_font(&cell);
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
|
|
||||||
from kitty.fast_data_types import coretext_all_fonts
|
from kitty.fast_data_types import coretext_all_fonts
|
||||||
from kitty.utils import safe_print
|
from kitty.utils import log_error
|
||||||
|
|
||||||
attr_map = {(False, False): 'font_family',
|
attr_map = {(False, False): 'font_family',
|
||||||
(True, False): 'bold_font',
|
(True, False): 'bold_font',
|
||||||
@@ -63,10 +62,7 @@ def find_best_match(family, bold=False, italic=False):
|
|||||||
# Let CoreText choose the font if the family exists, otherwise
|
# Let CoreText choose the font if the family exists, otherwise
|
||||||
# fallback to Menlo
|
# fallback to Menlo
|
||||||
if q not in font_map['family_map']:
|
if q not in font_map['family_map']:
|
||||||
safe_print(
|
log_error('The font {} was not found, falling back to Menlo'.format(family))
|
||||||
'The font {} was not found, falling back to Menlo'.format(family),
|
|
||||||
file=sys.stderr
|
|
||||||
)
|
|
||||||
family = 'Menlo'
|
family = 'Menlo'
|
||||||
return {
|
return {
|
||||||
'monospace': True,
|
'monospace': True,
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ calc_cell_width(Face *self) {
|
|||||||
for (char_type i = 32; i < 128; i++) {
|
for (char_type i = 32; i < 128; i++) {
|
||||||
int glyph_index = FT_Get_Char_Index(self->face, i);
|
int glyph_index = FT_Get_Char_Index(self->face, i);
|
||||||
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
|
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
|
||||||
ans = MAX(ans, (unsigned long)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
ans = MAX(ans, (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ans;
|
return ans;
|
||||||
@@ -324,7 +324,6 @@ typedef struct {
|
|||||||
unsigned int factor, right_edge;
|
unsigned int factor, right_edge;
|
||||||
} ProcessedBitmap;
|
} ProcessedBitmap;
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
trim_borders(ProcessedBitmap *ans, size_t extra) {
|
trim_borders(ProcessedBitmap *ans, size_t extra) {
|
||||||
bool column_has_text = false;
|
bool column_has_text = false;
|
||||||
@@ -354,9 +353,13 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
|
|||||||
ans->rows = bitmap->rows;
|
ans->rows = bitmap->rows;
|
||||||
ans->pixel_mode = bitmap->pixel_mode;
|
ans->pixel_mode = bitmap->pixel_mode;
|
||||||
if (ans->width > max_width) {
|
if (ans->width > max_width) {
|
||||||
size_t extra = bitmap->width - max_width;
|
size_t extra = ans->width - max_width;
|
||||||
if (italic && extra < cell_width / 2) {
|
if (italic && extra < cell_width / 2) {
|
||||||
trim_borders(ans, extra);
|
trim_borders(ans, extra);
|
||||||
|
} else if (extra == 2 && num_cells == 1) {
|
||||||
|
// there exist fonts that have bitmaps just a couple of pixels
|
||||||
|
// wider than their advances, rather than rescale, which looks
|
||||||
|
// bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352
|
||||||
} else if (rescale && self->is_scalable && extra > 1) {
|
} else if (rescale && self->is_scalable && extra > 1) {
|
||||||
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
|
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
|
||||||
float ar = (float)max_width / (float)bitmap->width;
|
float ar = (float)max_width / (float)bitmap->width;
|
||||||
@@ -532,7 +535,8 @@ render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *inf
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
display_name(Face *self) {
|
display_name(PyObject *s, PyObject *a UNUSED) {
|
||||||
|
Face *self = (Face*)s;
|
||||||
const char *psname = FT_Get_Postscript_Name(self->face);
|
const char *psname = FT_Get_Postscript_Name(self->face);
|
||||||
if (psname) return Py_BuildValue("s", psname);
|
if (psname) return Py_BuildValue("s", psname);
|
||||||
Py_INCREF(self->path);
|
Py_INCREF(self->path);
|
||||||
@@ -540,8 +544,8 @@ display_name(Face *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
extra_data(Face *self) {
|
extra_data(PyObject *self, PyObject *a UNUSED) {
|
||||||
return PyLong_FromVoidPtr(self->extra_data);
|
return PyLong_FromVoidPtr(((Face*)self)->extra_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ compile_shader(GLenum shader_type, const char *source) {
|
|||||||
if (ret != GL_TRUE) {
|
if (ret != GL_TRUE) {
|
||||||
GLsizei len;
|
GLsizei len;
|
||||||
glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);
|
glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);
|
||||||
fprintf(stderr, "Failed to compile GLSL shader!\n%s", glbuf);
|
log_error("Failed to compile GLSL shader!\n%s", glbuf);
|
||||||
glDeleteShader(shader_id);
|
glDeleteShader(shader_id);
|
||||||
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
10
kitty/glfw-wrapper.c
generated
10
kitty/glfw-wrapper.c
generated
@@ -254,14 +254,8 @@ load_glfw(const char* path) {
|
|||||||
*(void **) (&glfwSetCursor_impl) = dlsym(handle, "glfwSetCursor");
|
*(void **) (&glfwSetCursor_impl) = dlsym(handle, "glfwSetCursor");
|
||||||
if (glfwSetCursor_impl == NULL) fail("Failed to load glfw function glfwSetCursor with error: %s", dlerror());
|
if (glfwSetCursor_impl == NULL) fail("Failed to load glfw function glfwSetCursor with error: %s", dlerror());
|
||||||
|
|
||||||
*(void **) (&glfwSetKeyCallback_impl) = dlsym(handle, "glfwSetKeyCallback");
|
*(void **) (&glfwSetKeyboardCallback_impl) = dlsym(handle, "glfwSetKeyboardCallback");
|
||||||
if (glfwSetKeyCallback_impl == NULL) fail("Failed to load glfw function glfwSetKeyCallback with error: %s", dlerror());
|
if (glfwSetKeyboardCallback_impl == NULL) fail("Failed to load glfw function glfwSetKeyboardCallback with error: %s", dlerror());
|
||||||
|
|
||||||
*(void **) (&glfwSetCharCallback_impl) = dlsym(handle, "glfwSetCharCallback");
|
|
||||||
if (glfwSetCharCallback_impl == NULL) fail("Failed to load glfw function glfwSetCharCallback with error: %s", dlerror());
|
|
||||||
|
|
||||||
*(void **) (&glfwSetCharModsCallback_impl) = dlsym(handle, "glfwSetCharModsCallback");
|
|
||||||
if (glfwSetCharModsCallback_impl == NULL) fail("Failed to load glfw function glfwSetCharModsCallback with error: %s", dlerror());
|
|
||||||
|
|
||||||
*(void **) (&glfwSetMouseButtonCallback_impl) = dlsym(handle, "glfwSetMouseButtonCallback");
|
*(void **) (&glfwSetMouseButtonCallback_impl) = dlsym(handle, "glfwSetMouseButtonCallback");
|
||||||
if (glfwSetMouseButtonCallback_impl == NULL) fail("Failed to load glfw function glfwSetMouseButtonCallback with error: %s", dlerror());
|
if (glfwSetMouseButtonCallback_impl == NULL) fail("Failed to load glfw function glfwSetMouseButtonCallback with error: %s", dlerror());
|
||||||
|
|||||||
80
kitty/glfw-wrapper.h
generated
80
kitty/glfw-wrapper.h
generated
@@ -1133,9 +1133,15 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
|||||||
*/
|
*/
|
||||||
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
||||||
|
|
||||||
/*! @brief The function signature for keyboard key callbacks.
|
/*! @brief The function signature for key callbacks.
|
||||||
*
|
*
|
||||||
* This is the function signature for keyboard key callback functions.
|
* This is the function signature for key callback functions.
|
||||||
|
* The semantics of this function are that the key that is interacted with on the
|
||||||
|
* keyboard is reported, and the text, if any generated by the key is reported.
|
||||||
|
* So, for example, if on a US-ASCII keyboard the user presses Shift+= GLFW
|
||||||
|
* will report the text "+" and the key as GLFW_KEY_EQUAL. The reported key takes into
|
||||||
|
* account any current keyboard maps defined in the OS. So with a dvorak mapping, pressing
|
||||||
|
* the "s" key will generate text "o" and GLFW_KEY_O.
|
||||||
*
|
*
|
||||||
* @param[in] window The window that received the event.
|
* @param[in] window The window that received the event.
|
||||||
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
|
||||||
@@ -1143,56 +1149,22 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
|
|||||||
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
||||||
* held down.
|
* held down.
|
||||||
|
* @param[in] text UTF-8 encoded text generated by this key event or empty string.
|
||||||
|
* @param[in] reserved Reserved for future use.
|
||||||
|
*
|
||||||
|
* @note On X11/Wayland if a modifier other than the modifiers GLFW reports
|
||||||
|
* (ctrl/shift/alt/super) is used, GLFW will report the shifted key rather
|
||||||
|
* than the unshifted key. So for example, if ISO_Shift_Level_5 is used to
|
||||||
|
* convert the key A into UP GLFW will report the key as UP with no modifiers.
|
||||||
*
|
*
|
||||||
* @sa @ref input_key
|
* @sa @ref input_key
|
||||||
* @sa @ref glfwSetKeyCallback
|
* @sa @ref glfwSetKeyboardCallback
|
||||||
*
|
*
|
||||||
* @since Added in version 1.0.
|
* @since Added in version 4.0.
|
||||||
* @glfw3 Added window handle, scancode and modifier mask parameters.
|
|
||||||
*
|
*
|
||||||
* @ingroup input
|
* @ingroup input
|
||||||
*/
|
*/
|
||||||
typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int);
|
typedef void (* GLFWkeyboardfun)(GLFWwindow*, int, int, int, int, const char*, int);
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character callback functions.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharCallback
|
|
||||||
*
|
|
||||||
* @since Added in version 2.4.
|
|
||||||
* @glfw3 Added window handle parameter.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for Unicode character with modifiers
|
|
||||||
* callbacks.
|
|
||||||
*
|
|
||||||
* This is the function signature for Unicode character with modifiers callback
|
|
||||||
* functions. It is called for each input character, regardless of what
|
|
||||||
* modifier keys are held down.
|
|
||||||
*
|
|
||||||
* @param[in] window The window that received the event.
|
|
||||||
* @param[in] codepoint The Unicode code point of the character.
|
|
||||||
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
|
|
||||||
* held down.
|
|
||||||
*
|
|
||||||
* @sa @ref input_char
|
|
||||||
* @sa @ref glfwSetCharModsCallback
|
|
||||||
*
|
|
||||||
* @deprecated Scheduled for removal in version 4.0.
|
|
||||||
*
|
|
||||||
* @since Added in version 3.1.
|
|
||||||
*
|
|
||||||
* @ingroup input
|
|
||||||
*/
|
|
||||||
typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int);
|
|
||||||
|
|
||||||
/*! @brief The function signature for file drop callbacks.
|
/*! @brief The function signature for file drop callbacks.
|
||||||
*
|
*
|
||||||
@@ -1585,7 +1557,7 @@ typedef void (*glfwRequestWindowAttention_func)(GLFWwindow*);
|
|||||||
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
|
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
|
||||||
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
|
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
|
||||||
|
|
||||||
typedef int (*glfwWindowBell_func)(GLFWwindow*, int64_t);
|
typedef int (*glfwWindowBell_func)(GLFWwindow*);
|
||||||
glfwWindowBell_func glfwWindowBell_impl;
|
glfwWindowBell_func glfwWindowBell_impl;
|
||||||
#define glfwWindowBell glfwWindowBell_impl
|
#define glfwWindowBell glfwWindowBell_impl
|
||||||
|
|
||||||
@@ -1713,17 +1685,9 @@ typedef void (*glfwSetCursor_func)(GLFWwindow*, GLFWcursor*);
|
|||||||
glfwSetCursor_func glfwSetCursor_impl;
|
glfwSetCursor_func glfwSetCursor_impl;
|
||||||
#define glfwSetCursor glfwSetCursor_impl
|
#define glfwSetCursor glfwSetCursor_impl
|
||||||
|
|
||||||
typedef GLFWkeyfun (*glfwSetKeyCallback_func)(GLFWwindow*, GLFWkeyfun);
|
typedef GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboardfun);
|
||||||
glfwSetKeyCallback_func glfwSetKeyCallback_impl;
|
glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl;
|
||||||
#define glfwSetKeyCallback glfwSetKeyCallback_impl
|
#define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl
|
||||||
|
|
||||||
typedef GLFWcharfun (*glfwSetCharCallback_func)(GLFWwindow*, GLFWcharfun);
|
|
||||||
glfwSetCharCallback_func glfwSetCharCallback_impl;
|
|
||||||
#define glfwSetCharCallback glfwSetCharCallback_impl
|
|
||||||
|
|
||||||
typedef GLFWcharmodsfun (*glfwSetCharModsCallback_func)(GLFWwindow*, GLFWcharmodsfun);
|
|
||||||
glfwSetCharModsCallback_func glfwSetCharModsCallback_impl;
|
|
||||||
#define glfwSetCharModsCallback glfwSetCharModsCallback_impl
|
|
||||||
|
|
||||||
typedef GLFWmousebuttonfun (*glfwSetMouseButtonCallback_func)(GLFWwindow*, GLFWmousebuttonfun);
|
typedef GLFWmousebuttonfun (*glfwSetMouseButtonCallback_func)(GLFWwindow*, GLFWmousebuttonfun);
|
||||||
glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;
|
glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;
|
||||||
|
|||||||
113
kitty/glfw.c
113
kitty/glfw.c
@@ -16,6 +16,7 @@ extern void cocoa_set_titlebar_color(void *w);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
|
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
|
||||||
|
static bool event_loop_blocking_with_no_timeout = false;
|
||||||
|
|
||||||
void
|
void
|
||||||
update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
||||||
@@ -30,6 +31,8 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
|||||||
window->has_pending_resizes = false;
|
window->has_pending_resizes = false;
|
||||||
window->viewport_width = MAX(window->viewport_width, 100);
|
window->viewport_width = MAX(window->viewport_width, 100);
|
||||||
window->viewport_height = MAX(window->viewport_height, 100);
|
window->viewport_height = MAX(window->viewport_height, 100);
|
||||||
|
window->window_width = MAX(w, 100);
|
||||||
|
window->window_height = MAX(h, 100);
|
||||||
if (notify_boss) {
|
if (notify_boss) {
|
||||||
call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False);
|
call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False);
|
||||||
}
|
}
|
||||||
@@ -72,9 +75,20 @@ is_window_ready_for_callbacks() {
|
|||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
show_mouse_cursor(GLFWwindow *w) {
|
show_mouse_cursor(GLFWwindow *w) {
|
||||||
if (glfwGetInputMode(w, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
|
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On Cocoa, glfwWaitEvents() can block indefinitely because of the way Cocoa
|
||||||
|
// works. See https://github.com/glfw/glfw/issues/1251. I have noticed this
|
||||||
|
// happening in particular with window resize events, when waiting with no
|
||||||
|
// timeout. See https://github.com/kovidgoyal/kitty/issues/458
|
||||||
|
// So we use an unlovely hack to workaround that case
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define unjam_event_loop() { if (event_loop_blocking_with_no_timeout) wakeup_main_loop(); }
|
||||||
|
#else
|
||||||
|
#define unjam_event_loop()
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
||||||
if (!set_callback_window(w)) return;
|
if (!set_callback_window(w)) return;
|
||||||
@@ -82,7 +96,8 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
|||||||
OSWindow *window = global_state.callback_os_window;
|
OSWindow *window = global_state.callback_os_window;
|
||||||
window->has_pending_resizes = true; global_state.has_pending_resizes = true;
|
window->has_pending_resizes = true; global_state.has_pending_resizes = true;
|
||||||
window->last_resize_event_at = monotonic();
|
window->last_resize_event_at = monotonic();
|
||||||
} else fprintf(stderr, "Ignoring resize request for tiny size: %dx%d\n", width, height);
|
unjam_event_loop();
|
||||||
|
} else log_error("Ignoring resize request for tiny size: %dx%d", width, height);
|
||||||
global_state.callback_os_window = NULL;
|
global_state.callback_os_window = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +109,13 @@ refresh_callback(GLFWwindow *w) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
char_mods_callback(GLFWwindow *w, unsigned int codepoint, int mods) {
|
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods, const char* text, int state) {
|
||||||
if (!set_callback_window(w)) return;
|
|
||||||
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
|
|
||||||
if (is_window_ready_for_callbacks()) on_text_input(codepoint, mods);
|
|
||||||
global_state.callback_os_window = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) {
|
|
||||||
if (!set_callback_window(w)) return;
|
if (!set_callback_window(w)) return;
|
||||||
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
|
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
|
||||||
if (key >= 0 && key <= GLFW_KEY_LAST) {
|
if (key >= 0 && key <= GLFW_KEY_LAST) {
|
||||||
global_state.callback_os_window->is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
|
global_state.callback_os_window->is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
|
||||||
if (is_window_ready_for_callbacks()) on_key_input(key, scancode, action, mods);
|
|
||||||
}
|
}
|
||||||
|
if (is_window_ready_for_callbacks()) on_key_input(key, scancode, action, mods, text, state);
|
||||||
global_state.callback_os_window = NULL;
|
global_state.callback_os_window = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +236,11 @@ static GLFWimage logo = {0};
|
|||||||
static PyObject*
|
static PyObject*
|
||||||
set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
|
set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
|
||||||
Py_ssize_t sz;
|
Py_ssize_t sz;
|
||||||
if(!PyArg_ParseTuple(args, "s#ii", &(logo.pixels), &sz, &(logo.width), &(logo.height))) return NULL;
|
const char *logo_data;
|
||||||
|
if(!PyArg_ParseTuple(args, "s#ii", &(logo_data), &sz, &(logo.width), &(logo.height))) return NULL;
|
||||||
|
sz = (MAX(logo.width * logo.height, sz));
|
||||||
|
logo.pixels = malloc(sz);
|
||||||
|
if (logo.pixels) memcpy(logo.pixels, logo_data, sz);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +307,7 @@ set_dpi_from_os_window(OSWindow *w) {
|
|||||||
global_state.logical_dpi_y = yscale * factor;
|
global_state.logical_dpi_y = yscale * factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_first_window = true;
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
create_os_window(PyObject UNUSED *self, PyObject *args) {
|
create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||||
@@ -303,7 +315,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
char *title, *wm_class_class, *wm_class_name;
|
char *title, *wm_class_class, *wm_class_name;
|
||||||
PyObject *load_programs = NULL;
|
PyObject *load_programs = NULL;
|
||||||
if (!PyArg_ParseTuple(args, "iisss|Oiii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
|
if (!PyArg_ParseTuple(args, "iisss|Oiii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
|
||||||
bool is_first_window = standard_cursor == NULL;
|
|
||||||
|
|
||||||
if (is_first_window) {
|
if (is_first_window) {
|
||||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||||
@@ -336,11 +347,11 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
PyErr_SetString(PyExc_ValueError, "Too many windows");
|
PyErr_SetString(PyExc_ValueError, "Too many windows");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
bool want_semi_transparent = (1.0 - OPT(background_opacity) > 0.1) ? true : false;
|
bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) ? true : false;
|
||||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
|
||||||
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
||||||
if (glfw_window == NULL) {
|
if (glfw_window == NULL) {
|
||||||
fprintf(stderr, "Failed to create a window at size: %dx%d, using a standard size instead.\n", width, height);
|
log_error("Failed to create a window at size: %dx%d, using a standard size instead.", width, height);
|
||||||
glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
||||||
}
|
}
|
||||||
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
|
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
|
||||||
@@ -357,6 +368,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
cocoa_create_global_menu();
|
cocoa_create_global_menu();
|
||||||
#endif
|
#endif
|
||||||
|
is_first_window = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSWindow *w = add_os_window();
|
OSWindow *w = add_os_window();
|
||||||
@@ -372,17 +384,16 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
update_os_window_viewport(w, false);
|
update_os_window_viewport(w, false);
|
||||||
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
|
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
|
||||||
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
|
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
|
||||||
glfwSetCharModsCallback(glfw_window, char_mods_callback);
|
|
||||||
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
|
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
|
||||||
glfwSetScrollCallback(glfw_window, scroll_callback);
|
glfwSetScrollCallback(glfw_window, scroll_callback);
|
||||||
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
|
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
|
||||||
glfwSetKeyCallback(glfw_window, key_callback);
|
glfwSetKeyboardCallback(glfw_window, key_callback);
|
||||||
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
|
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
|
||||||
glfwSetDropCallback(glfw_window, drop_callback);
|
glfwSetDropCallback(glfw_window, drop_callback);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
if (OPT(macos_hide_titlebar)) {
|
if (OPT(macos_hide_titlebar)) {
|
||||||
if (glfwGetCocoaWindow) { if (!cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window))) { PyErr_Print(); } }
|
if (glfwGetCocoaWindow) { if (!cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window))) { PyErr_Print(); } }
|
||||||
else fprintf(stderr, "Failed to load glfwGetCocoaWindow\n");
|
else log_error("Failed to load glfwGetCocoaWindow");
|
||||||
}
|
}
|
||||||
cocoa_set_titlebar_color(glfwGetCocoaWindow(glfw_window));
|
cocoa_set_titlebar_color(glfwGetCocoaWindow(glfw_window));
|
||||||
#endif
|
#endif
|
||||||
@@ -394,7 +405,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (want_semi_transparent && !w->is_semi_transparent) {
|
if (want_semi_transparent && !w->is_semi_transparent) {
|
||||||
static bool warned = false;
|
static bool warned = false;
|
||||||
if (!warned) {
|
if (!warned) {
|
||||||
fprintf(stderr, "Failed to enable transparency. This happens when your desktop environment does not support compositing.\n");
|
log_error("Failed to enable transparency. This happens when your desktop environment does not support compositing.");
|
||||||
warned = true;
|
warned = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,7 +470,7 @@ destroy_os_window(OSWindow *w) {
|
|||||||
// Global functions {{{
|
// Global functions {{{
|
||||||
static void
|
static void
|
||||||
error_callback(int error, const char* description) {
|
error_callback(int error, const char* description) {
|
||||||
fprintf(stderr, "[glfw error %d]: %s\n", error, description);
|
log_error("[glfw error %d]: %s", error, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -480,31 +491,19 @@ glfw_init(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_terminate(PyObject UNUSED *self) {
|
glfw_terminate(PYNOARG) {
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_wait_events(PyObject UNUSED *self, PyObject *args) {
|
glfw_post_empty_event(PYNOARG) {
|
||||||
double time = -1;
|
|
||||||
if (PyTuple_GET_SIZE(args) > 0) {
|
|
||||||
time = PyFloat_AsDouble(PyTuple_GET_ITEM(args, 0));
|
|
||||||
if (PyErr_Occurred()) PyErr_Clear();
|
|
||||||
}
|
|
||||||
if (time < 0) glfwWaitEvents();
|
|
||||||
else glfwWaitEventsTimeout(time);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject*
|
|
||||||
glfw_post_empty_event(PyObject UNUSED *self) {
|
|
||||||
glfwPostEmptyEvent();
|
glfwPostEmptyEvent();
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_poll_events(PyObject UNUSED *self) {
|
glfw_poll_events(PYNOARG) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@@ -522,7 +521,7 @@ get_physical_dpi(GLFWmonitor *m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
glfw_get_physical_dpi(PyObject UNUSED *self) {
|
glfw_get_physical_dpi(PYNOARG) {
|
||||||
GLFWmonitor *m = glfwGetPrimaryMonitor();
|
GLFWmonitor *m = glfwGetPrimaryMonitor();
|
||||||
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
|
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
|
||||||
return get_physical_dpi(m);
|
return get_physical_dpi(m);
|
||||||
@@ -547,14 +546,14 @@ glfw_window_hint(PyObject UNUSED *self, PyObject *args) {
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_clipboard_string(PyObject UNUSED *self) {
|
get_clipboard_string(PYNOARG) {
|
||||||
OSWindow *w = current_os_window();
|
OSWindow *w = current_os_window();
|
||||||
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
|
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
|
||||||
return Py_BuildValue("s", "");
|
return Py_BuildValue("s", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_content_scale_for_window(PyObject UNUSED *self) {
|
get_content_scale_for_window(PYNOARG) {
|
||||||
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
|
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
|
||||||
float xscale, yscale;
|
float xscale, yscale;
|
||||||
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
|
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
|
||||||
@@ -571,7 +570,7 @@ set_clipboard_string(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
toggle_fullscreen(PyObject UNUSED *self) {
|
toggle_fullscreen(PYNOARG) {
|
||||||
GLFWmonitor *monitor;
|
GLFWmonitor *monitor;
|
||||||
OSWindow *w = current_os_window();
|
OSWindow *w = current_os_window();
|
||||||
if (!w) Py_RETURN_NONE;
|
if (!w) Py_RETURN_NONE;
|
||||||
@@ -601,7 +600,7 @@ ring_audio_bell(OSWindow *w) {
|
|||||||
if (now - last_bell_at <= 0.1) return;
|
if (now - last_bell_at <= 0.1) return;
|
||||||
last_bell_at = now;
|
last_bell_at = now;
|
||||||
if (w->handle) {
|
if (w->handle) {
|
||||||
glfwWindowBell(w->handle, OPT(x11_bell_volume));
|
glfwWindowBell(w->handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,9 +621,7 @@ set_os_window_title(OSWindow *w, const char *title) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
hide_mouse(OSWindow *w) {
|
hide_mouse(OSWindow *w) {
|
||||||
if (glfwGetInputMode(w->handle, GLFW_CURSOR) != GLFW_CURSOR_HIDDEN) {
|
glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||||
glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -634,8 +631,8 @@ swap_window_buffers(OSWindow *w) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
event_loop_wait(double timeout) {
|
event_loop_wait(double timeout) {
|
||||||
if (timeout < 0) glfwWaitEvents();
|
if (timeout < 0) { event_loop_blocking_with_no_timeout = true; glfwWaitEvents(); event_loop_blocking_with_no_timeout = false; }
|
||||||
else if (timeout > 0) glfwWaitEventsTimeout(timeout);
|
else glfwWaitEventsTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -661,14 +658,14 @@ should_os_window_close(OSWindow* w) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
primary_monitor_size(PyObject UNUSED *self) {
|
primary_monitor_size(PYNOARG) {
|
||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||||
return Py_BuildValue("ii", mode->width, mode->height);
|
return Py_BuildValue("ii", mode->width, mode->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
primary_monitor_content_scale(PyObject UNUSED *self) {
|
primary_monitor_content_scale(PYNOARG) {
|
||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
float xscale, yscale;
|
float xscale, yscale;
|
||||||
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
|
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
|
||||||
@@ -676,10 +673,10 @@ primary_monitor_content_scale(PyObject UNUSED *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
x11_display(PyObject UNUSED *self) {
|
x11_display(PYNOARG) {
|
||||||
if (glfwGetX11Display) {
|
if (glfwGetX11Display) {
|
||||||
return PyLong_FromVoidPtr(glfwGetX11Display());
|
return PyLong_FromVoidPtr(glfwGetX11Display());
|
||||||
} else fprintf(stderr, "Failed to load glfwGetX11Display\n");
|
} else log_error("Failed to load glfwGetX11Display");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,10 +695,10 @@ x11_window_id(PyObject UNUSED *self, PyObject *os_wid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_primary_selection(PyObject UNUSED *self) {
|
get_primary_selection(PYNOARG) {
|
||||||
if (glfwGetX11SelectionString) {
|
if (glfwGetX11SelectionString) {
|
||||||
return Py_BuildValue("y", glfwGetX11SelectionString());
|
return Py_BuildValue("y", glfwGetX11SelectionString());
|
||||||
} else fprintf(stderr, "Failed to load glfwGetX11SelectionString\n");
|
} else log_error("Failed to load glfwGetX11SelectionString");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +707,7 @@ set_primary_selection(PyObject UNUSED *self, PyObject *args) {
|
|||||||
char *text;
|
char *text;
|
||||||
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
||||||
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
|
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
|
||||||
else fprintf(stderr, "Failed to load glfwSetX11SelectionString\n");
|
else log_error("Failed to load glfwSetX11SelectionString");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,7 +765,6 @@ static PyMethodDef module_methods[] = {
|
|||||||
METHODB(glfw_poll_events, METH_NOARGS),
|
METHODB(glfw_poll_events, METH_NOARGS),
|
||||||
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
|
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
|
||||||
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
|
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
|
||||||
{"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""},
|
|
||||||
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""},
|
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""},
|
||||||
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
|
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
|
||||||
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
|
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
|
||||||
@@ -777,10 +773,19 @@ static PyMethodDef module_methods[] = {
|
|||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void cleanup_glfw(void) {
|
||||||
|
if (logo.pixels) free(logo.pixels);
|
||||||
|
logo.pixels = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// constants {{{
|
// constants {{{
|
||||||
bool
|
bool
|
||||||
init_glfw(PyObject *m) {
|
init_glfw(PyObject *m) {
|
||||||
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
|
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
|
||||||
|
if (Py_AtExit(cleanup_glfw) != 0) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Failed to register the glfw exit handler");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
||||||
ADDC(GLFW_RELEASE);
|
ADDC(GLFW_RELEASE);
|
||||||
ADDC(GLFW_PRESS);
|
ADDC(GLFW_PRESS);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ PyTypeObject GraphicsManager_Type;
|
|||||||
|
|
||||||
#define STORAGE_LIMIT (320 * (1024 * 1024))
|
#define STORAGE_LIMIT (320 * (1024 * 1024))
|
||||||
|
|
||||||
#define REPORT_ERROR(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
|
#define REPORT_ERROR(...) { log_error(__VA_ARGS__); }
|
||||||
|
|
||||||
|
|
||||||
static bool send_to_gpu = true;
|
static bool send_to_gpu = true;
|
||||||
@@ -509,7 +509,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
|
|
||||||
static inline const char*
|
static inline const char*
|
||||||
create_add_response(GraphicsManager UNUSED *self, bool data_loaded, uint32_t iid) {
|
create_add_response(GraphicsManager UNUSED *self, bool data_loaded, uint32_t iid) {
|
||||||
static char rbuf[sizeof(add_response)/sizeof(add_response[0])];
|
static char rbuf[sizeof(add_response)/sizeof(add_response[0]) + 64];
|
||||||
if (iid) {
|
if (iid) {
|
||||||
if (!has_add_respose) {
|
if (!has_add_respose) {
|
||||||
if (!data_loaded) return NULL;
|
if (!data_loaded) return NULL;
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ __str__(HistoryBuf *self) {
|
|||||||
if (lines == NULL) return PyErr_NoMemory();
|
if (lines == NULL) return PyErr_NoMemory();
|
||||||
for (index_type i = 0; i < self->count; i++) {
|
for (index_type i = 0; i < self->count; i++) {
|
||||||
init_line(self, index_of(self, i), self->line);
|
init_line(self, index_of(self, i), self->line);
|
||||||
PyObject *t = PyObject_Str((PyObject*)self->line);
|
PyObject *t = line_as_unicode(self->line);
|
||||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||||
PyTuple_SET_ITEM(lines, i, t);
|
PyTuple_SET_ITEM(lines, i, t);
|
||||||
}
|
}
|
||||||
@@ -215,7 +215,7 @@ as_text(HistoryBuf *self, PyObject *args) {
|
|||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
dirty_lines(HistoryBuf *self) {
|
dirty_lines(HistoryBuf *self, PyObject *a UNUSED) {
|
||||||
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
||||||
PyObject *ans = PyList_New(0);
|
PyObject *ans = PyList_New(0);
|
||||||
for (index_type i = 0; i < self->ynum; i++) {
|
for (index_type i = 0; i < self->ynum; i++) {
|
||||||
|
|||||||
99
kitty/keys.c
99
kitty/keys.c
@@ -76,66 +76,6 @@ is_modifier_key(int key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
|
||||||
get_localized_key(int key, int scancode) {
|
|
||||||
const char *name = glfwGetKeyName(key, scancode);
|
|
||||||
if (name == NULL || name[1] != 0) return key;
|
|
||||||
switch(name[0]) {
|
|
||||||
#define K(ch, name) case ch: return GLFW_KEY_##name
|
|
||||||
// key names {{{
|
|
||||||
K('A', A); K('a', A);
|
|
||||||
K('B', B); K('b', B);
|
|
||||||
K('C', C); K('c', C);
|
|
||||||
K('D', D); K('d', D);
|
|
||||||
K('E', E); K('e', E);
|
|
||||||
K('F', F); K('f', F);
|
|
||||||
K('G', G); K('g', G);
|
|
||||||
K('H', H); K('h', H);
|
|
||||||
K('I', I); K('i', I);
|
|
||||||
K('J', J); K('j', J);
|
|
||||||
K('K', K); K('k', K);
|
|
||||||
K('L', L); K('l', L);
|
|
||||||
K('M', M); K('m', M);
|
|
||||||
K('N', N); K('n', N);
|
|
||||||
K('O', O); K('o', O);
|
|
||||||
K('P', P); K('p', P);
|
|
||||||
K('Q', Q); K('q', Q);
|
|
||||||
K('R', R); K('r', R);
|
|
||||||
K('S', S); K('s', S);
|
|
||||||
K('T', T); K('t', T);
|
|
||||||
K('U', U); K('u', U);
|
|
||||||
K('V', V); K('v', V);
|
|
||||||
K('W', W); K('w', W);
|
|
||||||
K('X', X); K('x', X);
|
|
||||||
K('Y', Y); K('y', Y);
|
|
||||||
K('Z', Z); K('z', Z);
|
|
||||||
K('0', 0);
|
|
||||||
K('1', 1);
|
|
||||||
K('2', 2);
|
|
||||||
K('3', 3);
|
|
||||||
K('5', 5);
|
|
||||||
K('6', 6);
|
|
||||||
K('7', 7);
|
|
||||||
K('8', 8);
|
|
||||||
K('9', 9);
|
|
||||||
K('\'', APOSTROPHE);
|
|
||||||
K(',', COMMA);
|
|
||||||
K('.', PERIOD);
|
|
||||||
K('/', SLASH);
|
|
||||||
K('-', MINUS);
|
|
||||||
K(';', SEMICOLON);
|
|
||||||
K('=', EQUAL);
|
|
||||||
K('[', LEFT_BRACKET);
|
|
||||||
K(']', RIGHT_BRACKET);
|
|
||||||
K('`', GRAVE_ACCENT);
|
|
||||||
K('\\', BACKSLASH);
|
|
||||||
// }}}
|
|
||||||
#undef K
|
|
||||||
default:
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
send_key_to_child(Window *w, int key, int mods, int action) {
|
send_key_to_child(Window *w, int key, int mods, int action) {
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
@@ -152,14 +92,26 @@ send_key_to_child(Window *w, int key, int mods, int action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_ascii_control_char(char c) {
|
||||||
|
return (0 <= c && c <= 31) || c == 127;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
on_key_input(int key, int scancode, int action, int mods) {
|
on_key_input(int key, int scancode, int action, int mods, const char* text, int state UNUSED) {
|
||||||
Window *w = active_window();
|
Window *w = active_window();
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
if (global_state.in_sequence_mode) {
|
||||||
|
if (action != GLFW_RELEASE) call_boss(process_sequence, "iiii", key, scancode, action, mods);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
int lkey = get_localized_key(key, scancode);
|
bool has_text = text && !is_ascii_control_char(text[0]);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (has_text && IS_ALT_MODS(mods) && OPT(macos_option_as_alt)) has_text = false;
|
||||||
|
#endif
|
||||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||||
uint16_t qkey = key_map[lkey];
|
uint16_t qkey = (0 <= key && key < (ssize_t)arraysz(key_map)) ? key_map[key] : UINT8_MAX;
|
||||||
bool special = false;
|
bool special = false;
|
||||||
if (qkey != UINT8_MAX) {
|
if (qkey != UINT8_MAX) {
|
||||||
qkey = SPECIAL_INDEX(qkey);
|
qkey = SPECIAL_INDEX(qkey);
|
||||||
@@ -167,7 +119,7 @@ on_key_input(int key, int scancode, int action, int mods) {
|
|||||||
}
|
}
|
||||||
/* printf("key: %s mods: %d special: %d\n", key_name(lkey), mods, special); */
|
/* printf("key: %s mods: %d special: %d\n", key_name(lkey), mods, special); */
|
||||||
if (special) {
|
if (special) {
|
||||||
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", lkey, scancode, action, mods);
|
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", key, scancode, action, mods);
|
||||||
if (ret == NULL) { PyErr_Print(); }
|
if (ret == NULL) { PyErr_Print(); }
|
||||||
else {
|
else {
|
||||||
bool consumed = ret == Py_True;
|
bool consumed = ret == Py_True;
|
||||||
@@ -176,20 +128,17 @@ on_key_input(int key, int scancode, int action, int mods) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (action == GLFW_REPEAT && !screen->modes.mDECARM) return;
|
||||||
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
||||||
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
||||||
}
|
}
|
||||||
#ifdef __APPLE__
|
bool ok_to_send = action == GLFW_PRESS || action == GLFW_REPEAT || screen->modes.mEXTENDED_KEYBOARD;
|
||||||
if (!OPT(macos_option_as_alt) && IS_ALT_MODS(mods) && !(
|
if (ok_to_send) {
|
||||||
key == GLFW_KEY_UP || key == GLFW_KEY_DOWN || key == GLFW_KEY_LEFT || key == GLFW_KEY_RIGHT) // alt+arrow keys do not generate text on macOS
|
if (has_text) {
|
||||||
) return;
|
schedule_write_to_child(w->id, text, strlen(text));
|
||||||
#endif
|
} else {
|
||||||
if (
|
send_key_to_child(w, key, mods, action);
|
||||||
action == GLFW_PRESS ||
|
}
|
||||||
(action == GLFW_REPEAT && screen->modes.mDECARM) ||
|
|
||||||
screen->modes.mEXTENDED_KEYBOARD
|
|
||||||
) {
|
|
||||||
send_key_to_child(w, lkey, mods, action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
218
kitty/keys.h
generated
218
kitty/keys.h
generated
@@ -1126,6 +1126,58 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
case 0x6:
|
case 0x6:
|
||||||
switch(key & 0x7f) { default: return NULL;
|
switch(key & 0x7f) { default: return NULL;
|
||||||
|
case 18: // A
|
||||||
|
return "\x02\x1b\x01";
|
||||||
|
case 19: // B
|
||||||
|
return "\x02\x1b\x02";
|
||||||
|
case 20: // C
|
||||||
|
return "\x02\x1b\x03";
|
||||||
|
case 21: // D
|
||||||
|
return "\x02\x1b\x04";
|
||||||
|
case 22: // E
|
||||||
|
return "\x02\x1b\x05";
|
||||||
|
case 23: // F
|
||||||
|
return "\x02\x1b\x06";
|
||||||
|
case 24: // G
|
||||||
|
return "\x02\x1b\x07";
|
||||||
|
case 25: // H
|
||||||
|
return "\x02\x1b\x08";
|
||||||
|
case 26: // I
|
||||||
|
return "\x02\x1b\x09";
|
||||||
|
case 27: // J
|
||||||
|
return "\x02\x1b\x0a";
|
||||||
|
case 28: // K
|
||||||
|
return "\x02\x1b\x0b";
|
||||||
|
case 29: // L
|
||||||
|
return "\x02\x1b\x0c";
|
||||||
|
case 30: // M
|
||||||
|
return "\x02\x1b\x0d";
|
||||||
|
case 31: // N
|
||||||
|
return "\x02\x1b\x0e";
|
||||||
|
case 32: // O
|
||||||
|
return "\x02\x1b\x0f";
|
||||||
|
case 33: // P
|
||||||
|
return "\x02\x1b\x10";
|
||||||
|
case 34: // Q
|
||||||
|
return "\x02\x1b\x11";
|
||||||
|
case 35: // R
|
||||||
|
return "\x02\x1b\x12";
|
||||||
|
case 36: // S
|
||||||
|
return "\x02\x1b\x13";
|
||||||
|
case 37: // T
|
||||||
|
return "\x02\x1b\x14";
|
||||||
|
case 38: // U
|
||||||
|
return "\x02\x1b\x15";
|
||||||
|
case 39: // V
|
||||||
|
return "\x02\x1b\x16";
|
||||||
|
case 40: // W
|
||||||
|
return "\x02\x1b\x17";
|
||||||
|
case 41: // X
|
||||||
|
return "\x02\x1b\x18";
|
||||||
|
case 42: // Y
|
||||||
|
return "\x02\x1b\x19";
|
||||||
|
case 43: // Z
|
||||||
|
return "\x02\x1b\x1a";
|
||||||
case 50: // ESCAPE
|
case 50: // ESCAPE
|
||||||
return "\x01\x1b";
|
return "\x01\x1b";
|
||||||
case 51: // ENTER
|
case 51: // ENTER
|
||||||
@@ -1695,6 +1747,7 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
return "\x01\x0d";
|
return "\x01\x0d";
|
||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
} // end switch(mods)
|
} // end switch(mods)
|
||||||
|
break;
|
||||||
|
|
||||||
case 2: // REPEAT
|
case 2: // REPEAT
|
||||||
switch (mods & 0xf) {
|
switch (mods & 0xf) {
|
||||||
@@ -2332,6 +2385,58 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
case 0x6:
|
case 0x6:
|
||||||
switch(key & 0x7f) { default: return NULL;
|
switch(key & 0x7f) { default: return NULL;
|
||||||
|
case 18: // A
|
||||||
|
return "\x02\x1b\x01";
|
||||||
|
case 19: // B
|
||||||
|
return "\x02\x1b\x02";
|
||||||
|
case 20: // C
|
||||||
|
return "\x02\x1b\x03";
|
||||||
|
case 21: // D
|
||||||
|
return "\x02\x1b\x04";
|
||||||
|
case 22: // E
|
||||||
|
return "\x02\x1b\x05";
|
||||||
|
case 23: // F
|
||||||
|
return "\x02\x1b\x06";
|
||||||
|
case 24: // G
|
||||||
|
return "\x02\x1b\x07";
|
||||||
|
case 25: // H
|
||||||
|
return "\x02\x1b\x08";
|
||||||
|
case 26: // I
|
||||||
|
return "\x02\x1b\x09";
|
||||||
|
case 27: // J
|
||||||
|
return "\x02\x1b\x0a";
|
||||||
|
case 28: // K
|
||||||
|
return "\x02\x1b\x0b";
|
||||||
|
case 29: // L
|
||||||
|
return "\x02\x1b\x0c";
|
||||||
|
case 30: // M
|
||||||
|
return "\x02\x1b\x0d";
|
||||||
|
case 31: // N
|
||||||
|
return "\x02\x1b\x0e";
|
||||||
|
case 32: // O
|
||||||
|
return "\x02\x1b\x0f";
|
||||||
|
case 33: // P
|
||||||
|
return "\x02\x1b\x10";
|
||||||
|
case 34: // Q
|
||||||
|
return "\x02\x1b\x11";
|
||||||
|
case 35: // R
|
||||||
|
return "\x02\x1b\x12";
|
||||||
|
case 36: // S
|
||||||
|
return "\x02\x1b\x13";
|
||||||
|
case 37: // T
|
||||||
|
return "\x02\x1b\x14";
|
||||||
|
case 38: // U
|
||||||
|
return "\x02\x1b\x15";
|
||||||
|
case 39: // V
|
||||||
|
return "\x02\x1b\x16";
|
||||||
|
case 40: // W
|
||||||
|
return "\x02\x1b\x17";
|
||||||
|
case 41: // X
|
||||||
|
return "\x02\x1b\x18";
|
||||||
|
case 42: // Y
|
||||||
|
return "\x02\x1b\x19";
|
||||||
|
case 43: // Z
|
||||||
|
return "\x02\x1b\x1a";
|
||||||
case 50: // ESCAPE
|
case 50: // ESCAPE
|
||||||
return "\x01\x1b";
|
return "\x01\x1b";
|
||||||
case 51: // ENTER
|
case 51: // ENTER
|
||||||
@@ -2901,8 +3006,10 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
return "\x01\x0d";
|
return "\x01\x0d";
|
||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
} // end switch(mods)
|
} // end switch(mods)
|
||||||
|
break;
|
||||||
|
|
||||||
} // end switch(action) in mode NORMAL
|
} // end switch(action) in mode NORMAL
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case APPLICATION:
|
case APPLICATION:
|
||||||
@@ -3546,6 +3653,58 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
case 0x6:
|
case 0x6:
|
||||||
switch(key & 0x7f) { default: return NULL;
|
switch(key & 0x7f) { default: return NULL;
|
||||||
|
case 18: // A
|
||||||
|
return "\x02\x1b\x01";
|
||||||
|
case 19: // B
|
||||||
|
return "\x02\x1b\x02";
|
||||||
|
case 20: // C
|
||||||
|
return "\x02\x1b\x03";
|
||||||
|
case 21: // D
|
||||||
|
return "\x02\x1b\x04";
|
||||||
|
case 22: // E
|
||||||
|
return "\x02\x1b\x05";
|
||||||
|
case 23: // F
|
||||||
|
return "\x02\x1b\x06";
|
||||||
|
case 24: // G
|
||||||
|
return "\x02\x1b\x07";
|
||||||
|
case 25: // H
|
||||||
|
return "\x02\x1b\x08";
|
||||||
|
case 26: // I
|
||||||
|
return "\x02\x1b\x09";
|
||||||
|
case 27: // J
|
||||||
|
return "\x02\x1b\x0a";
|
||||||
|
case 28: // K
|
||||||
|
return "\x02\x1b\x0b";
|
||||||
|
case 29: // L
|
||||||
|
return "\x02\x1b\x0c";
|
||||||
|
case 30: // M
|
||||||
|
return "\x02\x1b\x0d";
|
||||||
|
case 31: // N
|
||||||
|
return "\x02\x1b\x0e";
|
||||||
|
case 32: // O
|
||||||
|
return "\x02\x1b\x0f";
|
||||||
|
case 33: // P
|
||||||
|
return "\x02\x1b\x10";
|
||||||
|
case 34: // Q
|
||||||
|
return "\x02\x1b\x11";
|
||||||
|
case 35: // R
|
||||||
|
return "\x02\x1b\x12";
|
||||||
|
case 36: // S
|
||||||
|
return "\x02\x1b\x13";
|
||||||
|
case 37: // T
|
||||||
|
return "\x02\x1b\x14";
|
||||||
|
case 38: // U
|
||||||
|
return "\x02\x1b\x15";
|
||||||
|
case 39: // V
|
||||||
|
return "\x02\x1b\x16";
|
||||||
|
case 40: // W
|
||||||
|
return "\x02\x1b\x17";
|
||||||
|
case 41: // X
|
||||||
|
return "\x02\x1b\x18";
|
||||||
|
case 42: // Y
|
||||||
|
return "\x02\x1b\x19";
|
||||||
|
case 43: // Z
|
||||||
|
return "\x02\x1b\x1a";
|
||||||
case 50: // ESCAPE
|
case 50: // ESCAPE
|
||||||
return "\x01\x1b";
|
return "\x01\x1b";
|
||||||
case 51: // ENTER
|
case 51: // ENTER
|
||||||
@@ -4115,6 +4274,7 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
return "\x01\x0d";
|
return "\x01\x0d";
|
||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
} // end switch(mods)
|
} // end switch(mods)
|
||||||
|
break;
|
||||||
|
|
||||||
case 2: // REPEAT
|
case 2: // REPEAT
|
||||||
switch (mods & 0xf) {
|
switch (mods & 0xf) {
|
||||||
@@ -4752,6 +4912,58 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
case 0x6:
|
case 0x6:
|
||||||
switch(key & 0x7f) { default: return NULL;
|
switch(key & 0x7f) { default: return NULL;
|
||||||
|
case 18: // A
|
||||||
|
return "\x02\x1b\x01";
|
||||||
|
case 19: // B
|
||||||
|
return "\x02\x1b\x02";
|
||||||
|
case 20: // C
|
||||||
|
return "\x02\x1b\x03";
|
||||||
|
case 21: // D
|
||||||
|
return "\x02\x1b\x04";
|
||||||
|
case 22: // E
|
||||||
|
return "\x02\x1b\x05";
|
||||||
|
case 23: // F
|
||||||
|
return "\x02\x1b\x06";
|
||||||
|
case 24: // G
|
||||||
|
return "\x02\x1b\x07";
|
||||||
|
case 25: // H
|
||||||
|
return "\x02\x1b\x08";
|
||||||
|
case 26: // I
|
||||||
|
return "\x02\x1b\x09";
|
||||||
|
case 27: // J
|
||||||
|
return "\x02\x1b\x0a";
|
||||||
|
case 28: // K
|
||||||
|
return "\x02\x1b\x0b";
|
||||||
|
case 29: // L
|
||||||
|
return "\x02\x1b\x0c";
|
||||||
|
case 30: // M
|
||||||
|
return "\x02\x1b\x0d";
|
||||||
|
case 31: // N
|
||||||
|
return "\x02\x1b\x0e";
|
||||||
|
case 32: // O
|
||||||
|
return "\x02\x1b\x0f";
|
||||||
|
case 33: // P
|
||||||
|
return "\x02\x1b\x10";
|
||||||
|
case 34: // Q
|
||||||
|
return "\x02\x1b\x11";
|
||||||
|
case 35: // R
|
||||||
|
return "\x02\x1b\x12";
|
||||||
|
case 36: // S
|
||||||
|
return "\x02\x1b\x13";
|
||||||
|
case 37: // T
|
||||||
|
return "\x02\x1b\x14";
|
||||||
|
case 38: // U
|
||||||
|
return "\x02\x1b\x15";
|
||||||
|
case 39: // V
|
||||||
|
return "\x02\x1b\x16";
|
||||||
|
case 40: // W
|
||||||
|
return "\x02\x1b\x17";
|
||||||
|
case 41: // X
|
||||||
|
return "\x02\x1b\x18";
|
||||||
|
case 42: // Y
|
||||||
|
return "\x02\x1b\x19";
|
||||||
|
case 43: // Z
|
||||||
|
return "\x02\x1b\x1a";
|
||||||
case 50: // ESCAPE
|
case 50: // ESCAPE
|
||||||
return "\x01\x1b";
|
return "\x01\x1b";
|
||||||
case 51: // ENTER
|
case 51: // ENTER
|
||||||
@@ -5321,8 +5533,10 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
return "\x01\x0d";
|
return "\x01\x0d";
|
||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
} // end switch(mods)
|
} // end switch(mods)
|
||||||
|
break;
|
||||||
|
|
||||||
} // end switch(action) in mode APPLICATION
|
} // end switch(action) in mode APPLICATION
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case EXTENDED:
|
case EXTENDED:
|
||||||
@@ -8926,6 +9140,7 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
return "\x05\x4b\x72\x50\x42\x68";
|
return "\x05\x4b\x72\x50\x42\x68";
|
||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
} // end switch(mods)
|
} // end switch(mods)
|
||||||
|
break;
|
||||||
|
|
||||||
case 1: // PRESS
|
case 1: // PRESS
|
||||||
switch (mods & 0xf) {
|
switch (mods & 0xf) {
|
||||||
@@ -12530,6 +12745,7 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
return "\x05\x4b\x70\x50\x42\x68";
|
return "\x05\x4b\x70\x50\x42\x68";
|
||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
} // end switch(mods)
|
} // end switch(mods)
|
||||||
|
break;
|
||||||
|
|
||||||
case 2: // REPEAT
|
case 2: // REPEAT
|
||||||
switch (mods & 0xf) {
|
switch (mods & 0xf) {
|
||||||
@@ -16134,8 +16350,10 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
return "\x05\x4b\x74\x50\x42\x68";
|
return "\x05\x4b\x74\x50\x42\x68";
|
||||||
} // end switch(key)
|
} // end switch(key)
|
||||||
} // end switch(mods)
|
} // end switch(mods)
|
||||||
|
break;
|
||||||
|
|
||||||
} // end switch(action) in mode EXTENDED
|
} // end switch(action) in mode EXTENDED
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ shift_alt_codes = alt_codes.copy()
|
|||||||
shift_alt_codes[defines.GLFW_KEY_TAB] = key_as_bytes('kcbt')
|
shift_alt_codes[defines.GLFW_KEY_TAB] = key_as_bytes('kcbt')
|
||||||
alt_mods = (defines.GLFW_MOD_ALT, defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT)
|
alt_mods = (defines.GLFW_MOD_ALT, defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT)
|
||||||
ctrl_shift_mod = defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL
|
ctrl_shift_mod = defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL
|
||||||
|
ctrl_alt_mod = defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL
|
||||||
|
|
||||||
for kf, kn in {
|
for kf, kn in {
|
||||||
defines.GLFW_KEY_UP: 'kcuu1',
|
defines.GLFW_KEY_UP: 'kcuu1',
|
||||||
@@ -204,6 +205,7 @@ ASCII_C0_SHIFTED = {
|
|||||||
'SLASH': b'\x7f',
|
'SLASH': b'\x7f',
|
||||||
}
|
}
|
||||||
CTRL_SHIFT_KEYS = {getattr(defines, 'GLFW_KEY_' + k): v for k, v in ASCII_C0_SHIFTED.items()}
|
CTRL_SHIFT_KEYS = {getattr(defines, 'GLFW_KEY_' + k): v for k, v in ASCII_C0_SHIFTED.items()}
|
||||||
|
CTRL_ALT_KEYS = {getattr(defines, 'GLFW_KEY_' + k) for k in string.ascii_uppercase}
|
||||||
|
|
||||||
|
|
||||||
def key_to_bytes(key, smkx, extended, mods, action):
|
def key_to_bytes(key, smkx, extended, mods, action):
|
||||||
@@ -222,6 +224,8 @@ def key_to_bytes(key, smkx, extended, mods, action):
|
|||||||
m = UN_SHIFTED_PRINTABLE if mods == defines.GLFW_MOD_ALT else SHIFTED_PRINTABLE
|
m = UN_SHIFTED_PRINTABLE if mods == defines.GLFW_MOD_ALT else SHIFTED_PRINTABLE
|
||||||
data.append(0o33)
|
data.append(0o33)
|
||||||
data.extend(m[key])
|
data.extend(m[key])
|
||||||
|
elif mods == ctrl_alt_mod and key in CTRL_ALT_KEYS:
|
||||||
|
data.append(0x1b), data.extend(control_codes[key])
|
||||||
else:
|
else:
|
||||||
key_map = cursor_key_mode_map[smkx]
|
key_map = cursor_key_mode_map[smkx]
|
||||||
x = key_map.get(key)
|
x = key_map.get(key)
|
||||||
@@ -247,6 +251,10 @@ def get_shortcut(keymap, mods, key, scancode):
|
|||||||
return keymap.get((mods & 0b1111, key))
|
return keymap.get((mods & 0b1111, key))
|
||||||
|
|
||||||
|
|
||||||
|
def shortcut_matches(s, mods, key, scancode):
|
||||||
|
return s[0] & 0b1111 == mods & 0b1111 and s[1] == key
|
||||||
|
|
||||||
|
|
||||||
def generate_key_table():
|
def generate_key_table():
|
||||||
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
||||||
import os
|
import os
|
||||||
@@ -332,13 +340,15 @@ def generate_key_table():
|
|||||||
ind('return NULL;')
|
ind('return NULL;')
|
||||||
i -= 2
|
i -= 2
|
||||||
i -= 1
|
i -= 1
|
||||||
ind('} // end switch(mods)\n')
|
ind('} // end switch(mods)')
|
||||||
|
ind('break;\n')
|
||||||
i -= 1
|
i -= 1
|
||||||
else:
|
else:
|
||||||
ind('return NULL;\n')
|
ind('return NULL;\n')
|
||||||
i -= 1
|
i -= 1
|
||||||
i -= 1
|
i -= 1
|
||||||
ind('}} // end switch(action) in mode {}\n\n'.format(mode))
|
ind('}} // end switch(action) in mode {}'.format(mode))
|
||||||
|
ind('break;\n\n')
|
||||||
i -= 1
|
i -= 1
|
||||||
i -= 1
|
i -= 1
|
||||||
ind('}')
|
ind('}')
|
||||||
|
|||||||
466
kitty/kitty.conf
466
kitty/kitty.conf
@@ -1,5 +1,11 @@
|
|||||||
# vim:fileencoding=utf-8:ft=conf
|
# vim:fileencoding=utf-8:ft=conf
|
||||||
|
|
||||||
|
# You can include secondary config files via the "include" directive.
|
||||||
|
# If you use a relative path for include, it is resolved with respect to the
|
||||||
|
# location od the current config file. For example:
|
||||||
|
# include other.conf
|
||||||
|
|
||||||
|
# Fonts {{{
|
||||||
# Font family. You can also specify different fonts for the
|
# Font family. You can also specify different fonts for the
|
||||||
# bold/italic/bold-italic variants. By default they are derived automatically,
|
# bold/italic/bold-italic variants. By default they are derived automatically,
|
||||||
# by the OSes font system. Setting them manually is useful for font families
|
# by the OSes font system. Setting them manually is useful for font families
|
||||||
@@ -11,6 +17,7 @@
|
|||||||
#
|
#
|
||||||
# You can get a list of full family names available on your computer by running
|
# You can get a list of full family names available on your computer by running
|
||||||
# kitty list-fonts
|
# kitty list-fonts
|
||||||
|
# The default values shown below rely on your OS to choose an appropriate monospace font family.
|
||||||
font_family monospace
|
font_family monospace
|
||||||
italic_font auto
|
italic_font auto
|
||||||
bold_font auto
|
bold_font auto
|
||||||
@@ -32,12 +39,189 @@ font_size_delta 2
|
|||||||
adjust_line_height 0
|
adjust_line_height 0
|
||||||
adjust_column_width 0
|
adjust_column_width 0
|
||||||
|
|
||||||
|
# Symbol mapping (special font for specified unicode code points). Map the
|
||||||
|
# specified unicode codepoints to a particular font. Useful if you need special
|
||||||
|
# rendering for some symbols, such as for Powerline. Avoids the need for
|
||||||
|
# patched fonts. Each unicode code point is specified in the form U+<code point
|
||||||
|
# in hexadecimal>. You can specify multiple code points, separated by commas
|
||||||
|
# and ranges separated by hyphens. symbol_map itself can be specified multiple times.
|
||||||
|
# Syntax is:
|
||||||
|
#
|
||||||
|
# symbol_map codepoints Font Family Name
|
||||||
|
#
|
||||||
|
# For example:
|
||||||
|
#
|
||||||
|
# symbol_map U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols
|
||||||
|
|
||||||
# Change the sizes of the lines used for the box drawing unicode characters
|
# Change the sizes of the lines used for the box drawing unicode characters
|
||||||
# These values are in pts. They will be scaled by the monitor DPI to arrive at
|
# These values are in pts. They will be scaled by the monitor DPI to arrive at
|
||||||
# a pixel value. There must be four values corresponding to thin, normal, thick,
|
# a pixel value. There must be four values corresponding to thin, normal, thick,
|
||||||
# and very thick lines;
|
# and very thick lines;
|
||||||
box_drawing_scale 0.001, 1, 1.5, 2
|
box_drawing_scale 0.001, 1, 1.5, 2
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Cursor customization {{{
|
||||||
|
# The cursor color
|
||||||
|
cursor #cccccc
|
||||||
|
|
||||||
|
# The cursor shape can be one of (block, beam, underline)
|
||||||
|
cursor_shape block
|
||||||
|
|
||||||
|
# The interval (in seconds) at which to blink the cursor. Set to zero to
|
||||||
|
# disable blinking. Note that numbers smaller than repaint_delay will be
|
||||||
|
# limited to repaint_delay.
|
||||||
|
cursor_blink_interval 0.5
|
||||||
|
|
||||||
|
# Stop blinking cursor after the specified number of seconds of keyboard inactivity. Set to
|
||||||
|
# zero to never stop blinking.
|
||||||
|
cursor_stop_blinking_after 15.0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Scrollback {{{
|
||||||
|
# Number of lines of history to keep in memory for scrolling back
|
||||||
|
scrollback_lines 2000
|
||||||
|
|
||||||
|
# Program with which to view scrollback in a new window. The scrollback buffer is passed as
|
||||||
|
# STDIN to this program. If you change it, make sure the program you use can
|
||||||
|
# handle ANSI escape sequences for colors and text formatting.
|
||||||
|
scrollback_pager less +G -R
|
||||||
|
|
||||||
|
# Wheel scroll multiplier (modify the amount scrolled by the mouse wheel). Use negative
|
||||||
|
# numbers to change scroll direction.
|
||||||
|
wheel_scroll_multiplier 5.0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Mouse {{{
|
||||||
|
# The color and style for highlighting URLs on mouse-over. url_style can be one of:
|
||||||
|
# none, single, double, curly
|
||||||
|
url_color #0087BD
|
||||||
|
url_style curly
|
||||||
|
|
||||||
|
# The modifier keys to press when clicking with the mouse on URLs to open the URL
|
||||||
|
open_url_modifiers 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Fade the text in inactive windows by the specified amount (a number between
|
||||||
|
# zero and one, with 0 being fully faded).
|
||||||
|
inactive_text_alpha 1.0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Tab bar {{{
|
||||||
|
# Which edge to show the tab bar on, top or bottom
|
||||||
|
tab_bar_edge bottom
|
||||||
|
|
||||||
|
# The separator between tabs in the tab bar
|
||||||
|
tab_separator " ┇"
|
||||||
|
|
||||||
|
# Tab bar colors and styles
|
||||||
|
active_tab_foreground #000
|
||||||
|
active_tab_background #eee
|
||||||
|
active_tab_font_style bold-italic
|
||||||
|
inactive_tab_foreground #444
|
||||||
|
inactive_tab_background #999
|
||||||
|
inactive_tab_font_style normal
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Color scheme {{{
|
||||||
# The foreground color
|
# The foreground color
|
||||||
foreground #dddddd
|
foreground #dddddd
|
||||||
|
|
||||||
@@ -62,155 +246,9 @@ selection_foreground #000000
|
|||||||
# The background for selections
|
# The background for selections
|
||||||
selection_background #FFFACD
|
selection_background #FFFACD
|
||||||
|
|
||||||
# The color and style for highlighting URLs on mouse-over. url_style can be one of:
|
|
||||||
# none, single, double, curly
|
|
||||||
url_color #0087BD
|
|
||||||
url_style curly
|
|
||||||
|
|
||||||
# The cursor color
|
|
||||||
cursor #cccccc
|
|
||||||
|
|
||||||
# The cursor shape can be one of (block, beam, underline)
|
|
||||||
cursor_shape block
|
|
||||||
|
|
||||||
# The interval (in seconds) at which to blink the cursor. Set to zero to
|
|
||||||
# disable blinking. Note that numbers smaller than repaint_delay will be
|
|
||||||
# limited to repaint_delay.
|
|
||||||
cursor_blink_interval 0.5
|
|
||||||
|
|
||||||
# Stop blinking cursor after the specified number of seconds of keyboard inactivity. Set to
|
|
||||||
# zero to never stop blinking.
|
|
||||||
cursor_stop_blinking_after 15.0
|
|
||||||
|
|
||||||
# Number of lines of history to keep in memory for scrolling back
|
|
||||||
scrollback_lines 2000
|
|
||||||
|
|
||||||
# Program with which to view scrollback in a new window. The scrollback buffer is passed as
|
|
||||||
# STDIN to this program. If you change it, make sure the program you use can
|
|
||||||
# handle ANSI escape sequences for colors and text formatting.
|
|
||||||
scrollback_pager less +G -R
|
|
||||||
|
|
||||||
# Wheel scroll multiplier (modify the amount scrolled by the mouse wheel). Use negative
|
|
||||||
# numbers to change scroll direction.
|
|
||||||
wheel_scroll_multiplier 5.0
|
|
||||||
|
|
||||||
# The interval between successive clicks to detect double/triple clicks (in seconds)
|
|
||||||
click_interval 0.5
|
|
||||||
|
|
||||||
# Characters considered part of a word when double clicking. In addition to these characters
|
|
||||||
# any character that is marked as an alpha-numeric character in the unicode
|
|
||||||
# database will be matched.
|
|
||||||
select_by_word_characters :@-./_~?&=%+#
|
|
||||||
|
|
||||||
# The shell program to execute. The default value of . means
|
|
||||||
# to use whatever shell is set as the default shell for the current user.
|
|
||||||
shell .
|
|
||||||
|
|
||||||
# Hide mouse cursor after the specified number of seconds of the mouse not being used. Set to
|
|
||||||
# zero to disable mouse cursor hiding.
|
|
||||||
mouse_hide_wait 3.0
|
|
||||||
|
|
||||||
# Set the active window to the window under the mouse when moving the mouse around
|
|
||||||
focus_follows_mouse no
|
|
||||||
|
|
||||||
# The enabled window layouts. A comma separated list of layout names. The special value * means
|
|
||||||
# all layouts. The first listed layout will be used as the startup layout.
|
|
||||||
# For a list of available layouts, see the file layouts.py
|
|
||||||
enabled_layouts *
|
|
||||||
|
|
||||||
# If enabled, the window size will be remembered so that new instances of kitty will have the same
|
|
||||||
# size as the previous instance. If disabled, the window will initially have size configured
|
|
||||||
# by initial_window_width/height, in pixels.
|
|
||||||
remember_window_size yes
|
|
||||||
initial_window_width 640
|
|
||||||
initial_window_height 400
|
|
||||||
|
|
||||||
# Delay (in milliseconds) between screen updates. Decreasing it, increases
|
|
||||||
# frames-per-second (FPS) at the cost of more CPU usage. The default value
|
|
||||||
# yields ~100 FPS which is more than sufficient for most uses. Note that to
|
|
||||||
# actually achieve 100FPS you have to either set sync_to_monitor to no or use a
|
|
||||||
# monitor with a high refresh rate.
|
|
||||||
repaint_delay 10
|
|
||||||
|
|
||||||
# Delay (in milliseconds) before input from the program running in the terminal
|
|
||||||
# is processed. Note that decreasing it will increase responsiveness, but also
|
|
||||||
# increase CPU usage and might cause flicker in full screen programs that
|
|
||||||
# redraw the entire screen on each loop, because kitty is so fast that partial
|
|
||||||
# screen updates will be drawn.
|
|
||||||
input_delay 3
|
|
||||||
|
|
||||||
# Sync screen updates to the refresh rate of the monitor. This prevents
|
|
||||||
# tearing (https://en.wikipedia.org/wiki/Screen_tearing) when scrolling. However,
|
|
||||||
# it limits the rendering speed to the refresh rate of your monitor. With a
|
|
||||||
# very high speed mouse/high keyboard repeat rate, you may notice some slight input latency.
|
|
||||||
# If so, set this to no.
|
|
||||||
sync_to_monitor yes
|
|
||||||
|
|
||||||
# Visual bell duration. Flash the screen when a bell occurs for the specified number of
|
|
||||||
# seconds. Set to zero to disable.
|
|
||||||
visual_bell_duration 0.0
|
|
||||||
|
|
||||||
# Enable/disable the audio bell. Useful in environments that require silence.
|
|
||||||
enable_audio_bell yes
|
|
||||||
|
|
||||||
# The modifier keys to press when clicking with the mouse on URLs to open the URL
|
|
||||||
open_url_modifiers ctrl+shift
|
|
||||||
|
|
||||||
# The program with which to open URLs that are clicked on. The special value "default" means to
|
|
||||||
# use the operating system's default URL handler.
|
|
||||||
open_url_with default
|
|
||||||
|
|
||||||
# The modifiers to use rectangular selection (i.e. to select text in a
|
|
||||||
# rectangular block with the mouse)
|
|
||||||
rectangle_select_modifiers ctrl+alt
|
|
||||||
|
|
||||||
|
|
||||||
# Allow other programs to control kitty. If you turn this on other programs can
|
|
||||||
# control all aspects of kitty, including sending text to kitty windows,
|
|
||||||
# opening new windows, closing windows, reading the content of windows, etc.
|
|
||||||
# Note that this even works over ssh connections.
|
|
||||||
allow_remote_control no
|
|
||||||
|
|
||||||
# The value of the TERM environment variable to set
|
|
||||||
term xterm-kitty
|
|
||||||
|
|
||||||
# The width (in pts) of window borders. Will be rounded to the nearest number of pixels based on screen resolution.
|
|
||||||
# Note that borders are displayed only when more than one window is visible. They are meant to separate multiple windows.
|
|
||||||
window_border_width 1
|
|
||||||
|
|
||||||
# The window margin (in pts) (blank area outside the border)
|
|
||||||
window_margin_width 0
|
|
||||||
|
|
||||||
# The window padding (in pts) (blank area between the text and the window border)
|
|
||||||
window_padding_width 0
|
|
||||||
|
|
||||||
# The color for the border of the active window
|
|
||||||
active_border_color #00ff00
|
|
||||||
|
|
||||||
# The color for the border of inactive windows
|
|
||||||
inactive_border_color #cccccc
|
|
||||||
|
|
||||||
# Fade the text in inactive windows by the specified amount (a number between
|
|
||||||
# zero and one, with 0 being fully faded).
|
|
||||||
inactive_text_alpha 1.0
|
|
||||||
|
|
||||||
# Which edge to show the tab bar on, top or bottom
|
|
||||||
tab_bar_edge bottom
|
|
||||||
|
|
||||||
# The separator between tabs in the tab bar
|
|
||||||
tab_separator " ┇"
|
|
||||||
|
|
||||||
# Tab bar colors and styles
|
|
||||||
active_tab_foreground #000
|
|
||||||
active_tab_background #eee
|
|
||||||
active_tab_font_style bold-italic
|
|
||||||
inactive_tab_foreground #444
|
|
||||||
inactive_tab_background #999
|
|
||||||
inactive_tab_font_style normal
|
|
||||||
|
|
||||||
|
|
||||||
# The 16 terminal colors. There are 8 basic colors, each color has a dull and
|
# The 16 terminal colors. There are 8 basic colors, each color has a dull and
|
||||||
# bright version.
|
# bright version. You can also set the remaining colors from the 256 color table
|
||||||
|
# as color16 to color256.
|
||||||
|
|
||||||
# black
|
# black
|
||||||
color0 #000000
|
color0 #000000
|
||||||
@@ -243,9 +281,42 @@ color14 #14ffff
|
|||||||
# white
|
# white
|
||||||
color7 #dddddd
|
color7 #dddddd
|
||||||
color15 #ffffff
|
color15 #ffffff
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
# Key mapping
|
# Advanced {{{
|
||||||
|
|
||||||
|
# The shell program to execute. The default value of . means
|
||||||
|
# to use whatever shell is set as the default shell for the current user.
|
||||||
|
# Note that on macOS if you change this, you might need to add --login to
|
||||||
|
# ensure that the shell starts in interactive mode and reads its startup rc files.
|
||||||
|
shell .
|
||||||
|
|
||||||
|
# Close the window when the child process (shell) exits. If no (the default),
|
||||||
|
# the terminal will remain open when the child exits as long as there are still
|
||||||
|
# processes outputting to the terminal (for example disowned or backgrounded
|
||||||
|
# processes). If yes, the window will close as soon as the child process exits.
|
||||||
|
# Note that setting it to yes means that any background processes still using
|
||||||
|
# the terminal can fail silently because their stdout/stderr/stdin no longer
|
||||||
|
# work.
|
||||||
|
close_on_child_death no
|
||||||
|
|
||||||
|
# Allow other programs to control kitty. If you turn this on other programs can
|
||||||
|
# control all aspects of kitty, including sending text to kitty windows,
|
||||||
|
# opening new windows, closing windows, reading the content of windows, etc.
|
||||||
|
# Note that this even works over ssh connections.
|
||||||
|
allow_remote_control no
|
||||||
|
|
||||||
|
# The value of the TERM environment variable to set. Changing this can break
|
||||||
|
# many terminal programs, only change it if you know what you are doing, not
|
||||||
|
# because you read some advice on Stack Overflow to change it.
|
||||||
|
term xterm-kitty
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# Keyboard shortcuts {{{
|
||||||
# For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html
|
# For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html
|
||||||
# For a list of modifier names, see: http://www.glfw.org/docs/latest/group__mods.html
|
# For a list of modifier names, see: http://www.glfw.org/docs/latest/group__mods.html
|
||||||
#
|
#
|
||||||
@@ -258,8 +329,14 @@ color15 #ffffff
|
|||||||
# For example:
|
# For example:
|
||||||
# map ctrl+shift+e combine : new_window : next_layout
|
# map ctrl+shift+e combine : new_window : next_layout
|
||||||
# this will create a new window and switch to the next available layout
|
# this will create a new window and switch to the next available layout
|
||||||
|
#
|
||||||
|
# You can use multi-key shortcuts using the syntax shown below:
|
||||||
|
# map key1>key2>key3 action
|
||||||
|
# For example:
|
||||||
|
# map ctrl+f>2 set_font_size 20
|
||||||
|
# this will change the font size to 20 points when you press ctrl+f and then 2
|
||||||
|
|
||||||
# Clipboard
|
# Clipboard {{{
|
||||||
map ctrl+shift+v paste_from_clipboard
|
map ctrl+shift+v paste_from_clipboard
|
||||||
map ctrl+shift+s paste_from_selection
|
map ctrl+shift+s paste_from_selection
|
||||||
map ctrl+shift+c copy_to_clipboard
|
map ctrl+shift+c copy_to_clipboard
|
||||||
@@ -269,8 +346,9 @@ map shift+insert paste_from_selection
|
|||||||
# you can specify your own, for example:
|
# you can specify your own, for example:
|
||||||
# map ctrl+shift+o pass_selection_to_program firefox
|
# map ctrl+shift+o pass_selection_to_program firefox
|
||||||
map ctrl+shift+o pass_selection_to_program
|
map ctrl+shift+o pass_selection_to_program
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Scrolling
|
# Scrolling {{{
|
||||||
map ctrl+shift+up scroll_line_up
|
map ctrl+shift+up scroll_line_up
|
||||||
map ctrl+shift+down scroll_line_down
|
map ctrl+shift+down scroll_line_down
|
||||||
map ctrl+shift+k scroll_line_up
|
map ctrl+shift+k scroll_line_up
|
||||||
@@ -280,8 +358,9 @@ map ctrl+shift+page_down scroll_page_down
|
|||||||
map ctrl+shift+home scroll_home
|
map ctrl+shift+home scroll_home
|
||||||
map ctrl+shift+end scroll_end
|
map ctrl+shift+end scroll_end
|
||||||
map ctrl+shift+h show_scrollback
|
map ctrl+shift+h show_scrollback
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Window management
|
# Window management {{{
|
||||||
map ctrl+shift+enter new_window
|
map ctrl+shift+enter new_window
|
||||||
map ctrl+shift+n new_os_window
|
map ctrl+shift+n new_os_window
|
||||||
map ctrl+shift+w close_window
|
map ctrl+shift+w close_window
|
||||||
@@ -315,9 +394,9 @@ map ctrl+shift+0 tenth_window
|
|||||||
# You can open a new window with the current working directory set to the
|
# You can open a new window with the current working directory set to the
|
||||||
# working directory of the current window using
|
# working directory of the current window using
|
||||||
# map ctrl+alt+enter new_window_with_cwd
|
# map ctrl+alt+enter new_window_with_cwd
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Tab management {{{
|
||||||
# Tab management
|
|
||||||
map ctrl+shift+right next_tab
|
map ctrl+shift+right next_tab
|
||||||
map ctrl+shift+left previous_tab
|
map ctrl+shift+left previous_tab
|
||||||
map ctrl+shift+t new_tab
|
map ctrl+shift+t new_tab
|
||||||
@@ -325,25 +404,63 @@ map ctrl+shift+q close_tab
|
|||||||
map ctrl+shift+l next_layout
|
map ctrl+shift+l next_layout
|
||||||
map ctrl+shift+. move_tab_forward
|
map ctrl+shift+. move_tab_forward
|
||||||
map ctrl+shift+, move_tab_backward
|
map ctrl+shift+, move_tab_backward
|
||||||
|
map ctrl+shift+alt+t set_tab_title
|
||||||
# You can also create shortcuts to go to specific tabs, with 1 being the first tab
|
# You can also create shortcuts to go to specific tabs, with 1 being the first tab
|
||||||
# map ctrl+alt+1 goto_tab 1
|
# map ctrl+alt+1 goto_tab 1
|
||||||
# map ctrl+alt+2 goto_tab 2
|
# map ctrl+alt+2 goto_tab 2
|
||||||
|
|
||||||
# Just as with new_window above, you can also pass the name of arbitrary
|
# Just as with new_window above, you can also pass the name of arbitrary
|
||||||
# commands to run when using new_tab and use new_tab_with_cwd.
|
# commands to run when using new_tab and use new_tab_with_cwd.
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Layout management {{{
|
||||||
|
# You can create shortcuts to switch to specific layouts
|
||||||
|
# map ctrl+alt+1 goto_layout tall
|
||||||
|
# map ctrl+alt+2 goto_layout stack
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Miscellaneous
|
# Font sizes {{{
|
||||||
map ctrl+shift+equal increase_font_size
|
map ctrl+shift+equal increase_font_size
|
||||||
map ctrl+shift+minus decrease_font_size
|
map ctrl+shift+minus decrease_font_size
|
||||||
map ctrl+shift+backspace restore_font_size
|
map ctrl+shift+backspace restore_font_size
|
||||||
|
# To setup shortcuts for specific font sizes, follow the example below:
|
||||||
|
# map ctrl+shift+f6 set_font_size 10.0
|
||||||
|
# map ctrl+shift+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 ctrl+shift+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 ctrl+shift+p>f run_kitten text hints --type path --program -
|
||||||
|
|
||||||
|
# Select a path/filename and open it with the default open program.
|
||||||
|
map ctrl+shift+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 ctrl+shift+p>l run_kitten text hints --type line --program -
|
||||||
|
|
||||||
|
# Select words and insert into terminal.
|
||||||
|
map ctrl+shift+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 ctrl+shift+f11 toggle_fullscreen
|
map ctrl+shift+f11 toggle_fullscreen
|
||||||
map ctrl+shift+u input_unicode_character
|
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.
|
map ctrl+shift+f2 edit_config_file
|
||||||
# You can customize how the URLs are detected and opened by specifying command line options to
|
You can customize how the URLs are
|
||||||
# url_hints. For example:
|
# Open the kitty shell in a new window/tab/overlay/os_window to control kitty using commands.
|
||||||
# map ctrl+shift+e run_simple_kitten text url_hints --program firefox --regex "http://[^ ]+"
|
map ctrl+shift+escape kitty_shell window
|
||||||
map ctrl+shift+e run_simple_kitten text url_hints
|
|
||||||
|
|
||||||
# Sending arbitrary text on shortcut key presses
|
# Sending arbitrary text on shortcut key presses
|
||||||
# You can tell kitty to send arbitrary (UTF-8) encoded text to
|
# You can tell kitty to send arbitrary (UTF-8) encoded text to
|
||||||
@@ -361,30 +478,12 @@ map ctrl+shift+e run_simple_kitten text url_hints
|
|||||||
# to the start of the line (same as pressing the Home key):
|
# to the start of the line (same as pressing the Home key):
|
||||||
# map ctrl+alt+a send_text normal Word\x1b[H
|
# map ctrl+alt+a send_text normal Word\x1b[H
|
||||||
# map ctrl+alt+a send_text application Word\x1bOH
|
# map ctrl+alt+a send_text application Word\x1bOH
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Symbol mapping (special font for specified unicode code points). Map the
|
# }}}
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
# OS specific tweaks
|
# OS specific tweaks {{{
|
||||||
|
|
||||||
# Copy to clipboard on select. With this enabled, simply selecting text with
|
|
||||||
# the mouse will cause the text to be copied to clipboard. Useful on platforms
|
|
||||||
# such as macOS/Wayland that do not have the concept of primary selections. Note
|
|
||||||
# that this is a security risk, as all programs, including websites open in your
|
|
||||||
# browser can read the contents of the clipboard.
|
|
||||||
copy_on_select no
|
|
||||||
|
|
||||||
# Change the color of the kitty window's titlebar on macOS. A value of "system"
|
# Change the color of the kitty window's titlebar on macOS. A value of "system"
|
||||||
# means to use the default system color, a value of "background" means to use
|
# means to use the default system color, a value of "background" means to use
|
||||||
@@ -400,7 +499,4 @@ macos_hide_titlebar no
|
|||||||
# break any Alt+key keyboard shortcuts in your terminal programs, but you
|
# break any Alt+key keyboard shortcuts in your terminal programs, but you
|
||||||
# can use the macOS unicode input technique.
|
# can use the macOS unicode input technique.
|
||||||
macos_option_as_alt yes
|
macos_option_as_alt yes
|
||||||
|
# }}}
|
||||||
# The number is a percentage of maximum volume.
|
|
||||||
# See man XBell for details.
|
|
||||||
x11_bell_volume 80
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ linebuf_mark_line_clean(LineBuf *self, index_type y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
clear(LineBuf *self) {
|
clear(LineBuf *self, PyObject *a UNUSED) {
|
||||||
#define clear_doc "Clear all lines in this LineBuf"
|
#define clear_doc "Clear all lines in this LineBuf"
|
||||||
linebuf_clear(self, BLANK_CHAR);
|
linebuf_clear(self, BLANK_CHAR);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -167,7 +167,7 @@ set_continued(LineBuf *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
dirty_lines(LineBuf *self) {
|
dirty_lines(LineBuf *self, PyObject *a UNUSED) {
|
||||||
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
||||||
PyObject *ans = PyList_New(0);
|
PyObject *ans = PyList_New(0);
|
||||||
for (index_type i = 0; i < self->ynum; i++) {
|
for (index_type i = 0; i < self->ynum; i++) {
|
||||||
@@ -424,7 +424,7 @@ __str__(LineBuf *self) {
|
|||||||
if (lines == NULL) return PyErr_NoMemory();
|
if (lines == NULL) return PyErr_NoMemory();
|
||||||
for (index_type i = 0; i < self->ynum; i++) {
|
for (index_type i = 0; i < self->ynum; i++) {
|
||||||
init_line(self, self->line, self->line_map[i]);
|
init_line(self, self->line, self->line_map[i]);
|
||||||
PyObject *t = PyObject_Str((PyObject*)self->line);
|
PyObject *t = line_as_unicode(self->line);
|
||||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||||
PyTuple_SET_ITEM(lines, i, t);
|
PyTuple_SET_ITEM(lines, i, t);
|
||||||
}
|
}
|
||||||
|
|||||||
12
kitty/line.c
12
kitty/line.c
@@ -199,8 +199,8 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
|
|||||||
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
PyObject *
|
||||||
as_unicode(Line* self) {
|
line_as_unicode(Line* self) {
|
||||||
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0);
|
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
as_ansi(Line* self) {
|
as_ansi(Line* self, PyObject *a UNUSED) {
|
||||||
#define as_ansi_doc "Return the line's contents with ANSI (SGR) escape codes for formatting"
|
#define as_ansi_doc "Return the line's contents with ANSI (SGR) escape codes for formatting"
|
||||||
static Py_UCS4 t[5120] = {0};
|
static Py_UCS4 t[5120] = {0};
|
||||||
index_type num = line_as_ansi(self, t, 5120);
|
index_type num = line_as_ansi(self, t, 5120);
|
||||||
@@ -272,7 +272,7 @@ as_ansi(Line* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
is_continued(Line* self) {
|
is_continued(Line* self, PyObject *a UNUSED) {
|
||||||
#define is_continued_doc "Return the line's continued flag"
|
#define is_continued_doc "Return the line's continued flag"
|
||||||
PyObject *ans = self->continued ? Py_True : Py_False;
|
PyObject *ans = self->continued ? Py_True : Py_False;
|
||||||
Py_INCREF(ans);
|
Py_INCREF(ans);
|
||||||
@@ -281,7 +281,7 @@ is_continued(Line* self) {
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
__repr__(Line* self) {
|
__repr__(Line* self) {
|
||||||
PyObject *s = as_unicode(self);
|
PyObject *s = line_as_unicode(self);
|
||||||
if (s == NULL) return NULL;
|
if (s == NULL) return NULL;
|
||||||
PyObject *ans = PyObject_Repr(s);
|
PyObject *ans = PyObject_Repr(s);
|
||||||
Py_CLEAR(s);
|
Py_CLEAR(s);
|
||||||
@@ -570,7 +570,7 @@ PyTypeObject Line_Type = {
|
|||||||
.tp_basicsize = sizeof(Line),
|
.tp_basicsize = sizeof(Line),
|
||||||
.tp_dealloc = (destructor)dealloc,
|
.tp_dealloc = (destructor)dealloc,
|
||||||
.tp_repr = (reprfunc)__repr__,
|
.tp_repr = (reprfunc)__repr__,
|
||||||
.tp_str = (reprfunc)as_unicode,
|
.tp_str = (reprfunc)line_as_unicode,
|
||||||
.tp_as_sequence = &sequence_methods,
|
.tp_as_sequence = &sequence_methods,
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
.tp_richcompare = richcmp,
|
.tp_richcompare = richcmp,
|
||||||
|
|||||||
@@ -57,10 +57,10 @@ unsigned int line_length(Line *self);
|
|||||||
size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type);
|
size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type);
|
||||||
size_t cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type);
|
size_t cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type);
|
||||||
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
||||||
|
PyObject* line_as_unicode(Line *);
|
||||||
|
|
||||||
void linebuf_init_line(LineBuf *, index_type);
|
void linebuf_init_line(LineBuf *, index_type);
|
||||||
void linebuf_clear(LineBuf *, char_type ch);
|
void linebuf_clear(LineBuf *, char_type ch);
|
||||||
void linebuf_init_line(LineBuf *, index_type);
|
|
||||||
void linebuf_index(LineBuf* self, index_type top, index_type bottom);
|
void linebuf_index(LineBuf* self, index_type top, index_type bottom);
|
||||||
void linebuf_reverse_index(LineBuf *self, index_type top, index_type bottom);
|
void linebuf_reverse_index(LineBuf *self, index_type top, index_type bottom);
|
||||||
void linebuf_clear_line(LineBuf *self, index_type y);
|
void linebuf_clear_line(LineBuf *self, index_type y);
|
||||||
@@ -105,7 +105,7 @@ void historybuf_clear(HistoryBuf *self);
|
|||||||
index_type num = line_as_ansi(line, buf, columns * 100 - 2); \
|
index_type num = line_as_ansi(line, buf, columns * 100 - 2); \
|
||||||
t = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, num); \
|
t = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, num); \
|
||||||
} else { \
|
} else { \
|
||||||
t = PyObject_Str((PyObject*)line); \
|
t = line_as_unicode(line); \
|
||||||
} \
|
} \
|
||||||
if (t == NULL) goto end; \
|
if (t == NULL) goto end; \
|
||||||
ret = PyObject_CallFunctionObjArgs(callback, t, NULL); \
|
ret = PyObject_CallFunctionObjArgs(callback, t, NULL); \
|
||||||
|
|||||||
73
kitty/logging.c
Normal file
73
kitty/logging.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* logging.c
|
||||||
|
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "data-types.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <os/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static bool use_os_log = false;
|
||||||
|
|
||||||
|
void
|
||||||
|
log_error(const char *fmt, ...) {
|
||||||
|
va_list ar;
|
||||||
|
struct timeval tv;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Apple does not provide a varargs style os_logv
|
||||||
|
char logbuf[16 * 1024] = {0};
|
||||||
|
#else
|
||||||
|
char logbuf[4];
|
||||||
|
#endif
|
||||||
|
char *p = logbuf;
|
||||||
|
#define bufprint(func, ...) { if ((size_t)(p - logbuf) < sizeof(logbuf) - 2) { p += func(p, sizeof(logbuf) - (p - logbuf), __VA_ARGS__); } }
|
||||||
|
if (!use_os_log) { // Apple's os_log already records timestamps
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
struct tm *tmp = localtime(&tv.tv_sec);
|
||||||
|
if (tmp) {
|
||||||
|
char tbuf[256] = {0}, buf[256] = {0};
|
||||||
|
if (strftime(buf, sizeof(buf), "%j %H:%M:%S.%%06u", tmp) != 0) {
|
||||||
|
snprintf(tbuf, sizeof(tbuf), buf, tv.tv_usec);
|
||||||
|
fprintf(stderr, "[%s] ", tbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
va_start(ar, fmt);
|
||||||
|
if (use_os_log) { bufprint(vsnprintf, fmt, ar); }
|
||||||
|
else vfprintf(stderr, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (use_os_log) os_log(OS_LOG_DEFAULT, "%{public}s", logbuf);
|
||||||
|
#endif
|
||||||
|
if (!use_os_log) fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
log_error_string(PyObject *self UNUSED, PyObject *args) {
|
||||||
|
const char *msg;
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &msg)) return NULL;
|
||||||
|
log_error("%s", msg);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
METHODB(log_error_string, METH_VARARGS),
|
||||||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
init_logging(PyObject *module) {
|
||||||
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (getenv("KITTY_LAUNCHED_BY_LAUNCH_SERVICES") != NULL) use_os_log = true;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
import signal
|
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
@@ -12,14 +11,14 @@ from .borders import load_borders_program
|
|||||||
from .boss import Boss
|
from .boss import Boss
|
||||||
from .cli import create_opts, parse_args
|
from .cli import create_opts, parse_args
|
||||||
from .config import cached_values_for, initial_window_size
|
from .config import cached_values_for, initial_window_size
|
||||||
from .constants import appname, glfw_path, is_macos, is_wayland, logo_data_file
|
from .constants import appname, glfw_path, is_macos, is_wayland, logo_data_file, config_dir
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
create_os_window, glfw_init, glfw_terminate, install_sigchld_handler,
|
create_os_window, glfw_init, glfw_terminate, set_default_window_icon,
|
||||||
set_default_window_icon, set_options, show_window
|
set_options, show_window
|
||||||
)
|
)
|
||||||
from .fonts.box_drawing import set_scale
|
from .fonts.box_drawing import set_scale
|
||||||
from .utils import (
|
from .utils import (
|
||||||
detach, end_startup_notification, init_startup_notification,
|
detach, end_startup_notification, init_startup_notification, log_error,
|
||||||
single_instance
|
single_instance
|
||||||
)
|
)
|
||||||
from .window import load_shader_programs
|
from .window import load_shader_programs
|
||||||
@@ -39,7 +38,7 @@ def init_graphics():
|
|||||||
|
|
||||||
def run_app(opts, args):
|
def run_app(opts, args):
|
||||||
set_scale(opts.box_drawing_scale)
|
set_scale(opts.box_drawing_scale)
|
||||||
set_options(opts, is_wayland, args.debug_gl)
|
set_options(opts, is_wayland, args.debug_gl, args.debug_font_fallback)
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from .fast_data_types import macos_change_titlebar_color
|
from .fast_data_types import macos_change_titlebar_color
|
||||||
macos_change_titlebar_color(opts.macos_titlebar_color)
|
macos_change_titlebar_color(opts.macos_titlebar_color)
|
||||||
@@ -93,7 +92,21 @@ def setup_profiling(args):
|
|||||||
print('To view the graphical call data, use: kcachegrind', cg)
|
print('To view the graphical call data, use: kcachegrind', cg)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def macos_cmdline():
|
||||||
|
try:
|
||||||
|
with open(os.path.join(config_dir, 'macos-launch-services-cmdline')) as f:
|
||||||
|
raw = f.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
return []
|
||||||
|
import shlex
|
||||||
|
raw = raw.strip()
|
||||||
|
ans = shlex.split(raw)
|
||||||
|
if ans and ans[0] == 'kitty':
|
||||||
|
del ans[0]
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def _main():
|
||||||
try:
|
try:
|
||||||
sys.setswitchinterval(1000.0) # we have only a single python thread
|
sys.setswitchinterval(1000.0) # we have only a single python thread
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -124,12 +137,21 @@ def main():
|
|||||||
if rpath and rpath not in items:
|
if rpath and rpath not in items:
|
||||||
os.environ['PATH'] += os.pathsep + rpath
|
os.environ['PATH'] += os.pathsep + rpath
|
||||||
|
|
||||||
if os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1':
|
args = sys.argv[1:]
|
||||||
|
if is_macos and os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1':
|
||||||
os.chdir(os.path.expanduser('~'))
|
os.chdir(os.path.expanduser('~'))
|
||||||
if not os.path.isdir(os.getcwd()):
|
args = macos_cmdline()
|
||||||
|
try:
|
||||||
|
cwd_ok = os.path.isdir(os.getcwd())
|
||||||
|
except Exception:
|
||||||
|
cwd_ok = False
|
||||||
|
if not cwd_ok:
|
||||||
os.chdir(os.path.expanduser('~'))
|
os.chdir(os.path.expanduser('~'))
|
||||||
args, rest = parse_args()
|
args, rest = parse_args(args=args)
|
||||||
args.args = rest
|
args.args = rest
|
||||||
|
if args.debug_config:
|
||||||
|
create_opts(args, debug_config=True)
|
||||||
|
return
|
||||||
if getattr(args, 'detach', False):
|
if getattr(args, 'detach', False):
|
||||||
detach()
|
detach()
|
||||||
if args.cmd:
|
if args.cmd:
|
||||||
@@ -143,7 +165,9 @@ def main():
|
|||||||
is_first = single_instance(args.instance_group)
|
is_first = single_instance(args.instance_group)
|
||||||
if not is_first:
|
if not is_first:
|
||||||
import json
|
import json
|
||||||
data = {'cmd': 'new_instance', 'args': tuple(sys.argv), 'startup_id': os.environ.get('DESKTOP_STARTUP_ID')}
|
data = {'cmd': 'new_instance', 'args': tuple(sys.argv),
|
||||||
|
'startup_id': os.environ.get('DESKTOP_STARTUP_ID'),
|
||||||
|
'cwd': os.getcwd()}
|
||||||
data = json.dumps(data, ensure_ascii=False).encode('utf-8')
|
data = json.dumps(data, ensure_ascii=False).encode('utf-8')
|
||||||
single_instance.socket.sendall(data)
|
single_instance.socket.sendall(data)
|
||||||
return
|
return
|
||||||
@@ -152,8 +176,16 @@ def main():
|
|||||||
try:
|
try:
|
||||||
with setup_profiling(args):
|
with setup_profiling(args):
|
||||||
# Avoid needing to launch threads to reap zombies
|
# Avoid needing to launch threads to reap zombies
|
||||||
install_sigchld_handler()
|
|
||||||
run_app(opts, args)
|
run_app(opts, args)
|
||||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
|
||||||
finally:
|
finally:
|
||||||
glfw_terminate()
|
glfw_terminate()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
_main()
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
tb = traceback.format_exc()
|
||||||
|
log_error(tb)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|||||||
@@ -16,12 +16,14 @@
|
|||||||
static MouseShape mouse_cursor_shape = BEAM;
|
static MouseShape mouse_cursor_shape = BEAM;
|
||||||
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
||||||
|
|
||||||
|
// Encoding of mouse events {{{
|
||||||
#define SHIFT_INDICATOR (1 << 2)
|
#define SHIFT_INDICATOR (1 << 2)
|
||||||
#define ALT_INDICATOR (1 << 3)
|
#define ALT_INDICATOR (1 << 3)
|
||||||
#define CONTROL_INDICATOR (1 << 4)
|
#define CONTROL_INDICATOR (1 << 4)
|
||||||
#define MOTION_INDICATOR (1 << 5)
|
#define MOTION_INDICATOR (1 << 5)
|
||||||
#define EXTRA_BUTTON_INDICATOR (1 << 6)
|
#define EXTRA_BUTTON_INDICATOR (1 << 6)
|
||||||
|
|
||||||
|
|
||||||
static inline unsigned int
|
static inline unsigned int
|
||||||
button_map(int button) {
|
button_map(int button) {
|
||||||
switch(button) {
|
switch(button) {
|
||||||
@@ -89,24 +91,53 @@ encode_mouse_event(Window *w, int button, MouseAction action, int mods) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
static inline double
|
||||||
|
window_left(Window *w) {
|
||||||
|
return w->geometry.left - OPT(window_padding_width) * (global_state.logical_dpi_x / 72.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double
|
||||||
|
window_right(Window *w) {
|
||||||
|
return w->geometry.right + OPT(window_padding_width) * (global_state.logical_dpi_x / 72.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double
|
||||||
|
window_top(Window *w) {
|
||||||
|
return w->geometry.top - OPT(window_padding_width) * (global_state.logical_dpi_y / 72.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double
|
||||||
|
window_bottom(Window *w) {
|
||||||
|
return w->geometry.bottom + OPT(window_padding_width) * (global_state.logical_dpi_y / 72.0);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
contains_mouse(Window *w) {
|
contains_mouse(Window *w) {
|
||||||
WindowGeometry *g = &w->geometry;
|
|
||||||
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
|
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
|
||||||
return (w->visible && g->left <= x && x <= g->right && g->top <= y && y <= g->bottom);
|
return (w->visible && window_left(w) <= x && x <= window_right(w) && window_top(w) <= y && y <= window_bottom(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
cell_for_pos(Window *w, unsigned int *x, unsigned int *y) {
|
cell_for_pos(Window *w, unsigned int *x, unsigned int *y) {
|
||||||
WindowGeometry *g = &w->geometry;
|
WindowGeometry *g = &w->geometry;
|
||||||
unsigned int qx = (unsigned int)((double)(global_state.callback_os_window->mouse_x - g->left) / global_state.cell_width);
|
|
||||||
unsigned int qy = (unsigned int)((double)(global_state.callback_os_window->mouse_y - g->top) / global_state.cell_height);
|
|
||||||
bool ret = false;
|
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
if (screen && qx <= screen->columns && qy <= screen->lines) {
|
if (!screen) return false;
|
||||||
*x = qx; *y = qy; ret = true;
|
unsigned int qx = 0, qy = 0;
|
||||||
|
double mouse_x = global_state.callback_os_window->mouse_x;
|
||||||
|
double mouse_y = global_state.callback_os_window->mouse_y;
|
||||||
|
double left = window_left(w), top = window_top(w), right = window_right(w), bottom = window_bottom(w);
|
||||||
|
if (mouse_x < left || mouse_y < top || mouse_x > right || mouse_y > bottom) return false;
|
||||||
|
if (mouse_x >= g->right) qx = screen->columns - 1;
|
||||||
|
else if (mouse_x >= g->left) qx = (unsigned int)((double)(mouse_x - g->left) / global_state.cell_width);
|
||||||
|
if (mouse_y >= g->bottom) qy = screen->lines - 1;
|
||||||
|
else if (mouse_y >= g->top) qy = (unsigned int)((double)(mouse_y - g->top) / global_state.cell_height);
|
||||||
|
if (qx < screen->columns && qy < screen->lines) {
|
||||||
|
*x = qx; *y = qy;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return ret;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HANDLER(name) static inline void name(Window UNUSED *w, int UNUSED button, int UNUSED modifiers, unsigned int UNUSED window_idx)
|
#define HANDLER(name) static inline void name(Window UNUSED *w, int UNUSED button, int UNUSED modifiers, unsigned int UNUSED window_idx)
|
||||||
@@ -116,10 +147,9 @@ update_drag(bool from_button, Window *w, bool is_release, int modifiers) {
|
|||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
if (from_button) {
|
if (from_button) {
|
||||||
if (is_release) screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, true);
|
if (is_release) screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, true);
|
||||||
else screen_start_selection(screen, w->mouse_cell_x, w->mouse_cell_y, modifiers == (int)OPT(rectangle_select_modifiers));
|
else screen_start_selection(screen, w->mouse_cell_x, w->mouse_cell_y, modifiers == (int)OPT(rectangle_select_modifiers) || modifiers == ((int)OPT(rectangle_select_modifiers) | GLFW_MOD_SHIFT), EXTEND_CELL);
|
||||||
} else if (screen->selection.in_progress) {
|
} else if (screen->selection.in_progress) {
|
||||||
screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, false);
|
screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, false);
|
||||||
call_boss(set_primary_selection, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,9 +157,10 @@ update_drag(bool from_button, Window *w, bool is_release, int modifiers) {
|
|||||||
bool
|
bool
|
||||||
drag_scroll(Window *w, OSWindow *frame) {
|
drag_scroll(Window *w, OSWindow *frame) {
|
||||||
unsigned int margin = global_state.cell_height / 2;
|
unsigned int margin = global_state.cell_height / 2;
|
||||||
|
double left = window_left(w), top = window_top(w), right = window_right(w), bottom = window_bottom(w);
|
||||||
double x = frame->mouse_x, y = frame->mouse_y;
|
double x = frame->mouse_x, y = frame->mouse_y;
|
||||||
if (y < w->geometry.top || y > w->geometry.bottom) return false;
|
if (y < top || y > bottom) return false;
|
||||||
if (x < w->geometry.left || x > w->geometry.right) return false;
|
if (x < left || x > right) return false;
|
||||||
bool upwards = y <= (w->geometry.top + margin);
|
bool upwards = y <= (w->geometry.top + margin);
|
||||||
if (upwards || y >= w->geometry.bottom - margin) {
|
if (upwards || y >= w->geometry.bottom - margin) {
|
||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
@@ -147,6 +178,14 @@ drag_scroll(Window *w, OSWindow *frame) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
extend_selection(Window *w) {
|
||||||
|
Screen *screen = w->render_data.screen;
|
||||||
|
if (screen_has_selection(screen)) {
|
||||||
|
screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
extend_url(Screen *screen, Line *line, index_type *x, index_type *y) {
|
extend_url(Screen *screen, Line *line, index_type *x, index_type *y) {
|
||||||
unsigned int count = 0;
|
unsigned int count = 0;
|
||||||
@@ -220,20 +259,23 @@ multi_click(Window *w, unsigned int count) {
|
|||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
index_type start, end;
|
index_type start, end;
|
||||||
bool found_selection = false;
|
bool found_selection = false;
|
||||||
|
SelectionExtendMode mode = EXTEND_CELL;
|
||||||
|
unsigned int y1 = w->mouse_cell_y, y2 = w->mouse_cell_y;
|
||||||
switch(count) {
|
switch(count) {
|
||||||
case 2:
|
case 2:
|
||||||
found_selection = screen_selection_range_for_word(screen, w->mouse_cell_x, w->mouse_cell_y, &start, &end);
|
found_selection = screen_selection_range_for_word(screen, w->mouse_cell_x, &y1, &y2, &start, &end);
|
||||||
|
mode = EXTEND_WORD;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
found_selection = screen_selection_range_for_line(screen, w->mouse_cell_y, &start, &end);
|
found_selection = screen_selection_range_for_line(screen, w->mouse_cell_y, &start, &end);
|
||||||
|
mode = EXTEND_LINE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (found_selection) {
|
if (found_selection) {
|
||||||
screen_start_selection(screen, start, w->mouse_cell_y, false);
|
screen_start_selection(screen, start, y1, false, mode);
|
||||||
screen_update_selection(screen, end, w->mouse_cell_y, true);
|
screen_update_selection(screen, end, y2, false);
|
||||||
call_boss(set_primary_selection, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +311,7 @@ HANDLER(handle_button_event) {
|
|||||||
Screen *screen = w->render_data.screen;
|
Screen *screen = w->render_data.screen;
|
||||||
if (!screen) return;
|
if (!screen) return;
|
||||||
bool handle_in_kitty = (
|
bool handle_in_kitty = (
|
||||||
modifiers == GLFW_MOD_SHIFT ||
|
modifiers == GLFW_MOD_SHIFT || modifiers == ((int)OPT(rectangle_select_modifiers) | GLFW_MOD_SHIFT) ||
|
||||||
screen->modes.mouse_tracking_mode == 0 ||
|
screen->modes.mouse_tracking_mode == 0 ||
|
||||||
button == GLFW_MOUSE_BUTTON_MIDDLE ||
|
button == GLFW_MOUSE_BUTTON_MIDDLE ||
|
||||||
(modifiers == (int)OPT(open_url_modifiers) && button == GLFW_MOUSE_BUTTON_LEFT)
|
(modifiers == (int)OPT(open_url_modifiers) && button == GLFW_MOUSE_BUTTON_LEFT)
|
||||||
@@ -279,16 +321,15 @@ HANDLER(handle_button_event) {
|
|||||||
case GLFW_MOUSE_BUTTON_LEFT:
|
case GLFW_MOUSE_BUTTON_LEFT:
|
||||||
update_drag(true, w, is_release, modifiers);
|
update_drag(true, w, is_release, modifiers);
|
||||||
if (is_release) {
|
if (is_release) {
|
||||||
if (modifiers == (int)OPT(open_url_modifiers)) {
|
if (modifiers == (int)OPT(open_url_modifiers)) open_url(w);
|
||||||
open_url(w);
|
} else add_click(w, button, modifiers, window_idx);
|
||||||
} else {
|
|
||||||
if (is_release) add_click(w, button, modifiers, window_idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case GLFW_MOUSE_BUTTON_MIDDLE:
|
case GLFW_MOUSE_BUTTON_MIDDLE:
|
||||||
if (is_release && !modifiers) { call_boss(paste_from_selection, NULL); return; }
|
if (is_release && !modifiers) { call_boss(paste_from_selection, NULL); return; }
|
||||||
break;
|
break;
|
||||||
|
case GLFW_MOUSE_BUTTON_RIGHT:
|
||||||
|
if (is_release) { extend_selection(w); }
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int sz = encode_mouse_event(w, button, is_release ? RELEASE : PRESS, modifiers);
|
int sz = encode_mouse_event(w, button, is_release ? RELEASE : PRESS, modifiers);
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ _report_params(PyObject *dump_callback, const char *name, unsigned int *params,
|
|||||||
|
|
||||||
#define DUMP_UNUSED UNUSED
|
#define DUMP_UNUSED UNUSED
|
||||||
|
|
||||||
#define REPORT_ERROR(...) fprintf(stderr, "%s ", ERROR_PREFIX); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");
|
#define REPORT_ERROR(...) log_error(ERROR_PREFIX " " __VA_ARGS__);
|
||||||
|
|
||||||
#define REPORT_COMMAND(...)
|
#define REPORT_COMMAND(...)
|
||||||
#define REPORT_VA_COMMAND(...)
|
#define REPORT_VA_COMMAND(...)
|
||||||
@@ -310,7 +310,7 @@ dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
|||||||
}
|
}
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
code = utoi(screen->parser_buf, i);
|
code = utoi(screen->parser_buf, i);
|
||||||
if (i < limit - 1 && screen->parser_buf[i] == ';') i++;
|
if (i < limit && screen->parser_buf[i] == ';') i++;
|
||||||
}
|
}
|
||||||
PyObject *string = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, screen->parser_buf + i, limit - i);
|
PyObject *string = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, screen->parser_buf + i, limit - i);
|
||||||
if (string != NULL) {
|
if (string != NULL) {
|
||||||
|
|||||||
@@ -4,394 +4,15 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from .cli import emph, parse_args
|
from .cli import emph, parse_args
|
||||||
from .config import parse_send_text_bytes
|
|
||||||
from .constants import appname, version
|
from .constants import appname, version
|
||||||
from .tabs import SpecialWindow
|
from .utils import parse_address_spec, read_with_timeout
|
||||||
from .utils import non_blocking_read, read_with_timeout
|
from .cmds import cmap, parse_subcommand_cli
|
||||||
|
|
||||||
|
|
||||||
def cmd(short_desc, desc=None, options_spec=None, no_response=False):
|
|
||||||
|
|
||||||
def w(func):
|
|
||||||
func.short_desc = short_desc
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def parse_subcommand_cli(func, args):
|
|
||||||
opts, items = parse_args(args[1:], (func.options_spec or '\n').format, '...', func.desc, '{} @ {}'.format(appname, func.name))
|
|
||||||
return opts, items
|
|
||||||
|
|
||||||
|
|
||||||
@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)
|
|
||||||
)
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
You can use the |_ ls| command to get a list of windows. Note that for
|
|
||||||
numeric fields such as id and pid the expression is interpreted as a number,
|
|
||||||
not a regular expression.
|
|
||||||
'''
|
|
||||||
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.
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
@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
|
|
||||||
)
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@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
|
|
||||||
)
|
|
||||||
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 ValueError('No matching windows for expression: {}'.format(match))
|
|
||||||
for window in windows:
|
|
||||||
if window:
|
|
||||||
window.set_title(payload['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
|
|
||||||
)
|
|
||||||
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 ValueError('No matching windows for expression: {}'.format(match))
|
|
||||||
else:
|
|
||||||
tabs = [boss.tab_for_window(window) if window else boss.active_tab]
|
|
||||||
for tab in tabs:
|
|
||||||
if tab:
|
|
||||||
tab.set_title(payload['title'])
|
|
||||||
|
|
||||||
|
|
||||||
@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.
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
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 ValueError('No matching windows for expression: {}'.format(match))
|
|
||||||
else:
|
|
||||||
windows = [window if window and payload['self'] else boss.active_window]
|
|
||||||
for window in windows:
|
|
||||||
if window:
|
|
||||||
boss.close_window(window)
|
|
||||||
|
|
||||||
|
|
||||||
@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.
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
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 ValueError('No matching windows for expression: {}'.format(match))
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@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.
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
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 ValueError('No matching windows for expression: {}'.format(match))
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@cmd(
|
|
||||||
'Focus the specified window',
|
|
||||||
options_spec=MATCH_WINDOW_OPTION
|
|
||||||
)
|
|
||||||
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 ValueError('No matching windows for expression: {}'.format(match))
|
|
||||||
for window in windows:
|
|
||||||
if window:
|
|
||||||
boss.set_active_window(window)
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
@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.
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
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 ValueError('No matching windows for expression: {}'.format(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
|
|
||||||
|
|
||||||
|
|
||||||
cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')}
|
|
||||||
|
|
||||||
|
|
||||||
def handle_cmd(boss, window, cmd):
|
def handle_cmd(boss, window, cmd):
|
||||||
@@ -411,21 +32,30 @@ def handle_cmd(boss, window, cmd):
|
|||||||
|
|
||||||
|
|
||||||
global_options_spec = partial('''\
|
global_options_spec = partial('''\
|
||||||
|
--to
|
||||||
|
An address for the kitty instance to control. Corresponds to the address
|
||||||
|
given to the kitty instance via the --listen-on option. If not specified,
|
||||||
|
messages are sent to the controlling terminal for this process, i.e. they
|
||||||
|
will only work if this process is run within an existing kitty window.
|
||||||
'''.format, appname=appname)
|
'''.format, appname=appname)
|
||||||
|
|
||||||
|
|
||||||
def read_from_stdin(send, no_response):
|
def do_io(to, send, no_response):
|
||||||
send = ('@kitty-cmd' + json.dumps(send)).encode('ascii')
|
send = ('@kitty-cmd' + json.dumps(send)).encode('ascii')
|
||||||
out = sys.stdout if sys.stdout.isatty() else sys.stderr
|
send = b'\x1bP' + send + b'\x1b\\'
|
||||||
if not out.isatty():
|
if to:
|
||||||
raise SystemExit('Neither stdout nor stderr is a terminal')
|
family, address = parse_address_spec(to)[:2]
|
||||||
out.buffer.write(b'\x1bP' + send + b'\x1b\\')
|
s = socket.socket(family)
|
||||||
out.flush()
|
s.connect(address)
|
||||||
|
out = s.makefile('wb')
|
||||||
|
else:
|
||||||
|
out = open('/dev/tty', 'wb')
|
||||||
|
with out:
|
||||||
|
out.write(send)
|
||||||
|
if to:
|
||||||
|
s.shutdown(socket.SHUT_WR)
|
||||||
if no_response:
|
if no_response:
|
||||||
return {'ok': True}
|
return {'ok': True}
|
||||||
if not sys.stdin.isatty():
|
|
||||||
raise SystemExit('stdin is not a terminal')
|
|
||||||
|
|
||||||
received = b''
|
received = b''
|
||||||
dcs = re.compile(br'\x1bP@kitty-cmd([^\x1b]+)\x1b\\')
|
dcs = re.compile(br'\x1bP@kitty-cmd([^\x1b]+)\x1b\\')
|
||||||
@@ -437,7 +67,12 @@ def read_from_stdin(send, no_response):
|
|||||||
match = dcs.search(received)
|
match = dcs.search(received)
|
||||||
return match is None
|
return match is None
|
||||||
|
|
||||||
read_with_timeout(more_needed)
|
if to:
|
||||||
|
src = s.makefile('rb')
|
||||||
|
else:
|
||||||
|
src = open('/dev/tty', 'rb')
|
||||||
|
with src:
|
||||||
|
read_with_timeout(more_needed, src=src)
|
||||||
if match is None:
|
if match is None:
|
||||||
raise SystemExit('Failed to receive response from ' + appname)
|
raise SystemExit('Failed to receive response from ' + appname)
|
||||||
response = json.loads(match.group(1).decode('ascii'))
|
response = json.loads(match.group(1).decode('ascii'))
|
||||||
@@ -458,7 +93,9 @@ def main(args):
|
|||||||
global_opts, items = parse_args(args[1:], global_options_spec, 'command ...', msg, '{} @'.format(appname))
|
global_opts, items = parse_args(args[1:], global_options_spec, 'command ...', msg, '{} @'.format(appname))
|
||||||
|
|
||||||
if not items:
|
if not items:
|
||||||
raise SystemExit('You must specify a command')
|
from kitty.shell import main
|
||||||
|
main(global_opts)
|
||||||
|
return
|
||||||
cmd = items[0]
|
cmd = items[0]
|
||||||
try:
|
try:
|
||||||
func = cmap[cmd]
|
func = cmap[cmd]
|
||||||
@@ -474,11 +111,11 @@ def main(args):
|
|||||||
if func.no_response and isinstance(payload, types.GeneratorType):
|
if func.no_response and isinstance(payload, types.GeneratorType):
|
||||||
for item in payload:
|
for item in payload:
|
||||||
send['payload'] = item
|
send['payload'] = item
|
||||||
read_from_stdin(send, func.no_response)
|
do_io(global_opts.to, send, func.no_response)
|
||||||
return
|
return
|
||||||
if payload is not None:
|
if payload is not None:
|
||||||
send['payload'] = payload
|
send['payload'] = payload
|
||||||
response = read_from_stdin(send, func.no_response)
|
response = do_io(global_opts.to, send, func.no_response)
|
||||||
if not response.get('ok'):
|
if not response.get('ok'):
|
||||||
if response.get('tb'):
|
if response.get('tb'):
|
||||||
print(response['tb'], file=sys.stderr)
|
print(response['tb'], file=sys.stderr)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf
|
|||||||
copy_range(src->line, src_x, dest->line, dest_x, num);
|
copy_range(src->line, src_x, dest->line, dest_x, num);
|
||||||
if (is_tracked_line && src_x <= *track_x && *track_x < src_x + num) {
|
if (is_tracked_line && src_x <= *track_x && *track_x < src_x + num) {
|
||||||
*track_y = dest_y;
|
*track_y = dest_y;
|
||||||
*track_x = dest_x + (*track_x - src_x);
|
*track_x = dest_x + (*track_x - src_x + 1);
|
||||||
}
|
}
|
||||||
src_x += num; dest_x += num;
|
src_x += num; dest_x += num;
|
||||||
}
|
}
|
||||||
|
|||||||
8
kitty/rgb.py
generated
8
kitty/rgb.py
generated
@@ -27,6 +27,14 @@ def parse_rgb(spec):
|
|||||||
return Color(*map(parse_single_color, colors))
|
return Color(*map(parse_single_color, colors))
|
||||||
|
|
||||||
|
|
||||||
|
def color_from_int(x):
|
||||||
|
return Color((x >> 16) & 255, (x >> 8) & 255, x & 255)
|
||||||
|
|
||||||
|
|
||||||
|
def color_as_int(x):
|
||||||
|
return x.red << 16 | x.green << 8 | x.blue
|
||||||
|
|
||||||
|
|
||||||
def to_color(raw, validate=False):
|
def to_color(raw, validate=False):
|
||||||
# See man XParseColor
|
# See man XParseColor
|
||||||
x = raw.strip().lower()
|
x = raw.strip().lower()
|
||||||
|
|||||||
124
kitty/screen.c
124
kitty/screen.c
@@ -219,7 +219,7 @@ screen_change_scrollback_size(Screen *self, unsigned int size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
reset_callbacks(Screen *self) {
|
reset_callbacks(Screen *self, PyObject *a UNUSED) {
|
||||||
Py_CLEAR(self->callbacks);
|
Py_CLEAR(self->callbacks);
|
||||||
self->callbacks = Py_None;
|
self->callbacks = Py_None;
|
||||||
Py_INCREF(self->callbacks);
|
Py_INCREF(self->callbacks);
|
||||||
@@ -561,7 +561,7 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) {
|
|||||||
default:
|
default:
|
||||||
private = mode >= 1 << 5;
|
private = mode >= 1 << 5;
|
||||||
if (private) mode >>= 5;
|
if (private) mode >>= 5;
|
||||||
fprintf(stderr, "%s %s %u %s\n", ERROR_PREFIX, "Unsupported screen mode: ", mode, private ? "(private)" : "");
|
log_error("%s %s %u %s", ERROR_PREFIX, "Unsupported screen mode: ", mode, private ? "(private)" : "");
|
||||||
}
|
}
|
||||||
#undef SIMPLE_MODE
|
#undef SIMPLE_MODE
|
||||||
#undef MOUSE_MODE
|
#undef MOUSE_MODE
|
||||||
@@ -648,7 +648,7 @@ screen_clear_tab_stop(Screen *self, unsigned int how) {
|
|||||||
for (unsigned int i = 0; i < self->columns; i++) self->tabstops[i] = false;
|
for (unsigned int i = 0; i < self->columns; i++) self->tabstops[i] = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "%s %s %u\n", ERROR_PREFIX, "Unsupported clear tab stop mode: ", how);
|
log_error("%s %s %u", ERROR_PREFIX, "Unsupported clear tab stop mode: ", how);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1066,7 +1066,7 @@ bool
|
|||||||
screen_invert_colors(Screen *self) {
|
screen_invert_colors(Screen *self) {
|
||||||
bool inverted = false;
|
bool inverted = false;
|
||||||
if (self->start_visual_bell_at > 0) {
|
if (self->start_visual_bell_at > 0) {
|
||||||
if (monotonic() - self->start_visual_bell_at <= global_state.opts.visual_bell_duration) inverted = true;
|
if (monotonic() - self->start_visual_bell_at <= OPT(visual_bell_duration)) inverted = true;
|
||||||
else self->start_visual_bell_at = 0;
|
else self->start_visual_bell_at = 0;
|
||||||
}
|
}
|
||||||
if (self->modes.mDECSCNM) inverted = inverted ? false : true;
|
if (self->modes.mDECSCNM) inverted = inverted ? false : true;
|
||||||
@@ -1076,6 +1076,7 @@ screen_invert_colors(Screen *self) {
|
|||||||
void
|
void
|
||||||
screen_bell(Screen *self) {
|
screen_bell(Screen *self) {
|
||||||
request_window_attention(self->window_id, OPT(enable_audio_bell));
|
request_window_attention(self->window_id, OPT(enable_audio_bell));
|
||||||
|
if (OPT(visual_bell_duration) > 0.0f) self->start_visual_bell_at = monotonic();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1397,13 +1398,20 @@ apply_selection(Screen *self, uint8_t *data, SelectionBoundary *start, Selection
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
screen_has_selection(Screen *self) {
|
||||||
|
SelectionBoundary start, end;
|
||||||
|
selection_limits_(selection, &start, &end);
|
||||||
|
return !is_selection_empty(self, start.x, start.y, end.x, end.y);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_apply_selection(Screen *self, void *address, size_t size) {
|
screen_apply_selection(Screen *self, void *address, size_t size) {
|
||||||
memset(address, 0, size);
|
memset(address, 0, size);
|
||||||
self->last_selection_scrolled_by = self->scrolled_by;
|
self->last_selection_scrolled_by = self->scrolled_by;
|
||||||
self->selection_updated_once = true;
|
self->selection_updated_once = true;
|
||||||
selection_limits_(selection, &self->last_rendered_selection_start, &self->last_rendered_selection_end);
|
selection_limits_(selection, &self->last_rendered_selection_start, &self->last_rendered_selection_end);
|
||||||
apply_selection(self, address, &self->last_rendered_selection_start, &self->last_rendered_selection_end, 1, self->rectangle_select);
|
apply_selection(self, address, &self->last_rendered_selection_start, &self->last_rendered_selection_end, 1, self->selection.rectangle_select);
|
||||||
selection_limits_(url_range, &self->last_rendered_url_start, &self->last_rendered_url_end);
|
selection_limits_(url_range, &self->last_rendered_url_start, &self->last_rendered_url_end);
|
||||||
apply_selection(self, address, &self->last_rendered_url_start, &self->last_rendered_url_end, 2, false);
|
apply_selection(self, address, &self->last_rendered_url_start, &self->last_rendered_url_end, 2, false);
|
||||||
}
|
}
|
||||||
@@ -1444,8 +1452,8 @@ screen_open_url(Screen *self) {
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Python interface {{{
|
// Python interface {{{
|
||||||
#define WRAP0(name) static PyObject* name(Screen *self) { screen_##name(self); Py_RETURN_NONE; }
|
#define WRAP0(name) static PyObject* name(Screen *self, PyObject *a UNUSED) { screen_##name(self); Py_RETURN_NONE; }
|
||||||
#define WRAP0x(name) static PyObject* xxx_##name(Screen *self) { screen_##name(self); Py_RETURN_NONE; }
|
#define WRAP0x(name) static PyObject* xxx_##name(Screen *self, PyObject *a UNUSED) { screen_##name(self); Py_RETURN_NONE; }
|
||||||
#define WRAP1(name, defval) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; if(!PyArg_ParseTuple(args, "|I", &v)) return NULL; screen_##name(self, v); Py_RETURN_NONE; }
|
#define WRAP1(name, defval) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; if(!PyArg_ParseTuple(args, "|I", &v)) return NULL; screen_##name(self, v); Py_RETURN_NONE; }
|
||||||
#define WRAP1B(name, defval) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; int b=false; if(!PyArg_ParseTuple(args, "|Ip", &v, &b)) return NULL; screen_##name(self, v, b); Py_RETURN_NONE; }
|
#define WRAP1B(name, defval) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; int b=false; if(!PyArg_ParseTuple(args, "|Ip", &v, &b)) return NULL; screen_##name(self, v, b); Py_RETURN_NONE; }
|
||||||
#define WRAP1E(name, defval, ...) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; if(!PyArg_ParseTuple(args, "|I", &v)) return NULL; screen_##name(self, v, __VA_ARGS__); Py_RETURN_NONE; }
|
#define WRAP1E(name, defval, ...) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; if(!PyArg_ParseTuple(args, "|I", &v)) return NULL; screen_##name(self, v, __VA_ARGS__); Py_RETURN_NONE; }
|
||||||
@@ -1458,7 +1466,7 @@ as_text(Screen *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
refresh_sprite_positions(Screen *self) {
|
refresh_sprite_positions(Screen *self, PyObject *a UNUSED) {
|
||||||
self->is_dirty = true;
|
self->is_dirty = true;
|
||||||
for (index_type i = 0; i < self->lines; i++) {
|
for (index_type i = 0; i < self->lines; i++) {
|
||||||
linebuf_mark_line_dirty(self->main_linebuf, i);
|
linebuf_mark_line_dirty(self->main_linebuf, i);
|
||||||
@@ -1572,13 +1580,13 @@ set_mode(Screen *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
reset_dirty(Screen *self) {
|
reset_dirty(Screen *self, PyObject *a UNUSED) {
|
||||||
screen_reset_dirty(self);
|
screen_reset_dirty(self);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
is_using_alternate_linebuf(Screen *self) {
|
is_using_alternate_linebuf(Screen *self, PyObject *a UNUSED) {
|
||||||
if (self->linebuf == self->alt_linebuf) Py_RETURN_TRUE;
|
if (self->linebuf == self->alt_linebuf) Py_RETURN_TRUE;
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
@@ -1619,9 +1627,17 @@ WRAP0(carriage_return)
|
|||||||
WRAP2(resize, 1, 1)
|
WRAP2(resize, 1, 1)
|
||||||
WRAP2(set_margins, 1, 1)
|
WRAP2(set_margins, 1, 1)
|
||||||
WRAP2(rescale_images, 1, 1)
|
WRAP2(rescale_images, 1, 1)
|
||||||
WRAP2B(start_selection)
|
|
||||||
WRAP2B(update_selection)
|
WRAP2B(update_selection)
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
start_selection(Screen *self, PyObject *args) {
|
||||||
|
unsigned int x, y;
|
||||||
|
int rectangle_select = 0, extend_mode = EXTEND_CELL;
|
||||||
|
if (!PyArg_ParseTuple(args, "II|pp", &x, &y, &rectangle_select, &extend_mode)) return NULL;
|
||||||
|
screen_start_selection(self, x, y, rectangle_select, extend_mode);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
change_scrollback_size(Screen *self, PyObject *args) {
|
change_scrollback_size(Screen *self, PyObject *args) {
|
||||||
unsigned int count = 1;
|
unsigned int count = 1;
|
||||||
@@ -1631,12 +1647,12 @@ change_scrollback_size(Screen *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
text_for_selection(Screen *self) {
|
text_for_selection(Screen *self, PyObject *a UNUSED) {
|
||||||
FullSelectionBoundary start, end;
|
FullSelectionBoundary start, end;
|
||||||
full_selection_limits_(selection, &start, &end);
|
full_selection_limits_(selection, &start, &end);
|
||||||
PyObject *ans = NULL;
|
PyObject *ans = NULL;
|
||||||
if (start.y == end.y && start.x == end.x) ans = PyTuple_New(0);
|
if (start.y == end.y && start.x == end.x) ans = PyTuple_New(0);
|
||||||
else text_for_range(ans, start, end, self->rectangle_select, true, range_line_, int);
|
else text_for_range(ans, start, end, self->selection.rectangle_select, true, range_line_, int);
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1660,17 +1676,31 @@ is_opt_word_char(char_type ch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_type *s, index_type *e) {
|
screen_selection_range_for_word(Screen *self, index_type x, index_type *y1, index_type *y2, index_type *s, index_type *e) {
|
||||||
if (y >= self->lines || x >= self->columns) return false;
|
if (*y1 >= self->lines || x >= self->columns) return false;
|
||||||
index_type start, end;
|
index_type start, end;
|
||||||
Line *line = visual_line_(self, y);
|
Line *line = visual_line_(self, *y1);
|
||||||
|
*y2 = *y1;
|
||||||
#define is_ok(x) (is_word_char((line->cells[x].ch)) || is_opt_word_char(line->cells[x].ch))
|
#define is_ok(x) (is_word_char((line->cells[x].ch)) || is_opt_word_char(line->cells[x].ch))
|
||||||
if (!is_ok(x)) {
|
if (!is_ok(x)) {
|
||||||
start = x; end = x + 1;
|
start = x; end = x + 1;
|
||||||
} else {
|
} else {
|
||||||
start = x, end = x;
|
start = x, end = x;
|
||||||
while(start > 0 && is_ok(start - 1)) start--;
|
while(true) {
|
||||||
while(end < self->columns - 1 && is_ok(end + 1)) end++;
|
while(start > 0 && is_ok(start - 1)) start--;
|
||||||
|
if (start > 0 || !line->continued || *y1 == 0) break;
|
||||||
|
line = visual_line_(self, *y1 - 1);
|
||||||
|
if (!is_ok(self->columns - 1)) break;
|
||||||
|
(*y1)--; start = self->columns - 1;
|
||||||
|
}
|
||||||
|
line = visual_line_(self, *y2);
|
||||||
|
while(true) {
|
||||||
|
while(end < self->columns - 1 && is_ok(end + 1)) end++;
|
||||||
|
if (end < self->columns - 1 || *y2 >= self->lines - 1) break;
|
||||||
|
line = visual_line_(self, *y2 + 1);
|
||||||
|
if (!line->continued || !is_ok(0)) break;
|
||||||
|
(*y2)++; end = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*s = start; *e = end;
|
*s = start; *e = end;
|
||||||
return true;
|
return true;
|
||||||
@@ -1726,10 +1756,10 @@ screen_is_selection_dirty(Screen *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_start_selection(Screen *self, index_type x, index_type y, bool rectangle_select) {
|
screen_start_selection(Screen *self, index_type x, index_type y, bool rectangle_select, SelectionExtendMode extend_mode) {
|
||||||
self->rectangle_select = rectangle_select;
|
|
||||||
#define A(attr, val) self->selection.attr = val;
|
#define A(attr, val) self->selection.attr = val;
|
||||||
A(start_x, x); A(end_x, x); A(start_y, y); A(end_y, y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by); A(in_progress, true);
|
A(start_x, x); A(end_x, x); A(start_y, y); A(end_y, y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by);
|
||||||
|
A(in_progress, true); A(rectangle_select, rectangle_select); A(extend_mode, extend_mode);
|
||||||
#undef A
|
#undef A
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1744,29 +1774,73 @@ void
|
|||||||
screen_update_selection(Screen *self, index_type x, index_type y, bool ended) {
|
screen_update_selection(Screen *self, index_type x, index_type y, bool ended) {
|
||||||
self->selection.end_x = x; self->selection.end_y = y; self->selection.end_scrolled_by = self->scrolled_by;
|
self->selection.end_x = x; self->selection.end_y = y; self->selection.end_scrolled_by = self->scrolled_by;
|
||||||
if (ended) self->selection.in_progress = false;
|
if (ended) self->selection.in_progress = false;
|
||||||
|
index_type start, end;
|
||||||
|
bool found = false;
|
||||||
|
bool extending_leftwards = self->selection.end_y < self->selection.start_y || (self->selection.end_y == self->selection.start_y && self->selection.end_x < self->selection.start_x);
|
||||||
|
switch(self->selection.extend_mode) {
|
||||||
|
case EXTEND_WORD: {
|
||||||
|
index_type y1 = y, y2;
|
||||||
|
found = screen_selection_range_for_word(self, x, &y1, &y2, &start, &end);
|
||||||
|
if (found) {
|
||||||
|
if (extending_leftwards) {
|
||||||
|
self->selection.end_x = start; self->selection.end_y = y1;
|
||||||
|
y1 = self->selection.start_y;
|
||||||
|
found = screen_selection_range_for_word(self, self->selection.start_x, &y1, &y2, &start, &end);
|
||||||
|
if (found) {
|
||||||
|
self->selection.start_x = end; self->selection.start_y = y2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self->selection.end_x = end; self->selection.end_y = y2;
|
||||||
|
y1 = self->selection.start_y;
|
||||||
|
found = screen_selection_range_for_word(self, self->selection.start_x, &y1, &y2, &start, &end);
|
||||||
|
if (found) {
|
||||||
|
self->selection.start_x = start; self->selection.start_y = y1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EXTEND_LINE:
|
||||||
|
if (extending_leftwards) {
|
||||||
|
found = screen_selection_range_for_line(self, self->selection.end_y, &start, &end);
|
||||||
|
if (found) { self->selection.end_x = start; }
|
||||||
|
found = screen_selection_range_for_line(self, self->selection.start_y, &start, &end);
|
||||||
|
if (found) { self->selection.start_x = end; }
|
||||||
|
} else {
|
||||||
|
found = screen_selection_range_for_line(self, self->selection.start_y, &start, &end);
|
||||||
|
if (found) { self->selection.start_x = start; }
|
||||||
|
found = screen_selection_range_for_line(self, self->selection.end_y, &start, &end);
|
||||||
|
if (found) { self->selection.end_x = end; }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EXTEND_CELL:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
call_boss(set_primary_selection, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
mark_as_dirty(Screen *self) {
|
mark_as_dirty(Screen *self, PyObject *a UNUSED) {
|
||||||
self->is_dirty = true;
|
self->is_dirty = true;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
current_char_width(Screen *self) {
|
current_char_width(Screen *self, PyObject *a UNUSED) {
|
||||||
#define current_char_width_doc "The width of the character under the cursor"
|
#define current_char_width_doc "The width of the character under the cursor"
|
||||||
return PyLong_FromUnsignedLong(screen_current_char_width(self));
|
return PyLong_FromUnsignedLong(screen_current_char_width(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
is_main_linebuf(Screen *self) {
|
is_main_linebuf(Screen *self, PyObject *a UNUSED) {
|
||||||
PyObject *ans = (self->linebuf == self->main_linebuf) ? Py_True : Py_False;
|
PyObject *ans = (self->linebuf == self->main_linebuf) ? Py_True : Py_False;
|
||||||
Py_INCREF(ans);
|
Py_INCREF(ans);
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
toggle_alt_screen(Screen *self) {
|
toggle_alt_screen(Screen *self, PyObject *a UNUSED) {
|
||||||
screen_toggle_screen_buffer(self);
|
screen_toggle_screen_buffer(self);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,12 @@ typedef struct {
|
|||||||
unsigned int x, y;
|
unsigned int x, y;
|
||||||
} SelectionBoundary;
|
} SelectionBoundary;
|
||||||
|
|
||||||
|
typedef enum SelectionExtendModes { EXTEND_CELL, EXTEND_WORD, EXTEND_LINE } SelectionExtendMode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int start_x, start_y, start_scrolled_by, end_x, end_y, end_scrolled_by;
|
unsigned int start_x, start_y, start_scrolled_by, end_x, end_y, end_scrolled_by;
|
||||||
bool in_progress;
|
bool in_progress, rectangle_select;
|
||||||
|
SelectionExtendMode extend_mode;
|
||||||
} Selection;
|
} Selection;
|
||||||
|
|
||||||
#define SAVEPOINTS_SZ 256
|
#define SAVEPOINTS_SZ 256
|
||||||
@@ -61,7 +64,7 @@ typedef struct {
|
|||||||
Selection selection;
|
Selection selection;
|
||||||
SelectionBoundary last_rendered_selection_start, last_rendered_selection_end, last_rendered_url_start, last_rendered_url_end;
|
SelectionBoundary last_rendered_selection_start, last_rendered_selection_end, last_rendered_url_start, last_rendered_url_end;
|
||||||
Selection url_range;
|
Selection url_range;
|
||||||
bool use_latin1, selection_updated_once, is_dirty, scroll_changed, rectangle_select;
|
bool use_latin1, selection_updated_once, is_dirty, scroll_changed;
|
||||||
Cursor *cursor;
|
Cursor *cursor;
|
||||||
SavepointBuffer main_savepoints, alt_savepoints;
|
SavepointBuffer main_savepoints, alt_savepoints;
|
||||||
SavemodesBuffer modes_savepoints;
|
SavemodesBuffer modes_savepoints;
|
||||||
@@ -150,12 +153,13 @@ void report_device_status(Screen *self, unsigned int which, bool UNUSED);
|
|||||||
void report_mode_status(Screen *self, unsigned int which, bool);
|
void report_mode_status(Screen *self, unsigned int which, bool);
|
||||||
void screen_apply_selection(Screen *self, void *address, size_t size);
|
void screen_apply_selection(Screen *self, void *address, size_t size);
|
||||||
bool screen_is_selection_dirty(Screen *self);
|
bool screen_is_selection_dirty(Screen *self);
|
||||||
|
bool screen_has_selection(Screen*);
|
||||||
bool screen_invert_colors(Screen *self);
|
bool screen_invert_colors(Screen *self);
|
||||||
void screen_update_cell_data(Screen *self, void *address, size_t sz);
|
void screen_update_cell_data(Screen *self, void *address, size_t sz);
|
||||||
bool screen_is_cursor_visible(Screen *self);
|
bool screen_is_cursor_visible(Screen *self);
|
||||||
bool screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end);
|
bool screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end);
|
||||||
bool screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_type *start, index_type *end);
|
bool screen_selection_range_for_word(Screen *self, index_type x, index_type *, index_type *, index_type *start, index_type *end);
|
||||||
void screen_start_selection(Screen *self, index_type x, index_type y, bool);
|
void screen_start_selection(Screen *self, index_type x, index_type y, bool, SelectionExtendMode);
|
||||||
void screen_update_selection(Screen *self, index_type x, index_type y, bool ended);
|
void screen_update_selection(Screen *self, index_type x, index_type y, bool ended);
|
||||||
bool screen_history_scroll(Screen *self, int amt, bool upwards);
|
bool screen_history_scroll(Screen *self, int amt, bool upwards);
|
||||||
Line* screen_visual_line(Screen *self, index_type y);
|
Line* screen_visual_line(Screen *self, index_type y);
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ def parse_session(raw, opts):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def create_session(opts, args=None, special_window=None, cwd_from=None):
|
def create_session(opts, args=None, special_window=None, cwd_from=None, respect_cwd=False):
|
||||||
if args and args.session:
|
if args and args.session:
|
||||||
with open(args.session) as f:
|
with open(args.session) as f:
|
||||||
return parse_session(f.read(), opts)
|
return parse_session(f.read(), opts)
|
||||||
@@ -122,6 +122,8 @@ def create_session(opts, args=None, special_window=None, cwd_from=None):
|
|||||||
cmd = args.args if args and args.args else resolved_shell(opts)
|
cmd = args.args if args and args.args else resolved_shell(opts)
|
||||||
from kitty.tabs import SpecialWindow
|
from kitty.tabs import SpecialWindow
|
||||||
k = {'cwd_from': cwd_from}
|
k = {'cwd_from': cwd_from}
|
||||||
|
if respect_cwd:
|
||||||
|
k['cwd'] = args.directory
|
||||||
if getattr(args, 'title', None):
|
if getattr(args, 'title', None):
|
||||||
k['override_title'] = args.title
|
k['override_title'] = args.title
|
||||||
special_window = SpecialWindow(cmd, **k)
|
special_window = SpecialWindow(cmd, **k)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ copy_image_sub_data(GLuint src_texture_id, GLuint dest_texture_id, unsigned int
|
|||||||
// ARB_copy_image not available, do a slow roundtrip copy
|
// ARB_copy_image not available, do a slow roundtrip copy
|
||||||
if (!copy_image_warned) {
|
if (!copy_image_warned) {
|
||||||
copy_image_warned = true;
|
copy_image_warned = true;
|
||||||
fprintf(stderr, "WARNING: Your system's OpenGL implementation does not have glCopyImageSubData, falling back to a slower implementation.\n");
|
log_error("WARNING: Your system's OpenGL implementation does not have glCopyImageSubData, falling back to a slower implementation");
|
||||||
}
|
}
|
||||||
size_t sz = width * height * num_levels;
|
size_t sz = width * height * num_levels;
|
||||||
pixel *src = malloc(sz * sizeof(pixel));
|
pixel *src = malloc(sz * sizeof(pixel));
|
||||||
@@ -214,9 +214,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
|
|||||||
struct CellRenderData {
|
struct CellRenderData {
|
||||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||||
|
|
||||||
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style;
|
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||||
|
|
||||||
GLint color1, color2;
|
|
||||||
|
|
||||||
GLuint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
GLuint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
||||||
};
|
};
|
||||||
@@ -241,7 +239,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
|
|||||||
unsigned int x, y, z;
|
unsigned int x, y, z;
|
||||||
sprite_tracker_current_layout(&x, &y, &z);
|
sprite_tracker_current_layout(&x, &y, &z);
|
||||||
rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y;
|
rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y;
|
||||||
rd->color1 = inverted & 1; rd->color2 = 1 - (inverted & 1);
|
rd->inverted = inverted ? 1 : 0;
|
||||||
rd->background_opacity = OPT(background_opacity);
|
rd->background_opacity = OPT(background_opacity);
|
||||||
|
|
||||||
#define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name)
|
#define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name)
|
||||||
@@ -472,7 +470,7 @@ init_cursor_program() {
|
|||||||
#define SET_LOC(which) if (strcmp(p->uniforms[i].name, #which) == 0) cursor_uniform_locations[CURSOR_##which] = p->uniforms[i].location
|
#define SET_LOC(which) if (strcmp(p->uniforms[i].name, #which) == 0) cursor_uniform_locations[CURSOR_##which] = p->uniforms[i].location
|
||||||
SET_LOC(color);
|
SET_LOC(color);
|
||||||
else SET_LOC(pos);
|
else SET_LOC(pos);
|
||||||
else { fatal("Unknown uniform in cursor program"); }
|
else { fatal("Unknown uniform in cursor program: %s", p->uniforms[i].name); }
|
||||||
}
|
}
|
||||||
if (left) { fatal("Left over uniforms in cursor program"); }
|
if (left) { fatal("Left over uniforms in cursor program"); }
|
||||||
#undef SET_LOC
|
#undef SET_LOC
|
||||||
@@ -573,7 +571,7 @@ compile_program(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (ret != GL_TRUE) {
|
if (ret != GL_TRUE) {
|
||||||
GLsizei len;
|
GLsizei len;
|
||||||
glGetProgramInfoLog(programs[which].id, sizeof(glbuf), &len, glbuf);
|
glGetProgramInfoLog(programs[which].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);
|
||||||
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@@ -587,9 +585,8 @@ end:
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PYWRAP0(name) static PyObject* py##name(PyObject UNUSED *self)
|
#define PYWRAP0(name) static PyObject* py##name(PYNOARG)
|
||||||
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
||||||
#define PYWRAP2(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args, PyObject *kw)
|
|
||||||
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
||||||
#define ONE_INT(name) PYWRAP1(name) { name(PyLong_AsSsize_t(args)); Py_RETURN_NONE; }
|
#define ONE_INT(name) PYWRAP1(name) { name(PyLong_AsSsize_t(args)); Py_RETURN_NONE; }
|
||||||
#define TWO_INT(name) PYWRAP1(name) { int a, b; PA("ii", &a, &b); name(a, b); Py_RETURN_NONE; }
|
#define TWO_INT(name) PYWRAP1(name) { int a, b; PA("ii", &a, &b); name(a, b); Py_RETURN_NONE; }
|
||||||
|
|||||||
206
kitty/shell.py
Normal file
206
kitty/shell.py
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
|
import readline
|
||||||
|
import shlex
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import types
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from .cli import (
|
||||||
|
emph, green, italic, parse_option_spec, print_help_for_seq, title
|
||||||
|
)
|
||||||
|
from .cmds import cmap, display_subcommand_help, parse_subcommand_cli
|
||||||
|
from .constants import cache_dir, version
|
||||||
|
|
||||||
|
all_commands = tuple(sorted(cmap))
|
||||||
|
match_commands = tuple(sorted(all_commands + ('exit', 'help', 'quit')))
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_names_matching(prefix):
|
||||||
|
for cmd in match_commands:
|
||||||
|
if not prefix or cmd.startswith(prefix):
|
||||||
|
yield cmd + ' '
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def options_for_cmd(cmd):
|
||||||
|
alias_map = {}
|
||||||
|
try:
|
||||||
|
func = cmap[cmd]
|
||||||
|
except KeyError:
|
||||||
|
return (), alias_map
|
||||||
|
seq, disabled = parse_option_spec(func.options_spec)
|
||||||
|
ans = []
|
||||||
|
for opt in seq:
|
||||||
|
if isinstance(opt, str):
|
||||||
|
continue
|
||||||
|
for alias in opt['aliases']:
|
||||||
|
ans.append(alias)
|
||||||
|
alias_map[alias] = opt
|
||||||
|
return tuple(sorted(ans)), alias_map
|
||||||
|
|
||||||
|
|
||||||
|
def options_matching(prefix, aliases, alias_map):
|
||||||
|
for alias in aliases:
|
||||||
|
if (not prefix or alias.startswith(prefix)) and alias.startswith('--'):
|
||||||
|
yield alias + ' '
|
||||||
|
|
||||||
|
|
||||||
|
class Completer:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.matches = []
|
||||||
|
ddir = cache_dir()
|
||||||
|
try:
|
||||||
|
os.makedirs(ddir)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
self.history_path = os.path.join(ddir, 'shell.history')
|
||||||
|
|
||||||
|
def complete(self, text, state):
|
||||||
|
if state == 0:
|
||||||
|
line = readline.get_line_buffer()
|
||||||
|
cmdline = shlex.split(line)
|
||||||
|
if len(cmdline) < 2 and not line.endswith(' '):
|
||||||
|
self.matches = list(cmd_names_matching(text))
|
||||||
|
else:
|
||||||
|
self.matches = list(options_matching(text, *options_for_cmd(cmdline[0])))
|
||||||
|
if state < len(self.matches):
|
||||||
|
return self.matches[state]
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if os.path.exists(self.history_path):
|
||||||
|
readline.read_history_file(self.history_path)
|
||||||
|
readline.set_completer(self.complete)
|
||||||
|
readline.parse_and_bind('tab: complete')
|
||||||
|
delims = readline.get_completer_delims()
|
||||||
|
readline.set_completer_delims(delims.replace('-', ''))
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *a):
|
||||||
|
readline.write_history_file(self.history_path)
|
||||||
|
|
||||||
|
|
||||||
|
def print_err(*a, **kw):
|
||||||
|
kw['file'] = sys.stderr
|
||||||
|
print(*a, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
def print_help(which=None):
|
||||||
|
if which is None:
|
||||||
|
print('Control kitty by sending it commands.')
|
||||||
|
print()
|
||||||
|
print(title('Commands') + ':')
|
||||||
|
for cmd in all_commands:
|
||||||
|
c = cmap[cmd]
|
||||||
|
print(' ', green(c.name))
|
||||||
|
print(' ', c.short_desc)
|
||||||
|
print(' ', green('exit'))
|
||||||
|
print(' ', 'Exit this shell')
|
||||||
|
print('\nUse help {} for help on individual commands'.format(italic('command')))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
func = cmap[which]
|
||||||
|
except KeyError:
|
||||||
|
if which == 'exit':
|
||||||
|
print('Exit this shell')
|
||||||
|
elif which == 'help':
|
||||||
|
print('Show help')
|
||||||
|
else:
|
||||||
|
print('Unknown command: {}'.format(emph(which)))
|
||||||
|
return
|
||||||
|
display_subcommand_help(func)
|
||||||
|
|
||||||
|
|
||||||
|
def run_cmd(global_opts, cmd, func, opts, items):
|
||||||
|
from .remote_control import do_io
|
||||||
|
payload = func(global_opts, opts, items)
|
||||||
|
send = {
|
||||||
|
'cmd': cmd,
|
||||||
|
'version': version,
|
||||||
|
}
|
||||||
|
if func.no_response and isinstance(payload, types.GeneratorType):
|
||||||
|
for item in payload:
|
||||||
|
send['payload'] = item
|
||||||
|
do_io(global_opts.to, send, func.no_response)
|
||||||
|
return
|
||||||
|
if payload is not None:
|
||||||
|
send['payload'] = payload
|
||||||
|
response = do_io(global_opts.to, send, func.no_response)
|
||||||
|
if not response.get('ok'):
|
||||||
|
if response.get('tb'):
|
||||||
|
print_err(response['tb'])
|
||||||
|
print_err(response['error'])
|
||||||
|
return
|
||||||
|
if 'data' in response:
|
||||||
|
print(response['data'])
|
||||||
|
|
||||||
|
|
||||||
|
def real_main(global_opts):
|
||||||
|
readline.read_init_file()
|
||||||
|
print_help_for_seq.allow_pager = False
|
||||||
|
print('Welcome to the kitty shell!')
|
||||||
|
print('Use {} for assistance or {} to quit'.format(green('help'), green('exit')))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
cmdline = input('🐱 ')
|
||||||
|
except EOFError:
|
||||||
|
break
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print()
|
||||||
|
continue
|
||||||
|
if not cmdline:
|
||||||
|
continue
|
||||||
|
cmdline = shlex.split(cmdline)
|
||||||
|
cmd = cmdline[0].lower()
|
||||||
|
|
||||||
|
try:
|
||||||
|
func = cmap[cmd]
|
||||||
|
except KeyError:
|
||||||
|
if cmd in ('exit', 'quit'):
|
||||||
|
break
|
||||||
|
if cmd == 'help':
|
||||||
|
print_help(cmdline[1] if len(cmdline) > 1 else None)
|
||||||
|
continue
|
||||||
|
print_err('"{}" is an unknown command. Use "help" to see a list of commands.'.format(emph(cmd)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, items = parse_subcommand_cli(func, cmdline)
|
||||||
|
except SystemExit as e:
|
||||||
|
if e.code != 0:
|
||||||
|
print_err(e)
|
||||||
|
print_err('Use "{}" to see how to use this command.'.format(emph('help ' + cmd)))
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
print_err('Unhandled error:')
|
||||||
|
traceback.print_exc()
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
run_cmd(global_opts, cmd, func, opts, items)
|
||||||
|
except SystemExit as e:
|
||||||
|
print_err(e)
|
||||||
|
continue
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print()
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
print_err('Unhandled error:')
|
||||||
|
traceback.print_exc()
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def main(global_opts):
|
||||||
|
try:
|
||||||
|
with Completer():
|
||||||
|
real_main(global_opts)
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
input('Press enter to quit...')
|
||||||
|
raise SystemExit(1)
|
||||||
@@ -201,6 +201,7 @@ static inline void
|
|||||||
set_active_tab(id_type os_window_id, unsigned int idx) {
|
set_active_tab(id_type os_window_id, unsigned int idx) {
|
||||||
WITH_OS_WINDOW(os_window_id)
|
WITH_OS_WINDOW(os_window_id)
|
||||||
os_window->active_tab = idx;
|
os_window->active_tab = idx;
|
||||||
|
os_window->needs_render = true;
|
||||||
END_WITH_OS_WINDOW
|
END_WITH_OS_WINDOW
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +209,7 @@ static inline void
|
|||||||
set_active_window(id_type os_window_id, id_type tab_id, unsigned int idx) {
|
set_active_window(id_type os_window_id, id_type tab_id, unsigned int idx) {
|
||||||
WITH_TAB(os_window_id, tab_id)
|
WITH_TAB(os_window_id, tab_id)
|
||||||
tab->active_window = idx;
|
tab->active_window = idx;
|
||||||
|
osw->needs_render = true;
|
||||||
END_WITH_TAB;
|
END_WITH_TAB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,9 +270,8 @@ os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) {
|
|||||||
|
|
||||||
|
|
||||||
// Python API {{{
|
// Python API {{{
|
||||||
#define PYWRAP0(name) static PyObject* py##name(PyObject UNUSED *self)
|
#define PYWRAP0(name) static PyObject* py##name(PYNOARG)
|
||||||
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
||||||
#define PYWRAP2(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args, PyObject *kw)
|
|
||||||
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
||||||
#define ONE_UINT(name) PYWRAP1(name) { name((unsigned int)PyLong_AsUnsignedLong(args)); Py_RETURN_NONE; }
|
#define ONE_UINT(name) PYWRAP1(name) { name((unsigned int)PyLong_AsUnsignedLong(args)); Py_RETURN_NONE; }
|
||||||
#define TWO_UINT(name) PYWRAP1(name) { unsigned int a, b; PA("II", &a, &b); name(a, b); Py_RETURN_NONE; }
|
#define TWO_UINT(name) PYWRAP1(name) { unsigned int a, b; PA("II", &a, &b); name(a, b); Py_RETURN_NONE; }
|
||||||
@@ -283,6 +284,7 @@ os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) {
|
|||||||
#define KKI(name) PYWRAP1(name) { id_type a, b; unsigned int c; PA("KKI", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
|
#define KKI(name) PYWRAP1(name) { id_type a, b; unsigned int c; PA("KKI", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
|
||||||
#define KKII(name) PYWRAP1(name) { id_type a, b; unsigned int c, d; PA("KKII", &a, &b, &c, &d); name(a, b, c, d); Py_RETURN_NONE; }
|
#define KKII(name) PYWRAP1(name) { id_type a, b; unsigned int c, d; PA("KKII", &a, &b, &c, &d); name(a, b, c, d); Py_RETURN_NONE; }
|
||||||
#define KK5I(name) PYWRAP1(name) { id_type a, b; unsigned int c, d, e, f, g; PA("KKIIIII", &a, &b, &c, &d, &e, &f, &g); name(a, b, c, d, e, f, g); Py_RETURN_NONE; }
|
#define KK5I(name) PYWRAP1(name) { id_type a, b; unsigned int c, d, e, f, g; PA("KKIIIII", &a, &b, &c, &d, &e, &f, &g); name(a, b, c, d, e, f, g); Py_RETURN_NONE; }
|
||||||
|
#define BOOL_SET(name) PYWRAP1(set_##name) { global_state.name = PyObject_IsTrue(args); Py_RETURN_NONE; }
|
||||||
|
|
||||||
static inline color_type
|
static inline color_type
|
||||||
color_as_int(PyObject *color) {
|
color_as_int(PyObject *color) {
|
||||||
@@ -323,10 +325,11 @@ PYWRAP1(handle_for_window_id) {
|
|||||||
|
|
||||||
PYWRAP1(set_options) {
|
PYWRAP1(set_options) {
|
||||||
PyObject *ret, *opts;
|
PyObject *ret, *opts;
|
||||||
int is_wayland = 0, debug_gl = 0;
|
int is_wayland = 0, debug_gl = 0, debug_font_fallback = 0;
|
||||||
PA("O|pp", &opts, &is_wayland, &debug_gl);
|
PA("O|ppp", &opts, &is_wayland, &debug_gl, &debug_font_fallback);
|
||||||
global_state.is_wayland = is_wayland ? true : false;
|
global_state.is_wayland = is_wayland ? true : false;
|
||||||
global_state.debug_gl = debug_gl ? true : false;
|
global_state.debug_gl = debug_gl ? true : false;
|
||||||
|
global_state.debug_font_fallback = debug_font_fallback ? true : false;
|
||||||
#define GA(name) ret = PyObject_GetAttrString(opts, #name); if (ret == NULL) return NULL;
|
#define GA(name) ret = PyObject_GetAttrString(opts, #name); if (ret == NULL) return NULL;
|
||||||
#define S(name, convert) { GA(name); global_state.opts.name = convert(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; }
|
#define S(name, convert) { GA(name); global_state.opts.name = convert(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; }
|
||||||
S(visual_bell_duration, PyFloat_AsDouble);
|
S(visual_bell_duration, PyFloat_AsDouble);
|
||||||
@@ -336,9 +339,9 @@ PYWRAP1(set_options) {
|
|||||||
S(cursor_stop_blinking_after, PyFloat_AsDouble);
|
S(cursor_stop_blinking_after, PyFloat_AsDouble);
|
||||||
S(background_opacity, PyFloat_AsDouble);
|
S(background_opacity, PyFloat_AsDouble);
|
||||||
S(inactive_text_alpha, PyFloat_AsDouble);
|
S(inactive_text_alpha, PyFloat_AsDouble);
|
||||||
|
S(window_padding_width, PyFloat_AsDouble);
|
||||||
S(cursor_shape, PyLong_AsLong);
|
S(cursor_shape, PyLong_AsLong);
|
||||||
S(url_style, PyLong_AsUnsignedLong);
|
S(url_style, PyLong_AsUnsignedLong);
|
||||||
S(x11_bell_volume, PyLong_AsLong);
|
|
||||||
S(tab_bar_edge, PyLong_AsLong);
|
S(tab_bar_edge, PyLong_AsLong);
|
||||||
S(mouse_hide_wait, PyFloat_AsDouble);
|
S(mouse_hide_wait, PyFloat_AsDouble);
|
||||||
S(wheel_scroll_multiplier, PyFloat_AsDouble);
|
S(wheel_scroll_multiplier, PyFloat_AsDouble);
|
||||||
@@ -352,6 +355,7 @@ PYWRAP1(set_options) {
|
|||||||
S(repaint_delay, repaint_delay);
|
S(repaint_delay, repaint_delay);
|
||||||
S(input_delay, repaint_delay);
|
S(input_delay, repaint_delay);
|
||||||
S(sync_to_monitor, PyObject_IsTrue);
|
S(sync_to_monitor, PyObject_IsTrue);
|
||||||
|
S(close_on_child_death, PyObject_IsTrue);
|
||||||
S(macos_option_as_alt, PyObject_IsTrue);
|
S(macos_option_as_alt, PyObject_IsTrue);
|
||||||
S(macos_hide_titlebar, PyObject_IsTrue);
|
S(macos_hide_titlebar, PyObject_IsTrue);
|
||||||
|
|
||||||
@@ -365,6 +369,8 @@ PYWRAP1(set_options) {
|
|||||||
|
|
||||||
GA(keymap); set_special_keys(ret);
|
GA(keymap); set_special_keys(ret);
|
||||||
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
||||||
|
GA(sequence_map); set_special_keys(ret);
|
||||||
|
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
||||||
|
|
||||||
#define read_adjust(name) { \
|
#define read_adjust(name) { \
|
||||||
PyObject *al = PyObject_GetAttrString(opts, #name); \
|
PyObject *al = PyObject_GetAttrString(opts, #name); \
|
||||||
@@ -384,6 +390,8 @@ PYWRAP1(set_options) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL_SET(in_sequence_mode)
|
||||||
|
|
||||||
PYWRAP1(set_tab_bar_render_data) {
|
PYWRAP1(set_tab_bar_render_data) {
|
||||||
ScreenRenderData d = {0};
|
ScreenRenderData d = {0};
|
||||||
id_type os_window_id;
|
id_type os_window_id;
|
||||||
@@ -553,6 +561,7 @@ KK5I(add_borders_rect)
|
|||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
MW(current_os_window, METH_NOARGS),
|
MW(current_os_window, METH_NOARGS),
|
||||||
MW(set_options, METH_VARARGS),
|
MW(set_options, METH_VARARGS),
|
||||||
|
MW(set_in_sequence_mode, METH_O),
|
||||||
MW(handle_for_window_id, METH_VARARGS),
|
MW(handle_for_window_id, METH_VARARGS),
|
||||||
MW(set_logical_dpi, METH_VARARGS),
|
MW(set_logical_dpi, METH_VARARGS),
|
||||||
MW(pt_to_px, METH_O),
|
MW(pt_to_px, METH_O),
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ typedef struct {
|
|||||||
bool macos_option_as_alt, macos_hide_titlebar;
|
bool macos_option_as_alt, macos_hide_titlebar;
|
||||||
int adjust_line_height_px, adjust_column_width_px;
|
int adjust_line_height_px, adjust_column_width_px;
|
||||||
float adjust_line_height_frac, adjust_column_width_frac;
|
float adjust_line_height_frac, adjust_column_width_frac;
|
||||||
int x11_bell_volume;
|
|
||||||
float background_opacity;
|
float background_opacity;
|
||||||
float inactive_text_alpha;
|
float inactive_text_alpha;
|
||||||
|
float window_padding_width;
|
||||||
Edge tab_bar_edge;
|
Edge tab_bar_edge;
|
||||||
bool sync_to_monitor;
|
bool sync_to_monitor;
|
||||||
|
bool close_on_child_death;
|
||||||
} Options;
|
} Options;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -97,11 +98,11 @@ typedef struct {
|
|||||||
void *handle;
|
void *handle;
|
||||||
id_type id;
|
id_type id;
|
||||||
OSWindowGeometry before_fullscreen;
|
OSWindowGeometry before_fullscreen;
|
||||||
int viewport_width, viewport_height;
|
int viewport_width, viewport_height, window_width, window_height;
|
||||||
double viewport_x_ratio, viewport_y_ratio;
|
double viewport_x_ratio, viewport_y_ratio;
|
||||||
Tab *tabs;
|
Tab *tabs;
|
||||||
unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id;
|
unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id;
|
||||||
bool focused_at_last_render;
|
bool focused_at_last_render, needs_render;
|
||||||
ScreenRenderData tab_bar_render_data;
|
ScreenRenderData tab_bar_render_data;
|
||||||
bool is_focused;
|
bool is_focused;
|
||||||
double cursor_blink_zero_time, last_mouse_activity_at;
|
double cursor_blink_zero_time, last_mouse_activity_at;
|
||||||
@@ -130,13 +131,14 @@ typedef struct {
|
|||||||
OSWindow *callback_os_window;
|
OSWindow *callback_os_window;
|
||||||
bool close_all_windows;
|
bool close_all_windows;
|
||||||
bool is_wayland;
|
bool is_wayland;
|
||||||
bool debug_gl;
|
bool debug_gl, debug_font_fallback;
|
||||||
bool has_pending_resizes;
|
bool has_pending_resizes;
|
||||||
|
bool in_sequence_mode;
|
||||||
} GlobalState;
|
} GlobalState;
|
||||||
|
|
||||||
extern GlobalState global_state;
|
extern GlobalState global_state;
|
||||||
|
|
||||||
#define call_boss(name, ...) { \
|
#define call_boss(name, ...) if (global_state.boss) { \
|
||||||
PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \
|
PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \
|
||||||
if (cret_ == NULL) { PyErr_Print(); } \
|
if (cret_ == NULL) { PyErr_Print(); } \
|
||||||
else Py_DECREF(cret_); \
|
else Py_DECREF(cret_); \
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ from .fast_data_types import (
|
|||||||
)
|
)
|
||||||
from .layout import Rect, all_layouts
|
from .layout import Rect, all_layouts
|
||||||
from .session import resolved_shell
|
from .session import resolved_shell
|
||||||
from .utils import color_as_int
|
from .utils import color_as_int, log_error
|
||||||
from .window import Window, calculate_gl_geometry
|
from .window import Window, calculate_gl_geometry
|
||||||
|
|
||||||
TabbarData = namedtuple('TabbarData', 'title is_active is_last')
|
TabbarData = namedtuple('TabbarData', 'title is_active is_last')
|
||||||
SpecialWindowInstance = namedtuple('SpecialWindow', 'cmd stdin override_title cwd_from cwd overlay_for')
|
SpecialWindowInstance = namedtuple('SpecialWindow', 'cmd stdin override_title cwd_from cwd overlay_for env')
|
||||||
|
|
||||||
|
|
||||||
def SpecialWindow(cmd, stdin=None, override_title=None, cwd_from=None, cwd=None, overlay_for=None):
|
def SpecialWindow(cmd, stdin=None, override_title=None, cwd_from=None, cwd=None, overlay_for=None, env=None):
|
||||||
return SpecialWindowInstance(cmd, stdin, override_title, cwd_from, cwd, overlay_for)
|
return SpecialWindowInstance(cmd, stdin, override_title, cwd_from, cwd, overlay_for, env)
|
||||||
|
|
||||||
|
|
||||||
class Tab: # {{{
|
class Tab: # {{{
|
||||||
@@ -38,7 +38,7 @@ class Tab: # {{{
|
|||||||
raise Exception('No OS window with id {} found, or tab counter has wrapped'.format(self.os_window_id))
|
raise Exception('No OS window with id {} found, or tab counter has wrapped'.format(self.os_window_id))
|
||||||
self.opts, self.args = tab_manager.opts, tab_manager.args
|
self.opts, self.args = tab_manager.opts, tab_manager.args
|
||||||
self.name = getattr(session_tab, 'name', '')
|
self.name = getattr(session_tab, 'name', '')
|
||||||
self.enabled_layouts = list(getattr(session_tab, 'enabled_layouts', None) or self.opts.enabled_layouts)
|
self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or self.opts.enabled_layouts]
|
||||||
self.borders = Borders(self.os_window_id, self.id, self.opts)
|
self.borders = Borders(self.os_window_id, self.id, self.opts)
|
||||||
self.windows = deque()
|
self.windows = deque()
|
||||||
self.active_window_idx = 0
|
self.active_window_idx = 0
|
||||||
@@ -116,25 +116,35 @@ class Tab: # {{{
|
|||||||
self.current_layout = self.create_layout_object(nl)
|
self.current_layout = self.create_layout_object(nl)
|
||||||
self.relayout()
|
self.relayout()
|
||||||
|
|
||||||
def launch_child(self, use_shell=False, cmd=None, stdin=None, cwd_from=None, cwd=None):
|
def goto_layout(self, layout_name):
|
||||||
|
layout_name = layout_name.lower()
|
||||||
|
if layout_name not in self.enabled_layouts:
|
||||||
|
log_error('Unknown or disabled layout: {}'.format(layout_name))
|
||||||
|
return
|
||||||
|
self.current_layout = self.create_layout_object(layout_name)
|
||||||
|
self.relayout()
|
||||||
|
|
||||||
|
def launch_child(self, use_shell=False, cmd=None, stdin=None, cwd_from=None, cwd=None, env=None):
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
if use_shell:
|
if use_shell:
|
||||||
cmd = resolved_shell(self.opts)
|
cmd = resolved_shell(self.opts)
|
||||||
else:
|
else:
|
||||||
cmd = self.args.args or resolved_shell(self.opts)
|
cmd = self.args.args or resolved_shell(self.opts)
|
||||||
env = {}
|
fenv = {}
|
||||||
|
if env:
|
||||||
|
fenv.update(env)
|
||||||
if not is_macos and not is_wayland:
|
if not is_macos and not is_wayland:
|
||||||
try:
|
try:
|
||||||
env['WINDOWID'] = str(x11_window_id(self.os_window_id))
|
fenv['WINDOWID'] = str(x11_window_id(self.os_window_id))
|
||||||
except Exception:
|
except Exception:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
ans = Child(cmd, cwd or self.cwd, self.opts, stdin, env, cwd_from)
|
ans = Child(cmd, cwd or self.cwd, self.opts, stdin, fenv, cwd_from)
|
||||||
ans.fork()
|
ans.fork()
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def new_window(self, use_shell=True, cmd=None, stdin=None, override_title=None, cwd_from=None, cwd=None, overlay_for=None):
|
def new_window(self, use_shell=True, cmd=None, stdin=None, override_title=None, cwd_from=None, cwd=None, overlay_for=None, env=None):
|
||||||
child = self.launch_child(use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd)
|
child = self.launch_child(use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env)
|
||||||
window = Window(self, child, self.opts, self.args, override_title=override_title)
|
window = Window(self, child, self.opts, self.args, override_title=override_title)
|
||||||
if overlay_for is not None:
|
if overlay_for is not None:
|
||||||
overlaid = next(w for w in self.windows if w.id == overlay_for)
|
overlaid = next(w for w in self.windows if w.id == overlay_for)
|
||||||
@@ -239,6 +249,7 @@ class TabBar: # {{{
|
|||||||
|
|
||||||
def __init__(self, os_window_id, opts):
|
def __init__(self, os_window_id, opts):
|
||||||
self.os_window_id = os_window_id
|
self.os_window_id = os_window_id
|
||||||
|
self.opts = opts
|
||||||
self.num_tabs = 1
|
self.num_tabs = 1
|
||||||
self.cell_width = 1
|
self.cell_width = 1
|
||||||
self.data_buffer_size = 0
|
self.data_buffer_size = 0
|
||||||
@@ -269,6 +280,16 @@ class TabBar: # {{{
|
|||||||
self.active_bg = as_rgb(color_as_int(opts.active_tab_background))
|
self.active_bg = as_rgb(color_as_int(opts.active_tab_background))
|
||||||
self.active_fg = as_rgb(color_as_int(opts.active_tab_foreground))
|
self.active_fg = as_rgb(color_as_int(opts.active_tab_foreground))
|
||||||
|
|
||||||
|
def patch_colors(self, spec):
|
||||||
|
if 'active_tab_foreground' in spec:
|
||||||
|
self.active_fg = (spec['active_tab_foreground'] << 8) | 2
|
||||||
|
if 'active_tab_background' in spec:
|
||||||
|
self.active_bg = (spec['active_tab_background'] << 8) | 2
|
||||||
|
self.screen.color_profile.set_configured_colors(
|
||||||
|
spec.get('inactive_tab_foreground', color_as_int(self.opts.inactive_tab_foreground)),
|
||||||
|
spec.get('inactive_tab_background', color_as_int(self.opts.inactive_tab_background))
|
||||||
|
)
|
||||||
|
|
||||||
def layout(self):
|
def layout(self):
|
||||||
central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id)
|
central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id)
|
||||||
if tab_bar.width < 2:
|
if tab_bar.width < 2:
|
||||||
@@ -448,6 +469,7 @@ class TabManager: # {{{
|
|||||||
self._add_tab(Tab(self, special_window=special_window, cwd_from=cwd_from))
|
self._add_tab(Tab(self, special_window=special_window, cwd_from=cwd_from))
|
||||||
self._set_active_tab(idx)
|
self._set_active_tab(idx)
|
||||||
self.update_tab_bar()
|
self.update_tab_bar()
|
||||||
|
return self.tabs[idx]
|
||||||
|
|
||||||
def remove(self, tab):
|
def remove(self, tab):
|
||||||
self._remove_tab(tab)
|
self._remove_tab(tab)
|
||||||
|
|||||||
@@ -3,14 +3,7 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from binascii import unhexlify, hexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
|
|
||||||
def safe_print(*a, **k):
|
|
||||||
try:
|
|
||||||
print(*a, **k)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
names = 'xterm-kitty', 'KovIdTTY'
|
names = 'xterm-kitty', 'KovIdTTY'
|
||||||
@@ -459,7 +452,8 @@ def get_capabilities(query_string):
|
|||||||
qname = termcap_aliases[name]
|
qname = termcap_aliases[name]
|
||||||
val = queryable_capabilities[qname]
|
val = queryable_capabilities[qname]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
safe_print(ERROR_PREFIX, 'Unknown terminfo property:', name)
|
from .utils import log_error
|
||||||
|
log_error(ERROR_PREFIX, 'Unknown terminfo property:', name)
|
||||||
raise
|
raise
|
||||||
if qname in string_capabilities and '%' not in val:
|
if qname in string_capabilities and '%' not in val:
|
||||||
val = key_as_bytes(qname).decode('ascii')
|
val = key_as_bytes(qname).decode('ascii')
|
||||||
|
|||||||
28
kitty/threading.h
Normal file
28
kitty/threading.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// 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 and that causes other issues on systems with old glibc
|
||||||
|
extern int pthread_setname_np(pthread_t, const char *name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ from time import monotonic
|
|||||||
|
|
||||||
from .constants import appname, is_macos, is_wayland
|
from .constants import appname, is_macos, is_wayland
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
GLSL_VERSION, redirect_std_streams, x11_display,
|
GLSL_VERSION, log_error_string, redirect_std_streams, x11_display,
|
||||||
x11_window_id
|
x11_window_id
|
||||||
)
|
)
|
||||||
from .rgb import Color, to_color
|
from .rgb import Color, to_color
|
||||||
@@ -40,19 +40,18 @@ def safe_print(*a, **k):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def log_error(*a, **k):
|
||||||
|
try:
|
||||||
|
msg = k.get('sep', ' ').join(map(str, a)) + k.get('end', '\n')
|
||||||
|
log_error_string(msg.replace('\0', ''))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def ceil_int(x):
|
def ceil_int(x):
|
||||||
return int(math.ceil(x))
|
return int(math.ceil(x))
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def timeit(name, do_timing=False):
|
|
||||||
if do_timing:
|
|
||||||
st = monotonic()
|
|
||||||
yield
|
|
||||||
if do_timing:
|
|
||||||
safe_print('Time for {}: {}'.format(name, monotonic() - st))
|
|
||||||
|
|
||||||
|
|
||||||
def sanitize_title(x):
|
def sanitize_title(x):
|
||||||
return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19\x80-\x9f]', '', x))
|
return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19\x80-\x9f]', '', x))
|
||||||
|
|
||||||
@@ -262,6 +261,31 @@ def single_instance(group_id=None):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def parse_address_spec(spec):
|
||||||
|
protocol, rest = spec.split(':', 1)
|
||||||
|
socket_path = None
|
||||||
|
if protocol == 'unix':
|
||||||
|
family = socket.AF_UNIX
|
||||||
|
address = rest
|
||||||
|
if address.startswith('@') and len(address) > 1:
|
||||||
|
address = '\0' + address[1:]
|
||||||
|
else:
|
||||||
|
socket_path = address
|
||||||
|
elif protocol in ('tcp', 'tcp6'):
|
||||||
|
family = socket.AF_INET if protocol == 'tcp' else socket.AF_INET6
|
||||||
|
host, port = rest.rsplit(':', 1)
|
||||||
|
address = host, int(port)
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown protocol in --listen-on value: {}'.format(spec))
|
||||||
|
return family, address, socket_path
|
||||||
|
|
||||||
|
|
||||||
|
def make_fd_non_blocking(fd):
|
||||||
|
oldfl = fcntl.fcntl(fd, fcntl.F_GETFL)
|
||||||
|
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl | os.O_NONBLOCK)
|
||||||
|
return oldfl
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def non_blocking_read(src=sys.stdin):
|
def non_blocking_read(src=sys.stdin):
|
||||||
import termios
|
import termios
|
||||||
@@ -271,22 +295,21 @@ def non_blocking_read(src=sys.stdin):
|
|||||||
if src.isatty():
|
if src.isatty():
|
||||||
old = termios.tcgetattr(fd)
|
old = termios.tcgetattr(fd)
|
||||||
tty.setraw(fd)
|
tty.setraw(fd)
|
||||||
oldfl = fcntl.fcntl(fd, fcntl.F_GETFL)
|
oldfl = make_fd_non_blocking(fd)
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl | os.O_NONBLOCK)
|
|
||||||
yield fd
|
yield fd
|
||||||
if src.isatty():
|
if src.isatty():
|
||||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl)
|
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl)
|
||||||
|
|
||||||
|
|
||||||
def read_with_timeout(more_needed, timeout=10, src=sys.stdin):
|
def read_with_timeout(more_needed, timeout=10, src=sys.stdin.buffer):
|
||||||
import select
|
import select
|
||||||
start_time = monotonic()
|
start_time = monotonic()
|
||||||
with non_blocking_read(src) as fd:
|
with non_blocking_read(src) as fd:
|
||||||
while timeout > monotonic() - start_time:
|
while timeout > monotonic() - start_time:
|
||||||
rd = select.select([fd], [], [], max(0, timeout - (monotonic() - start_time)))[0]
|
rd = select.select([fd], [], [], max(0, timeout - (monotonic() - start_time)))[0]
|
||||||
if rd:
|
if rd:
|
||||||
data = sys.stdin.buffer.read()
|
data = src.read()
|
||||||
if not data:
|
if not data:
|
||||||
break # eof
|
break # eof
|
||||||
if not more_needed(data):
|
if not more_needed(data):
|
||||||
|
|||||||
@@ -182,6 +182,9 @@ class TestParser(BaseTest):
|
|||||||
c.clear()
|
c.clear()
|
||||||
pb('\033]2;;;;\x07', ('set_title', ';;;'))
|
pb('\033]2;;;;\x07', ('set_title', ';;;'))
|
||||||
self.ae(c.titlebuf, ';;;')
|
self.ae(c.titlebuf, ';;;')
|
||||||
|
c.clear()
|
||||||
|
pb('\033]2;\x07', ('set_title', ''))
|
||||||
|
self.ae(c.titlebuf, '')
|
||||||
pb('\033]110\x07', ('set_dynamic_color', 110, ''))
|
pb('\033]110\x07', ('set_dynamic_color', 110, ''))
|
||||||
self.ae(c.colorbuf, '')
|
self.ae(c.colorbuf, '')
|
||||||
|
|
||||||
|
|||||||
@@ -245,6 +245,12 @@ class TestScreen(BaseTest):
|
|||||||
y = s.cursor.y
|
y = s.cursor.y
|
||||||
self.assertIn('|', str(s.line(y)))
|
self.assertIn('|', str(s.line(y)))
|
||||||
|
|
||||||
|
s = self.create_screen()
|
||||||
|
draw('a')
|
||||||
|
x_before = s.cursor.x
|
||||||
|
s.resize(s.lines - 1, s.columns)
|
||||||
|
self.ae(x_before, s.cursor.x)
|
||||||
|
|
||||||
def test_tab_stops(self):
|
def test_tab_stops(self):
|
||||||
# Taken from vttest/main.c
|
# Taken from vttest/main.c
|
||||||
s = self.create_screen(cols=80, lines=2)
|
s = self.create_screen(cols=80, lines=2)
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ int main(int argc, char *argv[]) {
|
|||||||
#ifdef FOR_LAUNCHER
|
#ifdef FOR_LAUNCHER
|
||||||
num = snprintf(lib, PATH_MAX, "%s%s", exe_dir, "/../Frameworks/kitty");
|
num = snprintf(lib, PATH_MAX, "%s%s", exe_dir, "/../Frameworks/kitty");
|
||||||
#else
|
#else
|
||||||
num = snprintf(lib, PATH_MAX, "%s%s", exe_dir, "/../lib/kitty");
|
num = snprintf(lib, PATH_MAX, "%s%s%s%s", exe_dir, "/../", LIB_DIR_NAME, "/kitty");
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user