Compare commits

...

265 Commits

Author SHA1 Message Date
Kovid Goyal
7926a140b8 version 0.8.4 2018-03-31 12:16:35 +05:30
Kovid Goyal
023487a1d7 Merge branch 'reduce-iconset' of https://github.com/justinnhli/kitty 2018-03-31 11:43:41 +05:30
Justin Li
71f84b1023 reduce icon set size to fit iconutil limits 2018-03-30 22:57:34 -07:00
Kovid Goyal
ce85382c68 Revert the change to use rendered bitmap width to calculate cell width
Instead if the bitmap is up to two pixels wider than the cell width
simply crop on the right. Fixes #352
2018-03-30 18:54:15 +05:30
Kovid Goyal
94d248d812 Fix presence of XDG_CONFIG_DIRS env var preventing kitty from starting up due to a silly typo
Fixes #421
2018-03-29 17:51:14 +05:30
Kovid Goyal
62db7639a8 version 0.8.3 2018-03-29 09:56:44 +05:30
Kovid Goyal
80f3fe6e9f ... 2018-03-29 00:05:37 +05:30
Kovid Goyal
9ba2c4b397 Update glfw from upstream 2018-03-28 19:57:01 +05:30
Kovid Goyal
bfa53d4b45 Update glfw from upstream
Includes proper fix for Wayland key repeat
2018-03-28 15:26:56 +05:30
Kovid Goyal
69f3ceb903 Merge glfw upstream changes to build on Wayland on FreeBSD 2018-03-28 09:47:16 +05:30
Kovid Goyal
d0f839bbd8 Fix audible bell not working under Wayland 2018-03-27 19:45:19 +05:30
Kovid Goyal
48538c0e25 Update bundled glfw 2018-03-27 15:07:25 +05:30
Kovid Goyal
02ab22f08c Update CHANGELOG 2018-03-27 12:15:25 +05:30
Kovid Goyal
b3b216a6dc ... 2018-03-27 12:09:18 +05:30
Kovid Goyal
431564c8bc Linux: When calculating cell widths use the actual rendered bitmap width in addition to the horizontal advance reported by FreeType.
The horizontal advance is insufficient for some fonts at some point
sizes. So although rendering the characters is slower, we do it
to ensure cell width is correct. Fixes #352
2018-03-27 12:04:38 +05:30
Kovid Goyal
57f1bbb71a Make cwd check more robust
Apparently getcwd() raises an exception on macOS when the cwd does not
exist.
2018-03-27 10:54:49 +05:30
Kovid Goyal
faac8649e4 Fix a regression that broke visual bell and invert screen colors escape code. Fixes #419 2018-03-27 10:46:26 +05:30
Kovid Goyal
1e6a41186f Move check for NULL boss into call_boss()
Negligible perf impact and makes it safe to use in different contexts
2018-03-27 09:56:29 +05:30
Kovid Goyal
eddee67130 Fix error when testing update_selection because not boss is defined 2018-03-27 09:54:14 +05:30
Kovid Goyal
9b834014e9 Merge branch 'zero-length-title' of https://github.com/jamessan/kitty 2018-03-27 09:41:33 +05:30
James McCoy
a467e79144 Stop setting title to ";" when 0-length OSC 2 is received
Various programs send a 0-length title if no title is configured (e.g.,
(n)vim with 'title' unset, or mutt without ts_enabled).  When this
happens, kitty is mis-parsing the data and setting the title to ";".
2018-03-26 21:30:45 -04:00
Kovid Goyal
90cc785b55 Allow double-click and triple-click + drag to extend selections word at a time or line at a time. Fixes #400 2018-03-26 16:48:28 +05:30
Kovid Goyal
09710425ab Move rectangle_select onto the selection object 2018-03-26 12:37:36 +05:30
Kovid Goyal
dd36727adf Show macOs version for --debug-config 2018-03-25 12:47:47 +05:30
Kovid Goyal
47b665a624 Prettyify --debug-config output 2018-03-25 12:36:27 +05:30
Kovid Goyal
7011329761 Add a --debug-config option to provide diagnostic info about the system and kitty 2018-03-25 12:30:56 +05:30
Kovid Goyal
ea24c23fde Allow specifying a system wide kitty config file
The system config is merged with any user specific config files, with
options in the user specific config files having higher priority.

Also follow the full XDG spec for finding config files, including
XDG_CONFIG_DIRS.
2018-03-25 11:47:11 +05:30
Kovid Goyal
be8f2b8106 Create a shortcut to set the tab title
No longer need to use remote control just to change tab titles.
2018-03-25 09:55:02 +05:30
Kovid Goyal
71db024a3b Add history based completion to the ask kitten 2018-03-24 11:49:34 +05:30
Kovid Goyal
3790af6897 Basic ask kitten 2018-03-23 18:03:43 +05:30
Kovid Goyal
dc6ef63ddd Merge branch 'oS' of https://github.com/jubalh/kitty 2018-03-23 15:33:40 +05:30
Michael Vetter
ef762f7a2b Add link to openSUSE package 2018-03-23 10:45:42 +01:00
Kovid Goyal
4ed646e062 Merge branch 'tic2' of https://github.com/jubalh/kitty 2018-03-21 21:17:46 +05:30
Michael Vetter
afbd843388 Mention tic as dependency 2018-03-21 16:42:44 +01:00
Kovid Goyal
18c3598ef2 Merge branch 'desktop' of https://github.com/jubalh/kitty 2018-03-21 20:43:50 +05:30
Michael Vetter
1c2623e609 Have more detailed desktop file category
Let's use the 'TerminalEmulator' category according to the spec:
https://specifications.freedesktop.org/menu-spec/menu-spec-1.0.html
2018-03-21 11:28:57 +01:00
Kovid Goyal
e232863028 Also recognize unicode aliases for macOS modifiers 2018-03-20 15:10:29 +05:30
Kovid Goyal
a89309153d Merge branch 'patch-1' of https://github.com/sle-odoo/kitty 2018-03-18 17:59:27 +05:30
Simon Lejeune
fee7c8e573 update dependencies to run make app
while imagemagick is defined as an optional dependency in the installation section, its presence is indeed mandatory to successfully run `make app` as seen at https://github.com/kovidgoyal/kitty/blob/master/logo/make.py#L27
2018-03-18 10:56:40 +01:00
Kovid Goyal
1a52127c55 version 0.8.2 2018-03-17 17:45:57 +05:30
Kovid Goyal
4920431c3a Update CHANGELOG 2018-03-17 17:40:41 +05:30
Kovid Goyal
773f26b61d Fix screen not being refreshed immediately after moving a window. Fixes #397 2018-03-16 22:00:57 +05:30
Kovid Goyal
16e77d7329 Replace use of PyObject_Str in a couple more places 2018-03-16 21:26:46 +05:30
Kovid Goyal
2cec0908a5 Fix a segfault when getting the contents of the scrollback buffer as text
Fixes #398
2018-03-16 21:10:48 +05:30
Kovid Goyal
b3ef62c188 Merge branch 'i386-fixes' of https://github.com/oleastre/kitty 2018-03-16 20:42:57 +05:30
Olivier Samyn
3385f6fe67 Avoid comparison between signed and unsigned compilation error (32bit) 2018-03-16 09:31:37 +01:00
Kovid Goyal
0170bf636b ... 2018-03-15 20:58:26 +05:30
Kovid Goyal
b2561fd61e Add a facility to specify command line arguments when running kitty from the GUI on macOS
Fixes #395
2018-03-15 20:57:08 +05:30
Kovid Goyal
64b48945d4 Add a focus-tab remote command
Fixes #394
2018-03-15 17:41:49 +05:30
Kovid Goyal
bbfd463357 Dont show a useless traceback when failing to match in a remote command
Fixes #393
2018-03-15 15:45:12 +05:30
Kovid Goyal
2179dab8ed Re-organize kitty.conf into sections
Sections are de-limited by vim folds
2018-03-15 13:07:02 +05:30
Kovid Goyal
801ead7f7f Add ntoe to readme about right click to extend selection 2018-03-15 12:20:46 +05:30
Kovid Goyal
9e4834d58b Right click should only extend selection if there is an existing selection 2018-03-15 12:19:45 +05:30
Kovid Goyal
f38e089de0 Merge branch 'extend-selection' of https://github.com/vsajko/kitty 2018-03-15 12:07:23 +05:30
Kovid Goyal
b252504306 ... 2018-03-15 11:31:37 +05:30
Kovid Goyal
81bcdaf48a Dont show [options] if there are no options in usage message 2018-03-15 11:30:31 +05:30
Kovid Goyal
44f654ff86 Cleanup remote_control.py 2018-03-15 11:26:22 +05:30
Kovid Goyal
84cfa9382f Add argspecs for the remote control command help text 2018-03-15 11:21:13 +05:30
Kovid Goyal
c6da374aa9 Add a configurable keyboard shortcut and remote command to set the font size to a specific value
Fixes #388
2018-03-15 11:15:35 +05:30
Vedran Sajko
d9fe0b2540 Extend selection with right click 2018-03-15 00:09:33 +01:00
Kovid Goyal
5a905dfe7f Add kitty.app to .gitignore
Also have make clean clean linux-package and kitty.app when running
manually.
2018-03-14 12:10:15 +05:30
Kovid Goyal
09b6d6e62b README cleanups 2018-03-14 08:01:10 +05:30
Kovid Goyal
6aa26cf75f Improve the wording for close_on_child_death option 2018-03-13 09:12:06 +05:30
Kovid Goyal
bae8915881 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.
Merge branch 'master' of https://github.com/josswright/kitty
2018-03-13 09:08:45 +05:30
Kovid Goyal
9c46de319e MacOS: Fix restoring of window size not correct on Retina screens
Fixes #382
2018-03-13 08:19:15 +05:30
Joss Wright
994eecadb7 Adds close_on_child_death option for disowned processes. 2018-03-12 21:02:37 +00:00
Kovid Goyal
f7f9ceec34 Consolidate signal handling
Move the signal handler for SIGCHLD into child-monitor.c
Makes for cleaner code.
2018-03-12 21:53:02 +05:30
Kovid Goyal
527255e3a1 Fix compilation with gcc 8
Apparently some nitwit Linux distros have made an unreleased compiler
their default compiler. Fixes #376

Fixes various new warnings that GCC 8 issues
2018-03-12 10:38:12 +05:30
Kovid Goyal
0333da991d ... 2018-03-12 08:11:43 +05:30
Kovid Goyal
2bf8e1b1a3 Allow the name of the lib directory in linux-package to be configurable. Fixes #372 2018-03-12 08:10:53 +05:30
Kovid Goyal
ffc717717c Oops 2018-03-12 07:53:31 +05:30
Kovid Goyal
2c8414db5d Be a bit more specific 2018-03-12 07:52:08 +05:30
Kovid Goyal
32473d7dc7 make clean fallsback to manual cleaning when not in a git checkout
Fixes #379
2018-03-12 07:50:54 +05:30
Kovid Goyal
079f10eebd ... 2018-03-11 22:31:19 +05:30
Kovid Goyal
ae1f305a81 Add kittens to the LOC script 2018-03-11 12:03:23 +05:30
Kovid Goyal
0b1f5209d5 Use CFLAGS and LDFLAGS env vars when building the launcher as well
Fixes #374
2018-03-11 08:12:17 +05:30
Kovid Goyal
fc9980903d Make layout names case-insensitive for enabled_layouts in sessions as well 2018-03-10 23:42:06 +05:30
Kovid Goyal
ad87131926 A bit of documentation for goto_layout 2018-03-10 23:38:18 +05:30
Kovid Goyal
4dfe1b664f Add configurable keyboard shortcuts to switch to a specific layout
Fixes #369
2018-03-10 23:34:47 +05:30
Kovid Goyal
d7b2ac97b5 ... 2018-03-09 21:04:20 +05:30
Kovid Goyal
fd9dac4ca4 Add a small delay when opening the editor with the config file to workaround bugs in vim that cause it to ignore SIGWINCH if it happens early during startup. 2018-03-09 21:03:42 +05:30
Kovid Goyal
8ca9348b07 ... 2018-03-09 18:10:17 +05:30
Kovid Goyal
5cc18fdb4b ... 2018-03-09 18:02:06 +05:30
Kovid Goyal
51b6b325b9 Add a keyboard shortcut to edit the kitty config file easily
Fixes #366
2018-03-09 17:57:09 +05:30
Kovid Goyal
b3cc6f3802 ... 2018-03-09 17:49:31 +05:30
Kovid Goyal
1295094715 README cleanups 2018-03-09 11:00:50 +05:30
Kovid Goyal
af85f58e3d version 0.8.1 2018-03-09 09:21:50 +05:30
Kovid Goyal
3b34821c86 Report unhandled exceptions during loop initialization in the unicode input kitten as well 2018-03-08 13:57:13 +05:30
Kovid Goyal
eaea0b16b5 Fix URL hints not working on macOS
Fixes #363
2018-03-08 13:38:16 +05:30
Kovid Goyal
964ffadc8e DRYer 2018-03-08 13:37:56 +05:30
Kovid Goyal
1b84064e5f ... 2018-03-08 13:01:35 +05:30
Kovid Goyal
dd128a99ac Report unahndled exceptions when starting loop in URL hints kitten 2018-03-08 12:58:54 +05:30
Kovid Goyal
bc829a9712 Use a dedicated static variable for tracking window system initialization 2018-03-06 13:55:06 +05:30
Kovid Goyal
3459c55ebf Add section on reporting bugs to CONTRIBUTING 2018-03-05 21:03:01 +05:30
Kovid Goyal
a631d950e1 Add a guide to contributing 2018-03-05 20:57:57 +05:30
Kovid Goyal
f35e2d76ad Add a note about using --login on macOS when changing the default shell 2018-03-05 14:54:20 +05:30
Kovid Goyal
8db838da9f macOS: Fix shell not starting in login mode on some computers. Fixes #247 2018-03-05 13:30:09 +05:30
Kovid Goyal
7254a880dc Guard against print failure 2018-03-05 13:29:26 +05:30
Kovid Goyal
a0df5b970b Report unhandled exceptions via log_error() as well 2018-03-05 10:28:33 +05:30
Kovid Goyal
54ed08c24e Ensure the extra glyphs array is always re-initialized 2018-03-04 23:36:39 +05:30
Kovid Goyal
97e1ecabdc Render private use unicode characters that are followed by a space as two cell ligatures. Fixes #357 2018-03-04 22:58:24 +05:30
Kovid Goyal
e716f6d35e Replace deprecated ASL with os_log on macOS 2018-03-04 12:28:06 +05:30
Kovid Goyal
c7506496da Unify error logging between python and C 2018-03-04 11:55:47 +05:30
Kovid Goyal
1e3d3df9b8 Remove unused code 2018-03-04 10:59:12 +05:30
Kovid Goyal
ed75ad0d41 Log a timestamp as well when printing error messages from python 2018-03-04 10:44:28 +05:30
Kovid Goyal
fd6159c0b4 Redirect stderr/stdout to console.app when launched via launchd on macOS 2018-03-04 10:17:43 +05:30
Kovid Goyal
976c322b83 Print a couple of errors to stderr instead of stdout 2018-03-04 08:58:36 +05:30
Kovid Goyal
28c1935800 Fix Unicode emoji presentation variant selector causing new fallback font to always be loaded 2018-03-03 21:32:43 +05:30
Kovid Goyal
1edf1524f8 Update bundled glfw 2018-03-03 11:21:09 +05:30
Kovid Goyal
0dfe101c0f brew's python3 handling seems to have changed 2018-03-03 11:09:07 +05:30
Kovid Goyal
a571821f73 Fix compilation on Travis (I hope) 2018-03-03 10:56:07 +05:30
Kovid Goyal
f8e6f87984 Dont use MSG_NOSIGNAL on Apple 2018-03-03 10:51:29 +05:30
Kovid Goyal
c7338bca6f Document how to control kitty from programs not running in a kitty window 2018-03-03 10:38:25 +05:30
Kovid Goyal
15f07f57bf Finish remote control via socket implementation 2018-03-03 10:32:49 +05:30
Kovid Goyal
ec989a45b5 ... 2018-03-03 08:55:05 +05:30
Kovid Goyal
e8286c50b2 Implement sending responses to peers 2018-03-02 12:35:32 +05:30
Kovid Goyal
bfbbe3db14 mutex remove_poll_fd()
It modifies talk_data.fds which can also be modified from the main
thread when adding a writer.
2018-03-02 11:13:47 +05:30
Kovid Goyal
6214a87522 Correct error handling for pthread_join 2018-03-02 11:08:11 +05:30
Kovid Goyal
743ff719c8 join the talk thread on exit
Also shutdown reading on the control socket after message received.
2018-03-02 11:02:18 +05:30
Kovid Goyal
b491eacb9c Ensure the moitor threads are shut down on exit 2018-03-02 10:32:12 +05:30
Kovid Goyal
5bfc89e13f Implement listening on the --listen-on socket 2018-03-02 10:26:19 +05:30
Kovid Goyal
52d2b7b09e Implement --listen-on option 2018-03-02 09:08:58 +05:30
Kovid Goyal
19e59d9575 Start refactoring peer message handling to allow multiple listening sockets and responses 2018-03-02 09:08:58 +05:30
Kovid Goyal
6fe4baf1de Move pthread includes into separate header 2018-03-02 09:08:58 +05:30
Kovid Goyal
bc21ec7551 Have kitty @ use /dev/tty instead of stdout/stdin
Allows redirecting those streams, while still being able to send
messages. Downside is that if user runs it outside kitty there wont
be any useful error message.
2018-03-02 09:08:58 +05:30
Kovid Goyal
aad4a1fd6a Dont fail for non-UTF-8 encoded config files, simply replace the undecodeable chars instead 2018-03-02 08:49:27 +05:30
Kovid Goyal
721595ef04 Ensure config file is always opened with UTF-8 2018-03-02 08:47:13 +05:30
Kovid Goyal
139838c717 Option to print debug information about font fallback 2018-02-28 21:43:40 +05:30
Kovid Goyal
c93238d8c9 Some documentation for the unicode input kitten 2018-02-28 00:29:11 +05:30
Kovid Goyal
5ee1fbe07d Copy the launcher script into linux-package
The launcher script is mostly useless on linux, but is needed
when using kitty.app built from source and might be useful on linux as
well, if running kitty without putting it on PATH.
2018-02-28 00:25:57 +05:30
Kovid Goyal
ee1287211d Typo in makefile app target 2018-02-27 21:06:42 +05:30
Kovid Goyal
94e8be6b1c ... 2018-02-27 20:29:08 +05:30
Kovid Goyal
053ba7f082 Nicer error message when user tries to run logo/make.py with missing tools 2018-02-27 19:56:52 +05:30
Kovid Goyal
e8f3f00792 Handle cols being zero 2018-02-27 18:48:48 +05:30
Kovid Goyal
740a89b77f Treat Ctrl+Alt+letter as Esc,Ctrl+letter
Follows the behavior of some other terminals and is better than
generating nothing, since apparently there are some terminal programs
that use these keys. Fixes #345
2018-02-27 08:46:09 +05:30
Kovid Goyal
1fe936dbbc Fix off by one when restoring cursor position after resize. Fixes #344 2018-02-25 09:15:48 +05:30
Kovid Goyal
11e85ac703 Swapped unicode input name and hex mode prompt strings 2018-02-24 14:51:49 +05:30
Kovid Goyal
4cb99be8e6 version 0.8.0 2018-02-24 11:11:56 +05:30
Kovid Goyal
8cf9fd3cdb ... 2018-02-23 09:38:07 +05:30
Kovid Goyal
b22950bb0a More work on README 2018-02-23 09:36:46 +05:30
Kovid Goyal
f87d62571b Add links to relevant parts of the README in the major features list 2018-02-23 09:13:20 +05:30
Kovid Goyal
42afc56b65 Update CHANGELOG with entry for macos_titlebar_color
Fixes #341
2018-02-22 21:21:33 +05:30
Splinter Suidman
10c3c7c41f Implement setting title bar color. 2018-02-22 21:19:55 +05:30
Kovid Goyal
e236539e3a Option for changing macOS titlebar color
Note that the actual implementation is still needed, this is just the
option definition/parsing code.
2018-02-22 15:07:24 +05:30
Kovid Goyal
1fc605cbc0 Move needs_special_handling definition into keys.c 2018-02-22 14:37:51 +05:30
Kovid Goyal
1e4963e727 Revert change to make alt+bs emit werase
Most linux terminals emit ESC+BS instead. And iTerm2 in Esc+ mode also
emits this. So go with the majority for greatest ecosystem
compatibility. Fixes #264
2018-02-22 13:51:14 +05:30
Kovid Goyal
95384d437a Use the nice array based interface for fcntl.ioctl 2018-02-20 17:01:14 +05:30
Kovid Goyal
e44910212c Sample code to use TIOCGWINSZ in python 2018-02-20 16:47:06 +05:30
Kovid Goyal
36b8802f43 Add a link to the graphics protocol page in the README 2018-02-20 16:32:57 +05:30
Kovid Goyal
a6b0554edc Merge branch 'master' of https://github.com/Polynomial-C/kitty 2018-02-20 15:42:23 +05:30
Lars Wendler
c1df5eac57 Added Gentoo to the list of Linux packagers. 2018-02-20 11:09:43 +01:00
Kovid Goyal
68bf1be8d0 render on cursor shape change as well 2018-02-20 11:54:35 +05:30
Kovid Goyal
d5edea3c7f render on cursor position change as well 2018-02-20 11:27:39 +05:30
Kovid Goyal
eb580f5ead Update CHANGELOG for padding color 2018-02-20 10:10:28 +05:30
Kovid Goyal
c99588082b Fix incorrect bottom blank rects in vertical and tall layouts 2018-02-20 10:06:37 +05:30
Kovid Goyal
96d241e9f3 Fix enabled_layouts in session files not being respected 2018-02-20 10:01:41 +05:30
Kovid Goyal
8f423c6d8f Fix padding color not tracking active window bg color 2018-02-20 09:52:19 +05:30
Kovid Goyal
f25ecedcab Always set the padding color to current window background 2018-02-20 09:43:59 +05:30
Kovid Goyal
2913c1aedc Change the color of the padding and margins to match the current background color of the window, when only a single window is visible.
Fixes #323
2018-02-20 09:43:49 +05:30
Kovid Goyal
5a15f7e8ad Sync to monitor refreshes by default
Prevents tearing while scrolling. See #318

Reduce draw calls while processing mouse move events to make latency
while selecting with the mouse acceptable. Also add a config option to
turn off monitor syncing, so people with keyboard repeat rates higher
than their monitor's refresh rate can use it.
2018-02-20 09:38:00 +05:30
Kovid Goyal
e6cb64baa5 Revert "Enable syncing of redraws to monitor refresh rate"
This reverts commit 6a51096304.
Turns out that dynamically changing the swap interval causes bad flicker
when rendering on X11 in a fullscreen window (or more generally, when
only a single X11 window is visible).
2018-02-20 08:32:33 +05:30
Kovid Goyal
537023ef5a Fix simple_render_screen() broken by multiple OSwindow draw calls optimization 2018-02-20 05:52:32 +05:30
Kovid Goyal
70125e0de5 Only build asan-launcher if --sanitize is given
Fixes #328
2018-02-16 08:56:30 +05:30
Kovid Goyal
8c5646b81b Merge branch 'build-package-fix' of https://github.com/13k/kitty 2018-02-16 08:22:30 +05:30
Kiyoshi '13k' Murata
bd741365cb Fix packaging trying to modify unrelated files
`package()` method in `setup.py` tries to compile and chmod all files
under the directory given by `--prefix PREFIX`.

If PREFIX is a directory containing other distributions (like
`/usr/local` or `$HOME/.local`), then it would try to compile - using
possibly the wrong python version - and chmod all files within PREFIX.
2018-02-15 19:46:51 -02:00
Kovid Goyal
efd9843920 Update CHANGELOG 2018-02-15 17:42:32 +05:30
Kovid Goyal
0bb8ec61e5 Fix regression caused by overlay window support code
Closing a window would not update the window layout, depending on the
position of the window in the current layout.
2018-02-15 09:16:00 +05:30
Kovid Goyal
6a51096304 Enable syncing of redraws to monitor refresh rate
Now, when large changes, such as scrolling occur, the OpenGL back buffer
swapping is synchronized to the monitor refresh rate (if supported by
the OS). This eliminates tearing artifacts when scrolling the screen fast.
Fixes #318
2018-02-14 20:59:58 +05:30
Kovid Goyal
807c8f9005 Do not send history buf in alternate screen mode 2018-02-14 20:46:12 +05:30
Kovid Goyal
9c21610215 Use a single generic function to get text and ANSI representations of all buffers 2018-02-14 20:38:53 +05:30
Kovid Goyal
97b4256ba4 Remove unused code 2018-02-14 18:50:52 +05:30
Kovid Goyal
e4b643fdaf Show a nice message when no URLs are found 2018-02-14 18:36:57 +05:30
Kovid Goyal
0bda7b3679 When running kittens respect current screen scroll 2018-02-14 18:35:32 +05:30
Kovid Goyal
51e3e9997c Allow clicking URLs via keybaord hints by pressing ctrl+shift+e 2018-02-14 17:18:57 +05:30
Kovid Goyal
9f52d427e5 http->https in readme 2018-02-14 17:08:09 +05:30
Kovid Goyal
ad706f9924 Make the list of URL prefixes configurable 2018-02-14 12:48:44 +05:30
Kovid Goyal
57cf789c75 Finish up the URL hints kitten 2018-02-14 12:34:01 +05:30
Kovid Goyal
62174fdbcc DRYer 2018-02-14 11:19:58 +05:30
Kovid Goyal
dff7ddea39 Make the default intense black a little lighter
Brings it closer to the color definitions at:
https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
2018-02-14 11:17:07 +05:30
Kovid Goyal
0c3257b6b9 Start work on url hints kitten 2018-02-14 11:01:17 +05:30
Kovid Goyal
4bbf5c0bc9 Allow choosing a match by index in the search by name panel as well 2018-02-13 11:25:45 +05:30
Kovid Goyal
b0820f6e13 Use . instead of r for the index char 2018-02-13 11:10:35 +05:30
Kovid Goyal
b0f5d8dfdc Fix table row generation for non-name unicode input panels 2018-02-13 10:50:06 +05:30
Kovid Goyal
67e0d3723a Implement favorites for unicode input 2018-02-13 10:33:55 +05:30
Kovid Goyal
a36f4a4670 Add a Emoji panel to the unicode input kitten 2018-02-13 09:06:39 +05:30
Kovid Goyal
dab852c412 Implement searching for unicode characters by name 2018-02-12 14:08:54 +05:30
Kovid Goyal
193ac6cd81 Forgot to remove from recent when adding to top of list 2018-02-12 12:39:02 +05:30
Kovid Goyal
34ca8419bb Finish chose unicode symbol by recently used 2018-02-12 10:19:29 +05:30
Kovid Goyal
6094aabe26 ... 2018-02-12 10:19:29 +05:30
Kovid Goyal
19648257f2 Have wcwidth() ignore SGR 2018-02-12 10:19:29 +05:30
Kovid Goyal
3363a9459b Implement drawing of titlebar, remembering of mode and fix various bugs in tui 2018-02-12 10:19:29 +05:30
Kovid Goyal
4d92f4871a Mark names.h as being generated 2018-02-12 10:19:29 +05:30
Kovid Goyal
f0d4ad16d2 Work on search by name in unicode input 2018-02-12 10:19:29 +05:30
Kovid Goyal
2cc8402458 A FAQ entry about terminfo over SSH 2018-02-12 10:19:12 +05:30
Kovid Goyal
1c79320133 Fix #319 2018-02-11 13:03:20 +05:30
Kovid Goyal
8b099d969f oops 2018-02-11 11:37:05 +05:30
Kovid Goyal
61157e8763 Remove the passing of swap_interval as a constructor parameter
It was unused and makes it more complicated to override via an option,
if needed.
2018-02-11 11:14:48 +05:30
Kovid Goyal
f97d078f81 Fix kitty saving incorrect window heights
Typo caused saved window height to be actually the window width
2018-02-10 10:00:54 +05:30
Kovid Goyal
a14cf81422 Store cached window sizes in the cache dir rather than the config dir
A side-effect is that on the very next start kitty wont remember window
sizes, apologies for the on convenience.
2018-02-10 10:00:47 +05:30
Kovid Goyal
feeba9b5f5 Add a note about where kitty @ messaging works 2018-02-10 07:35:36 +05:30
Kovid Goyal
b1b76ca250 Redraw windows when the window manager tells us they have been damaged
Needed on systems with non-compositing window managers. Fix #317
2018-02-10 06:54:30 +05:30
Kovid Goyal
e119ed0daa Ignore harmless KeyError while modifying selectors 2018-02-09 20:20:18 +05:30
Kovid Goyal
8c18486836 Module with all the data for unicode entry by character name 2018-02-09 19:56:25 +05:30
Kovid Goyal
b6ed3951bc Add the kittens dir when building the package 2018-02-09 16:59:15 +05:30
Kovid Goyal
ea178ea689 Forgot to marcj launcher script executable 2018-02-09 16:48:04 +05:30
Kovid Goyal
973bbf3f8a Fix #316 2018-02-09 16:44:52 +05:30
Kovid Goyal
3068846d8f Implement using Ctrl+Shift+u to input unicode
Fixes #302
2018-02-08 22:47:17 +05:30
Kovid Goyal
194454715f Basic unicode input works 2018-02-08 22:16:39 +05:30
Kovid Goyal
ac02e053ee Dont save/restore DECCOLM since it is actually unused in kitty 2018-02-08 22:12:51 +05:30
Kovid Goyal
75db360929 ... 2018-02-08 12:57:57 +05:30
Kovid Goyal
f9141120aa A few fixes for the extended key protocol 2018-02-08 12:57:16 +05:30
Kovid Goyal
d19f9e5b96 Fix error handling in tui.loop 2018-02-08 12:33:27 +05:30
Kovid Goyal
213a223861 Typo in incremental decoder init 2018-02-08 11:56:34 +05:30
Kovid Goyal
ef9a24932f Start work on unicode input kitten 2018-02-08 11:00:57 +05:30
Kovid Goyal
bf5b0070c1 Add note about terminfo property used for styled underlines 2018-02-08 10:29:37 +05:30
Kovid Goyal
2ab6f57159 Add non-standard terminfo capabilities
Advertises support for truecolor and styled underlines.
The truecolor properties are used by at least tmux and neovim.
2018-02-08 10:26:13 +05:30
Kovid Goyal
77d46630d7 Update bundled glfw 2018-02-08 09:15:40 +05:30
Kovid Goyal
b78612aebd Entry point to run python on cmd line and from scripts 2018-02-08 09:11:13 +05:30
Kovid Goyal
0f63d1bfa7 ... 2018-02-07 22:41:03 +05:30
Kovid Goyal
66a897cc80 Add interrupt and eot callbacks 2018-02-07 19:46:53 +05:30
Kovid Goyal
3828511051 forgot to include in last commit 2018-02-07 14:40:23 +05:30
Kovid Goyal
226a89c184 Add JSON definition of key encoding 2018-02-07 14:39:11 +05:30
Kovid Goyal
e3b4919c17 Finish up tui.Loop 2018-02-07 13:30:34 +05:30
Kovid Goyal
168bc58635 The kittens learn about mice 2018-02-07 12:40:04 +05:30
Kovid Goyal
f4f0b8fd5f Kittens learn about bracketed paste 2018-02-07 10:39:05 +05:30
Kovid Goyal
13c034c613 Add link to ncurses FAQ on bce as well 2018-02-07 08:06:24 +05:30
Kovid Goyal
35f1658f4a Key and text handler for the kittens 2018-02-06 20:58:46 +05:30
Kovid Goyal
328f22adab Let update_cell_emtrics() know if it is being called in response to a DPI change 2018-02-06 20:12:57 +05:30
Kovid Goyal
22cf67b407 More work on tui.loop 2018-02-06 20:08:28 +05:30
Kovid Goyal
0c7d51dd9d Avoid unnecessary draw calls with multiple OS Windows
Do not ask the GPU to draw and swap buffers for OS Windows that are not
dirtied. See #313
2018-02-06 19:32:24 +05:30
Kovid Goyal
fa87ce72a8 A terminal input parse helper for the kittens 2018-02-06 17:29:05 +05:30
Kovid Goyal
ff2e5b3966 Avoid unnecessary calls to mark_for_codepoint 2018-02-06 11:23:39 +05:30
Kovid Goyal
96c93fa252 Add support for emoji presentation when choosing fallback fonts 2018-02-06 11:11:22 +05:30
Kovid Goyal
e830b7edf7 Add emoji presentation variation selector support to wcswidth() as well 2018-02-06 10:16:23 +05:30
Kovid Goyal
9c874f6685 Add support for the unicode emoji presentation variation selector 2018-02-06 09:52:55 +05:30
Kovid Goyal
b8ab15bea8 Fix incorrect window border drawing for overlay windows 2018-02-06 09:41:19 +05:30
Kovid Goyal
6c7247f726 Disable display_scrollback for overlay windows 2018-02-06 09:36:36 +05:30
Kovid Goyal
7674aa4e7c Add FAQ entry about bce in vim 2018-02-06 08:55:42 +05:30
Kovid Goyal
ebe97bf492 Get rid of safe_wcwidth
Since wcwidth is now in our control, we dont need to wrap it.
2018-02-05 10:11:30 +05:30
Kovid Goyal
fbe4d036d8 Have wcwidth() return 0 for marks instead of -1
Since kitty always treats marks as combinig chars, this allows us to
remove a few unnecessary branches
2018-02-05 10:06:05 +05:30
Kovid Goyal
c572b8bb1a Refactor screen_draw()
Make it a little faster and also clearer, splitting out
the logic for combining chars into a separate function.
2018-02-05 09:55:17 +05:30
Kovid Goyal
59a5e23466 Almost always resize glyphs that are too wide
When using FreeType, rescale glyphs that dont fit if they are more than
1px wider than the cell rather than 25% of the cell width. This matches
the behavior of CoreText and is needed for rendering the various Emoji
with default text presentation (i.e. emoji that have wcwidth() == 1)
2018-02-05 09:21:14 +05:30
Kovid Goyal
b9857f9499 Only consider emoji characters with emoji presentation to have width two
Fixes #308
2018-02-05 09:21:13 +05:30
Kovid Goyal
b667e3a577 DRYer 2018-02-05 09:19:12 +05:30
Kovid Goyal
240b1475c7 Fix regression that caused a few ligatures to not render correctly in rare circumstances
This was caused by cache corruption. is_special_glyph() and
is_empty_glyph() were treading over each other if called in
the right sequence. Fixes #303
2018-02-05 00:04:05 +05:30
Kovid Goyal
3f24e5b571 A few more places to remove wcwidth from 2018-02-04 22:47:02 +05:30
Kovid Goyal
fc7ec1d3f7 Get rid of the option to use the system wcwidth
The system wcwidth() is often wrong. Not to mention that if you SSH into
a different machine, then you have a potentially different wcwidth. The
only sane way to deal with this is to use the unicode standard.
2018-02-04 21:02:30 +05:30
Kovid Goyal
452ff02b15 Oops added extra file in previous commit 2018-02-04 20:34:02 +05:30
Kovid Goyal
c2cb43cc07 Framework for kittens 2018-02-04 20:23:52 +05:30
Kovid Goyal
6736fd3835 Get rid of the query escape code for styled underlines 2018-02-04 16:40:48 +05:30
Kovid Goyal
a1bd69a9c2 Fix incorrect handling of CSI s/r/u 2018-02-04 16:16:57 +05:30
Kovid Goyal
561e63ee2d Use an overlay window for browsing the scrollback buffer in less 2018-02-04 11:28:13 +05:30
Kovid Goyal
647b95631f Add tests for layout operations with an overlay window 2018-02-04 11:28:13 +05:30
Kovid Goyal
aa518b2520 Add tests for basic layout operations 2018-02-04 11:28:13 +05:30
Kovid Goyal
51c0f01539 Support for overlay windows in the layout manager 2018-02-04 11:28:02 +05:30
Kovid Goyal
cc9a19b303 Move set_active_window into base class
It was only used in a single sub-class anyway
2018-02-02 13:56:25 +05:30
Kovid Goyal
2ee9844c2b Track cursor position explicitly during rewrap
Instead of using heuristics to position the cursor after a resize, track
the position during re-wrapping and place the cursor at the re-wrapped
position. Fixes #242 (I hope)
2018-02-02 13:06:18 +05:30
Kovid Goyal
4c53a74fa9 Fix resize debounce implementation 2018-02-02 13:06:18 +05:30
Kovid Goyal
9b0ffdbf5a A spot of refactoring 2018-02-02 11:19:42 +05:30
Kovid Goyal
2777b89e45 Update bundled glfw 2018-02-01 13:03:26 +05:30
Kovid Goyal
7f90430414 Designating charsets should not switch to them
Fixes #301
2018-02-01 12:05:07 +05:30
Kovid Goyal
ee4e56edf9 ... 2018-02-01 10:26:17 +05:30
Kovid Goyal
93c83d03c0 More robust private mode restore 2018-02-01 09:45:34 +05:30
Kovid Goyal
855b3de473 Implement support for save/restore of DEC private modes (CSI ? s/r) 2018-02-01 09:19:43 +05:30
Kovid Goyal
a9be05c885 ... 2018-01-31 18:13:36 +05:30
135 changed files with 71444 additions and 3663 deletions

1
.gitattributes vendored
View File

@@ -10,3 +10,4 @@ kitty/khrplatform.h linguist-generated=true
kitty/glfw-wrapper.* linguist-generated=true
glfw/*.c linguist-vendored=true
glfw/*.h linguist-vendored=true
kittens/unicode_input/names.h linguist-generated=true

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
tags
build
linux-package
kitty.app
logo/*.iconset
compile_commands.json
glad/out

View File

@@ -94,7 +94,7 @@ install: |
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
if [[ "$USE_BREW" == "1" ]]; then
brew update;
brew install python3;
brew upgrade python;
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
else
mkdir -p $SW;

View File

@@ -3,6 +3,141 @@ Changelog
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
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]
-----------------------------
- A framework for kittens, that is, small terminal programs designed to run
inside kitty and extend its capabilities. Examples include unicode input and
selecting URLs with the keyboard.
- Input arbitrary unicode characters by pressing Ctrl+Shift+u. You can choose
characters by name, by hex code, by recently used, etc. There is even and
editable Favorites list.
- Open URLs using only the keyboard. kitty has a new "hints mode". Press
Ctrl+Shift+e and all detected URLs on the screen are highlighted with a key
to press to open them. The facility is customizable so you can change
what is detected as a URL and which program is used to open it.
- Add an option to change the titlebar color of kitty windows on macOS
- Only consider Emoji characters with default Emoji presentation to be two
cells wide. This matches the standard. Also add support for the Unicode Emoji
variation presentation selector.
- Prevent video tearing during high speed scrolling by syncing draws
to the monitor's refresh rate. There is a new configuration option to
control this ``sync_to_monitor``.
- When displaying only a single window, use the default background color of the
window (which can be changed via escape codes) as the color for the margin
and padding of the window.
- Add some non standard terminfo capabilities used by neovim and tmux.
- Fix large drop in performance when using multiple top-level windows on macOS
- Fix save/restore of window sizes not working correctly.
- Remove option to use system wcwidth(). Now always use a wcwidth() based on
the Unicode standard. Only sane way.
- Fix a regression that caused a few ligature glyphs to not render correctly in
rare circumstances.
- Browsing the scrollback buffer now happens in an overlay window instead of a
new window/tab.
version 0.7.1 [2018-01-31]
---------------------------

25
CONTRIBUTING.md Normal file
View 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.

View File

@@ -31,4 +31,4 @@ logo/kitty.iconset/icon_256x256.png: logo/kitty.svg logo/make.py
rendered_logo: logo/kitty.iconset/icon_256x256.png
app: rendered_logo
python3 setup.py app $(VVAL)
python3 setup.py kitty.app $(VVAL)

View File

@@ -6,11 +6,13 @@
:sc_close_window: pass:quotes[`ctrl+shift+w`]
:sc_copy_to_clipboard: pass:quotes[`ctrl+shift+c`]
:sc_decrease_font_size: pass:quotes[`ctrl+shift+minus`]
:sc_edit_config_file: pass:quotes[`ctrl+shift+f2`]
:sc_eighth_window: pass:quotes[`ctrl+shift+8`]
:sc_fifth_window: pass:quotes[`ctrl+shift+5`]
:sc_first_window: pass:quotes[`ctrl+shift+1`]
:sc_fourth_window: pass:quotes[`ctrl+shift+4`]
:sc_increase_font_size: pass:quotes[`ctrl+shift+equal`]
:sc_input_unicode_character: pass:quotes[`ctrl+shift+u`]
:sc_move_tab_backward: pass:quotes[`ctrl+shift+,`]
:sc_move_tab_forward: pass:quotes[`ctrl+shift+.`]
:sc_move_window_backward: pass:quotes[`ctrl+shift+b`]
@@ -29,6 +31,7 @@
:sc_previous_tab: pass:quotes[`ctrl+shift+left`]
:sc_previous_window: pass:quotes[`ctrl+shift+[`]
:sc_restore_font_size: pass:quotes[`ctrl+shift+backspace`]
:sc_run_simple_kitten_text_url_hints: pass:quotes[`ctrl+shift+e`]
:sc_scroll_end: pass:quotes[`ctrl+shift+end`]
:sc_scroll_home: pass:quotes[`ctrl+shift+home`]
:sc_scroll_line_down: pass:quotes[`ctrl+shift+down` or `ctrl+shift+j`]
@@ -36,6 +39,7 @@
:sc_scroll_page_down: pass:quotes[`ctrl+shift+page_down`]
:sc_scroll_page_up: pass:quotes[`ctrl+shift+page_up`]
:sc_second_window: pass:quotes[`ctrl+shift+2`]
:sc_set_tab_title: pass:quotes[`ctrl+shift+alt+t`]
:sc_seventh_window: pass:quotes[`ctrl+shift+7`]
:sc_show_scrollback: pass:quotes[`ctrl+shift+h`]
:sc_sixth_window: pass:quotes[`ctrl+shift+6`]
@@ -51,29 +55,31 @@ image::https://travis-ci.org/kovidgoyal/kitty.svg?branch=master[Build status, li
== Major Features
* Uses OpenGL for rendering, offloads rendering to the GPU for lower system
load and buttery smooth scrolling. Uses threaded rendering to minimize input
* Uses OpenGL for rendering, offloads rendering to the GPU for link:#performance[lower system
load] and buttery smooth scrolling. Uses threaded rendering to minimize input
latency.
* Supports all modern terminal features: graphics (images), unicode,
* Supports all modern terminal features: link:graphics-protocol.asciidoc[graphics (images)], unicode,
true-color, OpenType ligatures, mouse protocol, focus tracking, bracketed paste and so on.
* Supports tiling multiple terminal windows side by side in different
layouts without needing to use an extra program like tmux
link:#layouts[layouts] without needing to use an extra program like tmux
* Can be 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.
* Supports startup sessions which allow you to specify the window/tab layout,
* 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].
* Supports link:#startup-sessions[startup sessions] which allow you to specify the window/tab layout,
working directories and programs to run on startup.
* Cross-platform support: kitty currently works on Linux and macOS, but because
it uses only OpenGL for rendering, it should be trivial to port to other
platforms.
* Allows you to open the scrollback buffer and window contents in a separate
* Allows you to open link:#the-scrollback-buffer[the scrollback buffer] in a separate
window using arbitrary programs of your choice. This is useful for browsing
the history comfortably in a pager or editor and also for doing things like
opening URLs using only the keyboard.
the history comfortably in a pager or editor.
image::screenshot.png?raw=true[Screenshot, showing three programs in the "Tall" layout]
@@ -97,7 +103,7 @@ the following dependencies are installed first.
* ImageMagick (optional, needed to use the `kitty icat` tool to display images in the terminal)
* gcc or clang (required only for building)
* pkg-config (required only for building)
* For building on Linux in addition to the above dependencies you might also need to install the `-dev` packages for `xcursor`, `xrandr`, `xinerama` and `xkbcommon-x11`, if they are not already installed by your distro.
* For building on Linux in addition to the above dependencies you might also need to install the `-dev` packages for `xcursor`, `xrandr`, `xinerama`, `libgl1-mesa` and `xkbcommon-x11`, if they are not already installed by your distro.
=== Install and run from source
@@ -125,6 +131,9 @@ python3 .
* NixOS / nixpkgs: https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/kitty/default.nix
* Gentoo: https://packages.gentoo.org/packages/x11-terms/kitty
* openSUSE: https://build.opensuse.org/package/show/X11:terminals/kitty
=== macOS packages
@@ -194,6 +203,7 @@ windows are:
|Next layout | {sc_next_layout}
|Move tab forward | {sc_move_tab_forward}
|Move tab backward | {sc_move_tab_backward}
|Set tab title | {sc_set_tab_title}
|===
@@ -226,7 +236,10 @@ windows are:
|Decrease font size | {sc_decrease_font_size}
|Restore font size | {sc_restore_font_size}
|Toggle fullscreen | {sc_toggle_fullscreen}
|Input unicode character | {sc_input_unicode_character}
|Click URL using the keyboard | {sc_run_simple_kitten_text_url_hints}
|Pass current selection to program | {sc_pass_selection_to_program}
|Edit kitty config file| {sc_edit_config_file}
|===
@@ -234,35 +247,60 @@ windows are:
You can control kitty from the command line/scripts by sending it messages.
You can tell kitty to open/close/rename tabs and windows. You can even send
arbitrary input/text to any specified window. Messages can be sent using
`kitty @`. Note that you must set `allow_remote_control yes` in your
kitty.conf to use this feature. It even works over SSH connections. This
feature is best illustrated with a link:remote-control.asciidoc[tutorial].
arbitrary input/text to any specified window. Messages can be sent using `kitty
@`. Note that you must set `allow_remote_control yes` in your
link:kitty/kitty.conf[kitty.conf] to use this feature. It even works over SSH
connections. This feature is best illustrated with a
link:remote-control.asciidoc[tutorial].
== The scrollback buffer
kitty supports scrolling back to view history, just like most terminals. You
can use either the <<scrolling-shortcuts,keyboard shortcuts>> or the mouse
scroll wheel to do so. However, kitty has an extra, neat feature. Sometimes you
need to explore the scrollback buffer in more detail, maybe search for some
scroll wheel to do so. However, kitty has an extra, neat feature. Sometimes
you need to explore the scrollback buffer in more detail, maybe search for some
text or refer to it side-by-side while typing in a follow-up command. kitty
allows you to do this by pressing the {sc_show_scrollback} key-combination,
which will open the scrollback buffer in a new window, using your favorite
pager program (which is `less` by default). You can then explore the scrollback
buffer using whatever program you normally use. Colors and text formatting are
preserved.
which will open the scrollback buffer in your favorite pager program (which is
`less` by default). Colors and text formatting are preserved. You can explore
the scrollback pager comfortably within the pager.
== Unicode input
You can input unicode characters by name, hex code, recently used and even an editable favorites list.
Press {sc_input_unicode_character} to start the unicode input widget, shown below.
image::unicode.png?raw=true[Unicode input widget, showing selection of unicode characters by name]
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.
In Name mode you instead type words from the character name and use the arrow
keys/tab to select the character from the displayed matches. You can also type
a leading period and the index for the match if you dont like to use arrow
keys.
== URL hints
kitty has a _hints mode_ to click URLs visible on the screen by using only the keyboard.
Press {sc_run_simple_kitten_text_url_hints} to activate hints mode, as shown below.
image::hints_mode.png?raw=true[URL hints mode]
== Miscellaneous features
You can double click to select a word and triple click to select a line. You can also
hold down `ctrl+shift` and click on a URL to open it in a browser.
* You can also hold down `ctrl+shift` and click on a URL to open it in a browser.
* You can double click to select a word and triple click to select a line.
* You can right click to extend a previous selection
== Layouts
Currently, there are three layouts available,
Currently, there are five layouts available,
* Stack -- Only a single maximized window is shown at a time
* Tall -- One window is shown full height on the left, the rest of the windows are shown one below the other on the right
@@ -270,7 +308,9 @@ Currently, there are three layouts available,
* Horizontal -- All windows are shown side-by-side
* Vertical -- All windows are shown one below the other
You can switch between layouts using the {sc_next_layout} key combination.
You can switch between layouts using the {sc_next_layout} key combination. You can
also create shortcuts to select particular layouts, and choose which layouts
you want to enable/disable, see link:kitty/kitty.conf[kitty.conf] for examples.
Writing a new layout only requires about fifty lines of code, so if there is
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
@@ -279,13 +319,17 @@ a pull request!
== Configuration
kitty is highly customizable, everything from keyboard shortcuts, to
painting frames-per-second. See the heavily commented
link:kitty/kitty.conf[default config file]. By default kitty looks for a
config file in the OS config directory (usually
`~/.config/kitty/kitty.conf` on Linux and
kitty is highly customizable, everything from keyboard shortcuts, to painting
frames-per-second. See the heavily commented link:kitty/kitty.conf[default
config file] for an overview of all customization possibilities.
By default kitty looks for a config file in the OS config directories (usually
`~/.config/kitty/kitty.conf` and additionally
`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a specific
path via the `--config` option.
path via the `--config` option or use the `KITTY_CONFIG_DIRECTORY` environment
variable. See the help for the `--config` option in `kitty --help` for full
details.
== Startup Sessions
@@ -348,6 +392,8 @@ smoothness.
There are two parameters you can tune to adjust the performance. ``repaint_delay``
and ``input_delay``. These control the artificial delays introduced into the
render loop to reduce CPU usage. See the link:kitty/kitty.conf[config file] for details.
See also the ``sync_to_monitor`` option to further decrease latency at the cost
of some link:https://en.wikipedia.org/wiki/Screen_tearing[tearing] while scrolling.
You can generate detailed per-function performance data using
link:https://github.com/gperftools/gperftools[gperftools]. Build kitty with
@@ -400,6 +446,8 @@ installs the terminfo file and `kitty` that installs the main program.
This allows users to install the terminfo file on servers into which they ssh,
without needing to install all of kitty.
You also need `tic` to compile the terminfo files, it is usually found in the development package of `ncurses`
This applies to creating packages for kitty for macOS package managers such as
brew or MacPorts as well.
@@ -408,31 +456,25 @@ brew or MacPorts as well.
=== Some special symbols are rendered small/truncated in kitty?
The number of cells a unicode character takes up are controlled by the
`wcwidth()` system function. If wcwidth() returns 2 then kitty will render the
character in two cells, otherwise it will render it in one cell. Often the
system `wcwidth()` is old/outdated. You can use the `use_system_wcwidth=no`
setting in your kitty.conf to workaround this. But note that it might cause
other issues, since now kitty and the programs running inside it may not agree
on how wide characters should be. When a symbol does not fit, it will either be
rescaled to be smaller or truncated (depending on how much extra space it
needs). This is often different from other terminals which just let the
character overflow into neighboring cells, leading to ugly artifacts.
The number of cells a unicode character takes up are controlled by the unicode
standard. All characters are rendered in a single cell unless the unicode
standard says they should be rendered in two cells. When a symbol does not fit,
it will either be rescaled to be smaller or truncated (depending on how much
extra space it needs). This is often different from other terminals which just
let the character overflow into neighboring cells, which is fine if the
neighboring cell is empty, but looks terrible if it is not.
In addition to the problem with `wcwidth()` above, some programs, like
powerline, vim with fancy gutter symbols/status-bar, etc. use unicode
characters from the private use area to represent symbols. Often these symbols
are square and should be rendered in two cells. However, since private use
area symbols all have `wcwdith() == 1` kitty renders them either smaller or
truncated. The correct solution for this is to use either use different symbols
that are not square, or to use a font that defines ligatures with the space
character for these symbols. See
link:https://github.com/kovidgoyal/kitty/issues/182[#182] for a discussion of
the approach using ligatures.
Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc.
misuse unicode characters from the private use area to represent symbols. Often
these symbols are square and should be rendered in two cells. However, since
private use area symbols all have their width set to one in the unicode
standard, kitty renders them either smaller or truncated. The exception is if
these characters are followed by a space or empty cell in which case kitty
makes use of the extra cell to render them in two cells.
=== How do I build kitty.app on macOS?
Install `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
And run:
```
@@ -448,24 +490,51 @@ Note that the released kitty.dmg includes all dependencies, unlike the
link:https://github.com/kovidgoyal/build-calibre[build-calibre] however, that
is designed to run on Linux and is not for the faint of heart.
== A tribute
While over the decades I am sure many people have contributed to the
development of the terminal emulator space, there is one individual in
particular I would like to thank. link:http://invisible-island.net[Thomas E.
Dickey], the creator of xterm. xterm is the most comprehensive and
feature-rich terminal emulator I have had the pleasure of using. As I worked on
kitty, I ran headlong into more and more gnarly corners of terminal behavior.
On all those occasions, either the excellent documentation at
link:http://invisible-island.net/xterm/ctlseqs/ctlseqs.html[xterm-ctlseqs] or
investigating the behavior and code of xterm or vttest were invaluable tools to
aid my understanding.
=== Using a color theme with a background color does not work well in vim?
My achievements, if any, in developing kitty would not have been possible without
his prior work and the generous sharing of knowledge accumulated over decades.
First make sure you have not changed the TERM environment variable, it should
be `xterm-kitty`. vim uses *background color erase* even if the terminfo file
does not contain the `bce` capability. This is a bug in vim. You can work around
it by adding the following to your vimrc:
Thank you.
....
let &t_ut=''
....
See link:https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc#setting-text-styles-colors-in-arbitrary-regions-of-the-screen[here]
for why kitty does not support background color erase.
=== I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
This happens because the kitty terminfo files are not available on the server.
Either install kitty on the server, or simply copy over the terminfo files,
using:
....
ssh myserver mkdir -p \~/.terminfo
infocmp xterm-kitty | ssh myserver "cat > /tmp/kitty.terminfo"
ssh myserver tic -x -o \~/.terminfo /tmp/kitty.terminfo
....
Really, the correct solution for this is to convince the OpenSSH maintainers to
have ssh do this automatically when connecting to a server, so that all
terminals work transparently.
=== 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
`~/Library/Preferences/kitty/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
@@ -473,4 +542,4 @@ http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
https://en.wikipedia.org/wiki/C0_and_C1_control_codes
http://vt100.net/
https://vt100.net/

View File

@@ -20,6 +20,17 @@ def remote_control(args):
main(args)
def runpy(args):
sys.argv = ['kitty'] + args[2:]
exec(args[1])
def launch(args):
import runpy
sys.argv = args[1:]
runpy.run_path(args[1], run_name='__main__')
def namespaced(args):
func = namespaced_entry_points[args[1]]
func(args[1:])
@@ -29,6 +40,8 @@ entry_points = {
# These two are here for backwards compat
'icat': icat,
'list-fonts': list_fonts,
'runpy': runpy,
'launch': launch,
'@': remote_control,
'+': namespaced,

View File

@@ -15,7 +15,7 @@ with open('terminfo/kitty.terminfo', 'w') as f:
f.write(generate_terminfo())
os.environ['TERMINFO'] = os.path.join(base, 'terminfo')
subprocess.check_call(['tic', 'terminfo/kitty.terminfo'])
subprocess.check_call(['tic', '-x', 'terminfo/kitty.terminfo'])
# On OS X tic puts the compiled database into a different directory
try:
os.mkdir('terminfo/78')

View File

@@ -1,2 +1,2 @@
#!/bin/bash
cloc --exclude-list-file <(echo -e 'kitty/wcwidth-std.h\nkitty/glfw.c\nkitty/keys.h\nkitty/charsets.c\nkitty/unicode-data.c\nkitty/key_encoding.py\nkitty/rgb.py\nkitty/gl.h\nkitty/gl-wrapper.h\nkitty/gl-wrapper.c\nkitty/khrplatform.h\nkitty/glfw-wrapper.h\nkitty/glfw-wrapper.c\nkitty/emoji.h') kitty
cloc --exclude-list-file <(echo -e 'kitty/wcwidth-std.h\nkitty/glfw.c\nkitty/keys.h\nkitty/charsets.c\nkitty/unicode-data.c\nkitty/key_encoding.py\nkitty/rgb.py\nkitty/gl.h\nkitty/gl-wrapper.h\nkitty/gl-wrapper.c\nkitty/khrplatform.h\nkitty/glfw-wrapper.h\nkitty/glfw-wrapper.c\nkitty/emoji.h\nkittens/unicode_input/names.h') kitty kittens

View File

@@ -4,16 +4,15 @@
import os
import sys
from collections import defaultdict
from contextlib import contextmanager
from datetime import date
from functools import partial
from html.entities import html5
from itertools import groupby
from operator import itemgetter
from urllib.request import urlopen
# We ignore the first few emojis as they are widely assumed to be single width
# in legacy applications
FIRST_EMOJI = 0x2194
os.chdir(os.path.dirname(os.path.abspath(__file__)))
@@ -34,15 +33,32 @@ def get_data(fname, folder='UCD'):
# Map of class names to set of codepoints in class
class_maps = {}
name_map = {}
word_search_map = defaultdict(set)
marks = set()
not_assigned = set(range(0, sys.maxunicode))
def parse_ucd():
def add_word(w, c):
if c <= 32 or c == 127 or 128 <= c <= 159:
return
word_search_map[w.lower()].add(c)
first = None
for word, c in html5.items():
if len(c) == 1:
add_word(word.rstrip(';'), ord(c))
word_search_map['nnbsp'].add(0x202f)
for line in get_data('ucd/UnicodeData.txt'):
parts = [x.strip() for x in line.split(';')]
codepoint = int(parts[0], 16)
name = parts[1]
if name:
name_map[codepoint] = name
for word in name.lower().split():
add_word(word, codepoint)
category = parts[2]
s = class_maps.setdefault(category, set())
desc = parts[1]
@@ -74,15 +90,22 @@ def split_two(line):
all_emoji = set()
emoji_categories = {}
emoji_presentation_bases = set()
def parse_emoji():
for line in get_data('emoji-data.txt', 'emoji'):
chars, rest = split_two(line)
if max(chars) >= FIRST_EMOJI:
s = emoji_categories.setdefault(rest, set())
s |= chars
all_emoji.update(chars)
s = emoji_categories.setdefault(rest, set())
s.update(chars)
all_emoji.update(chars)
for line in get_data('emoji-variation-sequences.txt', 'emoji'):
base, var, *rest = line.split()
if base.startswith('#'):
continue
base = int(base, 16)
if var.upper() == 'FE0F':
emoji_presentation_bases.add(base)
doublewidth, ambiguous = set(), set()
@@ -125,19 +148,21 @@ def write_case(spec, p):
@contextmanager
def create_header(path):
def create_header(path, include_data_types=True):
f = open(path, 'w')
p = partial(print, file=f)
p('// unicode data, built from the unicode standard on:', date.today())
p('// see gen-wcwidth.py')
if path.endswith('.h'):
p('#pragma once')
p('#include "data-types.h"\n')
p('START_ALLOW_CASE_RANGE')
if include_data_types:
p('#include "data-types.h"\n')
p('START_ALLOW_CASE_RANGE')
p()
yield p
p()
p('END_ALLOW_CASE_RANGE')
if include_data_types:
p('END_ALLOW_CASE_RANGE')
f.close()
@@ -176,13 +201,47 @@ def category_test(name, p, classes, comment, static=False):
p('\treturn false;\n}\n')
def codepoint_to_mark_map(p, mark_map):
p('\tswitch(c) { // {{{')
rmap = {c: m for m, c in enumerate(mark_map)}
for spec in get_ranges(mark_map):
if isinstance(spec, tuple):
s = rmap[spec[0]]
p(f'\t\tcase {spec[0]} ... {spec[1]}: return {s} + c - {spec[0]};')
else:
p(f'\t\tcase {spec}: return {rmap[spec]};')
p('default: return 0;')
p('\t} // }}}')
return rmap
def classes_to_regex(classes):
chars = set()
for c in classes:
chars |= class_maps[c]
def as_string(codepoint):
if codepoint < 256:
return r'\x{:02x}'.format(codepoint)
if codepoint <= 0xffff:
return r'\u{:04x}'.format(codepoint)
return r'\U{:08x}'.format(codepoint)
for spec in get_ranges(list(chars)):
if isinstance(spec, tuple):
yield '{}-{}'.format(*map(as_string, (spec[0], spec[1])))
else:
yield as_string(spec)
def gen_ucd():
cz = {c for c in class_maps if c[0] in 'CZ'}
with create_header('kitty/unicode-data.c') as p:
p('#include "unicode-data.h"')
category_test('is_combining_char', p, {c for c in class_maps if c.startswith('M')}, 'M category (marks)')
category_test('is_ignored_char', p, 'Cc Cf Cs'.split(), 'Control characters (Cc Cf Cs)')
category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories')
category_test('is_CZ_category', p, {c for c in class_maps if c[0] in 'CZ'}, 'C and Z categories')
category_test('is_CZ_category', p, cz, 'C and Z categories')
category_test('is_P_category', p, {c for c in class_maps if c[0] == 'P'}, 'P category (punctuation)')
mark_map = [0] + list(sorted(marks))
p('char_type codepoint_for_mark(combining_type m) {')
@@ -191,16 +250,57 @@ def gen_ucd():
p('\treturn 0;')
p('}\n')
p('combining_type mark_for_codepoint(char_type c) {')
p('\tswitch(c) { // {{{')
rmap = {c: m for m, c in enumerate(mark_map)}
for spec in get_ranges(mark_map):
if isinstance(spec, tuple):
s = rmap[spec[0]]
p(f'\t\tcase {spec[0]} ... {spec[1]}: return {s} + c - {spec[0]};')
rmap = codepoint_to_mark_map(p, mark_map)
p('}\n')
if rmap[0xfe0e] != 1275:
raise ValueError('The mark for 0xfe0e has changed, you have to update VS15 to {} and VS16 to {} in unicode-data.h'.format(
rmap[0xfe0e], rmap[0xfe0f]
))
with open('kittens/url_hints/url_regex.py', 'w') as f:
f.write("url_delimiters = '{}' # noqa".format(''.join(classes_to_regex(cz))))
def gen_names():
words = tuple(sorted(word_search_map))
with create_header('kittens/unicode_input/names.h') as p:
cp_map = list(sorted(name_map))
p(f'static const char* name_map[{len(cp_map)}] = {{' ' // {{{')
for cp in cp_map:
w = name_map[cp].replace('"', '\\"')
p(f'\t"{w}",')
p("}; // }}}\n")
p(f'static const char* idx_to_word[{len(words)}] = ' '{ // {{{')
for s in words:
s = s.replace('"', '\\"')
p(f'\t"{s}",')
p("}; // }}}\n")
first_letters = {ord(w[0]) for w in words if ord(w[0]) < 256}
wmap = {w: i for i, w in enumerate(words)}
p(f'static const unsigned short* words_for_first_letter[256] = ' '{ // {{{')
for fl in range(0, 256):
if fl in first_letters:
winds = [str(wmap[w]) for w in words if w.startswith(chr(fl))]
p(f'\t(const unsigned short[{len(winds) + 1}]){{{len(winds)}, ', ', '.join(winds), '},')
else:
p(f'\t\tcase {spec}: return {rmap[spec]};')
p('default: return 0;')
p('\t} // }}}')
p('NULL,')
p("}; // }}}\n")
p(f'static const char_type* codepoints_for_word_idx[{len(words)}] = ' '{ // {{{')
for s in words:
cps = word_search_map[s]
a = ', '.join(map(str, cps))
p(f'\t(const char_type[{len(cps) + 1}]){{{len(cps)}, ', a, '},')
p("}; // }}}\n")
p('static char_type mark_for_codepoint(char_type c) {')
codepoint_to_mark_map(p, cp_map)
p('}\n')
p('static inline const char* name_for_codepoint(char_type cp) {')
p('\tchar_type m = mark_for_codepoint(cp); if (m == 0) return NULL;')
p('\treturn name_map[m];')
p('}\n')
@@ -221,12 +321,13 @@ def gen_wcwidth():
p('\tswitch(code) {')
non_printing = class_maps['Cc'] | class_maps['Cf'] | class_maps['Cs']
add(p, 'Marks', marks | {0}, 0)
add(p, 'Non-printing characters', non_printing, -1)
add(p, 'Marks', marks, -1)
add(p, 'Private use', class_maps['Co'], -3)
add(p, 'Text Presentation', emoji_categories['Emoji'] - emoji_categories['Emoji_Presentation'], 1)
add(p, 'East Asian ambiguous width', ambiguous, -2)
add(p, 'East Asian double width', doublewidth, 2)
add(p, 'Emoji', all_emoji, 2)
add(p, 'Emoji Presentation', emoji_categories['Emoji_Presentation'], 2)
add(p, 'Not assigned in the unicode character database', not_assigned, -1)
@@ -234,6 +335,15 @@ def gen_wcwidth():
p('\t}')
p('\treturn 1;\n}')
p('static bool\nis_emoji_presentation_base(uint32_t code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(emoji_presentation_bases)):
write_case(spec, p)
p('\t\t\treturn true;')
p('\t\tdefault: return false;')
p('\t}')
p('\treturn 1;\n}')
parse_ucd()
parse_emoji()
@@ -241,3 +351,4 @@ parse_eaw()
gen_ucd()
gen_wcwidth()
gen_emoji()
gen_names()

View File

@@ -287,7 +287,7 @@ void _glfwPollMonitorsNS(void)
// Change the current video mode
//
GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
{
CFArrayRef modes;
CFIndex count, i;
@@ -299,7 +299,7 @@ GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
best = _glfwChooseVideoMode(monitor, desired);
_glfwPlatformGetVideoMode(monitor, &current);
if (_glfwCompareVideoModes(&current, best) == 0)
return GLFW_TRUE;
return;
CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
@@ -332,15 +332,6 @@ GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
CFRelease(modes);
CVDisplayLinkRelease(link);
if (!native)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Cocoa: Monitor mode list changed");
return GLFW_FALSE;
}
return GLFW_TRUE;
}
// Restore the previously saved (original) video mode
@@ -364,6 +355,10 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
{
}
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{
const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);

View File

@@ -164,6 +164,6 @@ typedef struct _GLFWtimerNS
void _glfwInitTimerNS(void);
void _glfwPollMonitorsNS(void);
GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);

View File

@@ -138,9 +138,9 @@ static float transformY(float y)
// Make the specified window and its video mode active on its monitor
//
static GLFWbool acquireMonitor(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
const GLFWbool status = _glfwSetVideoModeNS(window->monitor, &window->videoMode);
_glfwSetVideoModeNS(window->monitor, &window->videoMode);
const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID);
const NSRect frame = NSMakeRect(bounds.origin.x,
transformY(bounds.origin.y + bounds.size.height),
@@ -150,7 +150,6 @@ static GLFWbool acquireMonitor(_GLFWwindow* window)
[window->ns.object setFrame:frame display:YES];
_glfwInputMonitorWindow(window->monitor, window);
return status;
}
// Remove the window and restore the original video mode
@@ -583,6 +582,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
window->ns.xscale = xscale;
window->ns.yscale = yscale;
_glfwInputWindowContentScale(window, xscale, yscale);
if (window->ns.layer)
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
}
}
@@ -1172,11 +1174,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
{
_glfwPlatformShowWindow(window);
_glfwPlatformFocusWindow(window);
if (!acquireMonitor(window))
return GLFW_FALSE;
if (wndconfig->centerCursor)
centerCursor(window);
acquireMonitor(window);
}
return GLFW_TRUE;
@@ -1363,7 +1361,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
[NSApp requestUserAttention:NSInformationalRequest];
}
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
int _glfwPlatformWindowBell(_GLFWwindow* window)
{
NSBeep();
return GLFW_TRUE;
@@ -1418,6 +1416,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
const NSUInteger styleMask = getStyleMask(window);
[window->ns.object setStyleMask:styleMask];
// HACK: Changing the style mask can cause the first responder to be cleared
[window->ns.object makeFirstResponder:window->ns.view];
if (monitor)
@@ -1538,6 +1537,9 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
void _glfwPlatformPollEvents(void)
{
if (!initializeAppKit())
return;
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
@@ -1872,6 +1874,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
[window->ns.view setWantsLayer:YES];
memset(&sci, 0, sizeof(sci));
@@ -1903,4 +1906,3 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
return window->ns.object;
}

27
glfw/context.c vendored
View File

@@ -46,6 +46,16 @@
//
GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
{
if (ctxconfig->share)
{
if (ctxconfig->client == GLFW_NO_API ||
ctxconfig->share->context.client == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return GLFW_FALSE;
}
}
if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API &&
ctxconfig->source != GLFW_EGL_CONTEXT_API &&
ctxconfig->source != GLFW_OSMESA_CONTEXT_API)
@@ -607,7 +617,8 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
if (window && window->context.client == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
_glfwInputError(GLFW_NO_WINDOW_CONTEXT,
"Cannot make current with a window that has no OpenGL or OpenGL ES context");
return;
}
@@ -636,7 +647,8 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* handle)
if (window->context.client == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
_glfwInputError(GLFW_NO_WINDOW_CONTEXT,
"Cannot swap buffers of a window that has no OpenGL or OpenGL ES context");
return;
}
@@ -652,7 +664,8 @@ GLFWAPI void glfwSwapInterval(int interval)
window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (!window)
{
_glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
_glfwInputError(GLFW_NO_CURRENT_CONTEXT,
"Cannot set swap interval without a current OpenGL or OpenGL ES context");
return;
}
@@ -669,13 +682,14 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (!window)
{
_glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
_glfwInputError(GLFW_NO_CURRENT_CONTEXT,
"Cannot query extension without a current OpenGL or OpenGL ES context");
return GLFW_FALSE;
}
if (*extension == '\0')
{
_glfwInputError(GLFW_INVALID_VALUE, "Extension name is empty string");
_glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string");
return GLFW_FALSE;
}
@@ -734,7 +748,8 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (!window)
{
_glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
_glfwInputError(GLFW_NO_CURRENT_CONTEXT,
"Cannot query entry point without a current OpenGL or OpenGL ES context");
return NULL;
}

View File

@@ -10,6 +10,10 @@ import sys
_plat = sys.platform.lower()
is_macos = 'darwin' in _plat
is_freebsd = 'freebsd' in _plat
is_netbsd = 'netbsd' in _plat
is_dragonflybsd = 'dragonfly' in _plat
is_bsd = is_freebsd or is_netbsd or is_dragonflybsd
base = os.path.dirname(os.path.abspath(__file__))
@@ -100,13 +104,14 @@ def collect_source_information():
'common': dict(extract_sources('common')),
'wayland_protocols': wayland_protocols,
}
joystick = 'null' if is_bsd else 'linux'
for group in 'cocoa win32 x11 wayland osmesa'.split():
m = re.search('_GLFW_' + group.upper(), raw)
ans[group] = dict(extract_sources('glfw', m.start()))
if group == 'x11':
ans[group]['headers'].append('linux_joystick.h')
ans[group]['sources'].append('linux_joystick.c')
elif group == 'wayland':
if group in ('x11', 'wayland'):
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
if group == 'wayland':
ans[group]['protocols'] = p = []
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
p.append(m.group(1))

37
glfw/glfw3.h vendored
View File

@@ -1458,7 +1458,8 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**);
* This is the function signature for monitor configuration callback functions.
*
* @param[in] monitor The monitor that was connected or disconnected.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining
* values reserved for future use.
*
* @sa @ref monitor_event
* @sa @ref glfwSetMonitorCallback
@@ -1475,7 +1476,8 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int);
* functions.
*
* @param[in] jid The joystick that was connected or disconnected.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining
* values reserved for future use.
*
* @sa @ref joystick_event
* @sa @ref glfwSetJoystickCallback
@@ -2451,10 +2453,11 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
* GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override
* this.
*
* @remark @wayland The window frame is currently unimplemented, as if
* [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`.
* A compositor can still emit close, resize or maximize events, using for
* example a keybind mechanism.
* @remark @wayland The window frame is currently very simple, only allowing
* window resize or move. A compositor can still emit close, maximize or
* fullscreen events, using for example a keybind mechanism. Additionally,
* the wp_viewporter protocol is required for this feature, otherwise the
* window will not be decorated.
*
* @remark @wayland A full screen window will not attempt to change the mode,
* no matter what the requested size or refresh rate.
@@ -2896,10 +2899,6 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height)
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @remark @wayland The window frame is currently unimplemented, as if
* [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`,
* so the returned values will always be zero.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_size
@@ -3189,13 +3188,9 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
/*! @brief Sounds an audible bell associated with the window
*
* This function sounds an audible bell, on platforms where it is
* supported. Currently (macOS, Windows and X11).
* supported. Currently (macOS, Windows, X11 and Wayland).
*
* @param[in] window The window with which the bell is associated.
* @param[in] param The meaning of this parameter is platform dependent. On
* X11 it corresponds to the percentage controlling bell volume (see man
* XBell). On Windows it is the type of sound to make, see the MSDN docs for
* MessageBeep. On macOS, it is ignored.
* @return GLFW_TRUE if the bell succeeded otherwise GLFW_FALSE
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
@@ -3210,7 +3205,7 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
*
* @ingroup window
*/
GLFWAPI int glfwWindowBell(GLFWwindow* window, int64_t param);
GLFWAPI int glfwWindowBell(GLFWwindow* window);
/*! @brief Returns the monitor that the window uses for full screen mode.
@@ -4902,7 +4897,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
*
* Not all devices have all the buttons or axes provided by @ref
* GLFWgamepadstate. Unavailable buttons and axes will always report
* `GLFW_RELEASE` and 1.0 respectively.
* `GLFW_RELEASE` and 0.0 respectively.
*
* @param[in] jid The [joystick](@ref joysticks) to query.
* @param[out] state The gamepad input state of the joystick.
@@ -5468,6 +5463,11 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys
* glfwGetRequiredInstanceExtensions to check what instance extensions are
* required.
*
* The window surface cannot be shared with another API so the window must
* have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib)
* set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error
* and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`.
*
* The window surface must be destroyed before the specified Vulkan instance.
* It is the responsibility of the caller to destroy the window surface. GLFW
* does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the
@@ -5483,7 +5483,7 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
* GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE
*
* @remark If an error occurs before the creation call is made, GLFW returns
* the Vulkan error code most appropriate for the error. Appropriate use of
@@ -5542,4 +5542,3 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
#endif
#endif /* _glfw3_h_ */

83
glfw/input.c vendored
View File

@@ -62,11 +62,11 @@ static _GLFWmapping* findMapping(const char* guid)
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
const _GLFWjoystick* js)
{
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->value >> 4) >= js->hatCount)
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount)
return GLFW_FALSE;
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->value >= js->buttonCount)
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount)
return GLFW_FALSE;
else if (e->type == _GLFW_JOYSTICK_AXIS && e->value >= js->axisCount)
else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount)
return GLFW_FALSE;
return GLFW_TRUE;
@@ -167,6 +167,10 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
while (*c)
{
// TODO: Implement output modifiers
if (*c == '+' || *c == '-')
return GLFW_FALSE;
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
{
length = strlen(fields[i].name);
@@ -177,23 +181,50 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
if (fields[i].element)
{
_GLFWmapelement* e = fields[i].element;
int8_t minimum = -1;
int8_t maximum = 1;
if (*c == '+')
{
minimum = 0;
c += 1;
}
else if (*c == '-')
{
maximum = 0;
c += 1;
}
if (*c == 'a')
fields[i].element->type = _GLFW_JOYSTICK_AXIS;
e->type = _GLFW_JOYSTICK_AXIS;
else if (*c == 'b')
fields[i].element->type = _GLFW_JOYSTICK_BUTTON;
e->type = _GLFW_JOYSTICK_BUTTON;
else if (*c == 'h')
fields[i].element->type = _GLFW_JOYSTICK_HATBIT;
e->type = _GLFW_JOYSTICK_HATBIT;
else
break;
if (fields[i].element->type == _GLFW_JOYSTICK_HATBIT)
if (e->type == _GLFW_JOYSTICK_HATBIT)
{
const unsigned long hat = strtoul(c + 1, (char**) &c, 10);
const unsigned long bit = strtoul(c + 1, (char**) &c, 10);
fields[i].element->value = (uint8_t) ((hat << 4) | bit);
e->index = (uint8_t) ((hat << 4) | bit);
}
else
fields[i].element->value = (uint8_t) strtoul(c + 1, (char**) &c, 10);
e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10);
if (e->type == _GLFW_JOYSTICK_AXIS)
{
e->axisScale = 2 / (maximum - minimum);
e->axisOffset = -(maximum + minimum);
if (*c == '~')
{
e->axisScale = -e->axisScale;
e->axisOffset = -e->axisOffset;
}
}
}
else
{
@@ -1188,35 +1219,41 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
{
if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_AXIS)
const _GLFWmapelement* e = js->mapping->buttons + i;
if (e->type == _GLFW_JOYSTICK_AXIS)
{
if (fabs(js->axes[js->mapping->buttons[i].value]) > 0.5)
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
if (value > 0.f)
state->buttons[i] = GLFW_PRESS;
}
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
else if (e->type == _GLFW_JOYSTICK_HATBIT)
{
const unsigned int hat = js->mapping->buttons[i].value >> 4;
const unsigned int bit = js->mapping->buttons[i].value & 0xf;
const unsigned int hat = e->index >> 4;
const unsigned int bit = e->index & 0xf;
if (js->hats[hat] & bit)
state->buttons[i] = GLFW_PRESS;
}
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
state->buttons[i] = js->buttons[js->mapping->buttons[i].value];
else if (e->type == _GLFW_JOYSTICK_BUTTON)
state->buttons[i] = js->buttons[e->index];
}
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
{
if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS)
state->axes[i] = js->axes[js->mapping->axes[i].value];
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
const _GLFWmapelement* e = js->mapping->axes + i;
if (e->type == _GLFW_JOYSTICK_AXIS)
{
const unsigned int hat = js->mapping->axes[i].value >> 4;
const unsigned int bit = js->mapping->axes[i].value & 0xf;
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
state->axes[i] = fminf(fmaxf(value, -1.f), 1.f);
}
else if (e->type == _GLFW_JOYSTICK_HATBIT)
{
const unsigned int hat = e->index >> 4;
const unsigned int bit = e->index & 0xf;
if (js->hats[hat] & bit)
state->axes[i] = 1.f;
}
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
state->axes[i] = (float) js->buttons[js->mapping->axes[i].value];
else if (e->type == _GLFW_JOYSTICK_BUTTON)
state->axes[i] = (float) js->buttons[e->index];
}
return GLFW_TRUE;

55
glfw/internal.h vendored
View File

@@ -128,7 +128,7 @@ typedef enum VkStructureType
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000053000,
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
} VkStructureType;
@@ -456,7 +456,9 @@ struct _GLFWcursor
struct _GLFWmapelement
{
uint8_t type;
uint8_t value;
uint8_t index;
int8_t axisScale;
int8_t axisOffset;
};
// Gamepad mapping structure
@@ -596,7 +598,8 @@ const char* _glfwPlatformGetVersionString(void);
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos);
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos);
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode);
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot);
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image, int xhot, int yhot);
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape);
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
@@ -604,8 +607,10 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
const char* _glfwPlatformGetScancodeName(int scancode);
int _glfwPlatformGetKeyScancode(int key);
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale);
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale);
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
@@ -626,25 +631,33 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWfbconfig* fbconfig);
void _glfwPlatformDestroyWindow(_GLFWwindow* window);
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title);
void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
int count, const GLFWimage* images);
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos);
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height);
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height);
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight);
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
int minwidth, int minheight,
int maxwidth, int maxheight);
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom);
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height);
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom);
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale);
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
int* left, int* top,
int* right, int* bottom);
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale);
void _glfwPlatformIconifyWindow(_GLFWwindow* window);
void _glfwPlatformRestoreWindow(_GLFWwindow* window);
void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
void _glfwPlatformShowWindow(_GLFWwindow* window);
void _glfwPlatformHideWindow(_GLFWwindow* window);
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t);
int _glfwPlatformWindowBell(_GLFWwindow* window);
void _glfwPlatformFocusWindow(_GLFWwindow* window);
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
int xpos, int ypos, int width, int height,
int refreshRate);
int _glfwPlatformWindowFocused(_GLFWwindow* window);
int _glfwPlatformWindowIconified(_GLFWwindow* window);
int _glfwPlatformWindowVisible(_GLFWwindow* window);
@@ -663,8 +676,13 @@ void _glfwPlatformWaitEventsTimeout(double timeout);
void _glfwPlatformPostEmptyEvent(void);
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions);
int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily);
VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
VkPhysicalDevice device,
uint32_t queuefamily);
VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
_GLFWwindow* window,
const VkAllocationCallbacks* allocator,
VkSurfaceKHR* surface);
GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls);
void _glfwPlatformDestroyTls(_GLFWtls* tls);
@@ -685,15 +703,18 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused);
void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos);
void _glfwInputWindowSize(_GLFWwindow* window, int width, int height);
void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height);
void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale);
void _glfwInputWindowContentScale(_GLFWwindow* window,
float xscale, float yscale);
void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified);
void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized);
void _glfwInputWindowDamage(_GLFWwindow* window);
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods);
void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain);
void _glfwInputKey(_GLFWwindow* window,
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 _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
@@ -708,7 +729,8 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement);
void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window);
#if defined(__GNUC__)
void _glfwInputError(int code, const char* format, ...) __attribute__((format(printf, 2, 3)));
void _glfwInputError(int code, const char* format, ...)
__attribute__((format(printf, 2, 3)));
#else
void _glfwInputError(int code, const char* format, ...);
#endif
@@ -747,4 +769,3 @@ void _glfwTerminateVulkan(void);
const char* _glfwGetVulkanResultString(VkResult result);
char* _glfw_strdup(const char* source);

View File

@@ -38,6 +38,11 @@
#include <string.h>
#include <unistd.h>
#ifndef SYN_DROPPED // < v2.6.39 kernel headers
// Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32
#define SYN_DROPPED 3
#endif
// Apply an EV_KEY event to the specified joystick
//
static void handleKeyEvent(_GLFWjoystick* js, int code, int value)

624
glfw/mappings.h vendored
View File

@@ -60,243 +60,411 @@
const char* _glfwDefaultMappings[] =
{
"02200090000000000000504944564944,8Bitdo NES30 PRO USB,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"20380900000000000000504944564944,8Bitdo NES30 PRO Wireless,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,y:b3,x:b4,start:b11,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,platform:Windows,",
"8f0e1200000000000000504944564944,Acme,platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"c0111352000000000000504944564944,Battalife Joystick,platform:Windows,x:b4,a:b6,b:b7,y:b5,back:b2,start:b3,leftshoulder:b0,rightshoulder:b1,leftx:a0,lefty:a1,",
"d81d0b00000000000000504944564944,BUFFALO BSGP1601 Series ,platform:Windows,x:b4,a:b5,b:b3,y:b2,back:b12,start:b13,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b6,rightshoulder:b9,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"5e048e02000000000000504944564944,Controller (XBOX 360 For Windows),platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,righttrigger:a2,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"4f0423b3000000000000504944564944,Dual Trigger 3-in-1,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"341a0108000000000000504944564944,EXEQ RF USB Gamepad 8206,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,leftstick:b8,rightstick:b7,back:b8,start:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
"0d0f8500000000000000504944564944,Fighting Commander 2016 PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f5f00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f5e00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:a3,platform:Windows,",
"0d0f8400000000000000504944564944,Fighting Commander 5,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f8700000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f8800000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b9,guide:b12,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f2700000000000000504944564944,FIGHTING STICK V3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"79000600000000000000504944564944,G-Shark GS-GP702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"28040140000000000000504944564944,GamePad Pro USB,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
"ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"45130010000000000000504944564944,Generic USB Joystick,a:b0,b:b1,x:b2,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f4900000000000000504944564944,Hatsune Miku Sho Controller,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"d8140862000000000000504944564944,HitBox Edition Cthulhu+,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
"0d0f4000000000000000504944564944,Hori Fighting Stick Mini 3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
"0d0f6e00000000000000504944564944,HORIPAD 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f4d00000000000000504944564944,HORIPAD3 A,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"25090017000000000000504944564944,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b5,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b6,platform:Windows,",
"d81d0f00000000000000504944564944,iBUFFALO BSGP1204 Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"d81d1000000000000000504944564944,iBUFFALO BSGP1204P Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"83056020000000000000504944564944,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Windows,",
"6f0e2401000000000000504944564944,INJUSTICE FightStick for PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Windows",
"49190204000000000000504944564944,Ipega PG-9023,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,",
"6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"6d0418c2000000000000504944564944,Logitech RumblePad 2 USB,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"38075032000000000000504944564944,Mad Catz FightPad PRO PS3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"38075082000000000000504944564944,Mad Catz FightPad PRO PS4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b13,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"38078433000000000000504944564944,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38078483000000000000504944564944,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:b6,platform:Windows,",
"38078134000000000000504944564944,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b7,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b4,platform:Windows,",
"38078184000000000000504944564944,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:b7,platform:Windows,",
"38078034000000000000504944564944,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38078084000000000000504944564944,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"38078532000000000000504944564944,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38073888000000000000504944564944,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38071888000000000000504944564944,MadCatz SFIV FightStick PS3,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b6,platform:Windows,",
"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"25090128000000000000504944564944,Mayflash Arcade Stick,a:b1,b:b2,x:b5,y:b6,back:b8,start:b9,leftshoulder:b0,rightshoulder:b3,leftx:a0,lefty:a1,rightx:h0.4,righty:h0.0,lefttrigger:b4,righttrigger:b7,platform:Windows,",
"79004318000000000000504944564944,Mayflash GameCube Controller Adapter,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b0,start:b9,guide:b0,leftshoulder:b4,rightshoulder:b7,leftstick:b0,rightstick:b0,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"8f0e1030000000000000504944564944,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,x:b3,y:b4,start:b9,leftshoulder:b6,rightshoulder:b2,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"8f0e0d31000000000000504944564944,Multilaser JS071 USB,platform:Windows,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Windows,",
"bd1215d0000000000000504944564944,Nintendo Retrolink USB Super SNES Classic Controller,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,start:b9,back:b8,leftx:a0,lefty:a1,platform:Windows,",
"4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftstick:a0,rightstick:a2,leftshoulder:a3,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:h0.6,lefty:h0.12,rightx:h0.9,righty:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"36280100000000000000504944564944,OUYA Controller,platform:Windows,a:b0,b:b3,y:b2,x:b1,start:b14,guide:b15,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b8,dpleft:b10,dpdown:b9,dpright:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b12,righttrigger:b13,",
"120cf60e000000000000504944564944,P4 Wired Gamepad,a:b1,b:b2,x:b0,y:b3,back:b12,guide:b8,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.0,lefttrigger:b7,righttrigger:b6,platform:Windows,",
"8f0e0300000000000000504944564944,Piranha xtreme,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"d6206dca000000000000504944564944,PowerA Pro Ex,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"10080100000000000000504944564944,PS1 USB,platform:Windows,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"10080300000000000000504944564944,PS2 USB,platform:Windows,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a4,righty:a2,lefttrigger:b4,righttrigger:b5,",
"88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
"4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
"25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
"10008200000000000000504944564944,PS360+ v1.66,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"300f0011000000000000504944564944,QanBa Arcade JoyStick 1008,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b10,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
"300f1611000000000000504944564944,QanBa Arcade JoyStick 4018,a:b1,b:b2,x:b0,y:b3,back:b10,guide:b9,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"222c0020000000000000504944564944,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"300f1210000000000000504944564944,QanBa Joystick Plus,a:b0,b:b1,x:b2,y:b3,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,platform:Windows,",
"341a0104000000000000504944564944,QanBa Joystick Q4RAF,a:b5,b:b6,x:b1,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b0,rightshoulder:b3,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b7,platform:Windows,",
"222c0223000000000000504944564944,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"222c0023000000000000504944564944,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,x:b0,y:b3,back:b13,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"0d0f1100000000000000504944564944,REAL ARCADE PRO.3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f8b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"0d0f8a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"0d0f6b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"0d0f6a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"0d0f7000000000000000504944564944,REAL ARCADE PRO.4 VLX,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f2200000000000000504944564944,REAL ARCADE Pro.V3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"00f00300000000000000504944564944,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
"00f0f100000000000000504944564944,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
"6f0e1e01000000000000504944564944,Rock Candy Gamepad for PS3,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"300f1201000000000000504944564944,Saitek Dual Analog Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"a3060cff000000000000504944564944,Saitek P2500,a:b2,b:b3,y:b1,x:b0,start:b4,guide:b10,back:b5,leftstick:b8,rightstick:b9,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
"300f1001000000000000504944564944,Saitek P480 Rumble Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"9b280500000000000000504944564944,Saturn_Adapter_2.0,a:b1,b:b2,x:b0,y:b3,start:b9,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,platform:Windows,",
"79001100000000000000504944564944,Sega Saturn Gamepad,a:b1,b:b2,x:b4,y:b5,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a4,lefttrigger:b3,righttrigger:b0,platform:Windows,",
"4c05cc09000000000000504944564944,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"4c05a00b000000000000504944564944,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"ff113133000000000000504944564944,SVEN X-PAD,platform:Windows,a:b2,b:b3,y:b1,x:b0,start:b5,back:b4,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b8,righttrigger:b9,",
"4f0415b3000000000000504944564944,Thrustmaster Dual Analog 3.2,platform:Windows,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"4f0400b3000000000000504944564944,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"66660488000000000000504944564944,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpdown:b14,dpleft:b15,dpright:b13,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,platform:Windows,",
"38076652000000000000504944564944,UnKnown,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"63252305000000000000504944564944,USB Vibration Joystick (BM),platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"79001b18000000000000504944564944,Venom Arcade Joystick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"10280000000000000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
"79000000000000000600000000000000,G-Shark GP-702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
"AD1B00000000000001F9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
"0d0f0000000000004d00000000000000,HORI Gem Pad 3,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"0d0f0000000000006600000000000000,HORIPAD FPS PLUS 4,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:a4,",
"83050000000000006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
"6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"2509000000000000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
"79000000000000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,x:b0,y:b12,back:b32,start:b36,leftstick:b40,rightstick:b44,leftshoulder:b16,rightshoulder:b20,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a4,rightx:a8,righty:a12,lefttrigger:b24,righttrigger:b28,platform:Mac OS X,",
"d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,rightx:,righty:,lefttrigger:b6,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,righttrigger:b7,",
"8f0e0000000000000300000000000000,Piranha xtreme,platform:Mac OS X,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
"4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"891600000000000000fd000000000000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b8,guide:b10,back:b9,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b11,dpleft:b13,dpdown:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X,",
"79000000000000001100000000000000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a3,lefty:a4,platform:Mac OS X,",
"81170000000000007e05000000000000,Sega Saturn,x:b0,a:b2,b:b4,y:b6,start:b13,dpleft:b15,dpdown:b16,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,lefttrigger:b10,rightshoulder:b9,righttrigger:a4,righttrigger:b11,leftx:a0,lefty:a2,platform:Mac OS X,",
"b4040000000000000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,x:b3,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
"351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
"4c05000000000000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
"4c05000000000000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
"4f0400000000000015b3000000000000,Thrustmaster Dual Analog 3.2,platform:Mac OS X,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"4f0400000000000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Mac OS X,",
"bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
"10080000000000000100000000000000,Twin USB Joystick,a:b4,b:b2,x:b6,y:b0,back:b16,start:b18,leftstick:b20,rightstick:b22,leftshoulder:b12,rightshoulder:b14,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a2,rightx:a6,righty:a4,lefttrigger:b8,righttrigger:b10,platform:Mac OS X,",
"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,y:b9,x:b10,start:b6,guide:b8,back:b7,dpup:b2,dpleft:b0,dpdown:b3,dpright:b1,leftx:a0,lefty:a1,lefttrigger:b12,righttrigger:,leftshoulder:b11,platform:Mac OS X,",
"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,x:b18,y:b17,back:b7,guide:b8,start:b6,leftstick:b23,rightstick:b24,leftshoulder:b19,rightshoulder:b20,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b21,righttrigger:b22,platform:Mac OS X,",
"5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"5e04000000000000dd02000000000000,Xbox One Wired Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"5e04000000000000ea02000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"5e04000000000000e002000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b10,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux,",
"03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,",
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"06000000adde0000efbe000002010000,Hidromancer Game Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1,",
"03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux,",
"030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5,",
"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7",
"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,",
"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,",
"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
"030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux,",
"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,",
"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
"030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,",
"030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,",
"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,",
"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
"030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,",
"03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,",
"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,",
"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,",
"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,",
"03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
"030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,",
"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,",
"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,",
"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,",
"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows,",
"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,",
"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
"03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
"03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
"03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,",
"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,",
"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,",
"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,",
"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,",
"03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,",
"030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,",
"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
"030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,",
"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
"03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,",
"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,",
"03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X,",
"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,",
"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,",
"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,",
"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,",
"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,",
"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,",
"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,",
"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,",
"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,",
"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,",
"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,",
"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,",
"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,",
"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,",
"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,",
"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,",
"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,",
"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,",
"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,",
"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,",
"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,",
"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,",
"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,",
"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,",
"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,",
"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,",
"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,",
"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux,",
"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5,",
"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,",
"03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1,",
"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,",
"030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux,",
"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,",
"05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,",
"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,",
"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,",
"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,",
"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,",
"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
"050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,platform:Linux,x:b1,a:b0,b:b4,y:b5,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,platform:Linux,a:b12,b:b10,x:b13,y:b11,back:b4,start:b5,leftstick:b14,rightstick:b15,leftshoulder:b9,rightshoulder:b8,dpup:b0,dpdown:b2,dpleft:b3,dpright:b1,leftx:a1,lefty:a0,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
"050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
"030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,",
"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13,",
"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,",
"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,",
"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
"03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
"03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,",
"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,",
"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,",
"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,",
"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,",
"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,",
"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,",
"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,",
"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"030000005e040000a102000000010000,X360 Wireless Controller,platform:Linux,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,",
"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,platform:Linux,y:b0,x:b1,b:b3,a:b4,leftshoulder:b2,rightshoulder:b5,back:b6,start:b7,guide:b9,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftx:a0,lefty:a1,",
"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,",
"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,",
"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,",
"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,",
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,",
"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,",
"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,",
"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,",
"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,",
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,",
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",

2
glfw/monitor.c vendored
View File

@@ -177,6 +177,8 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor)
if (monitor == NULL)
return;
_glfwPlatformFreeMonitor(monitor);
_glfwFreeGammaArrays(&monitor->originalRamp);
_glfwFreeGammaArrays(&monitor->currentRamp);

View File

@@ -175,7 +175,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
// Info.plist for unbundled applications
// HACK: This assumes that NSOpenGLPixelFormat will remain
// a straightforward wrapper of its CGL counterpart
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
}

4
glfw/null_monitor.c vendored
View File

@@ -32,6 +32,10 @@
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
{
}
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{
}

3
glfw/null_window.c vendored
View File

@@ -205,7 +205,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
}
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
int _glfwPlatformWindowBell(_GLFWwindow* window)
{
return GLFW_FALSE;
}
@@ -323,4 +323,3 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
// This seems like the most appropriate error to return here
return VK_ERROR_INITIALIZATION_FAILED;
}

View File

@@ -55,15 +55,17 @@
"wayland": {
"headers": [
"wl_platform.h",
"linux_joystick.h",
"posix_time.h",
"posix_thread.h",
"xkb_glfw.h",
"xkb_unicode.h",
"egl_context.h",
"osmesa_context.h"
"osmesa_context.h",
"linux_joystick.h"
],
"protocols": [
"stable/xdg-shell/xdg-shell.xml",
"stable/viewporter/viewporter.xml",
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
@@ -72,12 +74,12 @@
"wl_init.c",
"wl_monitor.c",
"wl_window.c",
"linux_joystick.c",
"posix_time.c",
"posix_thread.c",
"xkb_unicode.c",
"egl_context.c",
"osmesa_context.c"
"osmesa_context.c",
"linux_joystick.c"
]
},
"wayland_protocols": [

9
glfw/vulkan.c vendored
View File

@@ -54,7 +54,7 @@ GLFWbool _glfwInitVulkan(int mode)
#elif defined(_GLFW_WIN32)
_glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
#elif defined(_GLFW_COCOA)
_glfw.vk.handle = _glfw_dlopen("libMoltenVK.dylib");
_glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib");
#else
_glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
#endif
@@ -317,6 +317,13 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance,
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
if (window->context.client != GLFW_NO_API)
{
_glfwInputError(GLFW_INVALID_VALUE,
"Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API");
return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
}
return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface);
}

17
glfw/win32_monitor.c vendored
View File

@@ -241,7 +241,7 @@ void _glfwPollMonitorsWin32(void)
// Change the current video mode
//
GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
{
GLFWvidmode current;
const GLFWvidmode* best;
@@ -251,7 +251,7 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire
best = _glfwChooseVideoMode(monitor, desired);
_glfwPlatformGetVideoMode(monitor, &current);
if (_glfwCompareVideoModes(&current, best) == 0)
return GLFW_TRUE;
return;
ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(dm);
@@ -270,7 +270,9 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire
NULL,
CDS_FULLSCREEN,
NULL);
if (result != DISP_CHANGE_SUCCESSFUL)
if (result == DISP_CHANGE_SUCCESSFUL)
monitor->win32.modeChanged = GLFW_TRUE;
else
{
const char* description = "Unknown error";
@@ -292,12 +294,7 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire
_glfwInputError(GLFW_PLATFORM_ERROR,
"Win32: Failed to set video mode: %s",
description);
return GLFW_FALSE;
}
monitor->win32.modeChanged = GLFW_TRUE;
return GLFW_TRUE;
}
// Restore the previously saved (original) video mode
@@ -337,6 +334,10 @@ void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* ysc
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
{
}
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{
DEVMODEW dm;

View File

@@ -398,7 +398,7 @@ void _glfwUpdateKeyNamesWin32(void);
void _glfwInitTimerWin32(void);
void _glfwPollMonitorsWin32(void);
GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor);
void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale);

67
glfw/win32_window.c vendored
View File

@@ -471,30 +471,29 @@ static int translateKey(WPARAM wParam, LPARAM lParam)
return _glfw.win32.keycodes[HIWORD(lParam) & 0x1FF];
}
static void fitToMonitor(_GLFWwindow* window)
{
MONITORINFO mi = { sizeof(mi) };
GetMonitorInfo(window->monitor->win32.handle, &mi);
SetWindowPos(window->win32.handle, HWND_TOPMOST,
mi.rcMonitor.left,
mi.rcMonitor.top,
mi.rcMonitor.right - mi.rcMonitor.left,
mi.rcMonitor.bottom - mi.rcMonitor.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
}
// Make the specified window and its video mode active on its monitor
//
static GLFWbool acquireMonitor(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
GLFWvidmode mode;
GLFWbool status;
int xpos, ypos;
if (!_glfw.win32.acquiredMonitorCount)
SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
if (!window->monitor->window)
_glfw.win32.acquiredMonitorCount++;
status = _glfwSetVideoModeWin32(window->monitor, &window->videoMode);
_glfwPlatformGetVideoMode(window->monitor, &mode);
_glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
SetWindowPos(window->win32.handle, HWND_TOPMOST,
xpos, ypos, mode.width, mode.height,
SWP_NOACTIVATE | SWP_NOCOPYBITS);
_glfwSetVideoModeWin32(window->monitor, &window->videoMode);
_glfwInputMonitorWindow(window->monitor, window);
return status;
}
// Remove the window and restore the original video mode
@@ -901,7 +900,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
if (iconified)
releaseMonitor(window);
else
{
acquireMonitor(window);
fitToMonitor(window);
}
}
window->win32.iconified = iconified;
@@ -1240,11 +1242,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
{
_glfwPlatformShowWindow(window);
_glfwPlatformFocusWindow(window);
if (!acquireMonitor(window))
return GLFW_FALSE;
if (wndconfig->centerCursor)
centerCursor(window);
acquireMonitor(window);
fitToMonitor(window);
}
return GLFW_TRUE;
@@ -1360,7 +1359,10 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
if (window->monitor)
{
if (window->monitor->window == window)
{
acquireMonitor(window);
fitToMonitor(window);
}
}
else
{
@@ -1472,9 +1474,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
FlashWindow(window->win32.handle, TRUE);
}
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
int _glfwPlatformWindowBell(_GLFWwindow* window)
{
return MessageBeep(0xFFFFFFFF & param) ? GLFW_TRUE : GLFW_FALSE;
return MessageBeep(0xFFFFFFFF) ? GLFW_TRUE : GLFW_FALSE;
}
void _glfwPlatformFocusWindow(_GLFWwindow* window)
@@ -1495,7 +1497,10 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
if (monitor)
{
if (monitor->window == window)
{
acquireMonitor(window);
fitToMonitor(window);
}
}
else
{
@@ -1518,20 +1523,27 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
if (monitor)
{
MONITORINFO mi = { sizeof(mi) };
UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
if (window->decorated)
{
DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
UINT flags = SWP_FRAMECHANGED | SWP_SHOWWINDOW |
SWP_NOACTIVATE | SWP_NOCOPYBITS |
SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE;
style &= ~WS_OVERLAPPEDWINDOW;
style |= getWindowStyle(window);
SetWindowLongW(window->win32.handle, GWL_STYLE, style);
SetWindowPos(window->win32.handle, HWND_TOPMOST, 0, 0, 0, 0, flags);
flags |= SWP_FRAMECHANGED;
}
acquireMonitor(window);
GetMonitorInfo(window->monitor->win32.handle, &mi);
SetWindowPos(window->win32.handle, HWND_TOPMOST,
mi.rcMonitor.left,
mi.rcMonitor.top,
mi.rcMonitor.right - mi.rcMonitor.left,
mi.rcMonitor.bottom - mi.rcMonitor.top,
flags);
}
else
{
@@ -2002,4 +2014,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return window->win32.handle;
}

26
glfw/window.c vendored
View File

@@ -182,16 +182,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
wndconfig.title = title;
ctxconfig.share = (_GLFWwindow*) share;
if (ctxconfig.share)
{
if (ctxconfig.client == GLFW_NO_API ||
ctxconfig.share->context.client == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return NULL;
}
}
if (!_glfwIsValidContextConfig(&ctxconfig))
return NULL;
@@ -236,7 +226,16 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
}
}
if (!window->monitor)
if (window->monitor)
{
if (wndconfig.centerCursor)
{
int width, height;
_glfwPlatformGetWindowSize(window, &width, &height);
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
}
}
else
{
if (wndconfig.visible)
{
@@ -769,14 +768,14 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
_glfwPlatformRequestWindowAttention(window);
}
GLFWAPI int glfwWindowBell(GLFWwindow* handle, int64_t param)
GLFWAPI int glfwWindowBell(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
return _glfwPlatformWindowBell(window, param);
return _glfwPlatformWindowBell(window);
}
GLFWAPI void glfwHideWindow(GLFWwindow* handle)
@@ -1107,4 +1106,3 @@ GLFWAPI void glfwPostEmptyEvent(void)
_glfwPlatformPostEmptyEvent();
}

626
glfw/wl_init.c vendored
View File

@@ -41,6 +41,39 @@ static inline int min(int n1, int n2)
return n1 < n2 ? n1 : n2;
}
static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, int* which)
{
int focus;
_GLFWwindow* window = _glfw.windowListHead;
if (!which)
which = &focus;
while (window)
{
if (surface == window->wl.decorations.top.surface)
{
*which = topDecoration;
break;
}
if (surface == window->wl.decorations.left.surface)
{
*which = leftDecoration;
break;
}
if (surface == window->wl.decorations.right.surface)
{
*which = rightDecoration;
break;
}
if (surface == window->wl.decorations.bottom.surface)
{
*which = bottomDecoration;
break;
}
window = window->next;
}
return window;
}
static void pointerHandleEnter(void* data,
struct wl_pointer* pointer,
uint32_t serial,
@@ -52,8 +85,16 @@ static void pointerHandleEnter(void* data,
if (!surface)
return;
int focus = 0;
_GLFWwindow* window = wl_surface_get_user_data(surface);
if (!window)
{
window = findWindowFromDecorationSurface(surface, &focus);
if (!window)
return;
}
window->wl.decorations.focus = focus;
_glfw.wl.pointerSerial = serial;
_glfw.wl.pointerFocus = window;
@@ -80,6 +121,39 @@ static void pointerHandleLeave(void* data,
_glfwInputCursorEnter(window, GLFW_FALSE);
}
static void setCursor(const char* name)
{
struct wl_buffer* buffer;
struct wl_cursor* cursor;
struct wl_cursor_image* image;
struct wl_surface* surface = _glfw.wl.cursorSurface;
cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
name);
if (!cursor)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Standard cursor not found");
return;
}
image = cursor->images[0];
if (!image)
return;
buffer = wl_cursor_image_get_buffer(image);
if (!buffer)
return;
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
surface,
image->hotspot_x,
image->hotspot_y);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage(surface, 0, 0,
image->width, image->height);
wl_surface_commit(surface);
}
static void pointerHandleMotion(void* data,
struct wl_pointer* pointer,
uint32_t time,
@@ -87,6 +161,7 @@ static void pointerHandleMotion(void* data,
wl_fixed_t sy)
{
_GLFWwindow* window = _glfw.wl.pointerFocus;
const char* cursorName;
if (!window)
return;
@@ -99,13 +174,47 @@ static void pointerHandleMotion(void* data,
window->wl.cursorPosY = wl_fixed_to_double(sy);
}
_glfwInputCursorPos(window,
wl_fixed_to_double(sx),
wl_fixed_to_double(sy));
switch (window->wl.decorations.focus)
{
case mainWindow:
_glfwInputCursorPos(window,
wl_fixed_to_double(sx),
wl_fixed_to_double(sy));
return;
case topDecoration:
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
cursorName = "n-resize";
else
cursorName = "left_ptr";
break;
case leftDecoration:
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
cursorName = "nw-resize";
else
cursorName = "w-resize";
break;
case rightDecoration:
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
cursorName = "ne-resize";
else
cursorName = "e-resize";
break;
case bottomDecoration:
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
cursorName = "sw-resize";
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
cursorName = "se-resize";
else
cursorName = "s-resize";
break;
default:
assert(0);
}
setCursor(cursorName);
}
static void pointerHandleButton(void* data,
struct wl_pointer* wl_pointer,
struct wl_pointer* pointer,
uint32_t serial,
uint32_t time,
uint32_t button,
@@ -114,8 +223,76 @@ static void pointerHandleButton(void* data,
_GLFWwindow* window = _glfw.wl.pointerFocus;
int glfwButton;
// Both xdg-shell and wl_shell use the same values.
uint32_t edges = WL_SHELL_SURFACE_RESIZE_NONE;
if (!window)
return;
if (button == BTN_LEFT)
{
switch (window->wl.decorations.focus)
{
case mainWindow:
break;
case topDecoration:
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
edges = WL_SHELL_SURFACE_RESIZE_TOP;
else
{
if (window->wl.xdg.toplevel)
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
else
wl_shell_surface_move(window->wl.shellSurface, _glfw.wl.seat, serial);
}
break;
case leftDecoration:
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
edges = WL_SHELL_SURFACE_RESIZE_TOP_LEFT;
else
edges = WL_SHELL_SURFACE_RESIZE_LEFT;
break;
case rightDecoration:
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
edges = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT;
else
edges = WL_SHELL_SURFACE_RESIZE_RIGHT;
break;
case bottomDecoration:
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT;
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT;
else
edges = WL_SHELL_SURFACE_RESIZE_BOTTOM;
break;
default:
assert(0);
}
if (edges != WL_SHELL_SURFACE_RESIZE_NONE)
{
if (window->wl.xdg.toplevel)
xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
serial, edges);
else
wl_shell_surface_resize(window->wl.shellSurface, _glfw.wl.seat,
serial, edges);
}
}
else if (button == BTN_RIGHT)
{
if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel)
{
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
_glfw.wl.seat, serial,
window->wl.cursorPosX,
window->wl.cursorPosY);
return;
}
}
// Dont pass the button to the user if it was related to a decoration.
if (window->wl.decorations.focus != mainWindow)
return;
_glfw.wl.pointerSerial = serial;
@@ -132,14 +309,17 @@ static void pointerHandleButton(void* data,
}
static void pointerHandleAxis(void* data,
struct wl_pointer* wl_pointer,
struct wl_pointer* pointer,
uint32_t time,
uint32_t axis,
wl_fixed_t value)
{
_GLFWwindow* window = _glfw.wl.pointerFocus;
double scrollFactor;
double x = 0, y = 0;
double x = 0.0, y = 0.0;
// Wayland scroll events are in pointer motion coordinate space (think two
// finger scroll). The factor 10 is commonly used to convert to "scroll
// step means 1.0.
const double scrollFactor = 1.0 / 10.0;
if (!window)
return;
@@ -147,22 +327,10 @@ static void pointerHandleAxis(void* data,
assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL ||
axis == WL_POINTER_AXIS_VERTICAL_SCROLL);
/* Wayland scroll events are in pointer motion coordinate space (think
* two finger scroll). The factor 10 is commonly used to convert to
* "scroll step means 1.0. */
scrollFactor = 1.0/10.0;
switch (axis)
{
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
x = wl_fixed_to_double(value) * scrollFactor;
y = 0.0;
break;
case WL_POINTER_AXIS_VERTICAL_SCROLL:
x = 0.0;
y = wl_fixed_to_double(value) * scrollFactor;
break;
}
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
x = wl_fixed_to_double(value) * scrollFactor;
else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
y = wl_fixed_to_double(value) * scrollFactor;
_glfwInputScroll(window, x, y);
}
@@ -181,16 +349,7 @@ static void keyboardHandleKeymap(void* data,
int fd,
uint32_t size)
{
struct xkb_keymap* keymap;
struct xkb_state* state;
#ifdef HAVE_XKBCOMMON_COMPOSE_H
struct xkb_compose_table* composeTable;
struct xkb_compose_state* composeState;
#endif
char* mapStr;
const char* locale;
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
{
@@ -203,78 +362,10 @@ static void keyboardHandleKeymap(void* data,
close(fd);
return;
}
keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
mapStr,
XKB_KEYMAP_FORMAT_TEXT_V1,
0);
xkb_glfw_compile_keymap(mapStr);
munmap(mapStr, size);
close(fd);
if (!keymap)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to compile keymap");
return;
}
state = xkb_state_new(keymap);
if (!state)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to create XKB state");
xkb_keymap_unref(keymap);
return;
}
// Look up the preferred locale, falling back to "C" as default.
locale = getenv("LC_ALL");
if (!locale)
locale = getenv("LC_CTYPE");
if (!locale)
locale = getenv("LANG");
if (!locale)
locale = "C";
#ifdef HAVE_XKBCOMMON_COMPOSE_H
composeTable =
xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
XKB_COMPOSE_COMPILE_NO_FLAGS);
if (composeTable)
{
composeState =
xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
xkb_compose_table_unref(composeTable);
if (composeState)
_glfw.wl.xkb.composeState = composeState;
else
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to create XKB compose state");
}
else
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to create XKB compose table");
}
#endif
xkb_keymap_unref(_glfw.wl.xkb.keymap);
xkb_state_unref(_glfw.wl.xkb.state);
_glfw.wl.xkb.keymap = keymap;
_glfw.wl.xkb.state = state;
_glfw.wl.xkb.controlMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
_glfw.wl.xkb.altMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
_glfw.wl.xkb.shiftMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
_glfw.wl.xkb.superMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
_glfw.wl.xkb.capsLockMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
_glfw.wl.xkb.numLockMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
}
static void keyboardHandleEnter(void* data,
@@ -288,6 +379,12 @@ static void keyboardHandleEnter(void* data,
return;
_GLFWwindow* window = wl_surface_get_user_data(surface);
if (!window)
{
window = findWindowFromDecorationSurface(surface, NULL);
if (!window)
return;
}
_glfw.wl.keyboardFocus = window;
_glfwInputWindowFocus(window, GLFW_TRUE);
@@ -307,15 +404,6 @@ static void keyboardHandleLeave(void* data,
_glfwInputWindowFocus(window, GLFW_FALSE);
}
static int toGLFWKeyCode(uint32_t key)
{
if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0]))
return _glfw.wl.keycodes[key];
return GLFW_KEY_UNKNOWN;
}
#ifdef HAVE_XKBCOMMON_COMPOSE_H
static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
{
if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
@@ -335,9 +423,8 @@ static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
return sym;
}
}
#endif
static void inputChar(_GLFWwindow* window, uint32_t key)
static void inputChar(_GLFWwindow* window, uint32_t key, GLFWbool *shouldRepeat)
{
uint32_t code, numSyms;
long cp;
@@ -346,19 +433,20 @@ static void inputChar(_GLFWwindow* window, uint32_t key)
code = key + 8;
numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms);
*shouldRepeat = xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, code);
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));
if (*shouldRepeat) {
_glfw.wl.keyRepeatInfo.codepoint = cp;
_glfw.wl.keyRepeatInfo.plain = plain;
}
_glfwInputChar(window, cp, mods, plain);
}
}
@@ -378,15 +466,28 @@ static void keyboardHandleKey(void* data,
if (!window)
return;
keyCode = toGLFWKeyCode(key);
keyCode = xkb_glfw_to_glfw_key_code(key);
action = state == WL_KEYBOARD_KEY_STATE_PRESSED
? GLFW_PRESS : GLFW_RELEASE;
_glfw.wl.keyRepeatInfo.nextRepeatAt = 0;
_glfw.wl.keyRepeatInfo.codepoint = -1;
_glfwInputKey(window, keyCode, key, action,
_glfw.wl.xkb.modifiers);
if (action == GLFW_PRESS)
inputChar(window, key);
{
GLFWbool shouldRepeat = GLFW_FALSE;
inputChar(window, key, &shouldRepeat);
if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0)
{
_glfw.wl.keyRepeatInfo.glfwKeyCode = keyCode;
_glfw.wl.keyRepeatInfo.scancode = key;
_glfw.wl.keyRepeatInfo.nextRepeatAt = glfwGetTime() + (double)(_glfw.wl.keyboardRepeatDelay) / 1000.0;
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
}
}
}
static void keyboardHandleModifiers(void* data,
@@ -397,38 +498,19 @@ static void keyboardHandleModifiers(void* data,
uint32_t modsLocked,
uint32_t group)
{
xkb_mod_mask_t mask;
unsigned int modifiers = 0;
xkb_glfw_update_modifiers(modsDepressed, modsLatched, modsLocked, group);
}
if (!_glfw.wl.xkb.keymap)
static void keyboardHandleRepeatInfo(void* data,
struct wl_keyboard* keyboard,
int32_t rate,
int32_t delay)
{
if (keyboard != _glfw.wl.keyboard)
return;
xkb_state_update_mask(_glfw.wl.xkb.state,
modsDepressed,
modsLatched,
modsLocked,
0,
0,
group);
mask = xkb_state_serialize_mods(_glfw.wl.xkb.state,
XKB_STATE_MODS_DEPRESSED |
XKB_STATE_LAYOUT_DEPRESSED |
XKB_STATE_MODS_LATCHED |
XKB_STATE_LAYOUT_LATCHED);
if (mask & _glfw.wl.xkb.controlMask)
modifiers |= GLFW_MOD_CONTROL;
if (mask & _glfw.wl.xkb.altMask)
modifiers |= GLFW_MOD_ALT;
if (mask & _glfw.wl.xkb.shiftMask)
modifiers |= GLFW_MOD_SHIFT;
if (mask & _glfw.wl.xkb.superMask)
modifiers |= GLFW_MOD_SUPER;
if (mask & _glfw.wl.xkb.capsLockMask)
modifiers |= GLFW_MOD_CAPS_LOCK;
if (mask & _glfw.wl.xkb.numLockMask)
modifiers |= GLFW_MOD_NUM_LOCK;
_glfw.wl.xkb.modifiers = modifiers;
_glfw.wl.keyboardRepeatRate = rate;
_glfw.wl.keyboardRepeatDelay = delay;
}
static const struct wl_keyboard_listener keyboardListener = {
@@ -437,6 +519,7 @@ static const struct wl_keyboard_listener keyboardListener = {
keyboardHandleLeave,
keyboardHandleKey,
keyboardHandleModifiers,
keyboardHandleRepeatInfo,
};
static void seatHandleCapabilities(void* data,
@@ -466,8 +549,15 @@ static void seatHandleCapabilities(void* data,
}
}
static void seatHandleName(void* data,
struct wl_seat* seat,
const char* name)
{
}
static const struct wl_seat_listener seatListener = {
seatHandleCapabilities
seatHandleCapabilities,
seatHandleName,
};
static void wmBaseHandlePing(void* data,
@@ -494,6 +584,11 @@ static void registryHandleGlobal(void* data,
wl_registry_bind(registry, name, &wl_compositor_interface,
_glfw.wl.compositorVersion);
}
else if (strcmp(interface, "wl_subcompositor") == 0)
{
_glfw.wl.subcompositor =
wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
}
else if (strcmp(interface, "wl_shm") == 0)
{
_glfw.wl.shm =
@@ -512,8 +607,10 @@ static void registryHandleGlobal(void* data,
{
if (!_glfw.wl.seat)
{
_glfw.wl.seatVersion = min(4, version);
_glfw.wl.seat =
wl_registry_bind(registry, name, &wl_seat_interface, 1);
wl_registry_bind(registry, name, &wl_seat_interface,
_glfw.wl.seatVersion);
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
}
}
@@ -523,6 +620,11 @@ static void registryHandleGlobal(void* data,
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
}
else if (strcmp(interface, "wp_viewporter") == 0)
{
_glfw.wl.viewporter =
wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
}
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
{
_glfw.wl.relativePointerManager =
@@ -550,6 +652,18 @@ static void registryHandleGlobalRemove(void *data,
struct wl_registry *registry,
uint32_t name)
{
int i;
_GLFWmonitor* monitor;
for (i = 0; i < _glfw.monitorCount; ++i)
{
monitor = _glfw.monitors[i];
if (monitor->wl.name == name)
{
_glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0);
return;
}
}
}
@@ -558,140 +672,6 @@ static const struct wl_registry_listener registryListener = {
registryHandleGlobalRemove
};
// Create key code translation tables
//
static void createKeyTables(void)
{
int scancode;
memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes));
memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes));
_glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT;
_glfw.wl.keycodes[KEY_1] = GLFW_KEY_1;
_glfw.wl.keycodes[KEY_2] = GLFW_KEY_2;
_glfw.wl.keycodes[KEY_3] = GLFW_KEY_3;
_glfw.wl.keycodes[KEY_4] = GLFW_KEY_4;
_glfw.wl.keycodes[KEY_5] = GLFW_KEY_5;
_glfw.wl.keycodes[KEY_6] = GLFW_KEY_6;
_glfw.wl.keycodes[KEY_7] = GLFW_KEY_7;
_glfw.wl.keycodes[KEY_8] = GLFW_KEY_8;
_glfw.wl.keycodes[KEY_9] = GLFW_KEY_9;
_glfw.wl.keycodes[KEY_0] = GLFW_KEY_0;
_glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE;
_glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS;
_glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL;
_glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q;
_glfw.wl.keycodes[KEY_W] = GLFW_KEY_W;
_glfw.wl.keycodes[KEY_E] = GLFW_KEY_E;
_glfw.wl.keycodes[KEY_R] = GLFW_KEY_R;
_glfw.wl.keycodes[KEY_T] = GLFW_KEY_T;
_glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y;
_glfw.wl.keycodes[KEY_U] = GLFW_KEY_U;
_glfw.wl.keycodes[KEY_I] = GLFW_KEY_I;
_glfw.wl.keycodes[KEY_O] = GLFW_KEY_O;
_glfw.wl.keycodes[KEY_P] = GLFW_KEY_P;
_glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET;
_glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET;
_glfw.wl.keycodes[KEY_A] = GLFW_KEY_A;
_glfw.wl.keycodes[KEY_S] = GLFW_KEY_S;
_glfw.wl.keycodes[KEY_D] = GLFW_KEY_D;
_glfw.wl.keycodes[KEY_F] = GLFW_KEY_F;
_glfw.wl.keycodes[KEY_G] = GLFW_KEY_G;
_glfw.wl.keycodes[KEY_H] = GLFW_KEY_H;
_glfw.wl.keycodes[KEY_J] = GLFW_KEY_J;
_glfw.wl.keycodes[KEY_K] = GLFW_KEY_K;
_glfw.wl.keycodes[KEY_L] = GLFW_KEY_L;
_glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON;
_glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE;
_glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z;
_glfw.wl.keycodes[KEY_X] = GLFW_KEY_X;
_glfw.wl.keycodes[KEY_C] = GLFW_KEY_C;
_glfw.wl.keycodes[KEY_V] = GLFW_KEY_V;
_glfw.wl.keycodes[KEY_B] = GLFW_KEY_B;
_glfw.wl.keycodes[KEY_N] = GLFW_KEY_N;
_glfw.wl.keycodes[KEY_M] = GLFW_KEY_M;
_glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA;
_glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD;
_glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH;
_glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH;
_glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE;
_glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB;
_glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT;
_glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT;
_glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL;
_glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL;
_glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT;
_glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT;
_glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER;
_glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER;
_glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU;
_glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK;
_glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK;
_glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN;
_glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK;
_glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE;
_glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE;
_glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE;
_glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER;
_glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME;
_glfw.wl.keycodes[KEY_END] = GLFW_KEY_END;
_glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP;
_glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN;
_glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT;
_glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT;
_glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT;
_glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN;
_glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP;
_glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1;
_glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2;
_glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3;
_glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4;
_glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5;
_glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6;
_glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7;
_glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8;
_glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9;
_glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10;
_glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11;
_glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12;
_glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13;
_glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14;
_glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15;
_glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16;
_glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17;
_glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18;
_glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19;
_glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20;
_glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21;
_glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22;
_glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23;
_glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24;
_glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE;
_glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY;
_glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT;
_glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD;
_glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0;
_glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1;
_glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2;
_glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3;
_glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4;
_glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5;
_glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6;
_glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7;
_glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8;
_glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9;
_glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL;
_glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL;
_glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER;
for (scancode = 0; scancode < 256; scancode++)
{
if (_glfw.wl.keycodes[scancode] > 0)
_glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode;
}
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
@@ -731,51 +711,7 @@ int _glfwPlatformInit(void)
_glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize)
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
_glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0");
if (!_glfw.wl.xkb.handle)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to open libxkbcommon");
return GLFW_FALSE;
}
_glfw.wl.xkb.context_new = (PFN_xkb_context_new)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new");
_glfw.wl.xkb.context_unref = (PFN_xkb_context_unref)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref");
_glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string");
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref");
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
_glfw.wl.xkb.state_new = (PFN_xkb_state_new)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new");
_glfw.wl.xkb.state_unref = (PFN_xkb_state_unref)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref");
_glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms");
_glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
_glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
#ifdef HAVE_XKBCOMMON_COMPOSE_H
_glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale");
_glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref");
_glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new");
_glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref");
_glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed");
_glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status");
_glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym)
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
#endif
load_glfw_xkb();
_glfw.wl.display = wl_display_connect(NULL);
if (!_glfw.wl.display)
@@ -788,15 +724,7 @@ int _glfwPlatformInit(void)
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);
createKeyTables();
_glfw.wl.xkb.context = xkb_context_new(0);
if (!_glfw.wl.xkb.context)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to initialize xkb context");
return GLFW_FALSE;
}
create_glfw_xkb_context();
// Sync so we got all registry objects
wl_display_roundtrip(_glfw.wl.display);
@@ -804,8 +732,10 @@ int _glfwPlatformInit(void)
// Sync so we got all initial output events
wl_display_roundtrip(_glfw.wl.display);
#ifdef __linux__
if (!_glfwInitJoysticksLinux())
return GLFW_FALSE;
#endif
_glfwInitTimerPOSIX();
@@ -827,7 +757,9 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void)
{
#ifdef __linux__
_glfwTerminateJoysticksLinux();
#endif
_glfwTerminateEGL();
if (_glfw.wl.egl.handle)
{
@@ -835,21 +767,7 @@ void _glfwPlatformTerminate(void)
_glfw.wl.egl.handle = NULL;
}
#ifdef HAVE_XKBCOMMON_COMPOSE_H
if (_glfw.wl.xkb.composeState)
xkb_compose_state_unref(_glfw.wl.xkb.composeState);
#endif
if (_glfw.wl.xkb.keymap)
xkb_keymap_unref(_glfw.wl.xkb.keymap);
if (_glfw.wl.xkb.state)
xkb_state_unref(_glfw.wl.xkb.state);
if (_glfw.wl.xkb.context)
xkb_context_unref(_glfw.wl.xkb.context);
if (_glfw.wl.xkb.handle)
{
_glfw_dlclose(_glfw.wl.xkb.handle);
_glfw.wl.xkb.handle = NULL;
}
release_glfw_xkb();
if (_glfw.wl.cursorTheme)
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
@@ -861,12 +779,16 @@ void _glfwPlatformTerminate(void)
if (_glfw.wl.cursorSurface)
wl_surface_destroy(_glfw.wl.cursorSurface);
if (_glfw.wl.subcompositor)
wl_subcompositor_destroy(_glfw.wl.subcompositor);
if (_glfw.wl.compositor)
wl_compositor_destroy(_glfw.wl.compositor);
if (_glfw.wl.shm)
wl_shm_destroy(_glfw.wl.shm);
if (_glfw.wl.shell)
wl_shell_destroy(_glfw.wl.shell);
if (_glfw.wl.viewporter)
wp_viewporter_destroy(_glfw.wl.viewporter);
if (_glfw.wl.wmBase)
xdg_wm_base_destroy(_glfw.wl.wmBase);
if (_glfw.wl.pointer)

7
glfw/wl_monitor.c vendored
View File

@@ -136,6 +136,7 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
monitor->wl.scale = 1;
monitor->wl.output = output;
monitor->wl.name = name;
wl_output_add_listener(output, &outputListener, monitor);
}
@@ -145,6 +146,12 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
{
if (monitor->wl.output)
wl_output_destroy(monitor->wl.output);
}
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{
if (xpos)

129
glfw/wl_platform.h vendored
View File

@@ -25,10 +25,6 @@
//========================================================================
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#ifdef HAVE_XKBCOMMON_COMPOSE_H
#include <xkbcommon/xkbcommon-compose.h>
#endif
#include <dlfcn.h>
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
@@ -47,12 +43,19 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#include "posix_thread.h"
#include "posix_time.h"
#ifdef __linux__
#include "linux_joystick.h"
#else
#include "null_joystick.h"
#endif
#define GLFW_XKB_GLOBAL_NAME _glfw.wl.xkb
#include "xkb_glfw.h"
#include "xkb_unicode.h"
#include "egl_context.h"
#include "osmesa_context.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland-viewporter-client-protocol.h"
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
@@ -100,44 +103,28 @@ typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int,
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
#define wl_egl_window_resize _glfw.wl.egl.window_resize
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
typedef void (* PFN_xkb_state_unref)(struct xkb_state*);
typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component);
#define xkb_context_new _glfw.wl.xkb.context_new
#define xkb_context_unref _glfw.wl.xkb.context_unref
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref
#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index
#define xkb_state_new _glfw.wl.xkb.state_new
#define xkb_state_unref _glfw.wl.xkb.state_unref
#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms
#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask
#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods
#define _GLFW_DECORATION_WIDTH 4
#define _GLFW_DECORATION_TOP 24
#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH)
#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH)
#ifdef HAVE_XKBCOMMON_COMPOSE_H
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*);
#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale
#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref
#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new
#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref
#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed
#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status
#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym
#endif
typedef enum _GLFWdecorationSideWayland
{
mainWindow,
topDecoration,
leftDecoration,
rightDecoration,
bottomDecoration,
} _GLFWdecorationSideWayland;
typedef struct _GLFWdecorationWayland
{
struct wl_surface* surface;
struct wl_subsurface* subsurface;
struct wp_viewport* viewport;
} _GLFWdecorationWayland;
// Wayland-specific per-window data
//
@@ -180,6 +167,12 @@ typedef struct _GLFWwindowWayland
// This is a hack to prevent auto-iconification on creation.
GLFWbool justCreated;
struct {
struct wl_buffer* buffer;
_GLFWdecorationWayland top, left, right, bottom;
int focus;
} decorations;
} _GLFWwindowWayland;
// Wayland-specific global data
@@ -189,64 +182,36 @@ typedef struct _GLFWlibraryWayland
struct wl_display* display;
struct wl_registry* registry;
struct wl_compositor* compositor;
struct wl_subcompositor* subcompositor;
struct wl_shell* shell;
struct wl_shm* shm;
struct wl_seat* seat;
struct wl_pointer* pointer;
struct wl_keyboard* keyboard;
struct xdg_wm_base* wmBase;
struct wp_viewporter* viewporter;
struct zwp_relative_pointer_manager_v1* relativePointerManager;
struct zwp_pointer_constraints_v1* pointerConstraints;
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
int compositorVersion;
int seatVersion;
struct wl_cursor_theme* cursorTheme;
struct wl_surface* cursorSurface;
uint32_t pointerSerial;
short int keycodes[256];
short int scancodes[GLFW_KEY_LAST + 1];
int32_t keyboardRepeatRate;
int32_t keyboardRepeatDelay;
struct {
void* handle;
struct xkb_context* context;
struct xkb_keymap* keymap;
struct xkb_state* state;
#ifdef HAVE_XKBCOMMON_COMPOSE_H
struct xkb_compose_state* composeState;
#endif
xkb_mod_mask_t controlMask;
xkb_mod_mask_t altMask;
xkb_mod_mask_t shiftMask;
xkb_mod_mask_t superMask;
xkb_mod_mask_t capsLockMask;
xkb_mod_mask_t numLockMask;
unsigned int modifiers;
PFN_xkb_context_new context_new;
PFN_xkb_context_unref context_unref;
PFN_xkb_keymap_new_from_string keymap_new_from_string;
PFN_xkb_keymap_unref keymap_unref;
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
PFN_xkb_state_new state_new;
PFN_xkb_state_unref state_unref;
PFN_xkb_state_key_get_syms state_key_get_syms;
PFN_xkb_state_update_mask state_update_mask;
PFN_xkb_state_serialize_mods state_serialize_mods;
#ifdef HAVE_XKBCOMMON_COMPOSE_H
PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
PFN_xkb_compose_table_unref compose_table_unref;
PFN_xkb_compose_state_new compose_state_new;
PFN_xkb_compose_state_unref compose_state_unref;
PFN_xkb_compose_state_feed compose_state_feed;
PFN_xkb_compose_state_get_status compose_state_get_status;
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
#endif
} xkb;
long codepoint;
int plain;
int glfwKeyCode;
int scancode;
double nextRepeatAt;
_GLFWwindow* keyboardFocus;
} keyRepeatInfo;
_GLFWXKBData xkb;
_GLFWwindow* pointerFocus;
_GLFWwindow* keyboardFocus;
@@ -275,6 +240,7 @@ typedef struct _GLFWlibraryWayland
typedef struct _GLFWmonitorWayland
{
struct wl_output* output;
int name;
int currentMode;
int x;
@@ -295,4 +261,3 @@ typedef struct _GLFWcursorWayland
void _glfwAddOutputWayland(uint32_t name, uint32_t version);

557
glfw/wl_window.c vendored
View File

@@ -57,6 +57,16 @@ static void handleConfigure(void* data,
if (!window->monitor)
{
if (_glfw.wl.viewporter && window->decorated)
{
width -= _GLFW_DECORATION_HORIZONTAL;
height -= _GLFW_DECORATION_VERTICAL;
}
if (width < 1)
width = 1;
if (height < 1)
height = 1;
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
{
aspectRatio = (float)width / (float)height;
@@ -94,9 +104,265 @@ static const struct wl_shell_surface_listener shellSurfaceListener = {
handlePopupDone
};
static int
createTmpfileCloexec(char* tmpname)
{
int fd;
fd = mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0)
unlink(tmpname);
return fd;
}
/*
* Create a new, unique, anonymous file of the given size, and
* return the file descriptor for it. The file descriptor is set
* CLOEXEC. The file is immediately suitable for mmap()'ing
* the given size at offset zero.
*
* The file should not have a permanent backing store like a disk,
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
*
* The file name is deleted from the file system.
*
* The file is suitable for buffer sharing between processes by
* transmitting the file descriptor over Unix sockets using the
* SCM_RIGHTS methods.
*
* posix_fallocate() is used to guarantee that disk space is available
* for the file at the given size. If disk space is insufficent, errno
* is set to ENOSPC. If posix_fallocate() is not supported, program may
* receive SIGBUS on accessing mmap()'ed file contents instead.
*/
static int
createAnonymousFile(off_t size)
{
static const char template[] = "/glfw-shared-XXXXXX";
const char* path;
char* name;
int fd;
int ret;
path = getenv("XDG_RUNTIME_DIR");
if (!path)
{
errno = ENOENT;
return -1;
}
name = calloc(strlen(path) + sizeof(template), 1);
strcpy(name, path);
strcat(name, template);
fd = createTmpfileCloexec(name);
free(name);
if (fd < 0)
return -1;
ret = posix_fallocate(fd, 0, size);
if (ret != 0)
{
close(fd);
errno = ret;
return -1;
}
return fd;
}
static struct wl_buffer* createShmBuffer(const GLFWimage* image)
{
struct wl_shm_pool* pool;
struct wl_buffer* buffer;
int stride = image->width * 4;
int length = image->width * image->height * 4;
void* data;
int fd, i;
fd = createAnonymousFile(length);
if (fd < 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Creating a buffer file for %d B failed: %m",
length);
return GLFW_FALSE;
}
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: mmap failed: %m");
close(fd);
return GLFW_FALSE;
}
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
close(fd);
unsigned char* source = (unsigned char*) image->pixels;
unsigned char* target = data;
for (i = 0; i < image->width * image->height; i++, source += 4)
{
unsigned int alpha = source[3];
*target++ = (unsigned char) ((source[2] * alpha) / 255);
*target++ = (unsigned char) ((source[1] * alpha) / 255);
*target++ = (unsigned char) ((source[0] * alpha) / 255);
*target++ = (unsigned char) alpha;
}
buffer =
wl_shm_pool_create_buffer(pool, 0,
image->width,
image->height,
stride, WL_SHM_FORMAT_ARGB8888);
munmap(data, length);
wl_shm_pool_destroy(pool);
return buffer;
}
static void createDecoration(_GLFWdecorationWayland* decoration,
struct wl_surface* parent,
struct wl_buffer* buffer, GLFWbool opaque,
int x, int y,
int width, int height)
{
struct wl_region* region;
decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor);
decoration->subsurface =
wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
decoration->surface, parent);
wl_subsurface_set_position(decoration->subsurface, x, y);
decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
decoration->surface);
wp_viewport_set_destination(decoration->viewport, width, height);
wl_surface_attach(decoration->surface, buffer, 0, 0);
if (opaque)
{
region = wl_compositor_create_region(_glfw.wl.compositor);
wl_region_add(region, 0, 0, width, height);
wl_surface_set_opaque_region(decoration->surface, region);
wl_surface_commit(decoration->surface);
wl_region_destroy(region);
}
else
wl_surface_commit(decoration->surface);
}
static void createDecorations(_GLFWwindow* window)
{
unsigned char data[] = { 224, 224, 224, 255 };
const GLFWimage image = { 1, 1, data };
GLFWbool opaque = (data[3] == 255);
if (!_glfw.wl.viewporter)
return;
if (!window->wl.decorations.buffer)
window->wl.decorations.buffer = createShmBuffer(&image);
createDecoration(&window->wl.decorations.top, window->wl.surface,
window->wl.decorations.buffer, opaque,
0, -_GLFW_DECORATION_TOP,
window->wl.width, _GLFW_DECORATION_TOP);
createDecoration(&window->wl.decorations.left, window->wl.surface,
window->wl.decorations.buffer, opaque,
-_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP,
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
createDecoration(&window->wl.decorations.right, window->wl.surface,
window->wl.decorations.buffer, opaque,
window->wl.width, -_GLFW_DECORATION_TOP,
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
createDecoration(&window->wl.decorations.bottom, window->wl.surface,
window->wl.decorations.buffer, opaque,
-_GLFW_DECORATION_WIDTH, window->wl.height,
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
}
static void destroyDecoration(_GLFWdecorationWayland* decoration)
{
if (decoration->surface)
wl_surface_destroy(decoration->surface);
if (decoration->subsurface)
wl_subsurface_destroy(decoration->subsurface);
if (decoration->viewport)
wp_viewport_destroy(decoration->viewport);
decoration->surface = NULL;
decoration->subsurface = NULL;
decoration->viewport = NULL;
}
static void destroyDecorations(_GLFWwindow* window)
{
destroyDecoration(&window->wl.decorations.top);
destroyDecoration(&window->wl.decorations.left);
destroyDecoration(&window->wl.decorations.right);
destroyDecoration(&window->wl.decorations.bottom);
}
// Makes the surface considered as XRGB instead of ARGB.
static void setOpaqueRegion(_GLFWwindow* window)
{
struct wl_region* region;
region = wl_compositor_create_region(_glfw.wl.compositor);
if (!region)
return;
wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
wl_surface_set_opaque_region(window->wl.surface, region);
wl_surface_commit(window->wl.surface);
wl_region_destroy(region);
}
static void resizeWindow(_GLFWwindow* window)
{
int scale = window->wl.scale;
int scaledWidth = window->wl.width * scale;
int scaledHeight = window->wl.height * scale;
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
if (!window->wl.transparent)
setOpaqueRegion(window);
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
_glfwInputWindowContentScale(window, scale, scale);
if (!window->wl.decorations.top.surface)
return;
// Top decoration.
wp_viewport_set_destination(window->wl.decorations.top.viewport,
window->wl.width, _GLFW_DECORATION_TOP);
wl_surface_commit(window->wl.decorations.top.surface);
// Left decoration.
wp_viewport_set_destination(window->wl.decorations.left.viewport,
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
wl_surface_commit(window->wl.decorations.left.surface);
// Right decoration.
wl_subsurface_set_position(window->wl.decorations.right.subsurface,
window->wl.width, -_GLFW_DECORATION_TOP);
wp_viewport_set_destination(window->wl.decorations.right.viewport,
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
wl_surface_commit(window->wl.decorations.right.surface);
// Bottom decoration.
wl_subsurface_set_position(window->wl.decorations.bottom.subsurface,
-_GLFW_DECORATION_WIDTH, window->wl.height);
wp_viewport_set_destination(window->wl.decorations.bottom.viewport,
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
wl_surface_commit(window->wl.decorations.bottom.surface);
}
static void checkScaleChange(_GLFWwindow* window)
{
int scaledWidth, scaledHeight;
int scale = 1;
int i;
int monitorScale;
@@ -117,12 +383,8 @@ static void checkScaleChange(_GLFWwindow* window)
if (scale != window->wl.scale)
{
window->wl.scale = scale;
scaledWidth = window->wl.width * scale;
scaledHeight = window->wl.height * scale;
wl_surface_set_buffer_scale(window->wl.surface, scale);
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
_glfwInputWindowContentScale(window, scale, scale);
resizeWindow(window);
}
}
@@ -172,21 +434,6 @@ static const struct wl_surface_listener surfaceListener = {
handleLeave
};
// Makes the surface considered as XRGB instead of ARGB.
static void setOpaqueRegion(_GLFWwindow* window)
{
struct wl_region* region;
region = wl_compositor_create_region(_glfw.wl.compositor);
if (!region)
return;
wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
wl_surface_set_opaque_region(window->wl.surface, region);
wl_surface_commit(window->wl.surface);
wl_region_destroy(region);
}
static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
{
if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
@@ -231,9 +478,32 @@ static GLFWbool createSurface(_GLFWwindow* window,
if (!window->wl.transparent)
setOpaqueRegion(window);
if (window->decorated && !window->monitor)
createDecorations(window);
return GLFW_TRUE;
}
static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refreshRate)
{
if (window->wl.xdg.toplevel)
{
xdg_toplevel_set_fullscreen(
window->wl.xdg.toplevel,
monitor->wl.output);
}
else if (window->wl.shellSurface)
{
wl_shell_surface_set_fullscreen(
window->wl.shellSurface,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
refreshRate * 1000, // Convert Hz to mHz.
monitor->wl.output);
}
setIdleInhibitor(window, GLFW_TRUE);
destroyDecorations(window);
}
static GLFWbool createShellSurface(_GLFWwindow* window)
{
if (!_glfw.wl.shell)
@@ -261,12 +531,7 @@ static GLFWbool createShellSurface(_GLFWwindow* window)
if (window->monitor)
{
wl_shell_surface_set_fullscreen(
window->wl.shellSurface,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
0,
window->monitor->wl.output);
setIdleInhibitor(window, GLFW_TRUE);
setFullscreen(window, window->monitor, 0);
}
else if (window->wl.maximized)
{
@@ -424,25 +689,34 @@ static GLFWbool createXdgSurface(_GLFWwindow* window)
return GLFW_TRUE;
}
static void
dispatchPendingKeyRepeats() {
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return;
double now = glfwGetTime();
const int mods = _glfw.wl.xkb.modifiers;
while (_glfw.wl.keyRepeatInfo.nextRepeatAt <= now) {
_glfwInputKey(_glfw.wl.keyRepeatInfo.keyboardFocus, _glfw.wl.keyRepeatInfo.glfwKeyCode, _glfw.wl.keyRepeatInfo.scancode, GLFW_REPEAT, mods);
if (_glfw.wl.keyRepeatInfo.codepoint > -1) _glfwInputChar(_glfw.wl.keyRepeatInfo.keyboardFocus, _glfw.wl.keyRepeatInfo.codepoint, mods, _glfw.wl.keyRepeatInfo.plain);
_glfw.wl.keyRepeatInfo.nextRepeatAt += 1.0 / _glfw.wl.keyboardRepeatRate;
now = glfwGetTime();
}
}
static int
createTmpfileCloexec(char* tmpname)
{
int fd;
fd = mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0)
unlink(tmpname);
return fd;
adjustTimeoutForKeyRepeat(int timeout) {
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return timeout;
double now = glfwGetTime();
if (timeout < 0 || now + timeout / 1000. > _glfw.wl.keyRepeatInfo.nextRepeatAt) {
timeout = _glfw.wl.keyRepeatInfo.nextRepeatAt <= now ? 0 : ( (_glfw.wl.keyRepeatInfo.nextRepeatAt - now) * 1000 + 1 );
}
return timeout;
}
static void
handleEvents(int timeout)
{
struct wl_display* display = _glfw.wl.display;
struct pollfd fds[] = {
{ wl_display_get_fd(display), POLLIN },
};
struct pollfd pfd = { wl_display_get_fd(display), POLLIN };
while (wl_display_prepare_read(display) != 0)
wl_display_dispatch_pending(display);
@@ -462,71 +736,27 @@ handleEvents(int timeout)
return;
}
if (poll(fds, 1, timeout) > 0)
dispatchPendingKeyRepeats();
timeout = adjustTimeoutForKeyRepeat(timeout);
if (poll(&pfd, 1, timeout) > 0)
{
wl_display_read_events(display);
wl_display_dispatch_pending(display);
if (pfd.revents & POLLIN)
{
wl_display_read_events(display);
wl_display_dispatch_pending(display);
}
else
{
wl_display_cancel_read(display);
}
}
else
{
wl_display_cancel_read(display);
}
}
/*
* Create a new, unique, anonymous file of the given size, and
* return the file descriptor for it. The file descriptor is set
* CLOEXEC. The file is immediately suitable for mmap()'ing
* the given size at offset zero.
*
* The file should not have a permanent backing store like a disk,
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
*
* The file name is deleted from the file system.
*
* The file is suitable for buffer sharing between processes by
* transmitting the file descriptor over Unix sockets using the
* SCM_RIGHTS methods.
*
* posix_fallocate() is used to guarantee that disk space is available
* for the file at the given size. If disk space is insufficent, errno
* is set to ENOSPC. If posix_fallocate() is not supported, program may
* receive SIGBUS on accessing mmap()'ed file contents instead.
*/
int
createAnonymousFile(off_t size)
{
static const char template[] = "/glfw-shared-XXXXXX";
const char* path;
char* name;
int fd;
int ret;
path = getenv("XDG_RUNTIME_DIR");
if (!path)
{
errno = ENOENT;
return -1;
}
name = calloc(strlen(path) + sizeof(template), 1);
strcpy(name, path);
strcat(name, template);
fd = createTmpfileCloexec(name);
free(name);
if (fd < 0)
return -1;
ret = posix_fallocate(fd, 0, size);
if (ret != 0)
{
close(fd);
errno = ret;
return -1;
}
return fd;
dispatchPendingKeyRepeats();
}
// Translates a GLFW standard cursor to a theme cursor name
@@ -639,6 +869,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
if (window->context.destroy)
window->context.destroy(window);
destroyDecorations(window);
if (window->wl.decorations.buffer)
wl_buffer_destroy(window->wl.decorations.buffer);
if (window->wl.native)
wl_egl_window_destroy(window->wl.native);
@@ -703,14 +937,9 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
int scaledWidth = width * window->wl.scale;
int scaledHeight = height * window->wl.scale;
window->wl.width = width;
window->wl.height = height;
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
if (!window->wl.transparent)
setOpaqueRegion(window);
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
resizeWindow(window);
}
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
@@ -754,8 +983,17 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
int* left, int* top,
int* right, int* bottom)
{
// TODO: will need a proper implementation once decorations are
// implemented, but for now just leave everything as 0.
if (window->decorated && !window->monitor)
{
if (top)
*top = _GLFW_DECORATION_TOP;
if (left)
*left = _GLFW_DECORATION_WIDTH;
if (right)
*right = _GLFW_DECORATION_WIDTH;
if (bottom)
*bottom = _GLFW_DECORATION_WIDTH;
}
}
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
@@ -851,11 +1089,15 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
"Wayland: Window attention request not implemented yet");
}
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
int _glfwPlatformWindowBell(_GLFWwindow* window)
{
// TODO
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Window bell request not implemented yet");
// TODO: Use an actual Wayland API to implement this when one becomes available
int fd = open("/dev/tty", O_WRONLY | O_CLOEXEC);
if (fd > -1) {
int ret = write(fd, "\x07", 1) == 1 ? GLFW_TRUE : GLFW_FALSE;
close(fd);
return ret;
}
return GLFW_FALSE;
}
@@ -871,35 +1113,20 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
int width, int height,
int refreshRate)
{
if (window->wl.xdg.toplevel)
if (monitor)
{
if (monitor)
{
xdg_toplevel_set_fullscreen(
window->wl.xdg.toplevel,
monitor->wl.output);
}
else
{
setFullscreen(window, monitor, refreshRate);
}
else
{
if (window->wl.xdg.toplevel)
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
}
}
else if (window->wl.shellSurface)
{
if (monitor)
{
wl_shell_surface_set_fullscreen(
window->wl.shellSurface,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
refreshRate * 1000, // Convert Hz to mHz.
monitor->wl.output);
}
else
{
else if (window->wl.shellSurface)
wl_shell_surface_set_toplevel(window->wl.shellSurface);
}
setIdleInhibitor(window, GLFW_FALSE);
if (window->decorated)
createDecorations(window);
}
setIdleInhibitor(window, monitor ? GLFW_TRUE : GLFW_FALSE);
_glfwInputWindowMonitor(window, monitor);
}
@@ -944,9 +1171,13 @@ void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
{
// TODO
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Window attribute setting not implemented yet");
if (!window->monitor)
{
if (enabled)
createDecorations(window);
else
destroyDecorations(window);
}
}
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
@@ -1019,60 +1250,14 @@ const char* _glfwPlatformGetScancodeName(int scancode)
int _glfwPlatformGetKeyScancode(int key)
{
return _glfw.wl.scancodes[key];
return _glfw.wl.xkb.scancodes[key];
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image,
int xhot, int yhot)
{
struct wl_shm_pool* pool;
int stride = image->width * 4;
int length = image->width * image->height * 4;
void* data;
int fd, i;
fd = createAnonymousFile(length);
if (fd < 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Creating a buffer file for %d B failed: %m",
length);
return GLFW_FALSE;
}
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Cursor mmap failed: %m");
close(fd);
return GLFW_FALSE;
}
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
close(fd);
unsigned char* source = (unsigned char*) image->pixels;
unsigned char* target = data;
for (i = 0; i < image->width * image->height; i++, source += 4)
{
unsigned int alpha = source[3];
*target++ = (unsigned char) ((source[2] * alpha) / 255);
*target++ = (unsigned char) ((source[1] * alpha) / 255);
*target++ = (unsigned char) ((source[0] * alpha) / 255);
*target++ = (unsigned char) alpha;
}
cursor->wl.buffer =
wl_shm_pool_create_buffer(pool, 0,
image->width,
image->height,
stride, WL_SHM_FORMAT_ARGB8888);
munmap(data, length);
wl_shm_pool_destroy(pool);
cursor->wl.buffer = createShmBuffer(image);
cursor->wl.width = image->width;
cursor->wl.height = image->height;
cursor->wl.xhot = xhot;

17
glfw/x11_monitor.c vendored
View File

@@ -216,7 +216,7 @@ void _glfwPollMonitorsX11(void)
// Set the current video mode for the specified monitor
//
GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
{
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
{
@@ -231,7 +231,7 @@ GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
best = _glfwChooseVideoMode(monitor, desired);
_glfwPlatformGetVideoMode(monitor, &current);
if (_glfwCompareVideoModes(&current, best) == 0)
return GLFW_TRUE;
return;
sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
@@ -269,16 +269,7 @@ GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
XRRFreeOutputInfo(oi);
XRRFreeCrtcInfo(ci);
XRRFreeScreenResources(sr);
if (!native)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: Monitor mode list changed");
return GLFW_FALSE;
}
}
return GLFW_TRUE;
}
// Restore the saved (original) video mode for the specified monitor
@@ -317,6 +308,10 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
{
}
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)

2
glfw/x11_platform.h vendored
View File

@@ -425,7 +425,7 @@ typedef struct _GLFWcursorX11
void _glfwPollMonitorsX11(void);
GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor);
Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot);

18
glfw/x11_window.c vendored
View File

@@ -1076,10 +1076,8 @@ static const char* getSelectionString(Atom selection)
// Make the specified window and its video mode active on its monitor
//
static GLFWbool acquireMonitor(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
GLFWbool status;
if (_glfw.x11.saver.count == 0)
{
// Remember old screen saver settings
@@ -1097,7 +1095,7 @@ static GLFWbool acquireMonitor(_GLFWwindow* window)
if (!window->monitor->window)
_glfw.x11.saver.count++;
status = _glfwSetVideoModeX11(window->monitor, &window->videoMode);
_glfwSetVideoModeX11(window->monitor, &window->videoMode);
if (window->x11.overrideRedirect)
{
@@ -1113,7 +1111,6 @@ static GLFWbool acquireMonitor(_GLFWwindow* window)
}
_glfwInputMonitorWindow(window->monitor, window);
return status;
}
// Remove the window and restore the original video mode
@@ -1977,11 +1974,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
{
_glfwPlatformShowWindow(window);
updateWindowMode(window);
if (!acquireMonitor(window))
return GLFW_FALSE;
if (wndconfig->centerCursor)
centerCursor(window);
acquireMonitor(window);
}
XFlush(_glfw.x11.display);
@@ -2347,9 +2340,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
0, 1, 0);
}
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
int _glfwPlatformWindowBell(_GLFWwindow* window)
{
return XkbBell(_glfw.x11.display, window->x11.handle, (int)param, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
}
void _glfwPlatformFocusWindow(_GLFWwindow* window)
@@ -3051,4 +3044,3 @@ GLFWAPI const char* glfwGetX11SelectionString(void)
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return getSelectionString(_glfw.x11.PRIMARY);
}

374
glfw/xkb_glfw.h vendored Normal file
View File

@@ -0,0 +1,374 @@
//========================================================================
// GLFW 3.3 XKB - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2014 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>
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t);
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_XKB_GLOBAL_NAME.context_new
#define xkb_context_unref GLFW_XKB_GLOBAL_NAME.context_unref
#define xkb_keymap_new_from_string GLFW_XKB_GLOBAL_NAME.keymap_new_from_string
#define xkb_keymap_unref GLFW_XKB_GLOBAL_NAME.keymap_unref
#define xkb_keymap_mod_get_index GLFW_XKB_GLOBAL_NAME.keymap_mod_get_index
#define xkb_keymap_key_repeats GLFW_XKB_GLOBAL_NAME.keymap_key_repeats
#define xkb_state_new GLFW_XKB_GLOBAL_NAME.state_new
#define xkb_state_unref GLFW_XKB_GLOBAL_NAME.state_unref
#define xkb_state_key_get_syms GLFW_XKB_GLOBAL_NAME.state_key_get_syms
#define xkb_state_update_mask GLFW_XKB_GLOBAL_NAME.state_update_mask
#define xkb_state_serialize_mods GLFW_XKB_GLOBAL_NAME.state_serialize_mods
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*);
#define xkb_compose_table_new_from_locale GLFW_XKB_GLOBAL_NAME.compose_table_new_from_locale
#define xkb_compose_table_unref GLFW_XKB_GLOBAL_NAME.compose_table_unref
#define xkb_compose_state_new GLFW_XKB_GLOBAL_NAME.compose_state_new
#define xkb_compose_state_unref GLFW_XKB_GLOBAL_NAME.compose_state_unref
#define xkb_compose_state_feed GLFW_XKB_GLOBAL_NAME.compose_state_feed
#define xkb_compose_state_get_status GLFW_XKB_GLOBAL_NAME.compose_state_get_status
#define xkb_compose_state_get_one_sym GLFW_XKB_GLOBAL_NAME.compose_state_get_one_sym
typedef struct {
void* handle;
struct xkb_context* context;
struct xkb_keymap* keymap;
struct xkb_state* state;
struct xkb_compose_state* composeState;
short int keycodes[256];
short int scancodes[GLFW_KEY_LAST + 1];
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_keymap_key_repeats keymap_key_repeats;
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;
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;
} _GLFWXKBData;
#define bind_xkb_sym(name) GLFW_XKB_GLOBAL_NAME.name = (PFN_xkb_##name) _glfw_dlsym(GLFW_XKB_GLOBAL_NAME.handle, "xkb_" #name)
#define load_glfw_xkb() {\
GLFW_XKB_GLOBAL_NAME.handle = _glfw_dlopen("libxkbcommon.so.0"); \
if (!GLFW_XKB_GLOBAL_NAME.handle) \
{ \
_glfwInputError(GLFW_PLATFORM_ERROR, \
"Failed to open libxkbcommon"); \
return GLFW_FALSE; \
} \
bind_xkb_sym(context_new); \
bind_xkb_sym(context_unref); \
bind_xkb_sym(keymap_new_from_string); \
bind_xkb_sym(keymap_unref); \
bind_xkb_sym(keymap_mod_get_index); \
bind_xkb_sym(keymap_key_repeats); \
bind_xkb_sym(state_new); \
bind_xkb_sym(state_unref); \
bind_xkb_sym(state_key_get_syms); \
bind_xkb_sym(state_update_mask); \
bind_xkb_sym(state_serialize_mods); \
bind_xkb_sym(compose_table_new_from_locale); \
bind_xkb_sym(compose_table_unref); \
bind_xkb_sym(compose_state_new); \
bind_xkb_sym(compose_state_unref); \
bind_xkb_sym(compose_state_feed); \
bind_xkb_sym(compose_state_get_status); \
bind_xkb_sym(compose_state_get_one_sym); \
}
#define release_glfw_xkb() {\
if (GLFW_XKB_GLOBAL_NAME.composeState) { \
xkb_compose_state_unref(GLFW_XKB_GLOBAL_NAME.composeState); \
GLFW_XKB_GLOBAL_NAME.composeState = NULL; \
} \
if (GLFW_XKB_GLOBAL_NAME.keymap) { \
xkb_keymap_unref(GLFW_XKB_GLOBAL_NAME.keymap); \
GLFW_XKB_GLOBAL_NAME.keymap = NULL; \
} \
if (GLFW_XKB_GLOBAL_NAME.state) { \
xkb_state_unref(GLFW_XKB_GLOBAL_NAME.state); \
GLFW_XKB_GLOBAL_NAME.state = NULL; \
} \
if (GLFW_XKB_GLOBAL_NAME.context) { \
xkb_context_unref(GLFW_XKB_GLOBAL_NAME.context); \
GLFW_XKB_GLOBAL_NAME.context = NULL; \
} \
if (GLFW_XKB_GLOBAL_NAME.handle) { \
_glfw_dlclose(GLFW_XKB_GLOBAL_NAME.handle); \
GLFW_XKB_GLOBAL_NAME.handle = NULL; \
} \
}
#define create_glfw_xkb_context() {\
GLFW_XKB_GLOBAL_NAME.context = xkb_context_new(0); \
if (!GLFW_XKB_GLOBAL_NAME.context) \
{ \
_glfwInputError(GLFW_PLATFORM_ERROR, \
"Failed to initialize XKB context"); \
return GLFW_FALSE; \
} \
int scancode; \
memset(GLFW_XKB_GLOBAL_NAME.keycodes, -1, sizeof(GLFW_XKB_GLOBAL_NAME.keycodes)); \
memset(GLFW_XKB_GLOBAL_NAME.scancodes, -1, sizeof(GLFW_XKB_GLOBAL_NAME.scancodes)); \
\
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_1] = GLFW_KEY_1; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_2] = GLFW_KEY_2; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_3] = GLFW_KEY_3; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_4] = GLFW_KEY_4; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_5] = GLFW_KEY_5; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_6] = GLFW_KEY_6; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_7] = GLFW_KEY_7; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_8] = GLFW_KEY_8; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_9] = GLFW_KEY_9; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_0] = GLFW_KEY_0; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_Q] = GLFW_KEY_Q; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_W] = GLFW_KEY_W; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_E] = GLFW_KEY_E; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_R] = GLFW_KEY_R; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_T] = GLFW_KEY_T; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_Y] = GLFW_KEY_Y; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_U] = GLFW_KEY_U; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_I] = GLFW_KEY_I; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_O] = GLFW_KEY_O; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_P] = GLFW_KEY_P; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_A] = GLFW_KEY_A; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_S] = GLFW_KEY_S; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_D] = GLFW_KEY_D; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F] = GLFW_KEY_F; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_G] = GLFW_KEY_G; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_H] = GLFW_KEY_H; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_J] = GLFW_KEY_J; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_K] = GLFW_KEY_K; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_L] = GLFW_KEY_L; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_Z] = GLFW_KEY_Z; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_X] = GLFW_KEY_X; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_C] = GLFW_KEY_C; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_V] = GLFW_KEY_V; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_B] = GLFW_KEY_B; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_N] = GLFW_KEY_N; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_M] = GLFW_KEY_M; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_TAB] = GLFW_KEY_TAB; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_MENU] = GLFW_KEY_MENU; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_HOME] = GLFW_KEY_HOME; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_END] = GLFW_KEY_END; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_UP] = GLFW_KEY_UP; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F1] = GLFW_KEY_F1; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F2] = GLFW_KEY_F2; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F3] = GLFW_KEY_F3; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F4] = GLFW_KEY_F4; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F5] = GLFW_KEY_F5; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F6] = GLFW_KEY_F6; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F7] = GLFW_KEY_F7; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F8] = GLFW_KEY_F8; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F9] = GLFW_KEY_F9; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F10] = GLFW_KEY_F10; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F11] = GLFW_KEY_F11; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F12] = GLFW_KEY_F12; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F13] = GLFW_KEY_F13; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F14] = GLFW_KEY_F14; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F15] = GLFW_KEY_F15; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F16] = GLFW_KEY_F16; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F17] = GLFW_KEY_F17; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F18] = GLFW_KEY_F18; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F19] = GLFW_KEY_F19; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F20] = GLFW_KEY_F20; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F21] = GLFW_KEY_F21; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F22] = GLFW_KEY_F22; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F23] = GLFW_KEY_F23; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F24] = GLFW_KEY_F24; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP0] = GLFW_KEY_KP_0; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP1] = GLFW_KEY_KP_1; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP2] = GLFW_KEY_KP_2; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP3] = GLFW_KEY_KP_3; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP4] = GLFW_KEY_KP_4; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP5] = GLFW_KEY_KP_5; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP6] = GLFW_KEY_KP_6; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP7] = GLFW_KEY_KP_7; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP8] = GLFW_KEY_KP_8; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP9] = GLFW_KEY_KP_9; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; \
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; \
\
for (scancode = 0; scancode < 256; scancode++) \
{ \
if (GLFW_XKB_GLOBAL_NAME.keycodes[scancode] > 0) \
GLFW_XKB_GLOBAL_NAME.scancodes[GLFW_XKB_GLOBAL_NAME.keycodes[scancode]] = scancode; \
} \
\
}
#define xkb_glfw_compile_keymap(map_str) { \
const char* locale = NULL; \
struct xkb_state* state = NULL; \
struct xkb_keymap* keymap = NULL; \
struct xkb_compose_table* compose_table = NULL; \
struct xkb_compose_state* compose_state = NULL; \
\
keymap = xkb_keymap_new_from_string(GLFW_XKB_GLOBAL_NAME.context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); \
if (!keymap) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to compile XKB keymap"); \
else { \
state = xkb_state_new(keymap); \
if (!state) { \
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB state"); \
xkb_keymap_unref(keymap); keymap = NULL; \
} else { \
/* 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(GLFW_XKB_GLOBAL_NAME.context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); \
if (!compose_table) { \
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose table"); \
xkb_keymap_unref(keymap); keymap = NULL; \
xkb_state_unref(state); state = NULL; \
} 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"); \
xkb_keymap_unref(keymap); keymap = NULL; \
xkb_state_unref(state); state = NULL; \
}\
} \
} \
} \
if (keymap && state && compose_state) { \
if (GLFW_XKB_GLOBAL_NAME.composeState) xkb_compose_state_unref(GLFW_XKB_GLOBAL_NAME.composeState); \
GLFW_XKB_GLOBAL_NAME.composeState = compose_state; \
if (GLFW_XKB_GLOBAL_NAME.keymap) xkb_keymap_unref(GLFW_XKB_GLOBAL_NAME.keymap); \
GLFW_XKB_GLOBAL_NAME.keymap = keymap; \
if (GLFW_XKB_GLOBAL_NAME.state) xkb_state_unref(GLFW_XKB_GLOBAL_NAME.state); \
GLFW_XKB_GLOBAL_NAME.state = state; \
}\
if (GLFW_XKB_GLOBAL_NAME.keymap) { \
GLFW_XKB_GLOBAL_NAME.controlMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Control"); \
GLFW_XKB_GLOBAL_NAME.altMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Mod1"); \
GLFW_XKB_GLOBAL_NAME.shiftMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Shift"); \
GLFW_XKB_GLOBAL_NAME.superMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Mod4"); \
GLFW_XKB_GLOBAL_NAME.capsLockMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Lock"); \
GLFW_XKB_GLOBAL_NAME.numLockMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Mod2"); \
} \
}
#define xkb_glfw_update_modifiers(depressed, latched, locked, group) {\
xkb_mod_mask_t mask; \
unsigned int modifiers = 0; \
if (!GLFW_XKB_GLOBAL_NAME.keymap) return; \
xkb_state_update_mask(GLFW_XKB_GLOBAL_NAME.state, depressed, latched, locked, 0, 0, group); \
mask = xkb_state_serialize_mods(GLFW_XKB_GLOBAL_NAME.state, XKB_STATE_MODS_DEPRESSED | XKB_STATE_LAYOUT_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_LAYOUT_LATCHED); \
if (mask & GLFW_XKB_GLOBAL_NAME.controlMask) modifiers |= GLFW_MOD_CONTROL; \
if (mask & GLFW_XKB_GLOBAL_NAME.altMask) modifiers |= GLFW_MOD_ALT; \
if (mask & GLFW_XKB_GLOBAL_NAME.shiftMask) modifiers |= GLFW_MOD_SHIFT; \
if (mask & GLFW_XKB_GLOBAL_NAME.superMask) modifiers |= GLFW_MOD_SUPER; \
if (mask & GLFW_XKB_GLOBAL_NAME.capsLockMask) modifiers |= GLFW_MOD_CAPS_LOCK; \
if (mask & GLFW_XKB_GLOBAL_NAME.numLockMask) modifiers |= GLFW_MOD_NUM_LOCK; \
GLFW_XKB_GLOBAL_NAME.modifiers = modifiers; \
}
#define xkb_glfw_to_glfw_key_code(key) \
((key < sizeof(GLFW_XKB_GLOBAL_NAME.keycodes) / sizeof(GLFW_XKB_GLOBAL_NAME.keycodes[0])) ? GLFW_XKB_GLOBAL_NAME.keycodes[key] : GLFW_KEY_UNKNOWN)

View File

@@ -24,20 +24,34 @@ kitty icat path/to/some/image.png
You can also see a screenshot with more sophisticated features such as alpha-blending and text over graphics
link:https://github.com/kovidgoyal/kitty/issues/33#issuecomment-334436100[here].
Some third-party programs that use the kitty graphics protocol:
* link:https://github.com/dsanson/termpdf[termdpf] - a terminal PDF/DJVU/CBR viewer
* link:https://github.com/ranger/ranger[ranger] - a terminal file manager, with image previews, see this link:https://github.com/ranger/ranger/pull/1077[PR]
toc::[]
== Getting the window size
In order to know what size of images to display and how to position them, the client must be able to get the
window size in pixels and the number of cells per row and column. This can be done by using the `TIOCGWINSZ` ioctl.
Some C code to demonstrate its use
Some code to demonstrate its use
In C:
```C
struct ttysize ts;
ioctl(0, TIOCGWINSZ, &ts);
printf("number of columns: %i, number of rows: %i, screen width: %i, screen height: %i\n", sz.ws_col, sz.ws_row, sz.ws_xpixel, sz.ws_ypixel);
```
In Python:
```py
import array, fcntl, termios
buf = array.array('H', [0, 0, 0, 0])
fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)
print('number of columns: {}, number of rows: {}, screen width: {}, screen height: {}'.format(*buf))
```
Note that some terminals return `0` for the width and height values. Such terminals should be modified to return the correct values.
Examples of terminals that return correct values: `kitty, xterm`

BIN
hints_mode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -1,6 +1,7 @@
= Key encoding for extended keyboard protocol
See link:protocol-extensions.asciidoc#keyboard-handling[Keyboard Handling protocol extension]
for more information and link:key_encoding.json[for this table in JSON format].
|===
| Name | Encoded representation (base64)

121
key_encoding.json Normal file
View File

@@ -0,0 +1,121 @@
{
"0": "G",
"1": "H",
"2": "I",
"3": "J",
"4": "K",
"5": "L",
"6": "M",
"7": "N",
"8": "O",
"9": "P",
"A": "S",
"APOSTROPHE": "B",
"B": "T",
"BACKSLASH": "t",
"BACKSPACE": "1",
"C": "U",
"CAPS LOCK": ":",
"COMMA": "C",
"D": "V",
"DELETE": "3",
"DOWN": "6",
"E": "W",
"END": "-",
"ENTER": "z",
"EQUAL": "R",
"ESCAPE": "y",
"F": "X",
"F1": "/",
"F10": "]",
"F11": "{",
"F12": "}",
"F13": "@",
"F14": "%",
"F15": "$",
"F16": "#",
"F17": "BA",
"F18": "BB",
"F19": "BC",
"F2": "*",
"F20": "BD",
"F21": "BE",
"F22": "BF",
"F23": "BG",
"F24": "BH",
"F25": "BI",
"F3": "?",
"F4": "&",
"F5": "<",
"F6": ">",
"F7": "(",
"F8": ")",
"F9": "[",
"G": "Y",
"GRAVE ACCENT": "v",
"H": "Z",
"HOME": ".",
"I": "a",
"INSERT": "2",
"J": "b",
"K": "c",
"KP 0": "BJ",
"KP 1": "BK",
"KP 2": "BL",
"KP 3": "BM",
"KP 4": "BN",
"KP 5": "BO",
"KP 6": "BP",
"KP 7": "BQ",
"KP 8": "BR",
"KP 9": "BS",
"KP ADD": "BX",
"KP DECIMAL": "BT",
"KP DIVIDE": "BU",
"KP ENTER": "BY",
"KP EQUAL": "BZ",
"KP MULTIPLY": "BV",
"KP SUBTRACT": "BW",
"L": "d",
"LEFT": "5",
"LEFT ALT": "Bc",
"LEFT BRACKET": "s",
"LEFT CONTROL": "Bb",
"LEFT SHIFT": "Ba",
"LEFT SUPER": "Bd",
"M": "e",
"MINUS": "D",
"N": "f",
"NUM LOCK": "=",
"O": "g",
"P": "h",
"PAGE DOWN": "9",
"PAGE UP": "8",
"PAUSE": "!",
"PERIOD": "E",
"PRINT SCREEN": "^",
"Q": "i",
"R": "j",
"RIGHT": "4",
"RIGHT ALT": "Bg",
"RIGHT BRACKET": "u",
"RIGHT CONTROL": "Bf",
"RIGHT SHIFT": "Be",
"RIGHT SUPER": "Bh",
"S": "k",
"SCROLL LOCK": "+",
"SEMICOLON": "Q",
"SLASH": "F",
"SPACE": "A",
"T": "l",
"TAB": "0",
"U": "m",
"UP": "7",
"V": "n",
"W": "o",
"WORLD 1": "w",
"WORLD 2": "x",
"X": "p",
"Y": "q",
"Z": "r"
}

0
kittens/__init__.py Normal file
View File

0
kittens/ask/__init__.py Normal file
View File

114
kittens/ask/main.py Normal file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os
import readline
import sys
from kitty.cli import parse_args
from kitty.constants import cache_dir
from ..tui.operations import alternate_screen, styled
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 real_main(args):
msg = 'Ask the user for input'
try:
args, items = parse_args(args[1:], option_text, '', msg, 'kitty ask')
except SystemExit as e:
print(e.args[0])
input('Press enter to quit...')
raise SystemExit(1)
readline.read_init_file()
with alternate_screen(), HistoryCompleter(args.name):
if args.message:
print(styled(args.message, bold=True))
prompt = '> '
try:
ans = input(prompt)
except (KeyboardInterrupt, EOFError):
return
print('OK:', json.dumps(ans))
def main(args=sys.argv):
try:
real_main(args)
except Exception as e:
import traceback
traceback.print_exc(file=sys.stdout)
input('Press enter to quit...')
raise SystemExit(1)

0
kittens/tui/__init__.py Normal file
View File

43
kittens/tui/handler.py Normal file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
class Handler:
def initialize(self, screen_size, quit_loop, wakeup):
self.screen_size, self.quit_loop = screen_size, quit_loop
self.wakeup = wakeup
def on_resize(self, screen_size):
self.screen_size = screen_size
def on_term(self):
self.quit_loop(1)
def on_text(self, text, in_bracketed_paste=False):
pass
def on_key(self, key_event):
pass
def on_mouse(self, mouse_event):
pass
def on_interrupt(self):
pass
def on_eot(self):
pass
def write(self, data):
if isinstance(data, str):
data = data.encode('utf-8')
self.write_buf.append(data)
def print(self, *args, sep=' ', end='\r\n'):
data = sep.join(map(str, args)) + end
self.write(data)
def suspend(self):
return self._term_manager.suspend()

374
kittens/tui/loop.py Normal file
View File

@@ -0,0 +1,374 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import codecs
import fcntl
import io
import os
import re
import selectors
import signal
import sys
import termios
import tty
from collections import namedtuple
from contextlib import closing, contextmanager
from functools import partial
from kitty.fast_data_types import parse_input_from_terminal, safe_pipe
from kitty.icat import screen_size
from kitty.key_encoding import (
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, C, D, backspace_key,
decode_key_event, enter_key
)
from .handler import Handler
from .operations import init_state, reset_state, clear_screen
def log(*a, **kw):
fd = getattr(log, 'fd', None)
if fd is None:
fd = log.fd = open('/tmp/kitten-debug', 'w')
kw['file'] = fd
print(*a, **kw)
fd.flush()
def write_all(fd, data):
if isinstance(data, str):
data = data.encode('utf-8')
while data:
n = os.write(fd, data)
if not n:
break
data = data[n:]
class TermManager:
def __init__(self, input_fd, output_fd):
self.input_fd = input_fd
self.output_fd = output_fd
self.original_fl = fcntl.fcntl(self.input_fd, fcntl.F_GETFL)
self.isatty = os.isatty(self.input_fd)
if self.isatty:
self.original_termios = termios.tcgetattr(self.input_fd)
def set_state_for_loop(self):
write_all(self.output_fd, init_state())
fcntl.fcntl(self.input_fd, fcntl.F_SETFL, self.original_fl | os.O_NONBLOCK)
if self.isatty:
tty.setraw(self.input_fd)
def reset_state_to_original(self):
if self.isatty:
termios.tcsetattr(self.input_fd, termios.TCSADRAIN, self.original_termios)
fcntl.fcntl(self.input_fd, fcntl.F_SETFL, self.original_fl)
write_all(self.output_fd, reset_state())
@contextmanager
def suspend(self):
self.reset_state_to_original()
yield self
self.set_state_for_loop()
def __enter__(self):
self.set_state_for_loop()
return self
def __exit__(self, *a):
self.reset_state_to_original()
LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 1, 2, 4, 8, 16
DRAG = REPEAT
MouseEvent = namedtuple('MouseEvent', 'x y type buttons mods')
bmap = {0: LEFT, 1: MIDDLE, 2: RIGHT}
MOTION_INDICATOR = 1 << 5
EXTRA_BUTTON_INDICATOR = 1 << 6
SHIFT_INDICATOR = 1 << 2
ALT_INDICATOR = 1 << 3
CTRL_INDICATOR = 1 << 4
def decode_sgr_mouse(text):
cb, x, y = text.split(';')
m, y = y[-1], y[:-1]
cb, x, y = map(int, (cb, x, y))
typ = RELEASE if m == 'm' else (DRAG if cb & MOTION_INDICATOR else PRESS)
buttons = 0
cb3 = cb & 3
if cb3 != 3:
if cb & EXTRA_BUTTON_INDICATOR:
buttons |= FIFTH if cb3 & 1 else FOURTH
else:
buttons |= bmap[cb3]
mods = 0
if cb & SHIFT_INDICATOR:
mods |= SHIFT
if cb & ALT_INDICATOR:
mods |= ALT
if cb & CTRL_INDICATOR:
mods |= CTRL
return MouseEvent(x, y, typ, buttons, mods)
class UnhandledException(Handler):
def __init__(self, tb):
self.tb = tb
def initialize(self, screen_size, quit_loop, wakeup):
Handler.initialize(self, screen_size, quit_loop, wakeup)
self.write(clear_screen())
self.write(self.tb.replace('\n', '\r\n'))
self.write('\r\n')
self.write('Press the Enter key to quit')
def on_key(self, key_event):
if key_event is enter_key:
self.quit_loop(1)
def on_interrupt(self):
self.quit_loop(1)
def on_eot(self):
self.quit_loop(1)
class Loop:
def __init__(self, input_fd=None, output_fd=None,
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
self.input_fd = sys.stdin.fileno() if input_fd is None else input_fd
self.output_fd = sys.stdout.fileno() if output_fd is None else output_fd
self.wakeup_read_fd, self.wakeup_write_fd = safe_pipe()
# For some reason on macOS the DefaultSelector fails when input_fd is
# open('/dev/tty')
self.sel = s = selectors.PollSelector()
s.register(self.input_fd, selectors.EVENT_READ, self._read_ready)
s.register(
self.wakeup_read_fd, selectors.EVENT_READ, self._wakeup_ready
)
s.register(self.output_fd, selectors.EVENT_WRITE, self._write_ready)
self.return_code = 0
self.read_allowed = True
self.read_buf = ''
self.decoder = codecs.getincrementaldecoder('utf-8')('ignore')
try:
self.iov_limit = os.sysconf('SC_IOV_MAX') - 1
except Exception:
self.iov_limit = 255
self.parse_input_from_terminal = partial(parse_input_from_terminal, self._on_text, self._on_dcs, self._on_csi, self._on_osc, self._on_pm, self._on_apc)
self.ebs_pat = re.compile('([\177\r\x03\x04])')
self.in_bracketed_paste = False
self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)
if self.sanitize_bracketed_paste:
self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)
def _read_ready(self, handler):
if not self.read_allowed:
return
try:
data = os.read(self.input_fd, io.DEFAULT_BUFFER_SIZE)
except BlockingIOError:
return
if not data:
raise EOFError('The input stream is closed')
data = self.decoder.decode(data)
if self.read_buf:
data = self.read_buf + data
self.read_buf = data
self.handler = handler
try:
self.read_buf = self.parse_input_from_terminal(self.read_buf, self.in_bracketed_paste)
except Exception:
self.read_buf = ''
raise
finally:
del self.handler
def _on_text(self, text):
if self.in_bracketed_paste and self.sanitize_bracketed_paste:
text = self.sanitize_ibp_pat.sub('', text)
for chunk in self.ebs_pat.split(text):
if len(chunk) == 1:
if chunk == '\r':
self.handler.on_key(enter_key)
elif chunk == '\177':
self.handler.on_key(backspace_key)
elif chunk == '\x03':
self.handler.on_interrupt()
elif chunk == '\x04':
self.handler.on_eot()
else:
self.handler.on_text(chunk, self.in_bracketed_paste)
else:
self.handler.on_text(chunk, self.in_bracketed_paste)
def _on_dcs(self, dcs):
pass
def _on_csi(self, csi):
q = csi[-1]
if q in 'mM':
if csi.startswith('<'):
# SGR mouse event
try:
ev = decode_sgr_mouse(csi[1:])
except Exception:
pass
else:
self.handler.on_mouse(ev)
elif q == '~':
if csi == '200~':
self.in_bracketed_paste = True
elif csi == '201~':
self.in_bracketed_paste = False
def _on_pm(self, pm):
pass
def _on_osc(self, osc):
pass
def _on_apc(self, apc):
if apc.startswith('K'):
try:
k = decode_key_event(apc)
except Exception:
pass
else:
if k.mods is CTRL and k.type is not RELEASE:
if k.key is C:
self.handler.on_interrupt()
return
if k.key is D:
self.handler.on_eot()
return
self.handler.on_key(k)
def _write_ready(self, handler):
if len(handler.write_buf) > self.iov_limit:
handler.write_buf[self.iov_limit - 1] = b''.join(handler.write_buf[self.iov_limit - 1:])
del handler.write_buf[self.iov_limit:]
sizes = tuple(map(len, handler.write_buf))
written = os.writev(self.output_fd, handler.write_buf)
if not written:
raise EOFError('The output stream is closed')
if written >= sum(sizes):
handler.write_buf = []
else:
consumed = 0
for i, buf in enumerate(handler.write_buf):
if not written:
break
if len(buf) <= written:
written -= len(buf)
consumed += 1
continue
handler.write_buf[i] = buf[written:]
break
del handler.write_buf[:consumed]
def _wakeup_ready(self, handler):
data = os.read(self.wakeup_read_fd, 1024)
if b'r' in data:
screen_size.changed = True
handler.on_resize(screen_size())
if b't' in data:
handler.on_term()
if b'i' in data:
handler.on_interrupt()
def _wakeup_write(self, val):
while not os.write(self.wakeup_write_fd, val):
pass
def _on_sigwinch(self, signum, frame):
self._wakeup_write(b'r')
def _on_sigterm(self, signum, frame):
self._wakeup_write(b't')
def _on_sigint(self, signum, frame):
self._wakeup_write(b'i')
def quit(self, return_code=None):
self.read_allowed = False
if return_code is not None:
self.return_code = return_code
def wakeup(self):
self._wakeup_write(b'1')
def _modify_output_selector(self, waiting_for_write):
if waiting_for_write:
try:
self.sel.register(self.output_fd, selectors.EVENT_WRITE, self._write_ready)
except KeyError:
pass
else:
try:
self.sel.unregister(self.output_fd)
except KeyError:
pass
def loop(self, handler):
select = self.sel.select
tb = None
waiting_for_write = True
with closing(self.sel), TermManager(self.input_fd, self.output_fd) as term_manager:
signal.signal(signal.SIGWINCH, self._on_sigwinch)
signal.signal(signal.SIGTERM, self._on_sigterm)
signal.signal(signal.SIGINT, self._on_sigint)
handler.write_buf = []
handler._term_manager = term_manager
keep_going = True
try:
handler.initialize(screen_size(), self.quit, self.wakeup)
except Exception:
import traceback
tb = traceback.format_exc()
self.return_code = 1
keep_going = False
while keep_going:
has_data_to_write = bool(handler.write_buf)
if not has_data_to_write and not self.read_allowed:
break
if has_data_to_write != waiting_for_write:
waiting_for_write = has_data_to_write
self._modify_output_selector(waiting_for_write)
events = select()
for key, mask in events:
try:
key.data(handler)
except Exception:
import traceback
tb = traceback.format_exc()
self.return_code = 1
keep_going = False
break
if tb is not None:
self._report_error_loop(tb, term_manager)
def _report_error_loop(self, tb, term_manager):
select = self.sel.select
waiting_for_write = False
handler = UnhandledException(tb)
handler.write_buf = []
handler._term_manager = term_manager
handler.initialize(screen_size(), self.quit, self.wakeup)
while True:
has_data_to_write = bool(handler.write_buf)
if not has_data_to_write and not self.read_allowed:
break
if has_data_to_write != waiting_for_write:
waiting_for_write = has_data_to_write
self._modify_output_selector(waiting_for_write)
events = select()
for key, mask in events:
key.data(handler)

162
kittens/tui/operations.py Normal file
View File

@@ -0,0 +1,162 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import sys
from contextlib import contextmanager
from kitty.terminfo import string_capabilities
S7C1T = '\033 F'
SAVE_CURSOR = '\0337'
RESTORE_CURSOR = '\0338'
SAVE_PRIVATE_MODE_VALUES = '\033[?s'
RESTORE_PRIVATE_MODE_VALUES = '\033[?r'
MODES = dict(
LNM=(20, ''),
IRM=(4, ''),
DECKM=(1, '?'),
DECSCNM=(5, '?'),
DECOM=(6, '?'),
DECAWM=(7, '?'),
DECARM=(8, '?'),
DECTCEM=(25, '?'),
MOUSE_BUTTON_TRACKING=(1000, '?'),
MOUSE_MOTION_TRACKING=(1002, '?'),
MOUSE_MOVE_TRACKING=(1003, '?'),
FOCUS_TRACKING=(1004, '?'),
MOUSE_UTF8_MODE=(1005, '?'),
MOUSE_SGR_MODE=(1006, '?'),
MOUSE_URXVT_MODE=(1015, '?'),
ALTERNATE_SCREEN=(1049, '?'),
BRACKETED_PASTE=(2004, '?'),
EXTENDED_KEYBOARD=(2017, '?'),
)
def set_mode(which, private=True):
num, private = MODES[which]
return '\033[{}{}h'.format(private, num)
def reset_mode(which):
num, private = MODES[which]
return '\033[{}{}l'.format(private, num)
def clear_screen():
return string_capabilities['clear'].replace(r'\E', '\033')
def set_window_title(value):
return ('\033]2;' + value.replace('\033', '').replace('\x9c', '') + '\033\\')
def set_line_wrapping(yes_or_no):
return (set_mode if yes_or_no else reset_mode)('DECAWM')
def set_cursor_visible(yes_or_no):
return (set_mode if yes_or_no else reset_mode)('DECTCEM')
STANDARD_COLORS = {name: i for i, name in enumerate(
'black red green yellow blue magenta cyan gray'.split())}
UNDERLINE_STYLES = {name: i + 1 for i, name in enumerate(
'straight double curly'.split())}
def color_code(color, intense=False, base=30):
if isinstance(color, str):
e = str((base + 60 if intense else base) + STANDARD_COLORS[color])
elif isinstance(color, int):
e = '{}:5:{}'.format(base + 8, max(0, min(color, 255)))
else:
e = '{}:2:{}:{}:{}'.format(base + 8, *color)
return e
def sgr(*parts):
return '\033[{}m'.format(';'.join(parts))
def colored(text, color, intense=False, reset_to=None, reset_to_intense=False):
e = color_code(color, intense)
return '\033[{}m{}\033[{}m'.format(e, text, 39 if reset_to is None else color_code(reset_to, reset_to_intense))
def faint(text):
return colored(text, 'black', True)
def styled(text, fg=None, bg=None, fg_intense=False, bg_intense=False, italic=None, bold=None, underline=None, underline_color=None, reverse=None):
start, end = [], []
if fg is not None:
start.append(color_code(fg, fg_intense))
end.append('39')
if bg is not None:
start.append(color_code(bg, bg_intense, 40))
end.append('49')
if underline_color is not None:
if isinstance(underline_color, str):
underline_color = STANDARD_COLORS[underline_color]
start.append(color_code(underline_color, base=50))
end.append('59')
if underline is not None:
start.append('4:{}'.format(UNDERLINE_STYLES[underline]))
end.append('4:0')
if italic is not None:
s, e = (start, end) if italic else (end, start)
s.append('3'), e.append('23')
if bold is not None:
s, e = (start, end) if bold else (end, start)
s.append('1'), e.append('22')
if reverse is not None:
s, e = (start, end) if reverse else (end, start)
s.append('7'), e.append('27')
if not start:
return text
return '\033[{}m{}\033[{}m'.format(';'.join(start), text, ';'.join(end))
def init_state(alternate_screen=True):
ans = (
S7C1T + SAVE_CURSOR + SAVE_PRIVATE_MODE_VALUES + reset_mode('LNM') +
reset_mode('IRM') + reset_mode('DECKM') + reset_mode('DECSCNM') +
set_mode('DECARM') + reset_mode('DECOM') + set_mode('DECAWM') +
set_mode('DECTCEM') + reset_mode('MOUSE_BUTTON_TRACKING') +
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING')
+ reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
set_mode('BRACKETED_PASTE') + set_mode('EXTENDED_KEYBOARD') +
'\033[*x' # reset DECSACE to default region select
)
if alternate_screen:
ans += set_mode('ALTERNATE_SCREEN')
ans += clear_screen()
return ans
def reset_state(normal_screen=True):
ans = ''
if normal_screen:
ans += reset_mode('ALTERNATE_SCREEN')
ans += RESTORE_PRIVATE_MODE_VALUES
ans += RESTORE_CURSOR
return ans
@contextmanager
def cursor(write):
write(SAVE_CURSOR)
yield
write(RESTORE_CURSOR)
@contextmanager
def alternate_screen(f=None):
f = f or sys.stdout
print(set_mode('ALTERNATE_SCREEN'), end='', file=f)
yield
print(reset_mode('ALTERNATE_SCREEN'), end='', file=f)

View File

View File

@@ -0,0 +1,477 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import os
import string
import subprocess
import sys
from functools import lru_cache
from gettext import gettext as _
from kitty.config import cached_values_for
from kitty.constants import config_dir, editor
from kitty.fast_data_types import wcswidth
from kitty.key_encoding import (
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
backspace_key, enter_key
)
from ..tui.handler import Handler
from ..tui.loop import Loop
from ..tui.operations import (
clear_screen, color_code, colored, cursor, faint, set_line_wrapping,
set_window_title, sgr, styled
)
HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
favorites_path = os.path.join(config_dir, 'unicode-input-favorites.conf')
INDEX_CHAR = '.'
DEFAULT_SET = tuple(map(
ord,
'‘’“”‹›«»‚„' '😀😛😇😈😉😍😎😮👍👎' '—–§¶†‡©®™' '→⇒•·°±−×÷¼½½¾'
'…µ¢£€¿¡¨´¸ˆ˜' 'ÀÁÂÃÄÅÆÇÈÉÊË' 'ÌÍÎÏÐÑÒÓÔÕÖØ' 'ŒŠÙÚÛÜÝŸÞßàá' 'âãäåæçèéêëìí'
'îïðñòóôõöøœš' 'ùúûüýÿþªºαΩ∞'
))
EMOTICONS_SET = tuple(range(0x1f600, 0x1f64f + 1))
def codepoint_ok(code):
return not (code <= 32 or code == 127 or 128 <= code <= 159 or 0xd800 <= code <= 0xdbff or 0xDC00 <= code <= 0xDFFF)
@lru_cache(maxsize=256)
def points_for_word(w):
from .unicode_names import codepoints_for_word
return codepoints_for_word(w)
@lru_cache(maxsize=4096)
def name(cp):
from .unicode_names import name_for_codepoint
if isinstance(cp, str):
cp = ord(cp[0])
return (name_for_codepoint(cp) or '').capitalize()
@lru_cache(maxsize=256)
def codepoints_matching_search(parts):
ans = []
if parts and parts[0]:
codepoints = points_for_word(parts[0])
for word in parts[1:]:
pts = points_for_word(word)
if pts:
intersection = codepoints & pts
if intersection:
codepoints = intersection
continue
codepoints = {c for c in codepoints if word in name(c).lower()}
if codepoints:
ans = list(sorted(codepoints))
return ans
def parse_favorites(raw):
for line in raw.splitlines():
line = line.strip()
if line.startswith('#') or not line:
continue
idx = line.find('#')
if idx > -1:
line = line[:idx]
code_text = line.partition(' ')[0]
try:
code = int(code_text, 16)
except Exception:
pass
else:
if codepoint_ok(code):
yield code
def serialize_favorites(favorites):
ans = '''\
# Favorite characters for unicode input
# Enter the hex code for each favorite character on a new line. Blank lines are
# ignored and anything after a # is considered a comment.
'''.splitlines()
for cp in favorites:
ans.append('{:x} # {} {}'.format(cp, chr(cp), name(cp)))
return '\n'.join(ans)
def load_favorites(refresh=False):
ans = getattr(load_favorites, 'ans', None)
if ans is None or refresh:
try:
with open(favorites_path, 'rb') as f:
raw = f.read().decode('utf-8')
ans = load_favorites.ans = list(parse_favorites(raw)) or list(DEFAULT_SET)
except FileNotFoundError:
ans = load_favorites.ans = list(DEFAULT_SET)
return ans
def encode_hint(num, digits=string.digits + string.ascii_lowercase):
res = ''
d = len(digits)
while not res or num > 0:
num, i = divmod(num, d)
res = digits[i] + res
return res
def decode_hint(x):
return int(x, 36)
class Table:
def __init__(self):
self.layout_dirty = True
self.last_rows = self.last_cols = -1
self.codepoints = []
self.current_idx = 0
self.text = ''
self.num_cols = 0
self.mode = HEX
@property
def current_codepoint(self):
if self.codepoints:
return self.codepoints[self.current_idx]
def set_codepoints(self, codepoints, mode=HEX):
self.codepoints = codepoints
self.mode = mode
self.layout_dirty = True
self.current_idx = 0
def codepoint_at_hint(self, hint):
return self.codepoints[decode_hint(hint)]
def layout(self, rows, cols):
if not self.layout_dirty and self.last_cols == cols and self.last_rows == rows:
return self.text
self.last_cols, self.last_rows = cols, rows
self.layout_dirty = False
if self.mode is NAME:
def as_parts(i, codepoint):
return encode_hint(i).ljust(idx_size), chr(codepoint), name(codepoint)
def cell(i, idx, c, desc):
is_current = i == self.current_idx
if is_current:
yield sgr(color_code('gray', base=40))
yield colored(idx, 'green') + ' '
yield colored(c, 'black' if is_current else 'gray', True) + ' '
w = wcswidth(c)
if w < 2:
yield ' ' * (2 - w)
if len(desc) > space_for_desc:
desc = desc[:space_for_desc - 1] + ''
yield faint(desc)
extra = space_for_desc - len(desc)
if extra > 0:
yield ' ' * extra
if is_current:
yield sgr('49')
else:
def as_parts(i, codepoint):
return encode_hint(i).ljust(idx_size), chr(codepoint), ''
def cell(i, idx, c, desc):
yield colored(idx, 'green') + ' '
yield colored(c, 'gray', True)
w = wcswidth(c)
if w < 2:
yield ' ' * (2 - w)
num = len(self.codepoints)
if num < 1:
self.text = ''
self.num_cols = 0
return self.text
idx_size = len(encode_hint(num - 1))
parts = [as_parts(i, c) for i, c in enumerate(self.codepoints)]
if self.mode is NAME:
sizes = [idx_size + 2 + len(p[2]) + 2 for p in parts]
else:
sizes = [idx_size + 3]
longest = max(sizes) if sizes else 0
col_width = longest + 2
col_width = min(col_width, 40)
space_for_desc = col_width - 2 - idx_size - 4
num_cols = self.num_cols = max(cols // col_width, 1)
buf = []
a = buf.append
rows_left = rows
for i, (idx, c, desc) in enumerate(parts):
if i > 0 and i % num_cols == 0:
rows_left -= 1
if rows_left == 0:
break
buf.append('\r\n')
buf.extend(cell(i, idx, c, desc))
a(' ')
self.text = ''.join(buf)
return self.text
def move_current(self, rows=0, cols=0):
if cols:
self.current_idx = (self.current_idx + len(self.codepoints) + cols) % len(self.codepoints)
self.layout_dirty = True
if rows:
amt = rows * self.num_cols
self.current_idx += amt
self.current_idx = max(0, min(self.current_idx, len(self.codepoints) - 1))
self.layout_dirty = True
def is_index(w):
try:
int(w.lstrip(INDEX_CHAR), 16)
return True
except Exception:
return False
class UnicodeInput(Handler):
def __init__(self, cached_values):
self.cached_values = cached_values
self.recent = list(self.cached_values.get('recent', DEFAULT_SET))
self.current_input = ''
self.current_char = None
self.prompt_template = '{}> '
self.last_updated_code_point_at = None
self.choice_line = ''
self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX')
self.table = Table()
self.update_prompt()
def update_codepoints(self):
codepoints = None
if self.mode is HEX:
q = self.mode, None
codepoints = self.recent
elif self.mode is EMOTICONS:
q = self.mode, None
codepoints = list(EMOTICONS_SET)
elif self.mode is FAVORITES:
codepoints = load_favorites()
q = self.mode, tuple(codepoints)
elif self.mode is NAME:
q = self.mode, self.current_input
if q != self.last_updated_code_point_at:
words = self.current_input.split()
words = [w for w in words if w != INDEX_CHAR]
index_words = [i for i, w in enumerate(words) if i > 0 and is_index(w)]
if index_words:
index_word = words[index_words[0]]
words = words[:index_words[0]]
codepoints = codepoints_matching_search(tuple(words))
if index_words:
index_word = int(index_word.lstrip(INDEX_CHAR), 16)
if index_word < len(codepoints):
codepoints = [codepoints[index_word]]
if q != self.last_updated_code_point_at:
self.last_updated_code_point_at = q
self.table.set_codepoints(codepoints, self.mode)
def update_current_char(self):
self.update_codepoints()
self.current_char = None
if self.mode is HEX:
try:
if self.current_input.startswith(INDEX_CHAR) and len(self.current_input) > 1:
self.current_char = chr(self.table.codepoint_at_hint(self.current_input[1:]))
else:
code = int(self.current_input, 16)
self.current_char = chr(code)
except Exception:
pass
elif self.mode is NAME:
cc = self.table.current_codepoint
if cc:
self.current_char = chr(cc)
else:
try:
if self.current_input:
self.current_char = chr(self.table.codepoint_at_hint(self.current_input.lstrip(INDEX_CHAR)))
except Exception:
pass
if self.current_char is not None:
code = ord(self.current_char)
if not codepoint_ok(code):
self.current_char = None
def update_prompt(self):
self.update_current_char()
if self.current_char is None:
c, color = '??', 'red'
self.choice_line = ''
else:
c, color = self.current_char, 'green'
self.choice_line = _('Chosen:') + ' {} U+{} {}'.format(
colored(c, 'green'), hex(ord(c))[2:], faint(styled(name(c) or '', italic=True)))
self.prompt = self.prompt_template.format(colored(c, color))
def init_terminal_state(self):
self.write(set_line_wrapping(False))
self.write(set_window_title(_('Unicode input')))
def initialize(self, *args):
Handler.initialize(self, *args)
self.init_terminal_state()
self.draw_screen()
def draw_title_bar(self):
entries = []
for name, key, mode in [
(_('Code'), 'F1', HEX),
(_('Name'), 'F2', NAME),
(_('Emoji'), 'F3', EMOTICONS),
(_('Favorites'), 'F4', FAVORITES),
]:
entry = ' {} ({}) '.format(name, key)
if mode is self.mode:
entry = styled(entry, reverse=False, bold=True)
entries.append(entry)
text = _('Search by:{}').format(' '.join(entries))
extra = self.screen_size.cols - wcswidth(text)
if extra > 0:
text += ' ' * extra
self.print(styled(text, reverse=True))
def draw_screen(self):
self.write(clear_screen())
self.draw_title_bar()
y = 1
def writeln(text=''):
nonlocal y
self.print(text)
y += 1
if self.mode is NAME:
writeln(_('Enter words from the name of the character'))
elif self.mode is HEX:
writeln(_('Enter the hex code for the character'))
else:
writeln(_('Enter the index for the character you want from the list below'))
self.write(self.prompt)
self.write(self.current_input)
with cursor(self.write):
writeln()
writeln(self.choice_line)
if self.mode is HEX:
writeln(faint(_('Type {} followed by the index for the recent entries below').format(INDEX_CHAR)))
elif self.mode is NAME:
writeln(faint(_('Use Tab or the arrow keys to choose a character from below')))
elif self.mode is FAVORITES:
writeln(faint(_('Press F12 to edit the list of favorites')))
self.table_at = y
self.write(self.table.layout(self.screen_size.rows - self.table_at, self.screen_size.cols))
def refresh(self):
self.update_prompt()
self.draw_screen()
def on_text(self, text, in_bracketed_paste):
self.current_input += text
self.refresh()
def on_key(self, key_event):
if key_event is backspace_key:
self.current_input = self.current_input[:-1]
self.refresh()
elif key_event is enter_key:
self.quit_loop(0)
elif key_event.type is RELEASE and not key_event.mods:
if key_event.key is ESCAPE:
self.quit_loop(1)
elif key_event.key is F1:
self.switch_mode(HEX)
elif key_event.key is F2:
self.switch_mode(NAME)
elif key_event.key is F3:
self.switch_mode(EMOTICONS)
elif key_event.key is F4:
self.switch_mode(FAVORITES)
elif key_event.key is F12 and self.mode is FAVORITES:
self.edit_favorites()
elif self.mode is NAME:
if key_event.key is TAB:
if key_event.mods == SHIFT:
self.table.move_current(cols=-1), self.refresh()
elif not key_event.mods:
self.table.move_current(cols=1), self.refresh()
elif key_event.key is LEFT and not key_event.mods:
self.table.move_current(cols=-1), self.refresh()
elif key_event.key is RIGHT and not key_event.mods:
self.table.move_current(cols=1), self.refresh()
elif key_event.key is UP and not key_event.mods:
self.table.move_current(rows=-1), self.refresh()
elif key_event.key is DOWN and not key_event.mods:
self.table.move_current(rows=1), self.refresh()
def edit_favorites(self):
if not os.path.exists(favorites_path):
with open(favorites_path, 'wb') as f:
f.write(serialize_favorites(load_favorites()).encode('utf-8'))
with self.suspend():
p = subprocess.Popen(editor + [favorites_path])
if p.wait() == 0:
load_favorites(refresh=True)
self.init_terminal_state()
self.refresh()
def switch_mode(self, mode):
if mode is not self.mode:
self.mode = mode
self.cached_values['mode'] = mode
self.current_input = ''
self.current_char = None
self.choice_line = ''
self.refresh()
def on_interrupt(self):
self.quit_loop(1)
def on_eot(self):
self.quit_loop(1)
def on_resize(self, new_size):
Handler.on_resize(self, new_size)
self.refresh()
def run_loop(args):
loop = Loop()
with cached_values_for('unicode-input') as cached_values:
handler = UnicodeInput(cached_values)
loop.loop(handler)
if handler.current_char and loop.return_code == 0:
print('OK:', hex(ord(handler.current_char))[2:])
try:
handler.recent.remove(ord(handler.current_char))
except Exception:
pass
recent = [ord(handler.current_char)] + handler.recent
cached_values['recent'] = recent[:len(DEFAULT_SET)]
return loop.return_code
def main(args=sys.argv):
try:
raise SystemExit(run_loop(args))
except Exception:
import traceback
traceback.print_exc()
input(_('Press Enter to quit.'))

63052
kittens/unicode_input/names.h generated Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,81 @@
/*
* unicode_names.c
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "names.h"
static PyObject*
all_words(PYNOARG) {
PyObject *ans = PyTuple_New(arraysz(idx_to_word));
if (!ans) return NULL;
for (size_t i = 0; i < arraysz(idx_to_word); i++) {
PyObject *w = PyUnicode_FromString(idx_to_word[i]);
if (w == NULL) { Py_DECREF(ans); return NULL; }
PyTuple_SET_ITEM(ans, i, w);
}
return ans;
}
static inline PyObject*
codepoints_for_word(const char *word, size_t len) {
PyObject *ans = PyFrozenSet_New(NULL); if (ans == NULL) return NULL;
const unsigned short *words = words_for_first_letter[(unsigned)*word];
if (words == NULL) return ans;
for (unsigned short i = 1; i <= words[0]; i++) {
unsigned short word_idx = words[i];
const char *w = idx_to_word[word_idx];
if (strncmp(word, w, len) == 0 && strlen(w) == len) {
const char_type* codepoints = codepoints_for_word_idx[word_idx];
for (char_type i = 1; i <= codepoints[0]; i++) {
PyObject *t = PyLong_FromUnsignedLong(codepoints[i]); if (t == NULL) { Py_DECREF(ans); return NULL; }
int ret = PySet_Add(ans, t); Py_DECREF(t); if (ret != 0) { Py_DECREF(ans); return NULL; }
}
break;
}
}
return ans;
}
static PyObject*
cfw(PyObject *self UNUSED, PyObject *args) {
const char *word;
if (!PyArg_ParseTuple(args, "s", &word)) return NULL;
return codepoints_for_word(word, strlen(word));
}
static PyObject*
nfc(PyObject *self UNUSED, PyObject *args) {
unsigned int cp;
if (!PyArg_ParseTuple(args, "I", &cp)) return NULL;
const char *n = name_for_codepoint(cp);
if (n == NULL) Py_RETURN_NONE;
return PyUnicode_FromString(n);
}
static PyMethodDef module_methods[] = {
METHODB(all_words, METH_NOARGS),
{"codepoints_for_word", (PyCFunction)cfw, METH_VARARGS, ""},
{"name_for_codepoint", (PyCFunction)nfc, METH_VARARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static struct PyModuleDef module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "unicode_names", /* name of module */
.m_doc = NULL,
.m_size = -1,
.m_methods = module_methods
};
EXPORTED PyMODINIT_FUNC
PyInit_unicode_names(void) {
PyObject *m;
m = PyModule_Create(&module);
if (m == NULL) return NULL;
return m;
}

View File

247
kittens/url_hints/main.py Normal file
View File

@@ -0,0 +1,247 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import re
import string
import subprocess
import sys
from collections import namedtuple
from functools import lru_cache, partial
from gettext import gettext as _
from kitty.cli import parse_args
from kitty.key_encoding import ESCAPE, backspace_key, enter_key
from kitty.utils import command_for_open
from ..tui.handler import Handler
from ..tui.loop import Loop
from ..tui.operations import (
clear_screen, faint, set_cursor_visible, set_window_title, styled
)
Mark = namedtuple('Mark', 'index start end text')
URL_PREFIXES = 'http https file ftp'.split()
HINT_ALPHABET = string.digits + string.ascii_lowercase
@lru_cache(maxsize=2048)
def encode_hint(num):
res = ''
d = len(HINT_ALPHABET)
while not res or num > 0:
num, i = divmod(num, d)
res = HINT_ALPHABET[i] + res
return res
def decode_hint(x):
return int(x, 36)
def render(lines, current_input):
ans = []
def mark(m):
hint = encode_hint(m.index)
text = m.text
if current_input and not hint.startswith(current_input):
return faint(text)
hint = hint[len(current_input):] or ' '
text = text[len(hint):]
return styled(
hint,
fg='black',
bg='green',
bold=True
) + styled(
text, fg='gray', fg_intense=True, bold=True
)
for line, marks in lines:
if not marks:
ans.append(faint(line))
continue
buf = []
for i, m in enumerate(marks):
if i == 0 and m.start:
buf.append(faint(line[:m.start]))
buf.append(mark(m))
if m is not marks[-1]:
buf.append(faint(line[m.end:marks[i + 1].start]))
rest = line[marks[-1].end:]
if rest:
buf.append(faint(rest))
ans.append(''.join(buf))
return '\r\n'.join(ans)
class URLHints(Handler):
def __init__(self, lines, index_map):
self.lines, self.index_map = tuple(lines), index_map
self.current_input = ''
self.current_text = None
self.chosen = None
def init_terminal_state(self):
self.write(set_cursor_visible(False))
self.write(set_window_title(_('Choose URL')))
def initialize(self, *args):
Handler.initialize(self, *args)
self.init_terminal_state()
self.draw_screen()
def on_text(self, text, in_bracketed_paste):
changed = False
for c in text:
if c in HINT_ALPHABET:
self.current_input += c
changed = True
if changed:
matches = [
t for idx, t in self.index_map.items()
if encode_hint(idx).startswith(self.current_input)
]
if len(matches) == 1:
self.chosen = matches[0]
self.quit_loop(0)
return
self.current_text = None
self.draw_screen()
def on_key(self, key_event):
if key_event is backspace_key:
self.current_input = self.current_input[:-1]
self.current_text = None
self.draw_screen()
elif key_event is enter_key and self.current_input:
idx = decode_hint(self.current_input)
self.chosen = self.index_map[idx]
self.quit_loop(0)
elif key_event.key is ESCAPE:
self.quit_loop(1)
def on_interrupt(self):
self.quit_loop(1)
def on_eot(self):
self.quit_loop(1)
def on_resize(self, new_size):
Handler.on_resize(self, new_size)
self.draw_screen()
def draw_screen(self):
if self.current_text is None:
self.current_text = render(self.lines, self.current_input)
self.write(clear_screen())
self.write(self.current_text)
def regex_finditer(pat, line):
for m in pat.finditer(line):
s, e = m.span()
if e - s > 2:
yield s, e
def find_urls(pat, line):
for m in pat.finditer(line):
s, e = m.span()
if s > 4 and line[s - 5:s] == 'link:': # asciidoc URLs
url = line[s:e]
idx = url.rfind('[')
if idx > -1:
e -= len(url) - idx
yield s, e
def mark(finditer, line, index_map):
marks = []
for s, e in finditer(line):
idx = len(index_map)
text = line[s:e]
marks.append(Mark(idx, s, e, text))
index_map[idx] = text
return line, marks
def run_loop(args, lines, index_map):
loop = Loop()
handler = URLHints(lines, index_map)
loop.loop(handler)
if handler.chosen and loop.return_code == 0:
cmd = command_for_open(args.program)
ret = subprocess.Popen(cmd + [handler.chosen]).wait()
if ret != 0:
print('URL handler "{}" failed with return code: {}'.format(' '.join(cmd), ret), file=sys.stderr)
input('Press Enter to quit')
loop.return_code = ret
raise SystemExit(loop.return_code)
def run(args, source_file=None):
if source_file is None:
text = sys.stdin.buffer.read().decode('utf-8')
sys.stdin = open('/dev/tty')
else:
with open(source_file, 'r') as f:
text = f.read()
if args.regex is None:
from .url_regex import url_delimiters
url_pat = '(?:{})://[^{}]{{3,}}'.format(
'|'.join(args.url_prefixes.split(',')), url_delimiters
)
finditer = partial(find_urls, re.compile(url_pat))
else:
finditer = partial(regex_finditer, re.compile(args.regex))
lines = []
index_map = {}
for line in text.splitlines():
marked = mark(finditer, line, index_map)
lines.append(marked)
if not index_map:
input(_('No URLs found, press Enter to abort.'))
return
try:
run_loop(args, lines, index_map)
except Exception:
import traceback
traceback.print_exc()
input(_('Press Enter to quit'))
OPTIONS = partial('''\
--program
default=default
What program to use to open matched URLs. Defaults
to the default URL open program for the operating system.
--regex
Instead of searching for URLs search for the specified regular
expression instead.
--url-prefixes
default={0}
Comma separated list of recognized URL prefixes. Defaults to:
{0}
'''.format, ','.join(sorted(URL_PREFIXES)))
def main(args=sys.argv):
msg = 'Highlight URLs inside the specified text'
try:
args, items = parse_args(args[1:], OPTIONS, '[path to file or omit to use stdin]', msg, 'url_hints')
except SystemExit as e:
print(e.args[0], file=sys.stderr)
input('Press enter to quit...')
return 1
run(args, (items or [None])[0])

View File

@@ -0,0 +1 @@
url_delimiters = '\x00-\x20\x7f-\xa0\xad\u0600-\u0605\u061c\u06dd\u070f\u08e2\u1680\u180e\u2000-\u200f\u2028-\u202f\u205f-\u2064\u2066-\u206f\u3000\ud800-\uf8ff\ufeff\ufff9-\ufffb\U000110bd\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f\U000f0000-\U000ffffd\U00100000-\U0010fffd' # noqa

View File

@@ -1,5 +1,8 @@
#version GLSL_VERSION
uniform uvec2 viewport;
uniform vec3 default_bg;
uniform vec3 active_border_color;
uniform vec3 inactive_border_color;
in uvec4 rect; // left, top, right, bottom
in uint rect_color;
out vec3 color;
@@ -9,6 +12,7 @@ const int LEFT = 0;
const int TOP = 1;
const int RIGHT = 2;
const int BOTTOM = 3;
const uint FF = uint(0xff);
const uvec2 pos_map[] = uvec2[4](
uvec2(RIGHT, TOP),
@@ -25,11 +29,13 @@ vec2 to_opengl(uint x, uint y) {
}
float to_color(uint c) {
return float(c & uint(0xff)) / 255.0;
return float(c & FF) / 255.0;
}
void main() {
uvec2 pos = pos_map[gl_VertexID];
gl_Position = vec4(to_opengl(rect[pos.x], rect[pos.y]), 0, 1);
color = vec3(to_color(rect_color >> 16), to_color(rect_color >> 8), to_color(rect_color));
int rc = int(rect_color);
vec3 window_bg = vec3(to_color(rect_color >> 24), to_color(rect_color >> 16), to_color(rect_color >> 8));
color = float(1 & rc) * default_bg + float((2 & rc) >> 1) * active_border_color + float((4 & rc) >> 2) * inactive_border_color + float((8 & rc) >> 3) * window_bg;
}

View File

@@ -9,7 +9,7 @@ from .fast_data_types import (
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
pt_to_px
)
from .utils import color_as_int, load_shaders
from .utils import load_shaders
def vertical_edge(os_window_id, tab_id, color, width, top, bottom, left):
@@ -44,9 +44,6 @@ class Borders:
self.tab_id = tab_id
self.border_width = pt_to_px(opts.window_border_width)
self.padding_width = pt_to_px(opts.window_padding_width)
self.background = color_as_int(opts.background)
self.active_border = color_as_int(opts.active_border_color)
self.inactive_border = color_as_int(opts.inactive_border_color)
def __call__(
self,
@@ -56,9 +53,9 @@ class Borders:
extra_blank_rects,
draw_window_borders=True
):
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, 0)
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, 1)
for br in chain(current_layout.blank_rects, extra_blank_rects):
add_borders_rect(self.os_window_id, self.tab_id, *br, self.background)
add_borders_rect(self.os_window_id, self.tab_id, *br, 1)
bw, pw = self.border_width, self.padding_width
fw = bw + pw
@@ -67,7 +64,7 @@ class Borders:
g = w.geometry
if bw > 0 and draw_window_borders:
# Draw the border rectangles
color = self.active_border if w is active_window else self.inactive_border
color = 2 if w is active_window else 4
border(
self.os_window_id, self.tab_id,
color, bw, g.left - fw, g.top - fw, g.right + fw,
@@ -75,9 +72,9 @@ class Borders:
)
if pw > 0:
# Draw the background rectangles over the padding region
color = self.background
color = w.screen.color_profile.default_bg
border(
self.os_window_id, self.tab_id,
color, pw, g.left - pw, g.top - pw, g.right + pw,
(color << 8) | 8, pw, g.left - pw, g.top - pw, g.right + pw,
g.bottom + pw
)

View File

@@ -2,13 +2,19 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import atexit
import json
import re
import socket
from functools import partial
from gettext import gettext as _
from weakref import WeakValueDictionary
from .cli import create_opts, parse_args
from .config import MINIMUM_FONT_SIZE, cached_values, initial_window_size
from .constants import appname, set_boss, wakeup
from .config import (
MINIMUM_FONT_SIZE, initial_window_size, prepare_config_file_for_editing
)
from .constants import appname, editor, set_boss
from .fast_data_types import (
ChildMonitor, create_os_window, current_os_window, destroy_global_data,
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
@@ -23,7 +29,8 @@ from .session import create_session
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
from .utils import (
end_startup_notification, get_primary_selection, init_startup_notification,
open_url, safe_print, set_primary_selection, single_instance
log_error, open_url, parse_address_spec, remove_socket_file, safe_print,
set_primary_selection, single_instance
)
@@ -32,6 +39,15 @@ def initialize_renderer():
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: # {{{
def __init__(self, args):
@@ -61,17 +77,21 @@ class DumpCommands: # {{{
class Boss:
def __init__(self, os_window_id, opts, args):
def __init__(self, os_window_id, opts, args, cached_values):
self.window_id_map = WeakValueDictionary()
self.cached_values = cached_values
self.os_window_map = {}
self.cursor_blinking = True
self.shutting_down = False
talk_fd = getattr(single_instance, 'socket', None)
talk_fd = -1 if talk_fd is None else talk_fd.fileno()
listen_fd = -1
if opts.allow_remote_control and args.listen_on:
listen_fd = listen_on(args.listen_on)
self.child_monitor = ChildMonitor(
self.on_child_death,
DumpCommands(args) if args.dump_commands or args.dump_bytes else None,
talk_fd
talk_fd, listen_fd
)
set_boss(self)
self.current_font_size = opts.font_size
@@ -84,7 +104,7 @@ class Boss:
def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, size=None, startup_id=None):
dpi_changed = False
if os_window_id is None:
w, h = initial_window_size(self.opts) if size is None else size
w, h = initial_window_size(self.opts, self.cached_values) if size is None else size
cls = wclass or self.args.cls or appname
os_window_id = create_os_window(w, h, appname, wname or self.args.name or cls, cls)
if startup_id:
@@ -105,7 +125,10 @@ class Boss:
}
def match_windows(self, match):
field, exp = match.split(':', 1)
try:
field, exp = match.split(':', 1)
except ValueError:
return
pat = re.compile(exp)
for tm in self.os_window_map.values():
for tab in tm:
@@ -121,7 +144,10 @@ class Boss:
return tab
def match_tabs(self, match):
field, exp = match.split(':', 1)
try:
field, exp = match.split(':', 1)
except ValueError:
return
pat = re.compile(exp)
tms = tuple(self.os_window_map.values())
found = False
@@ -164,30 +190,43 @@ class Boss:
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
self.window_id_map[window.id] = window
def peer_messages_received(self, messages):
import json
for msg in messages:
msg = json.loads(msg.decode('utf-8'))
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
startup_id = msg.get('startup_id')
args, rest = parse_args(msg['args'][1:])
args.args = rest
opts = create_opts(args)
session = create_session(opts, args)
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts), startup_id=startup_id)
else:
safe_print('Unknown message received from peer, ignoring')
def handle_remote_cmd(self, cmd, window=None):
def _handle_remote_command(self, cmd, window=None):
response = None
if self.opts.allow_remote_control:
try:
response = handle_cmd(self, window, cmd)
except Exception as err:
import traceback
response = {'ok': False, 'error': str(err), 'tb': traceback.format_exc()}
response = {'ok': False, 'error': str(err)}
if not getattr(err, 'hide_traceback', False):
response['tb'] = traceback.format_exc()
else:
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control yes to your kitty.conf'}
return response
def peer_message_received(self, msg):
msg = msg.decode('utf-8')
cmd_prefix = '\x1bP@kitty-cmd'
if msg.startswith(cmd_prefix):
cmd = msg[len(cmd_prefix):-2]
response = self._handle_remote_command(cmd)
if response is not None:
response = (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8')
return response
else:
msg = json.loads(msg)
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
startup_id = msg.get('startup_id')
args, rest = parse_args(msg['args'][1:])
args.args = rest
opts = create_opts(args)
session = create_session(opts, args)
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id)
else:
log_error('Unknown message received from peer, ignoring')
def handle_remote_cmd(self, cmd, window=None):
response = self._handle_remote_command(cmd, window)
if response is not None:
if window is not None:
window.send_cmd_response(response)
@@ -196,6 +235,12 @@ class Boss:
window = self.window_id_map.pop(window_id, None)
if window is None:
return
if window.action_on_close:
try:
window.action_on_close(window)
except Exception:
import traceback
traceback.print_exc()
os_window_id = window.os_window_id
window.destroy()
tm = self.os_window_map.get(os_window_id)
@@ -251,26 +296,23 @@ class Boss:
tm.resize()
def increase_font_size(self):
self.change_font_size(
self.set_font_size(
min(
self.opts.font_size * 5, self.current_font_size +
self.opts.font_size_delta))
def decrease_font_size(self):
self.change_font_size(
max(
MINIMUM_FONT_SIZE, self.current_font_size -
self.opts.font_size_delta))
self.set_font_size(self.current_font_size - self.opts.font_size_delta)
def restore_font_size(self):
self.change_font_size(self.opts.font_size)
self.set_font_size(self.opts.font_size)
def _change_font_size(self, new_size=None):
def _change_font_size(self, new_size=None, on_dpi_change=False):
if new_size is not None:
self.current_font_size = new_size
old_cell_width, old_cell_height = viewport_for_window()[-2:]
windows = tuple(filter(None, self.window_id_map.values()))
resize_fonts(self.current_font_size)
resize_fonts(self.current_font_size, on_dpi_change=on_dpi_change)
layout_sprite_map()
prerender()
for window in windows:
@@ -281,7 +323,8 @@ class Boss:
tm.refresh_sprite_positions()
glfw_post_empty_event()
def change_font_size(self, new_size):
def set_font_size(self, new_size):
new_size = max(MINIMUM_FONT_SIZE, new_size)
if new_size == self.current_font_size:
return
self._change_font_size(new_size)
@@ -312,6 +355,15 @@ class Boss:
self.current_key_press_info = key, scancode, action, mods
return self.dispatch_action(key_action)
def default_bg_changed_for(self, window_id):
w = self.window_id_map.get(window_id)
if w is not None:
tm = self.os_window_map.get(w.os_window_id)
if tm is not None:
t = tm.tab_for_id(w.tab_id)
if t is not None:
t.relayout_borders()
def dispatch_action(self, key_action):
if key_action is not None:
f = getattr(self, key_action.func, None)
@@ -352,22 +404,99 @@ class Boss:
w.paste('\n'.join(paths))
def on_os_window_closed(self, os_window_id, viewport_width, viewport_height):
cached_values['window-size'] = viewport_width, viewport_height
self.cached_values['window-size'] = viewport_width, viewport_height
tm = self.os_window_map.pop(os_window_id, None)
if tm is not None:
tm.destroy()
for window_id in tuple(w.id for w in self.window_id_map.values() if getattr(w, 'os_window_id', None) == os_window_id):
self.window_id_map.pop(window_id, None)
def display_scrollback(self, data):
if self.opts.scrollback_in_new_tab:
self.display_scrollback_in_new_tab(data)
else:
tab = self.active_tab
if tab is not None:
tab.new_special_window(
SpecialWindow(
self.opts.scrollback_pager, data, _('History')))
def display_scrollback(self, window, data):
tab = self.active_tab
if tab is not None and window.overlay_for is None:
tab.new_special_window(
SpecialWindow(
self.opts.scrollback_pager, data, _('History'), overlay_for=window.id))
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 input_unicode_character(self):
w = self.active_window
tab = self.active_tab
if w is not None and tab is not None and w.overlay_for is None:
overlay_window = tab.new_special_window(
SpecialWindow(
['kitty', '+runpy', 'from kittens.unicode_input.main import main; main()'],
overlay_for=w.id))
overlay_window.action_on_close = partial(self.send_unicode_character, w.id)
def get_output(self, source_window, num_lines=1):
output = ''
s = source_window.screen
for i in range(min(num_lines, s.lines)):
output += str(s.linebuf.line(i))
return output
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 = self.get_output(source_window)
if output.startswith('OK: '):
try:
text = chr(int(output.partition(' ')[2], 16))
except Exception:
import traceback
traceback.print_exc()
else:
w.paste(text)
def set_tab_title(self):
w = self.active_window
tab = self.active_tab
if w is not None and tab is not None and w.overlay_for is None:
args = ['--name=tab-title', '--message', _('Enter the new title for this tab below.')]
overlay_window = tab.new_special_window(
SpecialWindow(
['kitty', '+runpy', 'from kittens.ask.main import main; main()'] + args,
overlay_for=w.id))
overlay_window.action_on_close = partial(self.do_set_tab_title, tab.id)
def do_set_tab_title(self, tab_id, source_window):
output = self.get_output(source_window)
if output.startswith('OK: '):
title = json.loads(output.partition(' ')[2].strip())
tm = self.active_tab_manager
if tm is not None and title:
for tab in tm.tabs:
if tab.id == tab_id:
tab.set_title(title)
break
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'):
data = w.as_text(as_ansi='ansi' in type_of_input, add_history='history' in type_of_input).encode('utf-8')
elif type_of_input == 'none':
data = None
else:
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
tab.new_special_window(
SpecialWindow(
['kitty', '+runpy', 'from kittens.{}.main import main; main()'.format(kitten)] + args,
stdin=data,
overlay_for=w.id))
def switch_focus_to(self, window_idx):
tab = self.active_tab
@@ -387,8 +516,6 @@ class Boss:
def destroy(self):
self.shutting_down = True
self.child_monitor.shutdown_monitor()
wakeup()
self.child_monitor.join()
del self.child_monitor
for tm in self.os_window_map.values():
tm.destroy()
@@ -424,6 +551,11 @@ class Boss:
if tm is not None:
tm.goto_tab(tab_num - 1)
def set_active_tab(self, tab):
tm = self.active_tab_manager
if tm is not None:
tm.set_active_tab(tab)
def next_tab(self):
tm = self.active_tab_manager
if tm is not None:
@@ -443,9 +575,13 @@ class Boss:
if arg == '@selection':
return w.text_for_selection()
if arg == '@ansi':
return w.buffer_as_ansi()
return w.as_text(as_ansi=True, add_history=True)
if arg == '@text':
return w.buffer_as_text()
return w.as_text(add_history=True)
if arg == '@screen':
return w.as_text()
if arg == '@ansi_screen':
return w.as_text(as_ansi=True)
if args[0].startswith('@'):
stdin = data_for_at(args[0]) or None
@@ -494,6 +630,8 @@ class Boss:
def new_window_with_cwd(self, *args):
w = self.active_window
if w is None:
return self.new_window(*args)
cwd_from = w.child.pid if w is not None else None
self._new_window(args, cwd_from=cwd_from)
@@ -506,9 +644,3 @@ class Boss:
tm = self.active_tab_manager
if tm is not None:
tm.move_tab(-1)
def display_scrollback_in_new_tab(self, data):
tm = self.active_tab_manager
if tm is not None:
tm.new_tab(special_window=SpecialWindow(
self.opts.scrollback_pager, data, _('History')))

View File

@@ -6,9 +6,7 @@
layout(std140) uniform CellRenderData {
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style;
int color1, color2;
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
uint xnum, ynum, cursor_x, cursor_y, cursor_w;
@@ -21,7 +19,7 @@ layout(location=1) in uvec4 sprite_coords;
layout(location=2) in uint is_selected;
const int fg_index_map[] = int[3](0, 1, 0);
const uvec2 cell_pos_map[] = uvec2[4](
uvec2(1, 0), // right, top
uvec2(1, 1), // right, bottom
@@ -135,10 +133,10 @@ void main() {
// set cell color indices {{{
uvec2 default_colors = uvec2(default_fg, default_bg);
ivec2 color_indices = ivec2(color1, color2);
uint text_attrs = sprite_coords[3];
int fg_index = color_indices[(text_attrs >> 6) & REVERSE_MASK];
int bg_index = color_indices[1 - fg_index];
uint is_inverted = ((text_attrs >> 6) & REVERSE_MASK) + inverted;
int fg_index = fg_index_map[is_inverted];
int bg_index = 1 - fg_index;
float cursor = is_cursor(c, r);
vec3 bg = to_color(colors[bg_index], default_colors[bg_index]);
// }}}

View File

@@ -5,16 +5,7 @@
* Distributed under terms of the GPL3 license.
*/
#ifdef __APPLE__
#include <pthread.h>
// I cant figure out how to get pthread.h to include this definition on macOS. MACOSX_DEPLOYMENT_TARGET does not work.
extern int pthread_setname_np(const char *name);
#else
// Need _GNU_SOURCE for pthread_setname_np on linux
#define _GNU_SOURCE
#include <pthread.h>
#undef _GNU_SOURCE
#endif
#include "threading.h"
#include "state.h"
#include "screen.h"
#include <termios.h>
@@ -28,12 +19,17 @@ extern int pthread_setname_np(const char *name);
extern PyTypeObject Screen_Type;
#define EXTRA_FDS 2
#ifndef MSG_NOSIGNAL
// Apple does not implement MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
static void (*parse_func)(Screen*, PyObject*);
typedef struct {
char *data;
size_t sz;
int fd;
} Message;
typedef struct {
@@ -44,7 +40,7 @@ typedef struct {
bool shutting_down;
pthread_t io_thread, talk_thread;
int talk_fd;
int talk_fd, listen_fd;
Message *messages;
size_t messages_capacity, messages_count;
} ChildMonitor;
@@ -63,6 +59,9 @@ static const Child EMPTY_CHILD = {0};
pthread_mutex_##op(&screen->which##_buf_lock);
#define children_mutex(op) \
pthread_mutex_##op(&children_lock);
#define peer_mutex(op) \
pthread_mutex_##op(&talk_data.peer_lock);
static Child children[MAX_CHILDREN] = {{0}};
@@ -72,23 +71,12 @@ static unsigned long remove_notify[MAX_CHILDREN] = {0};
static size_t add_queue_count = 0, remove_queue_count = 0;
static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
static pthread_mutex_t children_lock;
static bool signal_received = false;
static bool kill_signal_received = false;
static ChildMonitor *the_monitor = NULL;
static uint8_t drain_buf[1024];
static int signal_fds[2], wakeup_fds[2];
static inline void
set_thread_name(const char *name) {
int ret = 0;
#ifdef __APPLE__
ret = pthread_setname_np(name);
#else
ret = pthread_setname_np(pthread_self(), name);
#endif
if (ret != 0) perror("Failed to set thread name");
}
// Main thread functions {{{
@@ -139,11 +127,11 @@ static PyObject *
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
ChildMonitor *self;
PyObject *dump_callback, *death_notify;
int talk_fd = -1;
int talk_fd = -1, listen_fd = -1;
int ret;
if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
if (!PyArg_ParseTuple(args, "OO|i", &death_notify, &dump_callback, &talk_fd)) return NULL;
if (!PyArg_ParseTuple(args, "OO|ii", &death_notify, &dump_callback, &talk_fd, &listen_fd)) return NULL;
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
return NULL;
@@ -151,12 +139,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
if (!self_pipe(wakeup_fds)) return PyErr_SetFromErrno(PyExc_OSError);
if (!self_pipe(signal_fds)) return PyErr_SetFromErrno(PyExc_OSError);
struct sigaction act = {.sa_handler=handle_signal};
if (sigaction(SIGINT, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
if (sigaction(SIGTERM, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError);
if (siginterrupt(SIGINT, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
if (siginterrupt(SIGTERM, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
#define SA(which) { if (sigaction(which, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError); if (siginterrupt(which, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);}
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD);
#undef SA
self = (ChildMonitor *)type->tp_alloc(type, 0);
self->talk_fd = talk_fd;
self->listen_fd = listen_fd;
if (self == NULL) return PyErr_NoMemory();
self->death_notify = death_notify; Py_INCREF(death_notify);
if (dump_callback != Py_None) {
@@ -205,12 +193,17 @@ wakeup_io_loop(bool in_signal_handler) {
static void* io_loop(void *data);
static void* talk_loop(void *data);
static void send_response(int fd, const char *msg, size_t msg_sz);
static void wakeup_talk_loop(bool);
static bool talk_thread_started = false;
static PyObject *
start(ChildMonitor *self) {
start(PyObject *s, PyObject *a UNUSED) {
#define start_doc "start() -> Start the I/O thread"
if (self->talk_fd > -1) {
ChildMonitor *self = (ChildMonitor*)s;
if (self->talk_fd > -1 || self->listen_fd > -1) {
if (pthread_create(&self->talk_thread, NULL, talk_loop, self) != 0) return PyErr_SetFromErrno(PyExc_OSError);
talk_thread_started = true;
}
int ret = pthread_create(&self->io_thread, NULL, io_loop, self);
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
@@ -218,17 +211,9 @@ start(ChildMonitor *self) {
Py_RETURN_NONE;
}
static PyObject *
join(ChildMonitor *self) {
#define join_doc "join() -> Wait for the I/O thread to finish"
int ret = pthread_join(self->io_thread, NULL);
if (ret != 0) return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
static PyObject *
wakeup(ChildMonitor UNUSED *self) {
wakeup(PYNOARG) {
#define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there."
wakeup_io_loop(false);
Py_RETURN_NONE;
@@ -266,7 +251,7 @@ schedule_write_to_child(unsigned long id, const char *data, size_t sz) {
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
if (space_left < sz) {
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
fprintf(stderr, "Too much data being sent to child with id: %lu, ignoring it\n", id);
log_error("Too much data being sent to child with id: %lu, ignoring it", id);
screen_mutex(unlock, write);
break;
}
@@ -301,11 +286,21 @@ needs_write(ChildMonitor UNUSED *self, PyObject *args) {
}
static PyObject *
shutdown_monitor(ChildMonitor *self) {
shutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {
#define shutdown_monitor_doc "shutdown_monitor() -> Shutdown the monitor loop."
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
self->shutting_down = true;
wakeup_talk_loop(false);
wakeup_io_loop(false);
int ret = pthread_join(self->io_thread, NULL);
if (ret != 0) return PyErr_Format(PyExc_OSError, "Failed to join() I/O thread with error: %s", strerror(ret));
if (talk_thread_started) {
ret = pthread_join(self->talk_thread, NULL);
if (ret != 0) return PyErr_Format(PyExc_OSError, "Failed to join() talk thread with error: %s", strerror(ret));
}
talk_thread_started = false;
Py_RETURN_NONE;
}
@@ -343,14 +338,14 @@ parse_input(ChildMonitor *self) {
if (msg) {
for (size_t i = 0; i < self->messages_count; i++) {
Message *m = self->messages + i;
PyTuple_SET_ITEM(msg, i, PyBytes_FromStringAndSize(m->data, m->sz));
PyTuple_SET_ITEM(msg, i, Py_BuildValue("y#i", m->data, (int)m->sz, m->fd));
free(m->data); m->data = NULL; m->sz = 0;
}
self->messages_count = 0;
} else fatal("Out of memory");
}
if (UNLIKELY(signal_received)) {
if (UNLIKELY(kill_signal_received)) {
global_state.close_all_windows = true;
} else {
count = self->count;
@@ -361,7 +356,13 @@ parse_input(ChildMonitor *self) {
}
children_mutex(unlock);
if (msg) {
call_boss(peer_messages_received, "(O)", msg);
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(msg); i++) {
PyObject *resp = PyObject_CallMethod(global_state.boss, "peer_message_received", "O", PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 0));
int peer_fd = (int)PyLong_AsLong(PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 1));
if (resp && PyBytes_Check(resp)) send_response(peer_fd, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
else { send_response(peer_fd, NULL, 0); if (!resp) PyErr_Print(); }
Py_CLEAR(resp);
}
Py_CLEAR(msg);
}
@@ -411,7 +412,7 @@ pty_resize(int fd, struct winsize *dim) {
if (ioctl(fd, TIOCSWINSZ, dim) == -1) {
if (errno == EINTR) continue;
if (errno != EBADF && errno != ENOTTY) {
fprintf(stderr, "Failed to resize tty associated with fd: %d with error: %s", fd, strerror(errno));
log_error("Failed to resize tty associated with fd: %d with error: %s", fd, strerror(errno));
return false;
}
}
@@ -439,7 +440,7 @@ resize_pty(ChildMonitor *self, PyObject *args) {
if (fd == -1) FIND(add_queue, add_queue_count);
if (fd != -1) {
if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);
} else fprintf(stderr, "Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)\n", window_id, self->count, add_queue_count);
} else log_error("Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)", window_id, self->count, add_queue_count);
children_mutex(unlock);
if (PyErr_Occurred()) return NULL;
Py_RETURN_NONE;
@@ -560,23 +561,33 @@ simple_render_screen(PyObject UNUSED *self, PyObject *args) {
if (vao_idx == -1) vao_idx = create_cell_vao();
if (gvao_idx == -1) gvao_idx = create_graphics_vao();
if (!PyArg_ParseTuple(args, "O!ffff", &Screen_Type, &screen, &xstart, &ystart, &dx, &dy)) return NULL;
send_cell_data_to_gpu(vao_idx, gvao_idx, xstart, ystart, dx, dy, screen, current_os_window());
draw_cells(vao_idx, gvao_idx, xstart, ystart, dx, dy, screen, current_os_window(), true);
Py_RETURN_NONE;
}
static inline void
render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id) {
static inline bool
prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows) {
#define TD os_window->tab_bar_render_data
bool needs_render = os_window->needs_render;
os_window->needs_render = false;
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 (OPT(mouse_hide_wait) > 0 && now - os_window->last_mouse_activity_at > OPT(mouse_hide_wait)) hide_mouse(os_window);
Tab *tab = os_window->tabs + os_window->active_tab;
*active_window_bg = OPT(background);
for (unsigned int i = 0; i < tab->num_windows; i++) {
Window *w = tab->windows + i;
#define WD w->render_data
if (w->visible && WD.screen) {
*num_visible_windows += 1;
if (w->last_drag_scroll_at > 0) {
if (now - w->last_drag_scroll_at >= 0.02) {
if (drag_scroll(w, os_window)) {
w->last_drag_scroll_at = now;
set_maximum_wait(0.02);
needs_render = true;
} else w->last_drag_scroll_at = 0;
} else set_maximum_wait(now - w->last_drag_scroll_at);
}
@@ -584,8 +595,27 @@ render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id
if (is_active_window) {
*active_window_id = w->id;
collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window);
if (w->cursor_visible_at_last_render != WD.screen->cursor_render_info.is_visible || w->last_cursor_x != WD.screen->cursor_render_info.x || w->last_cursor_y != WD.screen->cursor_render_info.y || w->last_cursor_shape != WD.screen->cursor_render_info.shape) needs_render = true;
update_window_title(w, os_window);
*active_window_bg = colorprofile_to_color(WD.screen->color_profile, WD.screen->color_profile->overridden.default_bg, WD.screen->color_profile->configured.default_bg);
} else WD.screen->cursor_render_info.is_visible = false;
if (send_cell_data_to_gpu(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true;
if (WD.screen->start_visual_bell_at != 0) needs_render = true;
}
}
return needs_render;
}
static inline void
render_os_window(OSWindow *os_window, double now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows) {
Tab *tab = os_window->tabs + os_window->active_tab;
BorderRects *br = &tab->border_rects;
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows);
if (TD.screen && os_window->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window, true);
for (unsigned int i = 0; i < tab->num_windows; i++) {
Window *w = tab->windows + i;
if (w->visible && WD.screen) {
bool is_active_window = i == tab->active_window;
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window, is_active_window);
if (is_active_window && WD.screen->cursor_render_info.is_visible && (!WD.screen->cursor_render_info.is_focused || WD.screen->cursor_render_info.shape != CURSOR_BLOCK)) {
draw_cursor(&WD.screen->cursor_render_info, os_window->is_focused);
@@ -594,9 +624,16 @@ render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id
double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at);
set_maximum_wait(bell_left);
}
w->cursor_visible_at_last_render = WD.screen->cursor_render_info.is_visible; w->last_cursor_x = WD.screen->cursor_render_info.x; w->last_cursor_y = WD.screen->cursor_render_info.y; w->last_cursor_shape = WD.screen->cursor_render_info.shape;
}
}
swap_window_buffers(os_window);
br->is_dirty = false;
os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id;
os_window->focused_at_last_render = os_window->is_focused;
os_window->is_damaged = false;
#undef WD
#undef TD
}
static inline void
@@ -606,26 +643,23 @@ render(double now) {
set_maximum_wait(OPT(repaint_delay) - time_since_last_render);
return;
}
#define TD w->tab_bar_render_data
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
if (!w->num_tabs || !should_os_window_be_rendered(w)) continue;
bool needs_render = w->is_damaged;
make_os_window_context_current(w);
if (w->viewport_size_dirty) {
w->clear_count = 0;
update_surface_size(w->viewport_width, w->viewport_height, w->offscreen_texture_id);
w->viewport_size_dirty = false;
needs_render = true;
}
unsigned int active_window_id = 0;
Tab *active_tab = w->tabs + w->active_tab;
BorderRects *br = &active_tab->border_rects;
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, w->viewport_width, w->viewport_height);
if (TD.screen && w->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, w, true);
render_os_window(w, now, &active_window_id);
swap_window_buffers(w);
w->last_active_tab = w->active_tab; w->last_num_tabs = w->num_tabs; w->last_active_window_id = active_window_id;
br->is_dirty = false;
unsigned int active_window_id = 0, num_visible_windows = 0;
color_type active_window_bg = 0;
if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows)) needs_render = true;
if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true;
if (needs_render) render_os_window(w, now, active_window_id, active_window_bg, num_visible_windows);
}
last_render_at = now;
#undef TD
@@ -657,7 +691,7 @@ thread_write(void *x) {
set_thread_name("KittyWriteStdin");
FILE *f = fdopen(data->fd, "w");
if (fwrite(data->buf, 1, data->sz, f) != data->sz) {
fprintf(stderr, "Failed to write all data\n");
log_error("Failed to write all data");
}
fclose(f);
free_twd(data);
@@ -692,17 +726,17 @@ process_pending_resizes(double now) {
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
if (w->has_pending_resizes) {
if (now - w->last_resize_at >= RESIZE_DEBOUNCE_TIME) update_os_window_viewport(w, true);
if (now - w->last_resize_event_at >= RESIZE_DEBOUNCE_TIME) update_os_window_viewport(w, true);
else {
global_state.has_pending_resizes = true;
set_maximum_wait(RESIZE_DEBOUNCE_TIME - now + w->last_resize_at);
set_maximum_wait(RESIZE_DEBOUNCE_TIME - now + w->last_resize_event_at);
}
}
}
}
static PyObject*
main_loop(ChildMonitor *self) {
main_loop(ChildMonitor *self, PyObject *a UNUSED) {
#define main_loop_doc "The main thread loop"
bool has_open_windows = true;
@@ -721,7 +755,7 @@ main_loop(ChildMonitor *self) {
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_width);
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);
@@ -841,6 +875,58 @@ drain_fd(int fd) {
}
}
static inline void
read_signals(int fd, bool *kill_signal, bool *child_died) {
static char buf[256];
while(true) {
ssize_t len = read(fd, buf, sizeof(buf));
if (len < 0) {
if (errno == EINTR) continue;
if (errno != EIO) perror("Call to read() from read_signals() failed");
break;
}
for (ssize_t i = 0; i < len; i++) {
switch(buf[i]) {
case SIGCHLD:
*child_died = true; break;
case SIGINT:
case SIGTERM:
*kill_signal = true; break;
default:
break;
}
}
break;
}
}
static inline void
mark_child_for_removal(ChildMonitor *self, pid_t pid) {
children_mutex(lock);
for (size_t i = 0; i < self->count; i++) {
if (children[i].pid == pid) {
children[i].needs_removal = true;
break;
}
}
children_mutex(unlock);
}
static inline void
reap_children(ChildMonitor *self, bool enable_close_on_child_death) {
int status;
pid_t pid;
(void)self;
while(true) {
pid = waitpid(-1, &status, WNOHANG);
if (pid == -1) {
if (errno != EINTR) break;
} else if (pid > 0) {
if (enable_close_on_child_death) mark_child_for_removal(self, pid);
} else break;
}
}
static inline void
write_to_child(int fd, Screen *screen) {
size_t written = 0;
@@ -892,10 +978,10 @@ io_loop(void *data) {
if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup
if (fds[1].revents && POLLIN) {
data_received = true;
drain_fd(fds[1].fd);
children_mutex(lock);
signal_received = true;
children_mutex(unlock);
bool kill_signal = false, child_died = false;
read_signals(fds[1].fd, &kill_signal, &child_died);
if (kill_signal) { children_mutex(lock); kill_signal_received = true; children_mutex(unlock); }
if (child_died) reap_children(self, OPT(close_on_child_death));
}
for (i = 0; i < self->count; i++) {
if (fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {
@@ -916,7 +1002,7 @@ io_loop(void *data) {
children_mutex(lock);
children[i].needs_removal = true;
children_mutex(unlock);
fprintf(stderr, "The child %lu had its fd unexpectedly closed\n", children[i].id);
log_error("The child %lu had its fd unexpectedly closed", children[i].id);
}
}
#ifdef DEBUG_POLL_EVENTS
@@ -943,58 +1029,258 @@ io_loop(void *data) {
// {{{ Talk thread functions
static inline void
handle_peer(ChildMonitor *self, int s) {
size_t bufsz = 0;
char *buf = NULL;
size_t buf_used = 0;
#define MAX_PEERS 256
#define MAX_LISTENERS 2
while(true) {
if (buf_used >= bufsz) {
bufsz = MAX(1024, bufsz) * 2;
if (bufsz > 1024 * 1024) return;
buf = realloc(buf, bufsz);
if (buf == NULL) return;
}
ssize_t n = recv(s, buf + buf_used, bufsz - buf_used, 0);
if (n == 0) break;
if (n < 0) {
if (errno == EINTR) continue;
perror("Error reading from talk peer");
typedef struct {
char *data;
size_t capacity, used;
int fd;
bool finished, close_socket;
} PeerReadData;
static PeerReadData empty_prd = {.fd = -1, 0};
typedef struct {
char *data;
size_t sz, pos;
int fd;
bool finished;
} PeerWriteData;
static PeerWriteData empty_pwd = {.fd = -1, 0};
typedef struct {
size_t num_listen_fds, num_talk_fds, num_reads, num_writes, num_queued_writes;
struct pollfd fds[MAX_PEERS + MAX_LISTENERS + 1];
PeerReadData reads[MAX_LISTENERS];
PeerWriteData writes[MAX_LISTENERS];
PeerWriteData queued_writes[MAX_LISTENERS];
int wakeup_fds[2];
pthread_mutex_t peer_lock;
} TalkData;
static TalkData talk_data = {0};
#define nuke_socket(s) { shutdown(s, SHUT_RDWR); close(s); }
static inline bool
accept_peer(int listen_fd, bool shutting_down) {
int peer = accept(listen_fd, NULL, NULL);
if (UNLIKELY(peer == -1)) {
if (errno == EINTR) return true;
if (!shutting_down) perror("accept() on talk socket failed!");
return false;
}
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
if (fd_idx < arraysz(talk_data.fds) && talk_data.num_reads < arraysz(talk_data.reads)) {
talk_data.fds[fd_idx].fd = peer; talk_data.fds[fd_idx].events = POLLIN;
talk_data.reads[talk_data.num_reads] = empty_prd; talk_data.reads[talk_data.num_reads++].fd = peer;
talk_data.num_talk_fds++;
} else nuke_socket(peer);
return true;
}
static inline bool
read_from_peer(ChildMonitor *self, int s) {
bool read_finished = false;
for (size_t i = 0; i < talk_data.num_reads; i++) {
PeerReadData *rd = talk_data.reads + i;
#define failed(msg) { read_finished = true; log_error("%s", msg); rd->finished = true; rd->close_socket = true; break; }
if (rd->fd == s) {
if (rd->used >= rd->capacity) {
if (rd->capacity >= 1024 * 1024) failed("Ignoring too large message from peer");
rd->capacity = MAX(8192, rd->capacity * 2);
rd->data = realloc(rd->data, rd->capacity);
if (!rd->data) failed("Out of memory");
}
ssize_t n = recv(s, rd->data + rd->used, rd->capacity - rd->used, 0);
if (n == 0) {
read_finished = true; rd->finished = true;
children_mutex(lock);
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
Message *m = self->messages + self->messages_count++;
m->data = rd->data; rd->data = NULL; m->sz = rd->used; m->fd = s;
children_mutex(unlock);
wakeup_main_loop();
} else if (n < 0) {
if (errno != EINTR) {
perror("Error reading from talk peer");
failed("");
}
} else rd->used += n;
break;
}
buf_used += n;
}
if (buf_used) {
children_mutex(lock);
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
Message *m = self->messages + self->messages_count++;
m->data = buf; m->sz = buf_used;
children_mutex(unlock);
wakeup_main_loop();
} else free(buf);
#undef failed
return read_finished;
}
static inline bool
write_to_peer(int fd) {
bool write_finished = false;
for (size_t i = 0; i < talk_data.num_writes; i++) {
PeerWriteData *wd = talk_data.writes + i;
#define failed(msg) { write_finished = true; log_error("%s", msg); wd->finished = true; break; }
if (wd->fd == fd) {
ssize_t n = send(fd, wd->data + wd->pos, wd->sz - wd->pos, MSG_NOSIGNAL);
if (n == 0) { failed("send() to peer failed to send any data"); }
else if (n < 0) {
if (errno != EINTR) { perror("write() to peer socket failed with error"); failed(""); }
} else {
wd->pos += n;
if (wd->pos >= wd->sz) { write_finished = true; wd->finished = true; }
}
break;
}
}
#undef failed
return write_finished;
}
static inline void
remove_poll_fd(int fd) {
size_t count = talk_data.num_talk_fds + talk_data.num_listen_fds;
for (size_t i = talk_data.num_listen_fds; i < count; i++) {
struct pollfd *pfd = talk_data.fds + i;
if (pfd->fd == fd) {
size_t num_to_right = count - 1 - i;
if (num_to_right) memmove(talk_data.fds + i, talk_data.fds + i + 1, num_to_right * sizeof(struct pollfd));
talk_data.num_talk_fds--;
break;
}
}
}
static inline void
prune_finished_reads() {
if (!talk_data.num_reads) return;
for (ssize_t i = talk_data.num_reads - 1; i >= 0; i--) {
PeerReadData *rd = talk_data.reads + i;
if (rd->finished) {
remove_poll_fd(rd->fd);
if (rd->close_socket) { nuke_socket(rd->fd); }
else shutdown(rd->fd, SHUT_RD);
free(rd->data);
ssize_t num_to_right = talk_data.num_reads - 1 - i;
if (num_to_right > 0) memmove(talk_data.reads + i, talk_data.reads + i + 1, num_to_right * sizeof(PeerReadData));
else talk_data.reads[i] = empty_prd;
talk_data.num_reads--;
}
}
}
static inline void
prune_finished_writes() {
if (!talk_data.num_writes) return;
for (ssize_t i = talk_data.num_writes - 1; i >= 0; i--) {
PeerWriteData *wd = talk_data.writes + i;
if (wd->finished) {
remove_poll_fd(wd->fd);
shutdown(wd->fd, SHUT_WR); close(wd->fd);
free(wd->data);
ssize_t num_to_right = talk_data.num_writes - 1 - i;
if (num_to_right > 0) memmove(talk_data.writes + i, talk_data.writes + i + 1, num_to_right * sizeof(PeerWriteData));
else talk_data.writes[i] = empty_pwd;
talk_data.num_writes--;
}
}
}
static void
wakeup_talk_loop(bool in_signal_handler) {
if (talk_data.wakeup_fds[1] <= 0) return;
while(true) {
ssize_t ret = write(talk_data.wakeup_fds[1], "w", 1);
if (ret < 0) {
if (errno == EINTR) continue;
if (!in_signal_handler) perror("Failed to write to talk wakeup fd with error");
}
break;
}
}
static inline void
move_queued_writes() {
while (talk_data.num_queued_writes) {
PeerWriteData *src = talk_data.queued_writes + --talk_data.num_queued_writes;
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
if (fd_idx < arraysz(talk_data.fds) && talk_data.num_writes < arraysz(talk_data.writes)) {
talk_data.fds[fd_idx].fd = src->fd; talk_data.fds[fd_idx].events = POLLOUT;
talk_data.writes[talk_data.num_writes++] = *src;
talk_data.num_talk_fds++;
} else {
log_error("Cannot send response to peer, too many peers");
free(src->data); nuke_socket(src->fd);
}
*src = empty_pwd;
}
}
static void*
talk_loop(void *data) {
// The I/O thread loop
// The talk thread loop
ChildMonitor *self = (ChildMonitor*)data;
set_thread_name("KittyTalkMon");
set_thread_name("KittyPeerMon");
if ((pthread_mutex_init(&talk_data.peer_lock, NULL)) != 0) { perror("Failed to create peer mutex"); return 0; }
if (!self_pipe(talk_data.wakeup_fds)) { perror("Failed to create wakeup fds for talk thread"); return 0; }
#define add_listener(which) \
if (self->which > -1) { \
talk_data.fds[talk_data.num_listen_fds].fd = self->which; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN; \
}
add_listener(talk_fd); add_listener(listen_fd);
#undef add_listener
talk_data.fds[talk_data.num_listen_fds].fd = talk_data.wakeup_fds[0]; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN;
while (LIKELY(!self->shutting_down)) {
int peer = accept(self->talk_fd, NULL, NULL);
if (peer == -1) {
if (errno == EINTR) continue;
if (!self->shutting_down) perror("accept() on talk socket failed!");
break;
}
handle_peer(self, peer);
shutdown(peer, SHUT_RDWR);
close(peer);
for (size_t i = 0; i < talk_data.num_listen_fds + talk_data.num_talk_fds; i++) { talk_data.fds[i].revents = 0; }
int ret = poll(talk_data.fds, talk_data.num_listen_fds + talk_data.num_talk_fds, -1);
if (ret > 0) {
bool has_finished_reads = false, has_finished_writes = false;
for (size_t i = 0; i < talk_data.num_listen_fds - 1; i++) {
if (talk_data.fds[i].revents & POLLIN) {if (!accept_peer(talk_data.fds[i].fd, self->shutting_down)) goto end; }
}
if (talk_data.fds[talk_data.num_listen_fds - 1].revents & POLLIN) drain_fd(talk_data.fds[talk_data.num_listen_fds - 1].fd); // wakeup
for (size_t i = talk_data.num_listen_fds; i < talk_data.num_talk_fds + talk_data.num_listen_fds; i++) {
if (talk_data.fds[i].revents & (POLLIN | POLLHUP)) { if (read_from_peer(self, talk_data.fds[i].fd)) has_finished_reads = true; }
if (talk_data.fds[i].revents & POLLOUT) { if (write_to_peer(talk_data.fds[i].fd)) has_finished_writes = true; }
}
if (has_finished_reads) prune_finished_reads();
if (has_finished_writes) prune_finished_writes();
peer_mutex(lock);
if (talk_data.num_queued_writes) move_queued_writes();
peer_mutex(unlock);
} else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror("poll() on talk fds failed"); }
}
end:
close(talk_data.wakeup_fds[0]); close(talk_data.wakeup_fds[1]);
return 0;
}
static inline bool
add_peer_writer(int fd, const char* msg, size_t msg_sz) {
bool ok = false;
peer_mutex(lock);
if (talk_data.num_queued_writes < arraysz(talk_data.queued_writes)) {
talk_data.queued_writes[talk_data.num_queued_writes] = empty_pwd;
talk_data.queued_writes[talk_data.num_queued_writes].data = malloc(msg_sz);
if (talk_data.queued_writes[talk_data.num_queued_writes].data) {
memcpy(talk_data.queued_writes[talk_data.num_queued_writes].data, msg, msg_sz);
talk_data.queued_writes[talk_data.num_queued_writes].sz = msg_sz;
talk_data.queued_writes[talk_data.num_queued_writes++].fd = fd;
ok = true;
}
} else log_error("Cannot send response to peer, too many peers");
peer_mutex(unlock);
return ok;
}
static void
send_response(int fd, const char *msg, size_t msg_sz) {
if (msg == NULL) { shutdown(fd, SHUT_WR); close(fd); return; }
if (!add_peer_writer(fd, msg, msg_sz)) { shutdown(fd, SHUT_WR); close(fd); }
else wakeup_talk_loop(false);
}
// }}}
// Boilerplate {{{
@@ -1002,7 +1288,6 @@ static PyMethodDef methods[] = {
METHOD(add_child, METH_VARARGS)
METHOD(needs_write, METH_VARARGS)
METHOD(start, METH_NOARGS)
METHOD(join, METH_NOARGS)
METHOD(wakeup, METH_NOARGS)
METHOD(shutdown_monitor, METH_NOARGS)
METHOD(main_loop, METH_NOARGS)
@@ -1024,8 +1309,16 @@ PyTypeObject ChildMonitor_Type = {
.tp_new = new,
};
static PyObject*
safe_pipe(PYNOARG) {
int fds[2] = {0};
if (!self_pipe(fds)) return PyErr_SetFromErrno(PyExc_OSError);
return Py_BuildValue("ii", fds[0], fds[1]);
}
static PyMethodDef module_methods[] = {
METHOD(simple_render_screen, METH_VARARGS)
METHODB(safe_pipe, METH_NOARGS),
{NULL} /* Sentinel */
};

View File

@@ -42,8 +42,8 @@ static PyObject*
spawn(PyObject *self UNUSED, PyObject *args) {
PyObject *argv_p, *env_p;
int master, slave, stdin_read_fd, stdin_write_fd;
char* cwd;
if (!PyArg_ParseTuple(args, "sO!O!iiii", &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
char *cwd, *exe;
if (!PyArg_ParseTuple(args, "ssO!O!iiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
char name[2048] = {0};
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
char **argv = serialize_string_tuple(argv_p);
@@ -76,7 +76,7 @@ spawn(PyObject *self UNUSED, PyObject *args) {
close(tfd);
environ = env;
execvp(argv[0], argv);
execvp(exe, argv);
// Report the failure and exec a shell instead, so that we are not left
// with a forked but not exec'ed process
write_to_stderr("Failed to launch child: ");

View File

@@ -7,7 +7,7 @@ import os
import kitty.fast_data_types as fast_data_types
from .constants import terminfo_dir, is_macos
from .constants import is_macos, shell_path, terminfo_dir
def cwd_of_process(pid):
@@ -70,7 +70,13 @@ class Child:
if os.path.isdir(terminfo_dir):
env['TERMINFO'] = terminfo_dir
env = tuple('{}={}'.format(k, v) for k, v in env.items())
pid = fast_data_types.spawn(self.cwd, tuple(self.argv), env, master, slave, stdin_read_fd, stdin_write_fd)
argv = list(self.argv)
exe = argv[0]
if is_macos and exe == shell_path:
# Some macOS machines need the shell to have argv[0] prefixed by
# hyphen, see https://github.com/kovidgoyal/kitty/issues/247
argv[0] = ('-' + exe.split('/')[-1])
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd)
os.close(slave)
self.pid = pid
self.child_fd = master

View File

@@ -2,16 +2,16 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import os
import re
import subprocess
import sys
from collections import deque
from .config import load_config
from .constants import appname, defconf, is_macos, str_version
from .config import defaults, load_config
from .constants import appname, defconf, is_macos, is_wayland, str_version
from .layout import all_layouts
is_macos
OPTIONS = '''
--class
dest=cls
@@ -32,9 +32,21 @@ only use this if you are running a program that does not set titles.
--config
type=list
default={config_path}
Specify a path to the configuration file(s) to use.
Can be specified multiple times to read multiple configuration files in sequence, which are merged.
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 used as a base config file onto
which any user config files are merged.
--override -o
@@ -84,6 +96,16 @@ with the same |_ --instance-group| will result in new windows being created
in the first |_ {appname}| instance within that group
--listen-on
Tell kitty to listen on the specified address for control
messages. For example, |_ --listen-on=unix:/tmp/mykitty| or
|_ --listen-on=tcp:localhost:12345|. On Linux systems, you can also use abstract
UNIX sockets, not associated with a file, like this: |_ --listen-on=unix:@mykitty|.
To control kitty, you can send it commands with |_ kitty @| using the |_ --to| option
to specify this address. Note that this option will be ignored, unless you set
|_ allow_remote_control| to yes in |_ kitty.conf|
# Debugging options
--version -v
@@ -107,6 +129,16 @@ Path to file in which to store the raw bytes received from the child process
--debug-gl
type=bool-set
Debug OpenGL commands. This will cause all OpenGL calls to check for errors instead of ignoring them. Useful when debugging rendering problems
--debug-font-fallback
type=bool-set
Print out information about the selection of fallback fonts for characters not present in the main font.
--debug-config
type=bool-set
Print out information about the system and kitty configuration.
'''
@@ -186,10 +218,10 @@ def parse_option_spec(spec=OPTIONS):
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
elif state is HELP:
if line:
current_cmd['help'] += ' ' + line
current_cmd['help'] += ' ' + line.lstrip()
else:
if prev_line:
current_cmd['help'] += '\n'
current_cmd['help'] += '\n\n\t'
else:
state = NORMAL
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
@@ -271,8 +303,9 @@ def print_help_for_seq(seq, usage, message, appname):
lines.append('')
a((' ' * leading_indent) + j.join(lines))
usage = usage or '[program-to-run ...]'
a('{}: {} [options] {}'.format(title('Usage'), bold(yellow(appname)), usage))
usage = '[program-to-run ...]' if usage is None else usage
optstring = '[options] ' if seq else ''
a('{}: {} {}{}'.format(title('Usage'), bold(yellow(appname)), optstring, usage))
a('')
message = message or (
'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
@@ -437,7 +470,7 @@ def parse_cmdline(oc, disabled, args=None):
def options_spec():
if not hasattr(options_spec, 'ans'):
options_spec.ans = OPTIONS.format(
appname=appname, config_path=defconf,
appname=appname, macos_confpath='~/Library/Preferences/kitty/kitty.conf' if is_macos else '',
window_layout_choices=', '.join(all_layouts)
)
return options_spec.ans
@@ -450,8 +483,85 @@ def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=
return parse_cmdline(oc, disabled, args=args)
def create_opts(args):
config = args.config or (defconf, )
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
def 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())
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 ())
opts = load_config(*config, overrides=overrides)
if debug_config:
compare_opts(opts)
return opts

View File

@@ -6,7 +6,7 @@
*/
#include "data-types.h"
#include "state.h"
#include <Cocoa/Cocoa.h>
#include <AvailabilityMacros.h>
@@ -23,6 +23,9 @@ typedef void* rusage_info_t; // needed for libproc.h
#endif
static NSMenuItem* title_menu = NULL;
static bool change_titlebar_color = false;
static color_type titlebar_color = 0;
static NSString*
find_app_name(void) {
@@ -56,11 +59,35 @@ find_app_name(void) {
return @"kitty";
}
@interface GlobalMenuTarget : NSObject
+ (GlobalMenuTarget *) shared_instance;
@end
@implementation GlobalMenuTarget
- (void) show_preferences : (id)sender {
(void)sender;
call_boss(edit_config_file, NULL);
}
+ (GlobalMenuTarget *) shared_instance
{
static GlobalMenuTarget *sharedGlobalMenuTarget = nil;
@synchronized(self)
{
if (!sharedGlobalMenuTarget)
sharedGlobalMenuTarget = [[GlobalMenuTarget alloc] init];
return sharedGlobalMenuTarget;
}
}
@end
void
cocoa_create_global_menu(void) {
NSString* app_name = find_app_name();
NSMenu* bar = [[NSMenu alloc] init];
GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];
[NSApp setMainMenu:bar];
NSMenuItem* appMenuItem =
@@ -72,6 +99,9 @@ cocoa_create_global_menu(void) {
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
NSMenuItem* preferences_menu_item = [[NSMenuItem alloc] initWithTitle:@"Preferences..." action:@selector(show_preferences:) keyEquivalent:@","];
[preferences_menu_item setTarget:global_menu_target];
[appMenu addItem:preferences_menu_item];
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name]
action:@selector(hide:)
keyEquivalent:@"h"];
@@ -121,7 +151,7 @@ cocoa_create_global_menu(void) {
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
[NSApp setWindowsMenu:windowMenu];
[windowMenu release];
[preferences_menu_item release];
[bar release];
}
@@ -154,7 +184,7 @@ cocoa_make_window_resizable(void *w) {
return true;
}
PyObject*
static PyObject*
cocoa_get_lang(PyObject UNUSED *self) {
NSString* locale = nil;
NSString* lang_code = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode];
@@ -168,7 +198,7 @@ cocoa_get_lang(PyObject UNUSED *self) {
return Py_BuildValue("s", [locale UTF8String]);
}
PyObject*
static PyObject*
cwd_of_process(PyObject *self UNUSED, PyObject *pid_) {
long pid = PyLong_AsLong(pid_);
struct proc_vnodepathinfo vpi;
@@ -177,10 +207,59 @@ cwd_of_process(PyObject *self UNUSED, PyObject *pid_) {
return PyUnicode_FromString(vpi.pvi_cdir.vip_path);
}
static inline color_type
color_as_int(PyObject *color) {
#define I(n, s) ((PyLong_AsUnsignedLong(PyTuple_GET_ITEM(color, n)) & 0xff) << s)
return (I(0, 16) | I(1, 8) | I(2, 0)) & 0xffffff;
#undef I
}
static PyObject*
macos_change_titlebar_color(PyObject *self UNUSED, PyObject *val) {
if (val == Py_None) change_titlebar_color = false;
else if (val == Py_True) {
change_titlebar_color = true;
titlebar_color = OPT(background);
} else {
if (!PyTuple_Check(val)) { PyErr_SetString(PyExc_TypeError, "Not a color tuple"); return NULL; }
change_titlebar_color = true;
titlebar_color = color_as_int(val);
}
Py_RETURN_NONE;
}
void
cocoa_set_titlebar_color(void *w)
{
if (!change_titlebar_color) return;
NSWindow *window = (NSWindow *)w;
double red = ((titlebar_color >> 16) & 0xFF) / 255.0;
double green = ((titlebar_color >> 8) & 0xFF) / 255.0;
double blue = (titlebar_color & 0xFF) / 255.0;
NSColor *background =
[NSColor colorWithSRGBRed:red
green:green
blue:blue
alpha:1.0];
[window setTitlebarAppearsTransparent:YES];
[window setBackgroundColor:background];
double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
if (luma < 0.5) {
[window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
} else {
[window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
}
}
static PyMethodDef module_methods[] = {
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
{"cwd_of_process", (PyCFunction)cwd_of_process, METH_O, ""},
{"macos_change_titlebar_color", (PyCFunction)macos_change_titlebar_color, METH_O, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};

View File

@@ -133,7 +133,7 @@ as_color(ColorProfile *self, PyObject *val) {
}
static PyObject*
reset_color_table(ColorProfile *self) {
reset_color_table(ColorProfile *self, PyObject *a UNUSED) {
#define reset_color_table_doc "Reset all customized colors back to defaults"
memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));
self->dirty = true;
@@ -179,7 +179,7 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size
}
static PyObject*
color_table_address(ColorProfile *self) {
color_table_address(ColorProfile *self, PyObject *a UNUSED) {
#define color_table_address_doc "Pointer address to start of color table"
return PyLong_FromVoidPtr((void*)self->color_table);
}

View File

@@ -10,16 +10,17 @@ import shlex
import sys
import tempfile
from collections import namedtuple
from contextlib import contextmanager
from . import fast_data_types as defines
from .config_utils import (
init_config, parse_config_base, positive_float, positive_int, to_bool,
to_color, unit_float
)
from .constants import config_dir
from .constants import cache_dir, defconf
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .utils import safe_print
from .utils import log_error
MINIMUM_FONT_SIZE = 4
@@ -49,19 +50,15 @@ def to_cursor_shape(x):
def parse_mods(parts):
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
for m in parts:
try:
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
except AttributeError:
safe_print(
'Shortcut: {} has an unknown modifier, ignoring'.format(
parts.join('+')
),
file=sys.stderr
)
log_error('Shortcut: {} has unknown modifier, ignoring'.format(
parts.join('+')))
return
return mods
@@ -92,7 +89,10 @@ def parse_shortcut(sc):
KeyAction = namedtuple('KeyAction', 'func args')
shlex_actions = {'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window', 'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd'}
shlex_actions = {
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd'
}
def parse_key_action(action):
@@ -107,8 +107,14 @@ def parse_key_action(action):
args = tuple(map(parse_key_action, filter(None, parts)))
elif func == 'send_text':
args = rest.split(' ', 1)
elif func == 'run_simple_kitten':
args = rest.split(' ', 2)
elif func == 'goto_tab':
args = (max(0, int(rest)),)
args = (max(0, int(rest)), )
elif func == 'goto_layout':
args = [rest]
elif func == 'set_font_size':
args = (float(rest),)
elif func in shlex_actions:
args = shlex.split(rest)
return KeyAction(func, args)
@@ -121,18 +127,14 @@ def parse_key(val, keymap):
return
mods, key = parse_shortcut(sc)
if key is None:
safe_print(
'Shortcut: {} has an unknown key, ignoring'.format(val),
file=sys.stderr
)
log_error('Shortcut: {} has unknown key, ignoring'.format(
val))
return
try:
paction = parse_key_action(action)
except Exception:
safe_print(
'Invalid shortcut action: {}. Ignoring.'.format(action),
file=sys.stderr
)
log_error('Invalid shortcut action: {}. Ignoring.'.format(
action))
else:
if paction is not None:
keymap[(mods, key)] = paction
@@ -143,9 +145,8 @@ def parse_symbol_map(val):
symbol_map = {}
def abort():
safe_print(
'Symbol map: {} is invalid, ignoring'.format(val), file=sys.stderr
)
log_error('Symbol map: {} is invalid, ignoring'.format(
val))
return {}
if len(parts) < 2:
@@ -172,17 +173,16 @@ def parse_symbol_map(val):
def parse_send_text_bytes(text):
return ast.literal_eval("'''" + text.replace("'''", "'\\''") + "'''").encode('utf-8')
return ast.literal_eval("'''" + text.replace("'''", "'\\''") + "'''"
).encode('utf-8')
def parse_send_text(val, keymap):
parts = val.split(' ')
def abort(msg):
safe_print(
'Send text: {} is invalid ({}), ignoring'.format(val, msg),
file=sys.stderr
)
log_error('Send text: {} is invalid ({}), ignoring'.format(
val, msg))
return {}
if len(parts) < 3:
@@ -213,6 +213,15 @@ def adjust_line_height(x):
return int(x)
def macos_titlebar_color(x):
x = x.strip('"')
if x == 'system':
return
if x == 'background':
return True
return to_color(x)
def box_drawing_scale(x):
ans = tuple(float(x.strip()) for x in x.split(','))
if len(ans) != 4:
@@ -231,7 +240,11 @@ def tab_separator(x):
def tab_font_style(x):
return {'bold-italic': (True, True), 'bold': (True, False), 'italic': (False, True)}.get(x.lower().replace('_', '-'), (False, False))
return {
'bold-italic': (True, True),
'bold': (True, False),
'italic': (False, True)
}.get(x.lower().replace('_', '-'), (False, False))
def tab_bar_edge(x):
@@ -242,8 +255,9 @@ def url_style(x):
return url_style.map.get(x, url_style.map['curly'])
url_style.map = dict(((v, i) for i, v in enumerate('none single double curly'.split())))
url_style.map = dict(
((v, i) for i, v in enumerate('none single double curly'.split()))
)
type_map = {
'allow_remote_control': to_bool,
@@ -251,7 +265,6 @@ type_map = {
'adjust_column_width': adjust_line_height,
'scrollback_lines': positive_int,
'scrollback_pager': shlex.split,
'scrollback_in_new_tab': to_bool,
'font_size': to_font_size,
'font_size_delta': positive_float,
'focus_follows_mouse': to_bool,
@@ -260,6 +273,8 @@ type_map = {
'rectangle_select_modifiers': to_modifiers,
'repaint_delay': positive_int,
'input_delay': positive_int,
'sync_to_monitor': to_bool,
'close_on_child_death': to_bool,
'window_border_width': positive_float,
'window_margin_width': positive_float,
'window_padding_width': positive_float,
@@ -274,18 +289,16 @@ type_map = {
'remember_window_size': to_bool,
'initial_window_width': positive_int,
'initial_window_height': positive_int,
'use_system_wcwidth': to_bool,
'macos_hide_titlebar': to_bool,
'macos_option_as_alt': to_bool,
'macos_titlebar_color': macos_titlebar_color,
'box_drawing_scale': box_drawing_scale,
'x11_bell_volume': int,
'background_opacity': unit_float,
'tab_separator': tab_separator,
'active_tab_font_style': tab_font_style,
'inactive_tab_font_style': tab_font_style,
'inactive_text_alpha': unit_float,
'url_style': url_style,
'prefer_color_emoji': to_bool,
'copy_on_select': to_bool,
'tab_bar_edge': tab_bar_edge,
}
@@ -316,7 +329,9 @@ def special_handling(key, val, ans):
defaults = None
default_config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty.conf')
default_config_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'kitty.conf'
)
def parse_config(lines, check_keys=True):
@@ -324,13 +339,21 @@ def parse_config(lines, check_keys=True):
'keymap': {},
'symbol_map': {},
}
parse_config_base(lines, defaults, type_map, special_handling, ans, check_keys=check_keys)
parse_config_base(
lines,
defaults,
type_map,
special_handling,
ans,
check_keys=check_keys
)
return ans
Options, defaults = init_config(default_config_path, parse_config)
actions = frozenset(a.func for a in defaults.keymap.values()) | frozenset(
'combine send_text goto_tab new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.split()
'combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
split()
)
no_op_actions = frozenset({'noop', 'no-op', 'no_op'})
@@ -373,7 +396,7 @@ def load_config(*paths, overrides=None) -> Options:
if not path:
continue
try:
f = open(path)
f = open(path, encoding='utf-8', errors='replace')
except FileNotFoundError:
continue
with f:
@@ -396,56 +419,75 @@ def build_ansi_color_table(opts: Options = defaults):
return list(map(col, range(16)))
cached_values = {}
cached_path = os.path.join(config_dir, 'cached.json')
def load_cached_values():
cached_values.clear()
try:
with open(cached_path, 'rb') as f:
cached_values.update(json.loads(f.read().decode('utf-8')))
except FileNotFoundError:
pass
except Exception as err:
safe_print(
'Failed to load cached values with error: {}'.format(err),
file=sys.stderr
)
def save_cached_values():
fd, p = tempfile.mkstemp(
dir=os.path.dirname(cached_path), suffix='cached.json.tmp'
)
def atomic_save(data, path):
fd, p = tempfile.mkstemp(dir=os.path.dirname(path), suffix='.tmp')
try:
with os.fdopen(fd, 'wb') as f:
f.write(json.dumps(cached_values).encode('utf-8'))
os.rename(p, cached_path)
except Exception as err:
safe_print(
'Failed to save cached values with error: {}'.format(err),
file=sys.stderr
)
f.write(data)
os.rename(p, path)
finally:
try:
os.remove(p)
except FileNotFoundError:
pass
except Exception as err:
safe_print(
'Failed to delete temp file for saved cached values with error: {}'.
format(err),
file=sys.stderr
)
log_error('Failed to delete temp file {} for atomic save with error: {}'.format(
p, err))
def initial_window_size(opts):
@contextmanager
def cached_values_for(name):
cached_path = os.path.join(cache_dir(), name + '.json')
cached_values = {}
try:
with open(cached_path, 'rb') as f:
cached_values.update(json.loads(f.read().decode('utf-8')))
except FileNotFoundError:
pass
except Exception as err:
log_error('Failed to load cached in {} values with error: {}'.format(
name, err))
yield cached_values
try:
data = json.dumps(cached_values).encode('utf-8')
atomic_save(data, cached_path)
except Exception as err:
log_error('Failed to save cached values with error: {}'.format(
err))
def initial_window_size(opts, cached_values):
w, h = opts.initial_window_width, opts.initial_window_height
if 'window-size' in cached_values and opts.remember_window_size:
ws = cached_values['window-size']
try:
w, h = map(int, ws)
except Exception:
safe_print('Invalid cached window size, ignoring', file=sys.stderr)
log_error('Invalid cached window size, ignoring')
return w, h
def commented_out_default_config():
with open(default_config_path, encoding='utf-8', errors='replace') as f:
config = f.read()
lines = []
for line in config.splitlines():
if line.strip() and not line.startswith('#'):
line = '# ' + line
lines.append(line)
config = '\n'.join(lines)
return config
def prepare_config_file_for_editing():
if not os.path.exists(defconf):
d = os.path.dirname(defconf)
try:
os.makedirs(d)
except FileExistsError:
pass
with open(defconf, 'w') as f:
f.write(commented_out_default_config())
return defconf

View File

@@ -3,10 +3,9 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import re
import sys
from collections import namedtuple
from .utils import safe_print
from .utils import log_error
from .rgb import to_color as as_color
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
@@ -48,10 +47,7 @@ def parse_config_base(
continue
if check_keys:
if key not in all_keys:
safe_print(
'Ignoring unknown config key: {}'.format(key),
file=sys.stderr
)
log_error('Ignoring unknown config key: {}'.format(key))
continue
tm = type_map.get(key)
if tm is not None:
@@ -60,7 +56,7 @@ def parse_config_base(
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)
Options = namedtuple('Defaults', ','.join(defaults.keys()))
defaults = Options(**defaults)

View File

@@ -4,17 +4,19 @@
import os
import pwd
import shlex
import sys
from collections import namedtuple
from .fast_data_types import set_boss as set_c_boss
appname = 'kitty'
version = (0, 7, 1)
version = (0, 8, 4)
str_version = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos = 'darwin' in _plat
base = os.path.dirname(os.path.abspath(__file__))
editor = shlex.split(os.environ.get('EDITOR', 'vim'))
ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy')
@@ -25,7 +27,22 @@ def _get_config_dir():
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or ('~/Library/Preferences' if is_macos else '~/.config')))
locations = []
if 'XDG_CONFIG_HOME' in os.environ:
locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME'])))
locations.append(os.path.expanduser('~/.config'))
if is_macos:
locations.append(os.path.expanduser('~/Library/Preferences'))
if 'XDG_CONFIG_DIRS' in os.environ:
for loc in os.environ['XDG_CONFIG_DIRS'].split(os.pathsep):
locations.append(os.path.abspath(os.path.expanduser(loc)))
for loc in locations:
if loc:
q = os.path.join(loc, appname)
if os.access(q, os.W_OK) and os.path.exists(os.path.join(q, 'kitty.conf')):
return q
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or '~/.config'))
ans = os.path.join(candidate, appname)
os.makedirs(ans, exist_ok=True)
return ans
@@ -36,6 +53,25 @@ del _get_config_dir
defconf = os.path.join(config_dir, 'kitty.conf')
def _get_cache_dir():
if 'KITTY_CACHE_DIRECTORY' in os.environ:
candidate = os.path.abspath(os.environ['KITTY_CACHE_DIRECTORY'])
elif is_macos:
candidate = os.path.join(os.path.expanduser('~/Library/Caches'), appname)
else:
candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')
candidate = os.path.join(os.path.expanduser(candidate), appname)
os.makedirs(candidate, exist_ok=True)
return candidate
def cache_dir():
ans = getattr(cache_dir, 'ans', None)
if ans is None:
ans = cache_dir.ans = _get_cache_dir()
return ans
def get_boss():
return get_boss.boss
@@ -55,7 +91,10 @@ logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba')
try:
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
except KeyError:
print('Failed to read login shell from /etc/passwd for current user, falling back to /bin/sh', file=sys.stderr)
try:
print('Failed to read login shell via getpwuid() for current user, falling back to /bin/sh', file=sys.stderr)
except Exception:
pass
shell_path = '/bin/sh'

View File

@@ -230,12 +230,6 @@
// Forward tab
#define CHT 'I'
// Save cursor
#define SC 's'
// Restore cursor
#define RC 'u'
// Misc sequences
// ----------------

View File

@@ -178,14 +178,18 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
}
PyObject*
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic) {
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation) {
CTFace *self = (CTFace*)base_face;
char text[256] = {0};
cell_as_utf8(cell, true, text, ' ');
CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
if (str == NULL) return PyErr_NoMemory();
CTFontRef new_font = find_substitute_face(str, self->ct_font);
CFRelease(str);
CTFontRef new_font;
if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL);
else {
char text[256] = {0};
cell_as_utf8(cell, true, text, ' ');
CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
if (str == NULL) return PyErr_NoMemory();
new_font = find_substitute_face(str, self->ct_font);
CFRelease(str);
}
if (new_font == NULL) return NULL;
return (PyObject*)ct_face(new_font);
}

View File

@@ -253,7 +253,7 @@ cursor_as_sgr(Cursor *self, Cursor *prev) {
}
static PyObject *
reset_display_attrs(Cursor *self) {
reset_display_attrs(Cursor *self, PyObject *a UNUSED) {
#define reset_display_attrs_doc "Reset all display attributes to unset"
cursor_reset_display_attrs(self);
Py_RETURN_NONE;
@@ -272,7 +272,7 @@ void cursor_copy_to(Cursor *src, Cursor *dest) {
}
static PyObject*
copy(Cursor *self);
copy(Cursor *self, PyObject*);
#define copy_doc "Create a clone of this cursor"
// Boilerplate {{{
@@ -342,7 +342,7 @@ cursor_copy(Cursor *self) {
}
static PyObject*
copy(Cursor *self) {
copy(Cursor *self, PyObject *a UNUSED) {
return (PyObject*)cursor_copy(self);
}

View File

@@ -17,7 +17,6 @@
#include <stddef.h>
#include <termios.h>
#include <signal.h>
#include <sys/wait.h>
#ifdef WITH_PROFILER
#include <gperftools/profiler.h>
#endif
@@ -70,17 +69,6 @@ static inline double monotonic_() {
double monotonic() { return monotonic_(); }
static PyObject*
wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {
return PyLong_FromUnsignedLong(safe_wcwidth(PyLong_AsLong(chr)));
}
static PyObject*
change_wcwidth_wrap(PyObject UNUSED *self, PyObject *use9) {
change_wcwidth(PyObject_IsTrue(use9));
Py_RETURN_NONE;
}
static PyObject*
redirect_std_streams(PyObject UNUSED *self, PyObject *args) {
char *devnull = NULL;
@@ -99,31 +87,6 @@ pyset_iutf8(PyObject UNUSED *self, PyObject *args) {
Py_RETURN_NONE;
}
static void
handle_sigchld(int UNUSED signum, siginfo_t *sinfo, void UNUSED *unused) {
if (sinfo->si_code != CLD_EXITED) return;
int sav_errno = errno, status;
while(true) {
if (waitpid(sinfo->si_pid, &status, WNOHANG) == -1) {
if (errno != EINTR) break;
} else break;
}
// wakeup I/O loop as without this on macOS sometimes poll() does not detect the fd close, so
// kitty does not detect child death.
wakeup_io_loop(true);
errno = sav_errno;
}
static PyObject*
install_sigchld_handler(PyObject UNUSED *self) {
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handle_sigchld;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == -1) return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
#ifdef WITH_PROFILER
static PyObject*
start_profiler(PyObject UNUSED *self, PyObject *args) {
@@ -146,9 +109,6 @@ static PyMethodDef module_methods[] = {
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
{"install_sigchld_handler", (PyCFunction)install_sigchld_handler, METH_NOARGS, ""},
#ifdef __APPLE__
METHODB(user_cache_dir, METH_NOARGS),
#endif
@@ -186,6 +146,8 @@ extern bool init_keys(PyObject *module);
extern bool init_graphics(PyObject *module);
extern bool init_shaders(PyObject *module);
extern bool init_mouse(PyObject *module);
extern bool init_kittens(PyObject *module);
extern bool init_logging(PyObject *module);
#ifdef __APPLE__
extern int init_CoreText(PyObject *);
extern bool init_cocoa(PyObject *module);
@@ -205,6 +167,7 @@ PyInit_fast_data_types(void) {
#endif
if (m != NULL) {
if (!init_logging(m)) return NULL;
if (!init_LineBuf(m)) return NULL;
if (!init_HistoryBuf(m)) return NULL;
if (!init_Line(m)) return NULL;
@@ -219,6 +182,7 @@ PyInit_fast_data_types(void) {
if (!init_graphics(m)) return NULL;
if (!init_shaders(m)) return NULL;
if (!init_mouse(m)) return NULL;
if (!init_kittens(m)) return NULL;
#ifdef __APPLE__
if (!init_CoreText(m)) return NULL;
if (!init_cocoa(m)) return NULL;

View File

@@ -18,6 +18,7 @@
#define OPENGL_REQUIRED_VERSION_MAJOR 3
#define OPENGL_REQUIRED_VERSION_MINOR 3
#define UNUSED __attribute__ ((unused))
#define PYNOARG PyObject *__a1 UNUSED, PyObject *__a2 UNUSED
#define EXPORTED __attribute__ ((visibility ("default")))
#define LIKELY(x) __builtin_expect (!!(x), 1)
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
@@ -26,7 +27,8 @@
#define xstr(s) str(s)
#define str(s) #s
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
#define fatal(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); }
void log_error(const char *fmt, ...);
#define fatal(...) { log_error(__VA_ARGS__); exit(EXIT_FAILURE); }
typedef unsigned long long id_type;
typedef uint32_t char_type;
@@ -201,29 +203,13 @@ typedef struct {
DynamicColor configured, overridden;
} ColorProfile;
#define SAVEPOINTS_SZ 256
typedef struct {
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
bool use_latin1;
Cursor cursor;
bool mDECOM, mDECAWM, mDECSCNM;
} Savepoint;
typedef struct {
Savepoint buf[SAVEPOINTS_SZ];
index_type start_of_data, count;
} SavepointBuffer;
#define PARSER_BUF_SZ (8 * 1024)
#define READ_BUF_SZ (1024*1024)
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
#define left_shift_line(line, at, num) \
#define left_shift_line(line, at, num) { \
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
COPY_CELL(line, __i__ + (num), line, __i__) \
} \
@@ -231,7 +217,8 @@ typedef struct {
(line)->cells[(at)].ch = BLANK_CHAR; \
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
clear_sprite_position((line)->cells[(at)]); \
}
}\
}
#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \
if ((base)->capacity < num) { \
@@ -271,8 +258,6 @@ bool set_iutf8(int, bool);
color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval);
void copy_color_table_to_buffer(ColorProfile *self, color_type *address, int offset, size_t stride);
unsigned int safe_wcwidth(uint32_t ch);
void change_wcwidth(bool use9);
void set_mouse_cursor(MouseShape);
void mouse_event(int, int);
void focus_in_event();

216
kitty/emoji.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2018-01-18
// unicode data, built from the unicode standard on: 2018-02-09
// see gen-wcwidth.py
#pragma once
#include "data-types.h"
@@ -8,6 +8,28 @@ START_ALLOW_CASE_RANGE
static inline bool
is_emoji(char_type code) {
switch(code) {
case 0x23:
return true;
case 0x2a:
return true;
case 0x30 ... 0x39:
return true;
case 0xa9:
return true;
case 0xae:
return true;
case 0x200d:
return true;
case 0x203c:
return true;
case 0x2049:
return true;
case 0x20e3:
return true;
case 0x2122:
return true;
case 0x2139:
return true;
case 0x2194 ... 0x2199:
return true;
case 0x21a9 ... 0x21aa:
@@ -16,6 +38,8 @@ is_emoji(char_type code) {
return true;
case 0x2328:
return true;
case 0x2388:
return true;
case 0x23cf:
return true;
case 0x23e9 ... 0x23f3:
@@ -32,89 +56,15 @@ is_emoji(char_type code) {
return true;
case 0x25fb ... 0x25fe:
return true;
case 0x2600 ... 0x2604:
case 0x2600 ... 0x2605:
return true;
case 0x260e:
case 0x2607 ... 0x2612:
return true;
case 0x2611:
case 0x2614 ... 0x2685:
return true;
case 0x2614 ... 0x2615:
case 0x2690 ... 0x2705:
return true;
case 0x2618:
return true;
case 0x261d:
return true;
case 0x2620:
return true;
case 0x2622 ... 0x2623:
return true;
case 0x2626:
return true;
case 0x262a:
return true;
case 0x262e ... 0x262f:
return true;
case 0x2638 ... 0x263a:
return true;
case 0x2640:
return true;
case 0x2642:
return true;
case 0x2648 ... 0x2653:
return true;
case 0x2660:
return true;
case 0x2663:
return true;
case 0x2665 ... 0x2666:
return true;
case 0x2668:
return true;
case 0x267b:
return true;
case 0x267f:
return true;
case 0x2692 ... 0x2697:
return true;
case 0x2699:
return true;
case 0x269b ... 0x269c:
return true;
case 0x26a0 ... 0x26a1:
return true;
case 0x26aa ... 0x26ab:
return true;
case 0x26b0 ... 0x26b1:
return true;
case 0x26bd ... 0x26be:
return true;
case 0x26c4 ... 0x26c5:
return true;
case 0x26c8:
return true;
case 0x26ce ... 0x26cf:
return true;
case 0x26d1:
return true;
case 0x26d3 ... 0x26d4:
return true;
case 0x26e9 ... 0x26ea:
return true;
case 0x26f0 ... 0x26f5:
return true;
case 0x26f7 ... 0x26fa:
return true;
case 0x26fd:
return true;
case 0x2702:
return true;
case 0x2705:
return true;
case 0x2708 ... 0x270d:
return true;
case 0x270f:
return true;
case 0x2712:
case 0x2708 ... 0x2712:
return true;
case 0x2714:
return true;
@@ -140,7 +90,7 @@ is_emoji(char_type code) {
return true;
case 0x2757:
return true;
case 0x2763 ... 0x2764:
case 0x2763 ... 0x2767:
return true;
case 0x2795 ... 0x2797:
return true;
@@ -168,11 +118,15 @@ is_emoji(char_type code) {
return true;
case 0x3299:
return true;
case 0x1f004:
case 0xfe0f:
return true;
case 0x1f0cf:
case 0x1f000 ... 0x1f0ff:
return true;
case 0x1f170 ... 0x1f171:
case 0x1f10d ... 0x1f10f:
return true;
case 0x1f12f:
return true;
case 0x1f16c ... 0x1f171:
return true;
case 0x1f17e ... 0x1f17f:
return true;
@@ -180,9 +134,9 @@ is_emoji(char_type code) {
return true;
case 0x1f191 ... 0x1f19a:
return true;
case 0x1f1e6 ... 0x1f1ff:
case 0x1f1ad ... 0x1f1ff:
return true;
case 0x1f201 ... 0x1f202:
case 0x1f201 ... 0x1f20f:
return true;
case 0x1f21a:
return true;
@@ -190,95 +144,35 @@ is_emoji(char_type code) {
return true;
case 0x1f232 ... 0x1f23a:
return true;
case 0x1f250 ... 0x1f251:
case 0x1f23c ... 0x1f23f:
return true;
case 0x1f300 ... 0x1f321:
case 0x1f249 ... 0x1f53d:
return true;
case 0x1f324 ... 0x1f393:
case 0x1f546 ... 0x1f64f:
return true;
case 0x1f396 ... 0x1f397:
case 0x1f680 ... 0x1f6ff:
return true;
case 0x1f399 ... 0x1f39b:
case 0x1f774 ... 0x1f77f:
return true;
case 0x1f39e ... 0x1f3f0:
case 0x1f7d5 ... 0x1f7ff:
return true;
case 0x1f3f3 ... 0x1f3f5:
case 0x1f80c ... 0x1f80f:
return true;
case 0x1f3f7 ... 0x1f4fd:
case 0x1f848 ... 0x1f84f:
return true;
case 0x1f4ff ... 0x1f53d:
case 0x1f85a ... 0x1f85f:
return true;
case 0x1f549 ... 0x1f54e:
case 0x1f888 ... 0x1f88f:
return true;
case 0x1f550 ... 0x1f567:
case 0x1f8ae ... 0x1f8ff:
return true;
case 0x1f56f ... 0x1f570:
case 0x1f90c ... 0x1f93a:
return true;
case 0x1f573 ... 0x1f57a:
case 0x1f93c ... 0x1f945:
return true;
case 0x1f587:
case 0x1f947 ... 0x1fffd:
return true;
case 0x1f58a ... 0x1f58d:
return true;
case 0x1f590:
return true;
case 0x1f595 ... 0x1f596:
return true;
case 0x1f5a4 ... 0x1f5a5:
return true;
case 0x1f5a8:
return true;
case 0x1f5b1 ... 0x1f5b2:
return true;
case 0x1f5bc:
return true;
case 0x1f5c2 ... 0x1f5c4:
return true;
case 0x1f5d1 ... 0x1f5d3:
return true;
case 0x1f5dc ... 0x1f5de:
return true;
case 0x1f5e1:
return true;
case 0x1f5e3:
return true;
case 0x1f5e8:
return true;
case 0x1f5ef:
return true;
case 0x1f5f3:
return true;
case 0x1f5fa ... 0x1f64f:
return true;
case 0x1f680 ... 0x1f6c5:
return true;
case 0x1f6cb ... 0x1f6d2:
return true;
case 0x1f6e0 ... 0x1f6e5:
return true;
case 0x1f6e9:
return true;
case 0x1f6eb ... 0x1f6ec:
return true;
case 0x1f6f0:
return true;
case 0x1f6f3 ... 0x1f6f8:
return true;
case 0x1f910 ... 0x1f93a:
return true;
case 0x1f93c ... 0x1f93e:
return true;
case 0x1f940 ... 0x1f945:
return true;
case 0x1f947 ... 0x1f94c:
return true;
case 0x1f950 ... 0x1f96b:
return true;
case 0x1f980 ... 0x1f997:
return true;
case 0x1f9c0:
return true;
case 0x1f9d0 ... 0x1f9e6:
case 0xe0020 ... 0xe007f:
return true;
default: return false;
}

View File

@@ -189,15 +189,14 @@ end:
}
PyObject*
create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic) {
create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation) {
PyObject *ans = NULL;
FcPattern *pat = FcPatternCreate();
if (pat == NULL) return PyErr_NoMemory();
bool emoji = is_emoji(cell->ch);
AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)(emoji ? "emoji" : "monospace"), "family");
if (!emoji && bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
if (!emoji && italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
if (emoji) { AP(FcPatternAddBool, FC_COLOR, OPT(prefer_color_emoji), "color"); }
AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)(emoji_presentation ? "emoji" : "monospace"), "family");
if (!emoji_presentation && bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
if (!emoji_presentation && italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
if (emoji_presentation) { AP(FcPatternAddBool, FC_COLOR, true, "color"); }
size_t num = cell_as_unicode(cell, true, char_buf, ' ');
add_charset(pat, num);
PyObject *d = _fc_match(pat);

View File

@@ -41,6 +41,7 @@ struct SpritePosition {
#define SPECIAL_VALUE_MASK 2
#define EMPTY_FILLED_MASK 4
#define EMPTY_VALUE_MASK 8
#define SPECIAL_GLYPH_CACHE_SIZE 1024
struct SpecialGlyphCache {
SpecialGlyphCache *next;
@@ -63,8 +64,8 @@ typedef struct {
PyObject *face;
// Map glyphs to sprite map co-ords
SpritePosition sprite_map[1024];
SpecialGlyphCache special_glyph_cache[1024];
bool bold, italic;
SpecialGlyphCache special_glyph_cache[SPECIAL_GLYPH_CACHE_SIZE];
bool bold, italic, emoji_presentation;
} Font;
typedef struct {
@@ -128,7 +129,7 @@ extra_glyphs_equal(ExtraGlyphs *a, ExtraGlyphs *b) {
static SpritePosition*
sprite_position_for(Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, uint8_t ligature_index, int *error) {
glyph_index idx = glyph & 0x3ff;
glyph_index idx = glyph & (SPECIAL_GLYPH_CACHE_SIZE - 1);
SpritePosition *s = font->sprite_map + idx;
// Optimize for the common case of glyph under 1024 already in the cache
if (LIKELY(s->glyph == glyph && s->filled && extra_glyphs_equal(&s->extra_glyphs, extra_glyphs) && s->ligature_index == ligature_index)) return s; // Cache hit
@@ -156,15 +157,16 @@ sprite_position_for(Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, ui
}
static inline SpecialGlyphCache*
special_glyph_cache_for(Font *font, glyph_index glyph, uint8_t mask) {
special_glyph_cache_for(Font *font, glyph_index glyph, uint8_t filled_mask) {
SpecialGlyphCache *s = font->special_glyph_cache + (glyph & 0x3ff);
// Optimize for the common case of glyph under 1024 already in the cache
if (LIKELY(s->glyph == glyph && s->data & mask)) return s; // Cache hit
// Optimize for the common case of glyph under SPECIAL_GLYPH_CACHE_SIZE already in the cache
if (LIKELY(s->glyph == glyph && s->data & filled_mask)) return s; // Cache hit
while(true) {
if (s->data & mask) {
if (s->data & filled_mask) {
if (s->glyph == glyph) return s; // Cache hit
} else {
break;
if (!s->glyph) break; // Empty cache slot
else if (s->glyph == glyph) return s; // Cache slot that contains other data than the data indicated by filled_mask
}
if (!s->next) {
s->next = calloc(1, sizeof(SpecialGlyphCache));
@@ -248,12 +250,12 @@ desc_to_face(PyObject *desc) {
static inline bool
init_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face) {
init_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face, bool emoji_presentation) {
PyObject *face;
if (is_face) { face = descriptor; Py_INCREF(face); }
else { face = desc_to_face(descriptor); if (face == NULL) return false; }
f->face = face;
f->bold = bold; f->italic = italic;
f->bold = bold; f->italic = italic; f->emoji_presentation = emoji_presentation;
return true;
}
@@ -281,7 +283,7 @@ python_send_to_gpu(unsigned int x, unsigned int y, unsigned int z, pixel* buf) {
static inline PyObject*
update_cell_metrics() {
update_cell_metrics(bool on_dpi_change UNUSED) {
#define CALL(idx, desired_height, force) { if (idx >= 0) { Font *f = fonts.fonts + idx; if ((f)->face) { if(!set_size_for_face((f)->face, desired_height, force)) return NULL; } clear_sprite_map((f)); }}
CALL(BOX_FONT, 0, false); CALL(fonts.medium_font_idx, 0, false);
CALL(fonts.bold_font_idx, 0, false); CALL(fonts.italic_font_idx, 0, false); CALL(fonts.bi_font_idx, 0, false);
@@ -318,8 +320,9 @@ update_cell_metrics() {
static PyObject*
set_font_size(PyObject UNUSED *m, PyObject *args) {
if (!PyArg_ParseTuple(args, "f", &global_state.font_sz_in_pts)) return NULL;
return update_cell_metrics();
int on_dpi_change = 0;
if (!PyArg_ParseTuple(args, "f|p", &global_state.font_sz_in_pts, &on_dpi_change)) return NULL;
return update_cell_metrics(on_dpi_change != 0);
}
static inline bool
@@ -327,63 +330,77 @@ face_has_codepoint(PyObject* face, char_type cp) {
return glyph_id_for_codepoint(face, cp) > 0;
}
static inline bool
has_emoji_presentation(Cell *cell) {
return (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15;
}
static inline bool
has_cell_text(Font *self, Cell *cell) {
if (!face_has_codepoint(self->face, cell->ch)) return false;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
if (!face_has_codepoint(self->face, codepoint_for_mark(cell->cc_idx[i]))) return false;
combining_type cc_idx = cell->cc_idx[i];
if (cc_idx == VS15 || cc_idx == VS16) continue;
if (!face_has_codepoint(self->face, codepoint_for_mark(cc_idx))) return false;
}
return true;
}
static inline void
output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
printf("U+%x ", cell->ch);
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
}
if (bold) printf("bold ");
if (italic) printf("italic ");
if (emoji_presentation) printf("emoji_presentation ");
PyObject_Print(face, stdout, 0);
if (new_face) printf(" (new face)");
printf("\n");
}
static inline ssize_t
load_fallback_font(Cell *cell, bool bold, bool italic) {
if (fonts.fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return MISSING_FONT; }
load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation) {
if (fonts.fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; }
ssize_t f;
if (bold) f = fonts.italic_font_idx > 0 ? fonts.bi_font_idx : fonts.bold_font_idx;
else f = italic ? fonts.italic_font_idx : fonts.medium_font_idx;
if (f < 0) f = fonts.medium_font_idx;
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic);
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic, emoji_presentation);
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face, true);
set_size_for_face(face, cell_height, true);
ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true);
ssize_t ans = fonts.first_fallback_font_idx + fonts.fallback_fonts_count;
Font *af = &fonts.fonts[ans];
if (!init_font(af, face, bold, italic, true)) fatal("Out of memory");
if (!init_font(af, face, bold, italic, true, emoji_presentation)) fatal("Out of memory");
Py_DECREF(face);
fonts.fallback_fonts_count++;
fonts.fonts_count++;
return ans;
}
static inline ssize_t
fallback_font(Cell *cell) {
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
// Load the emoji fallback font first as on Linux there are a bunch of
// non-color fonts that provide some emoji glyphs.
if (fonts.fallback_fonts_count < 1) {
Cell c = {0};
c.ch = 0x1f648; // 🙈
load_fallback_font(&c, false, false);
}
bool emoji_presentation = has_emoji_presentation(cell);
// Check if one of the existing fallback fonts has this text
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
Font *ff = fonts.fonts +j;
if (ff->bold == bold && ff->italic == italic && 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 load_fallback_font(cell, bold, italic);
return load_fallback_font(cell, bold, italic, emoji_presentation);
}
static inline ssize_t
@@ -422,7 +439,7 @@ START_ALLOW_CASE_RANGE
ans = fonts.bi_font_idx; break;
}
if (ans < 0) ans = fonts.medium_font_idx;
if (has_cell_text(fonts.fonts + ans, cell)) return ans;
if (!has_emoji_presentation(cell) && has_cell_text(fonts.fonts + ans, cell)) return ans;
return fallback_font(cell);
}
END_ALLOW_CASE_RANGE
@@ -538,7 +555,7 @@ render_group(unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_gl
}
clear_canvas();
bool was_colored = is_emoji(cells->ch);
bool was_colored = (cells->attrs & WIDTH_MASK) == 2 && is_emoji(cells->ch);
render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, &was_colored);
if (PyErr_Occurred()) PyErr_Print();
@@ -748,7 +765,7 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
if (num_cells_consumed) {
if (num_cells_consumed > MAX_GLYPHS_IN_GROUP) {
// Nasty, a single glyph used more than MAX_GLYPHS_IN_GROUP cells, we cannot render this case correctly
fprintf(stderr, "The glyph: %u needs more than %u cells, cannot render it\n", glyph_id, MAX_GLYPHS_IN_GROUP);
log_error("The glyph: %u needs more than %u cells, cannot render it", glyph_id, MAX_GLYPHS_IN_GROUP);
current_group->num_glyphs--;
while (num_cells_consumed) {
G(group_idx)++; current_group = G(groups) + G(group_idx);
@@ -777,6 +794,17 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
#undef MAX_GLYPHS_IN_GROUP
}
static inline void
merge_groups_for_pua_space_ligature() {
if (G(group_idx) == 1) {
Group *g = G(groups), *g1 = G(groups) + 1;
g->num_cells += g1->num_cells;
g->num_glyphs += g1->num_glyphs;
g->num_glyphs = MIN(g->num_glyphs, MAX_NUM_EXTRA_GLYPHS + 1);
G(group_idx) = 0;
}
}
static inline void
render_groups(Font *font) {
unsigned idx = 0;
@@ -787,7 +815,10 @@ render_groups(Font *font) {
/* printf("Group: idx: %u num_cells: %u num_glyphs: %u first_glyph_idx: %u first_cell_idx: %u total_num_glyphs: %zu\n", */
/* idx, group->num_cells, group->num_glyphs, group->first_glyph_idx, group->first_cell_idx, group_state.num_glyphs); */
glyph_index primary = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;
for (unsigned i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) ed.data[i-1] = G(info)[group->first_glyph_idx + i].codepoint;
unsigned int i;
int last = -1;
for (i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) { last = i - 1; ed.data[last] = G(info)[group->first_glyph_idx + i].codepoint; }
if ((size_t)(last + 1) < arraysz(ed.data)) ed.data[last + 1] = 0;
render_group(group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed);
idx++;
}
@@ -830,10 +861,11 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
#undef G
static inline void
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) {
switch(font_idx) {
default:
shape_run(first_cell, num_cells, &fonts.fonts[font_idx]);
if (pua_space_ligature) merge_groups_for_pua_space_ligature();
render_groups(&fonts.fonts[font_idx]);
break;
case BLANK_FONT:
@@ -848,9 +880,14 @@ render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
}
}
static inline bool
is_private_use(char_type ch) {
return (0xe000 <= ch && ch <= 0xf8ff) || (0xF0000 <= ch && ch <= 0xFFFFF) || (0x100000 <= ch && ch <= 0x10FFFF);
}
void
render_line(Line *line) {
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx);
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false);
ssize_t run_font_idx = NO_FONT;
index_type first_cell_in_run, i;
attrs_type prev_width = 0;
@@ -858,6 +895,16 @@ render_line(Line *line) {
if (prev_width == 2) { prev_width = 0; continue; }
Cell *cell = line->cells + i;
ssize_t cell_font_idx = font_for_cell(cell);
if (is_private_use(cell->ch) && i + 1 < line->xnum && (line->cells[i+1].ch == ' ' || line->cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) {
// We have a private use char followed by a space char, render it as a two cell ligature.
RENDER;
render_run(line->cells + i, 2, cell_font_idx, true);
run_font_idx = NO_FONT;
first_cell_in_run = i + 2;
prev_width = line->cells[i+1].attrs & WIDTH_MASK;
i++;
continue;
}
prev_width = cell->attrs & WIDTH_MASK;
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
if (run_font_idx == cell_font_idx) continue;
@@ -880,7 +927,7 @@ set_font(PyObject UNUSED *m, PyObject *args) {
for (size_t i = 0; i < fonts.fonts_count; i++) del_font(fonts.fonts + i);
ensure_space_for(&fonts, fonts, Font, num_fonts, fonts_capacity, 5, true);
fonts.fonts_count = 1;
#define A(attr, bold, italic) { if(attr) { if (!init_font(&fonts.fonts[fonts.fonts_count], attr, bold, italic, false)) return NULL; fonts.attr##_font_idx = fonts.fonts_count++; } else fonts.attr##_font_idx = -1; }
#define A(attr, bold, italic) { if(attr) { if (!init_font(&fonts.fonts[fonts.fonts_count], attr, bold, italic, false, false)) return NULL; fonts.attr##_font_idx = fonts.fonts_count++; } else fonts.attr##_font_idx = -1; }
A(medium, false, false);
A(bold, true, false); A(italic, false, true); A(bi, true, true);
#undef A
@@ -892,7 +939,7 @@ set_font(PyObject UNUSED *m, PyObject *args) {
PyObject *face;
int bold, italic;
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, i), "Opp", &face, &bold, &italic)) return NULL;
if (!init_font(fonts.fonts + fonts.fonts_count++, face, bold != 0, italic != 0, false)) return NULL;
if (!init_font(fonts.fonts + fonts.fonts_count++, face, bold != 0, italic != 0, false, false)) return NULL;
}
for (size_t i = 0; i < fonts.symbol_maps_count; i++) {
unsigned int left, right, font_idx;
@@ -901,7 +948,7 @@ set_font(PyObject UNUSED *m, PyObject *args) {
}
fonts.first_fallback_font_idx = fonts.fonts_count;
fonts.fallback_fonts_count = 0;
return update_cell_metrics();
return update_cell_metrics(false);
}
static void
@@ -1019,7 +1066,7 @@ concat_cells(PyObject UNUSED *self, PyObject *args) {
}
static PyObject*
current_fonts(PyObject UNUSED *self) {
current_fonts(PYNOARG) {
PyObject *ans = PyDict_New();
if (!ans) return NULL;
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fonts.fonts[val].face) != 0) { goto error; }}
@@ -1050,7 +1097,7 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)];
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
cell.ch = char_buf[0];
for (unsigned i = 0; i + 1 < PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
ssize_t ans = fallback_font(&cell);

View File

@@ -21,7 +21,7 @@ hb_font_t* harfbuzz_font_for_face(PyObject*);
bool set_size_for_face(PyObject*, unsigned int, bool);
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored);
PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic);
PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation);
PyObject* specialize_font_descriptor(PyObject *base_descriptor);
PyObject* face_from_path(const char *path, int index);
PyObject* face_from_descriptor(PyObject*);

View File

@@ -3,10 +3,9 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import re
import sys
from kitty.fast_data_types import coretext_all_fonts
from kitty.utils import safe_print
from kitty.utils import log_error
attr_map = {(False, False): 'font_family',
(True, False): 'bold_font',
@@ -63,10 +62,7 @@ def find_best_match(family, bold=False, italic=False):
# Let CoreText choose the font if the family exists, otherwise
# fallback to Menlo
if q not in font_map['family_map']:
safe_print(
'The font {} was not found, falling back to Menlo'.format(family),
file=sys.stderr
)
log_error('The font {} was not found, falling back to Menlo'.format(family))
family = 'Menlo'
return {
'monospace': True,

View File

@@ -10,7 +10,7 @@ from math import ceil, floor, pi, sin, sqrt
from kitty.config import defaults
from kitty.constants import is_macos
from kitty.fast_data_types import (
Screen, change_wcwidth, get_fallback_font, send_prerendered_sprites,
Screen, get_fallback_font, send_prerendered_sprites,
set_font, set_font_size, set_logical_dpi, set_options,
set_send_sprite_to_gpu, sprite_map_set_limits, test_render_line,
test_shape
@@ -61,9 +61,9 @@ def set_font_family(opts=None, override_font_size=None):
return cell_width, cell_height
def resize_fonts(new_sz):
def resize_fonts(new_sz, on_dpi_change=False):
s = set_font_family.state
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(new_sz)
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(new_sz, on_dpi_change)
set_font_family.state = FontState(
s.family, new_sz, cell_width, cell_height, baseline,
underline_position, underline_thickness
@@ -260,7 +260,6 @@ def test_fallback_font(qtext=None, bold=False, italic=False):
def showcase():
change_wcwidth(True)
f = 'monospace' if is_macos else 'Liberation Mono'
test_render_string('He\u0347\u0305llo\u0337, w\u0302or\u0306l\u0354d!', family=f)
test_render_string('你好,世界', family=f)

View File

@@ -279,7 +279,7 @@ calc_cell_width(Face *self) {
for (char_type i = 32; i < 128; i++) {
int glyph_index = FT_Get_Char_Index(self->face, i);
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
ans = MAX(ans, (unsigned long)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
ans = MAX(ans, (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
}
}
return ans;
@@ -324,7 +324,6 @@ typedef struct {
unsigned int factor, right_edge;
} ProcessedBitmap;
static inline void
trim_borders(ProcessedBitmap *ans, size_t extra) {
bool column_has_text = false;
@@ -354,10 +353,14 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
ans->rows = bitmap->rows;
ans->pixel_mode = bitmap->pixel_mode;
if (ans->width > max_width) {
size_t extra = bitmap->width - max_width;
size_t extra = ans->width - max_width;
if (italic && extra < cell_width / 2) {
trim_borders(ans, extra);
} else if (rescale && self->is_scalable && extra > MAX(2, cell_width / 4)) {
} else if (extra == 2 && num_cells == 1) {
// there exist fonts that have bitmaps just a couple of pixels
// wider than their advances, rather than rescale, which looks
// bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352
} else if (rescale && self->is_scalable && extra > 1) {
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
float ar = (float)max_width / (float)bitmap->width;
if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi, 0)) {
@@ -532,7 +535,8 @@ render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *inf
}
static PyObject*
display_name(Face *self) {
display_name(PyObject *s, PyObject *a UNUSED) {
Face *self = (Face*)s;
const char *psname = FT_Get_Postscript_Name(self->face);
if (psname) return Py_BuildValue("s", psname);
Py_INCREF(self->path);
@@ -540,8 +544,8 @@ display_name(Face *self) {
}
static PyObject*
extra_data(Face *self) {
return PyLong_FromVoidPtr(self->extra_data);
extra_data(PyObject *self, PyObject *a UNUSED) {
return PyLong_FromVoidPtr(((Face*)self)->extra_data);
}
// Boilerplate {{{

View File

@@ -117,7 +117,7 @@ compile_shader(GLenum shader_type, const char *source) {
if (ret != GL_TRUE) {
GLsizei len;
glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);
fprintf(stderr, "Failed to compile GLSL shader!\n%s", glbuf);
log_error("Failed to compile GLSL shader!\n%s", glbuf);
glDeleteShader(shader_id);
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
return 0;

8
kitty/glfw-wrapper.h generated
View File

@@ -1216,7 +1216,8 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**);
* This is the function signature for monitor configuration callback functions.
*
* @param[in] monitor The monitor that was connected or disconnected.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining
* values reserved for future use.
*
* @sa @ref monitor_event
* @sa @ref glfwSetMonitorCallback
@@ -1233,7 +1234,8 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int);
* functions.
*
* @param[in] jid The joystick that was connected or disconnected.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining
* values reserved for future use.
*
* @sa @ref joystick_event
* @sa @ref glfwSetJoystickCallback
@@ -1583,7 +1585,7 @@ typedef void (*glfwRequestWindowAttention_func)(GLFWwindow*);
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
typedef int (*glfwWindowBell_func)(GLFWwindow*, int64_t);
typedef int (*glfwWindowBell_func)(GLFWwindow*);
glfwWindowBell_func glfwWindowBell_impl;
#define glfwWindowBell glfwWindowBell_impl

View File

@@ -9,6 +9,7 @@
#include "glfw-wrapper.h"
extern bool cocoa_make_window_resizable(void *w);
extern void cocoa_create_global_menu(void);
extern void cocoa_set_titlebar_color(void *w);
#if GLFW_KEY_LAST >= MAX_KEY_COUNT
#error "glfw has too many keys, you should increase MAX_KEY_COUNT"
@@ -27,10 +28,13 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) {
bool dpi_changed = (xr != 0.0 && xr != window->viewport_x_ratio) || (yr != 0.0 && yr != window->viewport_y_ratio);
window->viewport_size_dirty = true;
window->has_pending_resizes = false;
window->viewport_width = MAX(window->viewport_width, 100);
window->viewport_height = MAX(window->viewport_height, 100);
window->window_width = MAX(w, 100);
window->window_height = MAX(h, 100);
if (notify_boss) {
call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False);
}
window->last_resize_at = monotonic();
}
@@ -77,14 +81,17 @@ static void
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
if (!set_callback_window(w)) return;
if (width > 100 && height > 100) {
double now = monotonic();
OSWindow *window = global_state.callback_os_window;
if (now - window->last_resize_at < RESIZE_DEBOUNCE_TIME) { window->has_pending_resizes = true; global_state.has_pending_resizes = true; }
else {
update_os_window_viewport(global_state.callback_os_window, is_window_ready_for_callbacks());
glfwPostEmptyEvent();
}
} else fprintf(stderr, "Ignoring resize request for tiny size: %dx%d\n", width, height);
window->has_pending_resizes = true; global_state.has_pending_resizes = true;
window->last_resize_event_at = monotonic();
} else log_error("Ignoring resize request for tiny size: %dx%d", width, height);
global_state.callback_os_window = NULL;
}
static void
refresh_callback(GLFWwindow *w) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->is_damaged = true;
global_state.callback_os_window = NULL;
}
@@ -291,14 +298,14 @@ set_dpi_from_os_window(OSWindow *w) {
global_state.logical_dpi_y = yscale * factor;
}
static bool is_first_window = true;
static PyObject*
create_os_window(PyObject UNUSED *self, PyObject *args) {
int width, height, swap_interval = 0, x = -1, y = -1;
int width, height, x = -1, y = -1;
char *title, *wm_class_class, *wm_class_name;
PyObject *load_programs = NULL;
if (!PyArg_ParseTuple(args, "iisss|Oiii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &swap_interval, &x, &y)) return NULL;
bool is_first_window = standard_cursor == NULL;
if (!PyArg_ParseTuple(args, "iisss|Oiii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
if (is_first_window) {
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
@@ -335,14 +342,14 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
if (glfw_window == NULL) {
fprintf(stderr, "Failed to create a window at size: %dx%d, using a standard size instead.\n", width, height);
log_error("Failed to create a window at size: %dx%d, using a standard size instead.", width, height);
glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
}
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
glfwMakeContextCurrent(glfw_window);
if (x != -1 && y != -1) glfwSetWindowPos(glfw_window, x, y);
current_os_window_ctx = glfw_window;
glfwSwapInterval(swap_interval); // a value of 1 makes mouse selection laggy
glfwSwapInterval(OPT(sync_to_monitor) ? 1 : 0); // a value of 1 makes mouse selection laggy
if (is_first_window) {
set_dpi_from_os_window(NULL);
gl_init();
@@ -352,6 +359,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
#ifdef __APPLE__
cocoa_create_global_menu();
#endif
is_first_window = false;
}
OSWindow *w = add_os_window();
@@ -366,6 +374,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
glfwSetCursor(glfw_window, standard_cursor);
update_os_window_viewport(w, false);
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
glfwSetCharModsCallback(glfw_window, char_mods_callback);
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
glfwSetScrollCallback(glfw_window, scroll_callback);
@@ -376,8 +385,9 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
#ifdef __APPLE__
if (OPT(macos_hide_titlebar)) {
if (glfwGetCocoaWindow) { if (!cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window))) { PyErr_Print(); } }
else fprintf(stderr, "Failed to load glfwGetCocoaWindow\n");
else log_error("Failed to load glfwGetCocoaWindow");
}
cocoa_set_titlebar_color(glfwGetCocoaWindow(glfw_window));
#endif
double now = monotonic();
w->is_focused = true;
@@ -387,7 +397,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
if (want_semi_transparent && !w->is_semi_transparent) {
static bool warned = false;
if (!warned) {
fprintf(stderr, "Failed to enable transparency. This happens when your desktop environment does not support compositing.\n");
log_error("Failed to enable transparency. This happens when your desktop environment does not support compositing.");
warned = true;
}
}
@@ -452,7 +462,7 @@ destroy_os_window(OSWindow *w) {
// Global functions {{{
static void
error_callback(int error, const char* description) {
fprintf(stderr, "[glfw error %d]: %s\n", error, description);
log_error("[glfw error %d]: %s", error, description);
}
@@ -473,7 +483,7 @@ glfw_init(PyObject UNUSED *self, PyObject *args) {
}
PyObject*
glfw_terminate(PyObject UNUSED *self) {
glfw_terminate(PYNOARG) {
glfwTerminate();
Py_RETURN_NONE;
}
@@ -491,13 +501,13 @@ glfw_wait_events(PyObject UNUSED *self, PyObject *args) {
}
PyObject*
glfw_post_empty_event(PyObject UNUSED *self) {
glfw_post_empty_event(PYNOARG) {
glfwPostEmptyEvent();
Py_RETURN_NONE;
}
PyObject*
glfw_poll_events(PyObject UNUSED *self) {
glfw_poll_events(PYNOARG) {
glfwPollEvents();
Py_RETURN_NONE;
}
@@ -515,7 +525,7 @@ get_physical_dpi(GLFWmonitor *m) {
}
PyObject*
glfw_get_physical_dpi(PyObject UNUSED *self) {
glfw_get_physical_dpi(PYNOARG) {
GLFWmonitor *m = glfwGetPrimaryMonitor();
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
return get_physical_dpi(m);
@@ -540,14 +550,14 @@ glfw_window_hint(PyObject UNUSED *self, PyObject *args) {
// }}}
static PyObject*
get_clipboard_string(PyObject UNUSED *self) {
get_clipboard_string(PYNOARG) {
OSWindow *w = current_os_window();
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
return Py_BuildValue("s", "");
}
static PyObject*
get_content_scale_for_window(PyObject UNUSED *self) {
get_content_scale_for_window(PYNOARG) {
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
float xscale, yscale;
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
@@ -564,7 +574,7 @@ set_clipboard_string(PyObject UNUSED *self, PyObject *args) {
}
static PyObject*
toggle_fullscreen(PyObject UNUSED *self) {
toggle_fullscreen(PYNOARG) {
GLFWmonitor *monitor;
OSWindow *w = current_os_window();
if (!w) Py_RETURN_NONE;
@@ -594,7 +604,7 @@ ring_audio_bell(OSWindow *w) {
if (now - last_bell_at <= 0.1) return;
last_bell_at = now;
if (w->handle) {
glfwWindowBell(w->handle, OPT(x11_bell_volume));
glfwWindowBell(w->handle);
}
}
@@ -654,14 +664,14 @@ should_os_window_close(OSWindow* w) {
}
static PyObject*
primary_monitor_size(PyObject UNUSED *self) {
primary_monitor_size(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
return Py_BuildValue("ii", mode->width, mode->height);
}
static PyObject*
primary_monitor_content_scale(PyObject UNUSED *self) {
primary_monitor_content_scale(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
float xscale, yscale;
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
@@ -669,10 +679,10 @@ primary_monitor_content_scale(PyObject UNUSED *self) {
}
static PyObject*
x11_display(PyObject UNUSED *self) {
x11_display(PYNOARG) {
if (glfwGetX11Display) {
return PyLong_FromVoidPtr(glfwGetX11Display());
} else fprintf(stderr, "Failed to load glfwGetX11Display\n");
} else log_error("Failed to load glfwGetX11Display");
Py_RETURN_NONE;
}
@@ -691,10 +701,10 @@ x11_window_id(PyObject UNUSED *self, PyObject *os_wid) {
}
static PyObject*
get_primary_selection(PyObject UNUSED *self) {
get_primary_selection(PYNOARG) {
if (glfwGetX11SelectionString) {
return Py_BuildValue("y", glfwGetX11SelectionString());
} else fprintf(stderr, "Failed to load glfwGetX11SelectionString\n");
} else log_error("Failed to load glfwGetX11SelectionString");
Py_RETURN_NONE;
}
@@ -703,7 +713,7 @@ set_primary_selection(PyObject UNUSED *self, PyObject *args) {
char *text;
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
else fprintf(stderr, "Failed to load glfwSetX11SelectionString\n");
else log_error("Failed to load glfwSetX11SelectionString");
Py_RETURN_NONE;
}

View File

@@ -21,7 +21,7 @@ PyTypeObject GraphicsManager_Type;
#define STORAGE_LIMIT (320 * (1024 * 1024))
#define REPORT_ERROR(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
#define REPORT_ERROR(...) { log_error(__VA_ARGS__); }
static bool send_to_gpu = true;
@@ -509,7 +509,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
static inline const char*
create_add_response(GraphicsManager UNUSED *self, bool data_loaded, uint32_t iid) {
static char rbuf[sizeof(add_response)/sizeof(add_response[0])];
static char rbuf[sizeof(add_response)/sizeof(add_response[0]) + 64];
if (iid) {
if (!has_add_respose) {
if (!data_loaded) return NULL;

View File

@@ -161,7 +161,7 @@ __str__(HistoryBuf *self) {
if (lines == NULL) return PyErr_NoMemory();
for (index_type i = 0; i < self->count; i++) {
init_line(self, index_of(self, i), self->line);
PyObject *t = PyObject_Str((PyObject*)self->line);
PyObject *t = line_as_unicode(self->line);
if (t == NULL) { Py_CLEAR(lines); return NULL; }
PyTuple_SET_ITEM(lines, i, t);
}
@@ -202,8 +202,20 @@ as_ansi(HistoryBuf *self, PyObject *callback) {
Py_RETURN_NONE;
}
static inline Line*
get_line(HistoryBuf *self, index_type y, Line *l) { init_line(self, y, l); return l; }
static PyObject*
dirty_lines(HistoryBuf *self) {
as_text(HistoryBuf *self, PyObject *args) {
Line l = {.xnum=self->xnum};
#define gl(self, y) get_line(self, y, &l);
as_text_generic(args, self, gl, self->count, self->xnum, callback, as_ansi);
#undef gl
}
static PyObject*
dirty_lines(HistoryBuf *self, PyObject *a UNUSED) {
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
PyObject *ans = PyList_New(0);
for (index_type i = 0; i < self->ynum; i++) {
@@ -223,6 +235,7 @@ static PyMethodDef methods[] = {
METHOD(change_num_of_lines, METH_O)
METHOD(line, METH_O)
METHOD(as_ansi, METH_O)
METHODB(as_text, METH_VARARGS),
METHOD(dirty_lines, METH_NOARGS)
METHOD(push, METH_VARARGS)
METHOD(rewrap, METH_VARARGS)
@@ -279,8 +292,9 @@ void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
return;
}
other->count = 0; other->start_of_data = 0;
index_type x = 0, y = 0;
if (self->count > 0) {
rewrap_inner(self, other, self->count, NULL);
rewrap_inner(self, other, self->count, NULL, &x, &y);
for (index_type i = 0; i < other->count; i++) other->line_attrs[(other->start_of_data + i) % other->ynum] |= TEXT_DIRTY_MASK;
}
}

View File

@@ -2,13 +2,13 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import array
import codecs
import fcntl
import mimetypes
import os
import re
import signal
import struct
import subprocess
import sys
import termios
@@ -18,8 +18,8 @@ from collections import namedtuple
from math import ceil, floor
from tempfile import NamedTemporaryFile
from kitty.constants import appname
from kitty.cli import parse_args
from kitty.constants import appname
from kitty.utils import read_with_timeout
try:
@@ -105,9 +105,9 @@ Size = namedtuple('Size', 'rows cols width height')
def screen_size():
if screen_size.changed:
s = struct.pack('HHHH', 0, 0, 0, 0)
x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
screen_size.ans = Size(*struct.unpack('HHHH', x))
buf = array.array('H', [0, 0, 0, 0])
fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)
screen_size.ans = Size(*buf)
screen_size.changed = False
return screen_size.ans

24
kitty/key_encoding.py generated
View File

@@ -3,6 +3,7 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import string
from collections import namedtuple
from . import fast_data_types as defines
@@ -302,3 +303,26 @@ def update_encoding():
f.seek(0), f.truncate()
f.write(nraw)
subprocess.check_call(['yapf', '-i', __file__])
PRESS, REPEAT, RELEASE = 1, 2, 4
SHIFT, ALT, CTRL, SUPER = 1, 2, 4, 8
KeyEvent = namedtuple('KeyEvent', 'type mods key')
type_map = {'p': PRESS, 't': REPEAT, 'r': RELEASE}
mod_map = {c: i for i, c in enumerate('ABCDEFGHIJKLMNOP')}
key_rmap = {}
g = globals()
for key_name, enc in ENCODING.items():
key_name = key_name.replace(' ', '_')
g[key_name] = key_name
key_rmap[enc] = key_name
del key_name, enc, g
enter_key = KeyEvent(PRESS, 0, ENCODING['ENTER'])
backspace_key = KeyEvent(PRESS, 0, ENCODING['BACKSPACE'])
def decode_key_event(text):
typ = type_map[text[1]]
mods = mod_map[text[2]]
key = key_rmap[text[3:5]]
return KeyEvent(typ, mods, key)

View File

@@ -11,6 +11,8 @@
#include "glfw-wrapper.h"
#include "control-codes.h"
static bool needs_special_handling[128 * 16] = {0};
const char*
key_to_bytes(int glfw_key, bool smkx, bool extended, int mods, int action) {
if ((action & 3) == 3) return NULL;
@@ -139,8 +141,10 @@ send_key_to_child(Window *w, int key, int mods, int action) {
Screen *screen = w->render_data.screen;
const char *data = key_to_bytes(key, screen->modes.mDECCKM, screen->modes.mEXTENDED_KEYBOARD, mods, action);
if (data) {
if (screen->modes.mEXTENDED_KEYBOARD) write_escape_code_to_child(screen, APC, data + 1);
else {
if (screen->modes.mEXTENDED_KEYBOARD) {
if (*data == 1) schedule_write_to_child(w->id, (data + 1), 1);
else write_escape_code_to_child(screen, APC, data + 1);
} else {
if (*data > 2 && data[1] == 0x1b && data[2] == '[') { // CSI code
write_escape_code_to_child(screen, CSI, data + 3);
} else schedule_write_to_child(w->id, (data + 1), *data);

719
kitty/keys.h generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,13 +30,13 @@ alt_codes = {
defines.GLFW_KEY_TAB: b'\033\t',
defines.GLFW_KEY_ENTER: b'\033\r',
defines.GLFW_KEY_ESCAPE: b'\033\033',
# alt+bs matches iTerm and gnome-terminal
defines.GLFW_KEY_BACKSPACE: b'\x17'
defines.GLFW_KEY_BACKSPACE: b'\033\177'
}
shift_alt_codes = alt_codes.copy()
shift_alt_codes[defines.GLFW_KEY_TAB] = key_as_bytes('kcbt')
alt_mods = (defines.GLFW_MOD_ALT, defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT)
ctrl_shift_mod = defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL
ctrl_alt_mod = defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL
for kf, kn in {
defines.GLFW_KEY_UP: 'kcuu1',
@@ -128,13 +128,17 @@ action_map = {
def extended_key_event(key, mods, action):
if key >= defines.GLFW_KEY_LAST or key == defines.GLFW_KEY_UNKNOWN or (
# Shifted printable key should be handled by on_text_input()
mods == defines.GLFW_MOD_SHIFT and 32 <= key <= 126
mods <= defines.GLFW_MOD_SHIFT and 32 <= key <= 126
):
return b''
if mods == 0 and key in (
defines.GLFW_KEY_BACKSPACE, defines.GLFW_KEY_ENTER
):
if action == defines.GLFW_RELEASE:
return b''
return smkx_key_map[key]
if key in (defines.GLFW_KEY_LEFT_SHIFT, defines.GLFW_KEY_RIGHT_SHIFT):
return b''
name = KEY_MAP.get(key)
if name is None:
return b''
@@ -201,6 +205,7 @@ ASCII_C0_SHIFTED = {
'SLASH': b'\x7f',
}
CTRL_SHIFT_KEYS = {getattr(defines, 'GLFW_KEY_' + k): v for k, v in ASCII_C0_SHIFTED.items()}
CTRL_ALT_KEYS = {getattr(defines, 'GLFW_KEY_' + k) for k in string.ascii_uppercase}
def key_to_bytes(key, smkx, extended, mods, action):
@@ -219,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
data.append(0o33)
data.extend(m[key])
elif mods == ctrl_alt_mod and key in CTRL_ALT_KEYS:
data.append(0x1b), data.extend(control_codes[key])
else:
key_map = cursor_key_mode_map[smkx]
x = key_map.get(key)
@@ -256,7 +263,6 @@ def generate_key_table():
w('#include <stdint.h>')
w('#include <stdbool.h>')
w('#include <limits.h>')
w('static bool needs_special_handling[%d] = {0};' % (128 * 16))
number_of_keys = defines.GLFW_KEY_LAST + 1
w('// map glfw key numbers to 7-bit numbers for compact data storage')
w('static const uint8_t key_map[%d] = {' % number_of_keys)
@@ -330,13 +336,15 @@ def generate_key_table():
ind('return NULL;')
i -= 2
i -= 1
ind('} // end switch(mods)\n')
ind('} // end switch(mods)')
ind('break;\n')
i -= 1
else:
ind('return NULL;\n')
i -= 1
i -= 1
ind('}} // end switch(action) in mode {}\n\n'.format(mode))
ind('}} // end switch(action) in mode {}'.format(mode))
ind('break;\n\n')
i -= 1
i -= 1
ind('}')

115
kitty/kittens.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* kittens.c
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "data-types.h"
static PyObject*
parse_input_from_terminal(PyObject *self UNUSED, PyObject *args) {
enum State { NORMAL, ESC, CSI, ST, ESC_ST };
enum State state = NORMAL;
PyObject *uo, *text_callback, *dcs_callback, *csi_callback, *osc_callback, *pm_callback, *apc_callback, *callback;
int inbp = 0;
if (!PyArg_ParseTuple(args, "OOOOOOUp", &text_callback, &dcs_callback, &csi_callback, &osc_callback, &pm_callback, &apc_callback, &uo, &inbp)) return NULL;
Py_ssize_t sz = PyUnicode_GET_LENGTH(uo), pos = 0, start = 0, count = 0, consumed = 0;
callback = text_callback;
int kind = PyUnicode_KIND(uo);
void *data = PyUnicode_DATA(uo);
bool in_bracketed_paste_mode = inbp != 0;
#define CALL(cb, s_, num_) {\
PyObject *fcb = cb; \
Py_ssize_t s = s_, num = num_; \
if (in_bracketed_paste_mode && fcb != text_callback) { \
fcb = text_callback; num += 2; s -= 2; \
} \
if (num > 0) { \
PyObject *ret = PyObject_CallFunction(fcb, "N", PyUnicode_Substring(uo, s, s + num)); \
if (ret == NULL) return NULL; \
Py_DECREF(ret); \
} \
consumed = s_ + num_; \
count = 0; \
}
START_ALLOW_CASE_RANGE;
while (pos < sz) {
Py_UCS4 ch = PyUnicode_READ(kind, data, pos);
switch(state) {
case NORMAL:
if (ch == 0x1b) {
state = ESC;
CALL(text_callback, start, count);
start = pos;
} else count++;
break;
case ESC:
start = pos;
count = 0;
switch(ch) {
case 'P':
state = ST; callback = dcs_callback; break;
case '[':
state = CSI; callback = csi_callback; break;
case ']':
state = ST; callback = osc_callback; break;
case '^':
state = ST; callback = pm_callback; break;
case '_':
state = ST; callback = apc_callback; break;
default:
state = NORMAL; break;
}
break;
case CSI:
count++;
switch (ch) {
case 'a' ... 'z':
case 'A' ... 'Z':
case '@':
case '`':
case '{':
case '|':
case '}':
case '~':
#define IBP(w) ch == '~' && PyUnicode_READ(kind, data, start + 1) == '2' && PyUnicode_READ(kind, data, start + 2) == '0' && PyUnicode_READ(kind, data, start + 3) == w
if (IBP('1')) in_bracketed_paste_mode = false;
CALL(callback, start + 1, count);
if (IBP('0')) in_bracketed_paste_mode = true;
#undef IBP
state = NORMAL;
start = pos + 1;
break;
}
break;
case ESC_ST:
if (ch == '\\') {
CALL(callback, start + 1, count);
state = NORMAL; start = pos + 1;
consumed += 2;
} else count += 2;
break;
case ST:
if (ch == 0x1b) { state = ESC_ST; }
else count++;
break;
}
pos++;
}
if (state == NORMAL && count > 0) CALL(text_callback, start, count);
return PyUnicode_Substring(uo, consumed, sz);
END_ALLOW_CASE_RANGE;
#undef CALL
}
static PyMethodDef module_methods[] = {
METHODB(parse_input_from_terminal, METH_VARARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
};
bool
init_kittens(PyObject *module) {
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
return true;
}

View File

@@ -1,5 +1,6 @@
# vim:fileencoding=utf-8:ft=conf
# Fonts {{{
# Font family. You can also specify different fonts for the
# bold/italic/bold-italic variants. By default they are derived automatically,
# by the OSes font system. Setting them manually is useful for font families
@@ -11,6 +12,7 @@
#
# You can get a list of full family names available on your computer by running
# kitty list-fonts
# The default values shown below rely on your OS to choose an appropriate monospace font family.
font_family monospace
italic_font auto
bold_font auto
@@ -32,12 +34,189 @@ font_size_delta 2
adjust_line_height 0
adjust_column_width 0
# Symbol mapping (special font for specified unicode code points). Map the
# specified unicode codepoints to a particular font. Useful if you need special
# rendering for some symbols, such as for Powerline. Avoids the need for
# patched fonts. Each unicode code point is specified in the form U+<code point
# in hexadecimal>. You can specify multiple code points, separated by commas
# and ranges separated by hyphens. symbol_map itself can be specified multiple times.
# Syntax is:
#
# symbol_map codepoints Font Family Name
#
# For example:
#
# symbol_map U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols
# Change the sizes of the lines used for the box drawing unicode characters
# These values are in pts. They will be scaled by the monitor DPI to arrive at
# a pixel value. There must be four values corresponding to thin, normal, thick,
# and very thick lines;
box_drawing_scale 0.001, 1, 1.5, 2
# }}}
# Cursor customization {{{
# The cursor color
cursor #cccccc
# The cursor shape can be one of (block, beam, underline)
cursor_shape block
# The interval (in seconds) at which to blink the cursor. Set to zero to
# disable blinking. Note that numbers smaller than repaint_delay will be
# limited to repaint_delay.
cursor_blink_interval 0.5
# Stop blinking cursor after the specified number of seconds of keyboard inactivity. Set to
# zero to never stop blinking.
cursor_stop_blinking_after 15.0
# }}}
# Scrollback {{{
# Number of lines of history to keep in memory for scrolling back
scrollback_lines 2000
# Program with which to view scrollback in a new window. The scrollback buffer is passed as
# STDIN to this program. If you change it, make sure the program you use can
# handle ANSI escape sequences for colors and text formatting.
scrollback_pager less +G -R
# Wheel scroll multiplier (modify the amount scrolled by the mouse wheel). Use negative
# numbers to change scroll direction.
wheel_scroll_multiplier 5.0
# }}}
# Mouse {{{
# The color and style for highlighting URLs on mouse-over. url_style can be one of:
# none, single, double, curly
url_color #0087BD
url_style curly
# The modifier keys to press when clicking with the mouse on URLs to open the URL
open_url_modifiers 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
foreground #dddddd
@@ -62,162 +241,12 @@ selection_foreground #000000
# The background for selections
selection_background #FFFACD
# The color and style for highlighting URLs on mouse-over. url_style can be one of:
# none, single, double, curly
url_color #0087BD
url_style curly
# The cursor color
cursor #cccccc
# The cursor shape can be one of (block, beam, underline)
cursor_shape block
# The interval (in seconds) at which to blink the cursor. Set to zero to
# disable blinking. Note that numbers smaller than repaint_delay will be
# limited to repaint_delay.
cursor_blink_interval 0.5
# Stop blinking cursor after the specified number of seconds of keyboard inactivity. Set to
# zero to never stop blinking.
cursor_stop_blinking_after 15.0
# Number of lines of history to keep in memory for scrolling back
scrollback_lines 2000
# Program with which to view scrollback in a new window. The scrollback buffer is passed as
# STDIN to this program. If you change it, make sure the program you use can
# handle ANSI escape sequences for colors and text formatting.
scrollback_pager less +G -R
# When viewing scrollback in a new window, put it in a new tab as well
scrollback_in_new_tab no
# 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.
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
# 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
# Choose whether to use the system implementation of wcwidth() (used to
# control how many cells a character is rendered in). If you use the system
# implementation, then kitty and any programs running in it will agree. The
# problem is that system implementations often are based on outdated unicode
# standards and get the width of many characters, such as emoji, wrong. So if
# you are using kitty with programs that have their own up-to-date wcwidth()
# implementation, set this option to no, otherwise set it to yes.
use_system_wcwidth no
# The value of the TERM environment variable to set
term xterm-kitty
# The width (in pts) of window borders. Will be rounded to the nearest number of pixels based on screen resolution.
# Note that borders are displayed only when more than one window is visible. They are meant to separate multiple windows.
window_border_width 1
# The window margin (in pts) (blank area outside the border)
window_margin_width 0
# The window padding (in pts) (blank area between the text and the window border)
window_padding_width 0
# The color for the border of the active window
active_border_color #00ff00
# The color for the border of inactive windows
inactive_border_color #cccccc
# Fade the text in inactive windows by the specified amount (a number between
# zero and one, with 0 being fully faded).
inactive_text_alpha 1.0
# Which edge to show the tab bar on, top or bottom
tab_bar_edge bottom
# The separator between tabs in the tab bar
tab_separator " ┇"
# Tab bar colors and styles
active_tab_foreground #000
active_tab_background #eee
active_tab_font_style bold-italic
inactive_tab_foreground #444
inactive_tab_background #999
inactive_tab_font_style normal
# The 16 terminal colors. There are 8 basic colors, each color has a dull and
# bright version.
# black
color0 #000000
color8 #4d4d4d
color8 #767676
# red
color1 #cc0403
@@ -246,9 +275,42 @@ color14 #14ffff
# white
color7 #dddddd
color15 #ffffff
# }}}
# Key mapping
# Advanced {{{
# The shell program to execute. The default value of . means
# to use whatever shell is set as the default shell for the current user.
# Note that on macOS if you change this, you might need to add --login to
# ensure that the shell starts in interactive mode and reads its startup rc files.
shell .
# Close the window when the child process (shell) exits. If no (the default),
# the terminal will remain open when the child exits as long as there are still
# processes outputting to the terminal (for example disowned or backgrounded
# processes). If yes, the window will close as soon as the child process exits.
# Note that setting it to yes means that any background processes still using
# the terminal can fail silently because their stdout/stderr/stdin no longer
# work.
close_on_child_death no
# Allow other programs to control kitty. If you turn this on other programs can
# control all aspects of kitty, including sending text to kitty windows,
# opening new windows, closing windows, reading the content of windows, etc.
# Note that this even works over ssh connections.
allow_remote_control no
# The value of the TERM environment variable to set. Changing this can break
# many terminal programs, only change it if you know what you are doing, not
# because you read some advice on Stack Overflow to change it.
term xterm-kitty
# }}}
# Keyboard shortcuts {{{
# For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html
# For a list of modifier names, see: http://www.glfw.org/docs/latest/group__mods.html
#
@@ -262,7 +324,7 @@ color15 #ffffff
# map ctrl+shift+e combine : new_window : next_layout
# this will create a new window and switch to the next available layout
# Clipboard
# Clipboard {{{
map ctrl+shift+v paste_from_clipboard
map ctrl+shift+s paste_from_selection
map ctrl+shift+c copy_to_clipboard
@@ -272,8 +334,9 @@ map shift+insert paste_from_selection
# you can specify your own, for example:
# map ctrl+shift+o pass_selection_to_program firefox
map ctrl+shift+o pass_selection_to_program
# }}}
# Scrolling
# Scrolling {{{
map ctrl+shift+up scroll_line_up
map ctrl+shift+down scroll_line_down
map ctrl+shift+k scroll_line_up
@@ -283,8 +346,9 @@ map ctrl+shift+page_down scroll_page_down
map ctrl+shift+home scroll_home
map ctrl+shift+end scroll_end
map ctrl+shift+h show_scrollback
# }}}
# Window management
# Window management {{{
map ctrl+shift+enter new_window
map ctrl+shift+n new_os_window
map ctrl+shift+w close_window
@@ -310,16 +374,17 @@ map ctrl+shift+0 tenth_window
# map ctrl+shift+y new_window less @selection
#
# You can even send the contents of the current screen + history buffer as stdin using
# the placeholders @text (which is the plain text) and @ansi (which includes text styling escape codes)
# the placeholders @text (which is the plain text) and @ansi (which includes text styling escape codes).
# For only the current screen, use @screen or @ansi_screen.
# For example, the following command opens the scrollback buffer in less in a new window.
# map ctrl+shift+y new_window @ansi less +G -R
#
# You can open a new window with the current working directory set to the
# working directory of the current window using
# map ctrl+alt+enter new_window_with_cwd
# }}}
# Tab management
# Tab management {{{
map ctrl+shift+right next_tab
map ctrl+shift+left previous_tab
map ctrl+shift+t new_tab
@@ -327,19 +392,39 @@ map ctrl+shift+q close_tab
map ctrl+shift+l next_layout
map ctrl+shift+. move_tab_forward
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
# map ctrl+alt+1 goto_tab 1
# map ctrl+alt+2 goto_tab 2
# Just as with new_window above, you can also pass the name of arbitrary
# commands to run when using new_tab and use new_tab_with_cwd.
# }}}
# Layout management {{{
# You can create shortcuts to switch to specific layouts
# map ctrl+alt+1 goto_layout tall
# map ctrl+alt+2 goto_layout stack
# }}}
# Miscellaneous
# Font sizes {{{
map ctrl+shift+equal increase_font_size
map ctrl+shift+minus decrease_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
# }}}
# Miscellaneous {{{
map ctrl+shift+f11 toggle_fullscreen
map ctrl+shift+u input_unicode_character
map ctrl+shift+f2 edit_config_file
# Open a currently visible URL using the keyboard. The program used to open the URL is specified in open_url_with.
# You can customize how the URLs are detected and opened by specifying command line options to
# url_hints. For example:
# map ctrl+shift+e run_simple_kitten text url_hints --program firefox --regex "http://[^ ]+"
map ctrl+shift+e run_simple_kitten text url_hints
# Sending arbitrary text on shortcut key presses
# You can tell kitty to send arbitrary (UTF-8) encoded text to
@@ -357,30 +442,18 @@ map ctrl+shift+f11 toggle_fullscreen
# to the start of the line (same as pressing the Home key):
# map ctrl+alt+a send_text normal Word\x1b[H
# map ctrl+alt+a send_text application Word\x1bOH
# }}}
# Symbol mapping (special font for specified unicode code points). Map the
# specified unicode codepoints to a particular font. Useful if you need special
# rendering for some symbols, such as for Powerline. Avoids the need for
# patched fonts. Each unicode code point is specified in the form U+<code point
# in hexadecimal>. You can specify multiple code points, separated by commas
# and ranges separated by hyphens. symbol_map itself can be specified multiple times.
# Syntax is:
#
# symbol_map codepoints Font Family Name
#
# For example:
#
# symbol_map U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols
# }}}
# 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"
# means to use the default system color, a value of "background" means to use
# the default background color and finally you can use an arbitrary color, such
# as #12af59 or "red".
macos_titlebar_color system
# Hide the kitty window's title bar on macOS.
macos_hide_titlebar no
@@ -390,13 +463,4 @@ macos_hide_titlebar no
# break any Alt+key keyboard shortcuts in your terminal programs, but you
# can use the macOS unicode input technique.
macos_option_as_alt yes
# The number is a percentage of maximum volume.
# See man XBell for details.
x11_bell_volume 80
# Prefer color emoji fonts when available. Note that this only works
# on systems such as Linux that use fontconfig. On other OSes, the emoji
# font used is system dependent. It can be overriden using symbol_map in the kitty
# configuration.
prefer_color_emoji yes
# }}}

View File

@@ -1,11 +1,11 @@
#!/usr/bin/env python3
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
# Launch kitty from source
import os
import sys
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
base = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.insert(0, base)
with open(os.path.join(base, '__main__.py')) as f:
src = f.read()

View File

@@ -3,15 +3,24 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from collections import namedtuple
from functools import partial
from itertools import islice
from .constants import WindowGeometry
from .fast_data_types import pt_to_px, viewport_for_window, Region
from .fast_data_types import (
Region, pt_to_px, set_active_window, swap_windows, viewport_for_window
)
central = Region((0, 0, 199, 199, 200, 200))
cell_width = cell_height = 20
def idx_for_id(win_id, windows):
for i, w in enumerate(windows):
if w.id == win_id:
return i
def layout_dimension(start_at, length, cell_length, number_of_windows=1, border_length=0, margin_length=0, padding_length=0, left_align=False):
number_of_cells = length // cell_length
border_length += padding_length
@@ -40,13 +49,24 @@ def layout_dimension(start_at, length, cell_length, number_of_windows=1, border_
Rect = namedtuple('Rect', 'left top right bottom')
def process_overlaid_windows(all_windows):
id_map = {w.id: w for w in all_windows}
overlaid_windows = frozenset(w for w in all_windows if w.overlay_window_id is not None and w.overlay_window_id in id_map)
windows = [w for w in all_windows if w not in overlaid_windows]
return overlaid_windows, windows
class Layout:
name = None
needs_window_borders = True
only_active_window_visible = False
def __init__(self, os_window_id, opts, border_width, windows):
def __init__(self, os_window_id, tab_id, opts, border_width):
self.os_window_id = os_window_id
self.tab_id = tab_id
self.set_active_window_in_os_window = partial(set_active_window, os_window_id, tab_id)
self.swap_windows_in_os_window = partial(swap_windows, os_window_id, tab_id)
self.opts = opts
self.border_width = border_width
self.margin_width = pt_to_px(opts.window_margin_width)
@@ -55,24 +75,128 @@ class Layout:
# this layout, i.e. spaces that are not covered by any window
self.blank_rects = ()
def next_window(self, windows, active_window_idx, delta=1):
active_window_idx = (active_window_idx + len(windows) + delta) % len(windows)
self.set_active_window(windows, active_window_idx)
def nth_window(self, all_windows, num):
windows = process_overlaid_windows(all_windows)[1]
w = windows[min(num, len(windows) - 1)]
active_window_idx = idx_for_id(w.id, all_windows)
return self.set_active_window(all_windows, active_window_idx)
def next_window(self, all_windows, active_window_idx, delta=1):
w = all_windows[active_window_idx]
windows = process_overlaid_windows(all_windows)[1]
idx = idx_for_id(w.id, windows)
if idx is None:
idx = idx_for_id(w.overlay_window_id, windows)
active_window_idx = (idx + len(windows) + delta) % len(windows)
active_window_idx = idx_for_id(windows[active_window_idx].id, all_windows)
return self.set_active_window(all_windows, active_window_idx)
def move_window(self, all_windows, active_window_idx, delta=1):
w = all_windows[active_window_idx]
windows = process_overlaid_windows(all_windows)[1]
if len(windows) < 2 or abs(delta) == 0:
return active_window_idx
idx = idx_for_id(w.id, windows)
if idx is None:
idx = idx_for_id(w.overlay_window_id, windows)
nidx = (idx + len(windows) + delta) % len(windows)
nw = windows[nidx]
nidx = idx_for_id(nw.id, all_windows)
idx = active_window_idx
all_windows[nidx], all_windows[idx] = all_windows[idx], all_windows[nidx]
self.swap_windows_in_os_window(nidx, idx)
return self.set_active_window(all_windows, nidx)
def add_window(self, all_windows, window, current_active_window_idx):
active_window_idx = None
if window.overlay_for is not None:
i = idx_for_id(window.overlay_for, all_windows)
if i is not None:
# put the overlay window in the position occupied by the
# overlaid window and move the overlaid window to the end
self.swap_windows_in_os_window(len(all_windows), i)
all_windows.append(all_windows[i])
all_windows[i] = window
active_window_idx = i
if active_window_idx is None:
active_window_idx = len(all_windows)
all_windows.append(window)
self(all_windows, active_window_idx)
self.set_active_window_in_os_window(active_window_idx)
return active_window_idx
def add_window(self, windows, window, active_window_idx):
active_window_idx = len(windows)
windows.append(window)
self(windows, active_window_idx)
def remove_window(self, all_windows, window, current_active_window_idx, swapped=False):
try:
active_window = all_windows[current_active_window_idx]
except Exception:
active_window = window
if not swapped and window.overlay_for is not None:
nidx = idx_for_id(window.overlay_for, all_windows)
if nidx is not None:
idx = all_windows.index(window)
all_windows[nidx], all_windows[idx] = all_windows[idx], all_windows[nidx]
self.swap_windows_in_os_window(nidx, idx)
return self.remove_window(all_windows, window, current_active_window_idx, swapped=True)
position = all_windows.index(window)
del all_windows[position]
active_window_idx = None
if window.overlay_for is not None:
i = idx_for_id(window.overlay_for, all_windows)
if i is not None:
overlaid_window = all_windows[i]
overlaid_window.overlay_window_id = None
if active_window is window:
active_window = overlaid_window
active_window_idx = idx_for_id(active_window.id, all_windows)
if active_window_idx is None:
if active_window is window:
active_window_idx = max(0, min(current_active_window_idx, len(all_windows) - 1))
else:
active_window_idx = idx_for_id(active_window.id, all_windows)
if all_windows:
self(all_windows, active_window_idx)
self.set_active_window(all_windows, active_window_idx)
return active_window_idx
def remove_window(self, windows, window, active_window_idx):
windows.remove(window)
active_window_idx = max(0, min(active_window_idx, len(windows) - 1))
if windows:
self(windows, active_window_idx)
def update_visibility(self, all_windows, active_window, overlaid_windows=None):
if overlaid_windows is None:
overlaid_windows = process_overlaid_windows(all_windows)[0]
for i, w in enumerate(all_windows):
w.set_visible_in_layout(i, w is active_window or (not self.only_active_window_visible and w not in overlaid_windows))
def set_active_window(self, all_windows, active_window_idx):
if not all_windows:
self.set_active_window_in_os_window(0)
return 0
w = all_windows[active_window_idx]
if w.overlay_window_id is not None:
i = idx_for_id(w.overlay_window_id, all_windows)
if i is not None:
active_window_idx = i
self.update_visibility(all_windows, all_windows[active_window_idx])
self.set_active_window_in_os_window(active_window_idx)
return active_window_idx
def __call__(self, all_windows, active_window_idx):
global central, cell_width, cell_height
central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id)
active_window = all_windows[active_window_idx]
overlaid_windows, windows = process_overlaid_windows(all_windows)
if overlaid_windows:
windows = [w for w in all_windows if w not in overlaid_windows]
active_window_idx = idx_for_id(active_window.id, windows)
if active_window_idx is None:
active_window_idx = idx_for_id(active_window.overlay_window_id, windows) or 0
active_window = windows[active_window_idx]
else:
windows = all_windows
self.update_visibility(all_windows, active_window, overlaid_windows)
self.do_layout(windows, active_window_idx)
return idx_for_id(active_window.id, all_windows)
# Utils {{{
def xlayout(self, num):
return layout_dimension(
central.left, central.width, cell_width, num, self.border_width,
@@ -92,14 +216,7 @@ class Layout:
def bottom_blank_rect(self, window):
self.blank_rects.append(Rect(window.geometry.left, window.geometry.bottom, window.geometry.right, central.bottom + 1))
def set_active_window(self, windows, active_window_idx):
pass
def __call__(self, windows, active_window_idx):
global central, cell_width, cell_height
central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id)
self.do_layout(windows, active_window_idx)
# }}}
def do_layout(self, windows, active_window_idx):
raise NotImplementedError()
@@ -149,16 +266,12 @@ class Stack(Layout):
name = 'stack'
needs_window_borders = False
def set_active_window(self, windows, active_window_idx):
for i, w in enumerate(windows):
w.set_visible_in_layout(i, i == active_window_idx)
only_active_window_visible = True
def do_layout(self, windows, active_window_idx):
self.blank_rects = []
wg = layout_single_window(self.margin_width, self.padding_width)
for i, w in enumerate(windows):
w.set_visible_in_layout(i, i == active_window_idx)
w.set_geometry(i, wg)
if w.is_visible_in_layout:
self.blank_rects = blank_rects_for_window(w)
@@ -183,6 +296,8 @@ class Tall(Layout):
ylayout = self.ylayout(len(windows) - 1)
for i, (w, (ystart, ynum)) in enumerate(zip(islice(windows, 1, None), ylayout)):
w.set_geometry(i + 1, window_geometry(xstart, xnum, ystart, ynum))
# right bottom blank rect
self.bottom_blank_rect(windows[i + 1])
# left, top and right blank rects
self.simple_blank_rects(windows[0], windows[-1])
@@ -190,8 +305,6 @@ class Tall(Layout):
self.between_blank_rect(windows[0], windows[1])
# left bottom blank rect
self.bottom_blank_rect(windows[0])
# right bottom blank rect
self.bottom_blank_rect(windows[-1])
class Grid(Tall):
@@ -262,11 +375,11 @@ class Vertical(Layout):
for i in range(window_count):
ystart, ynum = next(ylayout)
windows[i].set_geometry(i, window_geometry(xstart, xnum, ystart, ynum))
# bottom blank rect
self.bottom_blank_rect(windows[i])
# left, top and right blank rects
self.simple_blank_rects(windows[0], windows[-1])
# bottom blank rect
self.bottom_blank_rect(windows[-1])
class Horizontal(Layout):

View File

@@ -46,7 +46,7 @@ linebuf_mark_line_clean(LineBuf *self, index_type y) {
}
static PyObject*
clear(LineBuf *self) {
clear(LineBuf *self, PyObject *a UNUSED) {
#define clear_doc "Clear all lines in this LineBuf"
linebuf_clear(self, BLANK_CHAR);
Py_RETURN_NONE;
@@ -167,7 +167,7 @@ set_continued(LineBuf *self, PyObject *args) {
}
static PyObject*
dirty_lines(LineBuf *self) {
dirty_lines(LineBuf *self, PyObject *a UNUSED) {
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
PyObject *ans = PyList_New(0);
for (index_type i = 0; i < self->ynum; i++) {
@@ -406,13 +406,25 @@ as_ansi(LineBuf *self, PyObject *callback) {
Py_RETURN_NONE;
}
static inline Line*
get_line(LineBuf *self, index_type y) {
linebuf_init_line(self, y);
return self->line;
}
static PyObject*
as_text(LineBuf *self, PyObject *args) {
as_text_generic(args, self, get_line, self->ynum, self->xnum, callback, as_ansi);
}
static PyObject*
__str__(LineBuf *self) {
PyObject *lines = PyTuple_New(self->ynum);
if (lines == NULL) return PyErr_NoMemory();
for (index_type i = 0; i < self->ynum; i++) {
init_line(self, self->line, self->line_map[i]);
PyObject *t = PyObject_Str((PyObject*)self->line);
PyObject *t = line_as_unicode(self->line);
if (t == NULL) { Py_CLEAR(lines); return NULL; }
PyTuple_SET_ITEM(lines, i, t);
}
@@ -440,6 +452,7 @@ static PyMethodDef methods[] = {
METHOD(rewrap, METH_VARARGS)
METHOD(clear, METH_NOARGS)
METHOD(as_ansi, METH_O)
METHODB(as_text, METH_VARARGS),
METHOD(set_attribute, METH_VARARGS)
METHOD(set_continued, METH_VARARGS)
METHOD(dirty_lines, METH_NOARGS)
@@ -494,7 +507,7 @@ copy_old(LineBuf *self, PyObject *y) {
#include "rewrap.h"
void
linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_before, index_type *num_content_lines_after, HistoryBuf *historybuf) {
linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_before, index_type *num_content_lines_after, HistoryBuf *historybuf, index_type *track_x, index_type *track_y) {
index_type first, i;
bool is_empty = true;
@@ -523,7 +536,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo
return;
}
rewrap_inner(self, other, first + 1, historybuf);
rewrap_inner(self, other, first + 1, historybuf, track_x, track_y);
*num_content_lines_after = other->line->ynum + 1;
for (i = 0; i < *num_content_lines_after; i++) other->line_attrs[i] |= TEXT_DIRTY_MASK;
*num_content_lines_before = first + 1;
@@ -536,7 +549,8 @@ rewrap(LineBuf *self, PyObject *args) {
unsigned int nclb, ncla;
if (!PyArg_ParseTuple(args, "O!O!", &LineBuf_Type, &other, &HistoryBuf_Type, &historybuf)) return NULL;
linebuf_rewrap(self, other, &nclb, &ncla, historybuf);
index_type x = 0, y = 0;
linebuf_rewrap(self, other, &nclb, &ncla, historybuf, &x, &y);
return Py_BuildValue("II", nclb, ncla);
}

View File

@@ -199,8 +199,8 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
}
static PyObject *
as_unicode(Line* self) {
PyObject *
line_as_unicode(Line* self) {
return unicode_in_range(self, 0, xlimit_for_line(self), true, 0);
}
@@ -263,7 +263,7 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) {
}
static PyObject*
as_ansi(Line* self) {
as_ansi(Line* self, PyObject *a UNUSED) {
#define as_ansi_doc "Return the line's contents with ANSI (SGR) escape codes for formatting"
static Py_UCS4 t[5120] = {0};
index_type num = line_as_ansi(self, t, 5120);
@@ -272,7 +272,7 @@ as_ansi(Line* self) {
}
static PyObject*
is_continued(Line* self) {
is_continued(Line* self, PyObject *a UNUSED) {
#define is_continued_doc "Return the line's continued flag"
PyObject *ans = self->continued ? Py_True : Py_False;
Py_INCREF(ans);
@@ -281,7 +281,7 @@ is_continued(Line* self) {
static PyObject*
__repr__(Line* self) {
PyObject *s = as_unicode(self);
PyObject *s = line_as_unicode(self);
if (s == NULL) return NULL;
PyObject *ans = PyObject_Repr(s);
Py_CLEAR(s);
@@ -570,7 +570,7 @@ PyTypeObject Line_Type = {
.tp_basicsize = sizeof(Line),
.tp_dealloc = (destructor)dealloc,
.tp_repr = (reprfunc)__repr__,
.tp_str = (reprfunc)as_unicode,
.tp_str = (reprfunc)line_as_unicode,
.tp_as_sequence = &sequence_methods,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_richcompare = richcmp,

View File

@@ -44,6 +44,7 @@ xlimit_for_line(Line *line) {
return xlimit;
}
void line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch);
void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, bool clear_char);
void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *, bool);
@@ -56,17 +57,17 @@ 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_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* line_as_unicode(Line *);
void linebuf_init_line(LineBuf *, index_type);
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_reverse_index(LineBuf *self, index_type top, index_type bottom);
void linebuf_clear_line(LineBuf *self, index_type y);
void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom);
void linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom);
void linebuf_set_attribute(LineBuf *, unsigned int , unsigned int );
void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *);
void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *, index_type *, index_type *);
void linebuf_mark_line_dirty(LineBuf *self, index_type y);
void linebuf_mark_line_clean(LineBuf *self, index_type y);
unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y);
@@ -79,3 +80,39 @@ void historybuf_mark_line_clean(HistoryBuf *self, index_type y);
void historybuf_mark_line_dirty(HistoryBuf *self, index_type y);
void historybuf_refresh_sprite_positions(HistoryBuf *self);
void historybuf_clear(HistoryBuf *self);
#define as_text_generic(args, container, get_line, lines, columns, callback, as_ansi) { \
PyObject *callback; \
int as_ansi = 0; \
if (!PyArg_ParseTuple(args, "O|p", &callback, &as_ansi)) return NULL; \
PyObject *ret = NULL, *t = NULL; \
Py_UCS4 *buf = NULL; \
PyObject *nl = PyUnicode_FromString("\n"); \
if (nl == NULL) goto end; \
if (as_ansi) { \
buf = malloc(sizeof(Py_UCS4) * columns * 100); \
if (buf == NULL) { PyErr_NoMemory(); goto end; } \
} \
for (index_type y = 0; y < lines; y++) { \
Line *line = get_line(container, y); \
if (!line->continued && y > 0) { \
ret = PyObject_CallFunctionObjArgs(callback, nl, NULL); \
if (ret == NULL) goto end; \
Py_CLEAR(ret); \
} \
if (as_ansi) { \
index_type num = line_as_ansi(line, buf, columns * 100 - 2); \
t = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, num); \
} else { \
t = line_as_unicode(line); \
} \
if (t == NULL) goto end; \
ret = PyObject_CallFunctionObjArgs(callback, t, NULL); \
Py_DECREF(t); if (ret == NULL) goto end; Py_DECREF(ret); \
} \
end: \
Py_CLEAR(nl); free(buf); \
if (PyErr_Occurred()) return NULL; \
Py_RETURN_NONE; \
}

73
kitty/logging.c Normal file
View 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;
}

Some files were not shown because too many files have changed in this diff Show More