Compare commits

...

416 Commits

Author SHA1 Message Date
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
Kovid Goyal
9f3dfc27da version 0.7.1 2018-01-31 10:37:58 +05:30
Kovid Goyal
4b4f6bc928 Update CHANGELOG 2018-01-31 10:34:54 +05:30
Kovid Goyal
273735f9dc ... 2018-01-31 10:14:08 +05:30
Kovid Goyal
b95e0fb80e Shorten build/run commands 2018-01-30 15:59:31 +05:30
Kovid Goyal
6ad21fc98f Update bundled glfw 2018-01-29 12:30:05 +05:30
Kovid Goyal
bff9b03935 ... 2018-01-28 10:18:02 +05:30
Kovid Goyal
edf9413356 Add a setting to adjust the width of cells, similar to the existing setting to adjust the height of cells 2018-01-28 10:16:59 +05:30
Kovid Goyal
c74f4a8165 Fix extra columns cut-off from the right edge of italic characters that are too wide to fit in a cell 2018-01-28 10:06:39 +05:30
Kovid Goyal
8201cd3288 Add a Makefile target for building kitty-profile 2018-01-26 21:35:48 +05:30
Kovid Goyal
1603310b86 Fix off-by-one when iterating over selections that stretch into the history buf
Fixes #295
2018-01-25 20:50:34 +05:30
Kovid Goyal
627b91ffba version 0.7.0 2018-01-24 15:33:16 +05:30
Kovid Goyal
9ad65f2e1d Cleanup layouts patch
Fix bottom rect in horizontal layout not covering all windows. Fix pep8
issues. Add README and CHANGELOG entries.
2018-01-23 19:35:30 +05:30
Guybrush Threepwood
a6a606ff3d Added vertical and horizontal layouts 2018-01-23 19:30:41 +05:30
Kovid Goyal
63317f9c78 macOS: Fix alt+arrow keys not working when disabling the macos_option_as_alt config option. 2018-01-21 15:41:56 +05:30
Kovid Goyal
46d9dfb614 ... 2018-01-20 17:33:55 +05:30
Kovid Goyal
be3b33af06 Update CHANGELOG 2018-01-19 17:08:21 +05:30
Kovid Goyal
04e1b8b1ae tab bar blank rects dont extend down enough 2018-01-19 17:05:18 +05:30
Kovid Goyal
213768b6e0 Option to place tab bar at the top edge
Fixes #288
2018-01-19 17:04:58 +05:30
Kovid Goyal
aff1abdb26 Fix top line of hollow block cursor 1px too high 2018-01-19 16:23:47 +05:30
Kovid Goyal
44a413df81 Fix blank rects on the sides of the tab bar not being drawn 2018-01-19 16:02:07 +05:30
Kovid Goyal
4f12c91fff Use the new region function everywhere
Centralizes window layout calculation and no longer assumes
tab bar geometry
2018-01-19 15:56:07 +05:30
Kovid Goyal
b6188bf436 Move window region calculations into one place 2018-01-19 11:31:44 +05:30
Kovid Goyal
b9a14e0e4a A Makefile target for building kitty.app 2018-01-19 10:42:12 +05:30
Kovid Goyal
6882c23f43 Update CHANGELOG 2018-01-18 16:26:33 +05:30
Kovid Goyal
80301d465b Handle non-BMP combining characters
Use a level of indirection to store combining characters. This allows
combining characters to be stored using only two bytes, even if they are
after USHORT_MAX
2018-01-18 16:25:42 +05:30
Kovid Goyal
32632264ee Mapping that can be used to store unicode mark symbols in only two bytes 2018-01-18 16:06:07 +05:30
Kovid Goyal
409bd37db5 Store combining chars as an array in the Cell
Makes it easier to increase the number of combining chars if needed,
rather than storing them as a mask.
2018-01-18 14:10:58 +05:30
Kovid Goyal
5fd4bc965f Annotate Cell structure
Make it clear what parts are needed only on the CPU
2018-01-18 12:47:49 +05:30
Kovid Goyal
e86e5ce1d8 ... 2018-01-18 00:15:39 +05:30
Kovid Goyal
5faa649452 Drop the dependency on libunistring 2018-01-18 00:09:40 +05:30
Kovid Goyal
2ddc7e8c80 Have all marks return a wcwidth of -1
Mirrors recent change to onsidering all marks combining chars instead of
only the marks with a non-zero combining class
2018-01-17 23:25:59 +05:30
Kovid Goyal
ed700ff830 ... 2018-01-17 21:59:10 +05:30
Kovid Goyal
33ed873997 Remove unnecessary extra test for combinig characters
There are not combining characters with a non-zero combining class that
are not in the marks category.
2018-01-17 21:56:30 +05:30
Kovid Goyal
9125c00f96 Update CHANGELOG 2018-01-17 21:50:00 +05:30
Kovid Goyal
73fe3293be Fix copying from selection being restricted to only the visible part of the selection. Fixes #287 2018-01-17 21:39:24 +05:30
Kovid Goyal
804c4fbe19 Recognize characters from the unicode Mark categories as combining characters, even if they do not have a combining class (i.e. are not re-ordered). Fixes #286 2018-01-15 11:24:11 +05:30
Kovid Goyal
d6c82ba21c oops 2018-01-14 11:18:44 +05:30
Kovid Goyal
67308f2ca1 Add some documentation for the shaping algorithm 2018-01-14 11:18:44 +05:30
Kovid Goyal
4a17a946fb Ensure groups dont have too many cells 2018-01-14 11:18:44 +05:30
Kovid Goyal
1ae7ae4a1d Fix consecutive separate ligatures being rendered in the same group
Rendering multiple ligatures in the smae group is bad for performance
and also causes incorrect rendering if the last ligature in the group
does not fit into the number of cells in the group.
2018-01-14 11:18:44 +05:30
Kovid Goyal
404ca23de3 Make handling of last glyph a little more intelligent 2018-01-14 11:18:44 +05:30
Kovid Goyal
d385952fef Add a bounds check 2018-01-14 11:18:44 +05:30
Kovid Goyal
f03cac8a9d Allow storing more flags in the special glyph cache 2018-01-14 11:18:44 +05:30
Kovid Goyal
4b501fd694 Fix rendering of ligatures with ligature fonts that dont use dummy glyphs 2018-01-14 11:18:43 +05:30
Kovid Goyal
872d39eb2c Have the ctrl+shift+<key> generate the ASCII C0 control codes for the few shifted control codes there are.
See #285
2018-01-14 11:18:30 +05:30
Kovid Goyal
0341cae922 Fix unused function warning 2018-01-13 15:49:44 +05:30
Kovid Goyal
5357a9a1cc Merge branch 'us/master' of https://github.com/koekeishiya/kitty 2018-01-13 07:33:30 +05:30
koekeishiya
3695538a11 fix mac os_window focus issue 2018-01-12 20:24:11 +01:00
Kovid Goyal
7780d52930 DRYer 2018-01-12 11:27:23 +05:30
Kovid Goyal
6a23b4ee47 Allow kitty @xxx in addition to kitty @ xxx 2018-01-12 11:23:34 +05:30
Kovid Goyal
f48b770036 macOS: Fix focus not being transferred on OSWindow close
Fixes #280
2018-01-12 08:05:12 +05:30
Kovid Goyal
81b5d2c4f2 Option to copy to clipboard on mouse select 2018-01-12 07:33:21 +05:30
Kovid Goyal
d43d6ab0a9 Update bundled glfw 2018-01-12 05:34:53 +05:30
Kovid Goyal
5a4e883360 Merge branch 'macos-logo-make' of https://github.com/zchee/kitty 2018-01-11 22:31:18 +05:30
Koichi Shiraishi
eb93cd4048 Add missing 64 size because doesn't create icon_32x32@2x.png 2018-01-12 00:59:19 +09:00
Koichi Shiraishi
d83e3fddb0 Add install 'optipng' description because logo/make.py depends 'optipng' 2018-01-12 00:58:02 +09:00
Kovid Goyal
80ca25e879 ... 2018-01-11 16:03:23 +05:30
Kovid Goyal
c86b34aacd Move to_color into config_utils 2018-01-11 16:02:52 +05:30
Kovid Goyal
8da426039b Make the config parsing code more re-useable 2018-01-11 12:17:18 +05:30
Kovid Goyal
9184e9a28f Update CHANGELOG 2018-01-11 10:46:50 +05:30
Kovid Goyal
578d537d5f kitty icat: Fix some PNG images not being displayed
Work around for bug in ImageMagick where it converts images
into rgba data with fewer rows than specified. Fixes #276
2018-01-11 09:56:40 +05:30
Kovid Goyal
236c92b2b4 No longer use a shell script for the wrapper .app 2018-01-11 08:40:35 +05:30
Kovid Goyal
66a46279f4 ... 2018-01-11 00:26:29 +05:30
Kovid Goyal
eb35edbe20 kitty icat: Fix typo that broke the SIGWNCH signal handler 2018-01-11 00:24:26 +05:30
Kovid Goyal
7cf2404331 Update CHANGELOG 2018-01-10 15:29:58 +05:30
Kovid Goyal
9ccc6bf835 Fix incorrect handling of zero extra pixel offset 2018-01-10 13:54:51 +05:30
Kovid Goyal
025a46bb35 Fix the in cell offset calculations 2018-01-10 13:50:44 +05:30
Kovid Goyal
677e8a1ae9 Make the signal handler a little simpler 2018-01-10 13:37:43 +05:30
Kovid Goyal
03702772d3 kitty icat: Add a --scale-up option to expand images when used with the --place operator 2018-01-10 13:32:05 +05:30
Kovid Goyal
3cd744b247 Implement @focus-window 2018-01-10 13:23:04 +05:30
Kovid Goyal
88523b2692 Add a note about @ls to the tutorial 2018-01-10 13:00:59 +05:30
Kovid Goyal
dcb39eb210 ... 2018-01-10 12:57:15 +05:30
Kovid Goyal
102823809b Add a tutorial for controlling kitty from scripts 2018-01-10 12:54:21 +05:30
Kovid Goyal
23f2b29069 Add --keep-focus for @new-window 2018-01-10 12:36:15 +05:30
Kovid Goyal
7b33a87725 @send-text allow sending from files and stdin 2018-01-10 10:37:43 +05:30
Kovid Goyal
6dde573ed9 Fix __name__ != __main__ when using runpy in the osx-bundle 2018-01-10 08:31:22 +05:30
Kovid Goyal
566e641be1 Allow easily building a kitty.app wrapper to run kitty from source 2018-01-10 07:44:16 +05:30
Kovid Goyal
08d5448523 Move creation of Info.plist and kitty.icns into osx-bundle 2018-01-10 06:58:57 +05:30
Kovid Goyal
c11a20f1ec Add --new-tab to @new-window 2018-01-09 23:09:57 +05:30
Kovid Goyal
e5b9bd2e5a Implement @get-text 2018-01-09 22:48:24 +05:30
Kovid Goyal
6ad49bd7fb Add an option to override the default shell 2018-01-09 16:28:46 +05:30
Kovid Goyal
5d082d8d5a Implement @new-window 2018-01-09 16:13:28 +05:30
Kovid Goyal
977575da73 Implement @close-tab 2018-01-09 15:30:46 +05:30
Kovid Goyal
bced6f31b1 Implement @close-window 2018-01-09 15:27:12 +05:30
Kovid Goyal
6bf04e22cc Fix send_text parsing choking on ''' 2018-01-09 13:10:28 +05:30
Kovid Goyal
2f9784809d Chunk up overly large send text inputs 2018-01-09 02:51:22 +05:30
Kovid Goyal
3e85497c2c Implement @set-tab-title 2018-01-08 16:13:38 +05:30
Kovid Goyal
3b551a1dee Implement @set-window-title 2018-01-08 14:04:27 +05:30
Kovid Goyal
d2fdb1c1eb DRYer 2018-01-08 13:57:43 +05:30
Kovid Goyal
db92af68e9 Fix a couple of typos 2018-01-08 13:54:08 +05:30
Kovid Goyal
815087460c ... 2018-01-08 13:25:25 +05:30
Kovid Goyal
b4cb9d7c4d ... 2018-01-08 13:07:47 +05:30
Kovid Goyal
c6d10c6a23 Use the launch time argv for cmdline on macOS 2018-01-08 13:07:06 +05:30
Kovid Goyal
464c7a80e2 Implement window matching for remote control 2018-01-08 13:01:57 +05:30
Kovid Goyal
4f56cce727 Fix client eating up send-text when seding to same window 2018-01-08 12:48:15 +05:30
Kovid Goyal
7dea6e0ef4 Initial implementation of send-text remote cmd 2018-01-08 12:48:15 +05:30
Kovid Goyal
8c143db381 Add documentation for the ls command 2018-01-08 12:48:15 +05:30
Kovid Goyal
0cea6d5a1b Implement window listing 2018-01-08 12:48:15 +05:30
Kovid Goyal
eff881f4bb Allow sending arbitrary sized escpae codes to children 2018-01-08 12:48:14 +05:30
Kovid Goyal
c64fd1760c More work on remote control 2018-01-08 12:48:14 +05:30
Kovid Goyal
9e361700d2 Handle reading of RC response via stdin 2018-01-08 12:48:14 +05:30
Kovid Goyal
1f976644bf Refactor read with timeout logic into its own function 2018-01-08 12:48:14 +05:30
Kovid Goyal
f3cb68ee40 Framework for remote control 2018-01-08 12:48:14 +05:30
Kovid Goyal
85a3da057f Session file: Allow setting the title for windows 2018-01-08 12:47:47 +05:30
Kovid Goyal
c551384369 Session file: Allow specifying multiple cd directives per tab to have different windows launched with different working directories 2018-01-08 12:38:01 +05:30
Kovid Goyal
ce230b071b Add tests for SGR protocol encoding 2018-01-07 23:48:16 +05:30
Kovid Goyal
13d5f04ad9 A spot of refactoring 2018-01-07 21:26:28 +05:30
Daniels Kursits
3b861de3d2 Fix mouse move events being reported as drag events
when using the SGR mouse reporting protocol

Fixes #271
2018-01-07 21:12:50 +05:30
Kovid Goyal
1ef010689d Add support for clearing the scrollback buffer
kitty now supports using \E3J to clear the screen and scrollback buffer.
See #268
2018-01-06 23:55:49 +05:30
Kovid Goyal
3523ab283f More trailing whitespace 2018-01-06 09:12:23 +05:30
Kovid Goyal
5b2a9b101d Fix trailing whitespace 2018-01-05 21:27:24 +05:30
Kovid Goyal
fdcfcfa1b9 Make alt+backspace delete the previous word
i.e. alt+backspace now sends ^W (werase). THis matches the behavior of
terminal.app and gnome-terminal. Fixes #264
2018-01-05 21:08:42 +05:30
Kovid Goyal
88e9c21a3b Ensure a kitty executable is on PATH even when running from source 2018-01-05 14:56:01 +05:30
Kovid Goyal
973352a98d Allow + prefixing of special invocations 2018-01-05 14:36:31 +05:30
Kovid Goyal
f06f871dfc Handle fork() failures more gracefully 2018-01-05 13:57:49 +05:30
Kovid Goyal
e03c713294 Remove a few more signal unsafe function calls between fork() and exec() 2018-01-05 00:24:15 +05:30
Kovid Goyal
aa107956f9 ... 2018-01-04 23:55:03 +05:30
Kovid Goyal
d725578e1b Silence spurious compiler warning 2018-01-04 23:54:35 +05:30
Kovid Goyal
239eb8202b Move fork()+exec() of child processes into C
By avoiding python in the child process before exec we ensure that
malloc and other unsafe to use after fork functions are not used.
Should also mean that less pages will need to be copied into thec hild
process, leading to marginally faster startups.
2018-01-04 23:19:09 +05:30
Kovid Goyal
58d7439719 Add a note on installing dev packages for X11 on linux
Fixes #260
2018-01-04 18:06:11 +05:30
Kovid Goyal
cc145ec3dd DRYer 2018-01-04 12:38:35 +05:30
Kovid Goyal
847ce37160 Take the mouse wheel multiplier into account when generating keypresses for mouse wheel events. Fixes #262 2018-01-04 12:34:31 +05:30
Kovid Goyal
0305009766 Remove --without-cairo from brew install since it is ignored anyway 2018-01-03 09:22:23 +05:30
Kovid Goyal
fe1b056371 Allow using macOS specific modifier key names in the config file
cmd and option can now be used instead of super and alt respectively.
2018-01-03 08:59:36 +05:30
Kovid Goyal
0c4804db79 DRYer 2017-12-31 09:04:22 +05:30
Kovid Goyal
f7e84df332 Add an explicit OpenGL version test
Apparently there are some pre 3.3 drivers that include the
texture_storage extension. Fixes #258
2017-12-31 08:36:50 +05:30
Kovid Goyal
bd654d22f4 Read the required wayland protocols version from upstream instead of hardcoding it 2017-12-28 14:12:46 +05:30
Kovid Goyal
115de36599 version 0.6.1 2017-12-28 10:40:42 +05:30
Kovid Goyal
b01c3483de Add an option to fade text in inactive windows
Fixes #256
2017-12-28 10:36:48 +05:30
Kovid Goyal
db7f566a0c Update Changelog 2017-12-27 10:17:34 +05:30
Kovid Goyal
9f806e76e7 Fix incorrect condition for aborting cursor geometry calculation 2017-12-27 10:07:21 +05:30
Kovid Goyal
12bdd4dddd Bump min version of wayland-protocols to match upstream 2017-12-27 07:24:38 +05:30
Kovid Goyal
72a1eb237d Fix regression that broke drawing of hollow cursor when window loses focus
Fixes #255
2017-12-27 07:15:48 +05:30
Kovid Goyal
6a89289907 Merge branch 'recmod' of https://github.com/grimpy/kitty 2017-12-27 06:40:53 +05:30
Kovid Goyal
7ed835cf13 Fix a crash when detecting URLs continued onto multiple lines
Fixes #244
2017-12-27 06:35:09 +05:30
Kovid Goyal
4621ff41d9 Skip building the wayland backend on systems with missing wayland protocol definitions
Fixes #254
2017-12-27 06:22:36 +05:30
Jo De Boeck
60e17d5224 Make it possible to modify rectangle selection modifiers
Signed-off-by: Jo De Boeck <deboeck.jo@gmail.com>
2017-12-24 18:40:25 +02:00
Kovid Goyal
1e2f7c705d ... 2017-12-22 12:36:40 +05:30
Kovid Goyal
1ebf0b95c4 Implement reading of cwd of a process on macOS as well 2017-12-21 12:30:34 +05:30
Kovid Goyal
c5acd94456 Add new actions to open tabs/windows with the working directory set to the working directory of the current window
Fixes #237
2017-12-21 11:29:21 +05:30
Kovid Goyal
448ffe01c3 Fix new window being created with different DPI not changing global DPI 2017-12-21 09:06:07 +05:30
Kovid Goyal
5c882f3290 Fix Travis flake failure on gen-wcwidth.py because of old python version 2017-12-20 23:38:16 +05:30
Kovid Goyal
70a4720bf9 Fix for window being opened on secondary monitor using primary monitor DPI 2017-12-20 23:01:06 +05:30
Kovid Goyal
c8aee8c881 Automatically adjust cell height when DPI changes
DPI changes are detected as a change in the ration of the window size to
the framebuffer size. I dont know how reliable that is. Possiblly Fix #77
2017-12-20 22:55:05 +05:30
Kovid Goyal
c2ad0607b8 Fix trailing whitespace in generated unicde data files 2017-12-20 22:44:20 +05:30
Kovid Goyal
d1282b9f55 Add a script to generate wcwidth as well
Generated function is more efficient than the implementation from
wcwidth9 and also makes it easy to update when the unicode standard
changes.
2017-12-20 16:18:05 +05:30
Kovid Goyal
11ee317884 Fix some emoji glyphs not colored on Linux
Linux has a plethora of fonts that contain some emoji glyphs. If one of
those fonts is used as a fallback font for a non-emoji character, it
also gets used for emojis -- leading to non-colored emoji. Fix by making
sure the first fallback font is a dedicated emoji font.
2017-12-20 10:40:27 +05:30
Kovid Goyal
e69de2f968 Use the new glfw API for setting WM_CLASS 2017-12-20 09:18:29 +05:30
Kovid Goyal
2f4f3e3331 Update bundled glfw 2017-12-20 09:07:06 +05:30
Kovid Goyal
020f8a2047 Add trailing space test to travis 2017-12-20 08:52:06 +05:30
Kovid Goyal
b8faba2a16 More trailing space removal 2017-12-20 08:50:09 +05:30
Kovid Goyal
0fcce6ec58 Remove trailing whitespace from native code files 2017-12-20 08:44:47 +05:30
Kovid Goyal
15f5ea92b8 Merge branch 'master' of https://github.com/FabioLolix/kitty 2017-12-20 08:24:20 +05:30
FabioLolix
2bbb5d25eb Updated readme 2017-12-20 00:12:58 +01:00
Kovid Goyal
8d94285a5d Ensure underlines are rendered even for fonts with very poor metrics
Fixes #236
2017-12-19 00:54:04 +05:30
Kovid Goyal
eed11361c5 Fix typo in config directory override env var 2017-12-18 06:59:19 +05:30
151 changed files with 81294 additions and 6946 deletions

4
.gitattributes vendored
View File

@@ -1,11 +1,13 @@
kitty/wcwidth9.h linguist-generated=true
kitty/wcwidth-std.h linguist-generated=true
kitty/emoji.h linguist-generated=true
kitty/keys.h linguist-generated=true
kitty/charsets.c linguist-generated=true
kitty/key_encoding.py linguist-generated=true
kitty/unicode-data.c
kitty/rgb.py linguist-generated=true
kitty/gl-wrapper.* linguist-generated=true
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

@@ -56,7 +56,7 @@ matrix:
env:
- RUN_FLAKE=1 BUILD_PKG=1
language: python
python: "3.5"
python: "3.6"
addons:
apt:
packages:
@@ -94,8 +94,8 @@ install: |
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
if [[ "$USE_BREW" == "1" ]]; then
brew update;
brew install python3;
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype --without-cairo;
brew upgrade python;
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
else
mkdir -p $SW;
curl https://download.calibre-ebook.com/travis/kitty/osx.tar.xz | tar xJ -C $SW;
@@ -123,6 +123,7 @@ before_script:
- $PYTHON setup.py build --debug --verbose $SANITIZE_ARG;
script:
- if grep -Inr '\s$' kitty kitty_tests *.py *.asciidoc .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
- if [[ -z $SANITIZE_ARG ]]; then $PYTHON test.py; else ./asan-launcher test.py; fi
- if [[ "$RUN_FLAKE" == "1" ]]; then flake8 --count .; fi
- if [[ "$BUILD_PKG" == "1" ]]; then $PYTHON setup.py linux-package; fi

View File

@@ -3,6 +3,216 @@ Changelog
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
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]
---------------------------
- Add an option to adjust the width of character cells
- Fix selecting text with the mouse in the scrollback buffer selecting text
from the line above the actually selected line
- Fix some italic fonts having the right edge of characters cut-off,
unnecessarily
version 0.7.0 [2018-01-24]
---------------------------
- Allow controlling kitty from the shell prompt/scripts. You can
open/close/rename windows and tabs and even send input to specific windows.
See the README for details.
- Add option to put tab bar at the top instead of the bottom
- Add option to override the default shell
- Add "Horizontal" and "Vertical" window layouts
- Sessions: Allow setting titles and working directories for individual windows
- Option to copy to clipboard on mouse select
- Fix incorrect reporting of mouse move events when using the SGR protocol
- Make alt+backspace delete the previous word
- Take the mouse wheel multiplier option in to account when generating fake key
scroll events
- macOS: Fix closing top-level window does not transfer focus to other
top-level windows.
- macOS: Fix alt+arrow keys not working when disabling the macos_option_as_alt
config option.
- kitty icat: Workaround for bug in ImageMagick that would cause some images
to fail to display at certain sizes.
- Fix rendering of text with ligature fonts that do not use dummy glyphs
- Fix a regression that caused copying of the selection to clipboard to only
copy the visible part of the selection
- Fix incorrect handling of some unicode combining marks that are not re-ordered
- Fix handling on non-BMP combining characters
- Drop the dependency on libunistring
version 0.6.1 [2017-12-28]
---------------------------
- Add an option to fade the text in inactive windows
- Add new actions to open windows/tabs/etc. with the working directory set to
the working directory of the current window.
- Automatically adjust cell size when DPI changes, for example when kitty is
moved from one monitor to another with a different DPI
- Ensure underlines are rendered even for fonts with very poor metrics
- Fix some emoji glyphs not colored on Linux
- Internal wcwidth() implementation is now auto-generated from the unicode
standard database
- Allow configuring the modifiers to use for rectangular selection with the
mouse.
- Fix incorrect minimum wayland version in the build script
- Fix a crash when detecting a URL that ends at the end of the line
- Fix regression that broke drawing of hollow cursor when window loses focus
version 0.6.0 [2017-12-18]
---------------------------

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

@@ -21,3 +21,14 @@ debug:
# Build with the ASAN and UBSAN sanitizers
asan:
python3 setup.py build $(VVAL) --debug --sanitize
profile:
python3 setup.py build $(VVAL) --profile
logo/kitty.iconset/icon_256x256.png: logo/kitty.svg logo/make.py
logo/make.py
rendered_logo: logo/kitty.iconset/icon_256x256.png
app: rendered_logo
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,30 +55,32 @@ image::https://travis-ci.org/kovidgoyal/kitty.svg?branch=master[Build status, li
== Major Features
* Supports tiling multiple terminal windows side by side in different
layouts without needing to use an extra program like tmux
* 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 startup sessions which allow you to specify the window/tab layout,
working directories and programs to run on startup.
* Supports tiling multiple terminal windows side by side in different
link:#layouts[layouts] without needing to use an extra program like tmux
* Uses OpenGL for rendering, offloads rendering to the GPU for lower system
load and buttery smooth scrolling. Uses threaded rendering to minimize input
latency.
* Can be link:remote-control.asciidoc[controlled from scripts or the shell prompt], even over SSH.
* Has a framework for _kittens_, small terminal programs that can be used to extend kitty's functionality.
For example, they are used for link:#unicode-input[Unicode input] and link:#url-hints[URL hints].
* 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.
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.
* Easily hackable (UI layer written in python, inner loops in C for
speed). Less than fifteen thousand lines of code.
image::screenshot.png?raw=true[Screenshot, showing three programs in the "Tall" layout]
@@ -89,8 +95,7 @@ the following dependencies are installed first.
=== Dependencies
* python >= 3.5
* harfbuzz >= 1.5.0
* libunistring
* harfbuzz >= 1.5.0
* zlib
* libpng
* freetype (not needed on macOS)
@@ -98,6 +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`, `libgl1-mesa` and `xkbcommon-x11`, if they are not already installed by your distro.
=== Install and run from source
@@ -108,21 +114,26 @@ git clone https://github.com/kovidgoyal/kitty && cd kitty
Now build the C parts of kitty with the following command:
....
python3 setup.py build
make
....
You can run kitty, as:
....
python3 /path/to/kitty/folder
python3 .
....
=== Linux packages
* Arch linux: AUR git package https://aur.archlinux.org/packages/kitty-git/
* Arch Linux: AUR git package https://aur.archlinux.org/packages/kitty-git/
* Arch Linux: AUR stable package https://aur.archlinux.org/packages/kitty/
* 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
@@ -192,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}
|===
@@ -222,42 +234,83 @@ windows are:
|Increase font size | {sc_increase_font_size}
|Decrease font size | {sc_decrease_font_size}
|Restore font size | {sc_restore_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}
|===
== Controlling kitty from scripts
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
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.
* Tall -- One window is shown full height on the left, the rest of the windows are shown one below the other on the right
* Grid -- All windows are shown in a grid
* 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
@@ -266,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
@@ -283,11 +340,13 @@ command line flag. For example:
....
# Set the window layout for the current tab
layout tall
# Set the working directory for the current tab
# Set the working directory for windows in the current tab
cd ~
# Create a window and run the specified command in it
launch zsh
launch vim
# Set the title for the next window
title Chat with x
launch irssi --profile x
# Create a new tab (the part after new_tab is the optional tab name which will
@@ -333,18 +392,20 @@ 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 the
`--profile` flag which will create an executable called `kitty-profile`. Run
that and perform the task you want to analyse, for example, scrolling a large
link:https://github.com/gperftools/gperftools[gperftools]. Build kitty with
`make profile` which will create an executable called `kitty-profile`. Run
that and perform the task you want to analyse, for example, scrolling a large
file with `less`. After you quit, function call statistics will be printed to
`stdout` and you can use tools like *kcachegrind* for more detailed analysis.
Here are some CPU usage numbers for the task of scrolling a file continuously in less.
The CPU usage is for the terminal process and X together and is measured using htop.
The measurements are taken at the same font and window size for all terminals on a
`Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz` CPU with a
The measurements are taken at the same font and window size for all terminals on a
`Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz` CPU with a
`Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD 7770/8760 / R7 250X]` GPU.
|===
@@ -385,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.
@@ -393,76 +456,85 @@ 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?
You dont need to build kitty.app to run kitty from source. Simply install the dependencies
listed above using brew/MacPorts or similar and then compile and run with:
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
And run:
```
make
python3 .
make app
```
You can also create a shell script to run kitty:
This `kitty.app` unlike the released one does not include its own copy of
python and the other dependencies. So if you ever un-install/upgrade those dependencies
you might have to rebuild the app.
```
#!/bin/sh
exec python3 /path/to/kitty/source/dir
```
And using something like link:https://mathiasbynens.be/notes/shell-script-mac-apps[app-wrapper-for-shell-script]
you can create a simple launcher app to run the shell script.
If you only want to make some small changes to a python file, you can do that
and simply replace the corresponding file inside an existing kitty.app. (Note
that kitty.app will contain `.pyo` files which are compiled versions of `.py`
files, delete the `.pyo` file and replace it with a `.py` file).
Finally, note that the released kitty.dmg is built automatically by using the
`kitty` branch of
Note that the released kitty.dmg includes all dependencies, unlike the
`kitty.app` built above and is built automatically by using the `kitty` branch of
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.
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
@@ -470,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

@@ -4,12 +4,65 @@
import sys
if len(sys.argv) > 1 and sys.argv[1] == 'icat':
def icat(args):
from kitty.icat import main
main(sys.argv[1:])
elif len(sys.argv) > 1 and sys.argv[1] == 'list-fonts':
main(args)
def list_fonts(args):
from kitty.fonts.list import main
main(sys.argv[1:])
else:
from kitty.main import main
main(args)
def remote_control(args):
from kitty.remote_control import main
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:])
entry_points = {
# These two are here for backwards compat
'icat': icat,
'list-fonts': list_fonts,
'runpy': runpy,
'launch': launch,
'@': remote_control,
'+': namespaced,
}
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
def main():
first_arg = '' if len(sys.argv) < 2 else sys.argv[1]
func = entry_points.get(first_arg)
if func is None:
if first_arg.startswith('@'):
remote_control(['@', first_arg[1:]] + sys.argv[2:])
elif first_arg.startswith('+'):
namespaced(['+', first_arg[1:]] + sys.argv[2:])
else:
from kitty.main import main
main()
else:
func(sys.argv[1:])
if __name__ == '__main__':
main()

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/wcwidth9.h\nkitty/glfw.c\nkitty/keys.h\nkitty/charsets.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

@@ -1,75 +0,0 @@
#!/usr/bin/env python3
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import os
from collections import defaultdict
from functools import partial
from itertools import groupby
from operator import itemgetter
from urllib.request import urlopen
os.chdir(os.path.dirname(os.path.abspath(__file__)))
raw = urlopen('http://unicode.org/Public/emoji/5.0/emoji-data.txt').read().decode('utf-8')
seen = set()
cmap = defaultdict(set)
for line in raw.splitlines():
line = line.strip()
if not line or line.startswith('#'):
continue
spec, rest = line.partition(';')[::2]
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
if '.' in spec:
spec = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))
spec = set(range(spec[0], spec[1] + 1))
else:
spec = {int(spec, 16)}
cmap[rest] |= spec
seen |= spec
items = list(seen)
def get_ranges(items):
items.sort()
for k, g in groupby(enumerate(items), lambda m: m[0]-m[1]):
group = tuple(map(itemgetter(1), g))
a, b = group[0], group[-1]
if a == b:
yield a
else:
yield a, b
def write_case(spec, p):
if isinstance(spec, tuple):
p('\t\tcase 0x{:x} ... 0x{:x}:'.format(*spec))
else:
p('\t\tcase 0x{:x}:'.format(spec))
with open('kitty/emoji.h', 'w') as f:
p = partial(print, file=f)
p('#pragma once')
p('#include "data-types.h"\n')
p('START_ALLOW_CASE_RANGE')
p('static inline bool is_emoji(uint32_t code) {')
p('\tswitch(code) {')
for spec in get_ranges(items):
last = spec[1] if isinstance(spec, tuple) else spec
if last < 0x231a:
continue
write_case(spec, p)
p('\t\t\treturn true;')
p('\t\tdefault: return false;')
p('\t}')
p('\treturn false; \n}')
p('static inline bool is_emoji_modifier(uint32_t code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(cmap['Emoji_Modifier'])):
write_case(spec, p)
p('\t\t\treturn true;')
p('\t\tdefault: return false;')
p('\t}')
p('\treturn false; \n}')
p('END_ALLOW_CASE_RANGE')

354
gen-wcwidth.py Executable file
View File

@@ -0,0 +1,354 @@
#!/usr/bin/env python3
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
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
os.chdir(os.path.dirname(os.path.abspath(__file__)))
def get_data(fname, folder='UCD'):
url = f'https://www.unicode.org/Public/{folder}/latest/{fname}'
bn = os.path.basename(url)
local = os.path.join('/tmp', bn)
if os.path.exists(local):
data = open(local, 'rb').read()
else:
data = urlopen(url).read()
open(local, 'wb').write(data)
for line in data.decode('utf-8').splitlines():
line = line.strip()
if line and not line.startswith('#'):
yield line
# 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]
codepoints = (codepoint,)
if first is None:
if desc.endswith(', First>'):
first = codepoint
continue
else:
codepoints = range(first, codepoint + 1)
first = None
for codepoint in codepoints:
s.add(codepoint)
not_assigned.discard(codepoint)
if category.startswith('M'):
marks.add(codepoint)
def split_two(line):
spec, rest = line.split(';', 1)
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
if '..' in spec:
chars = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))
chars = set(range(chars[0], chars[1] + 1))
else:
chars = {int(spec, 16)}
return chars, rest
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)
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()
def parse_eaw():
global doublewidth, ambiguous
seen = set()
for line in get_data('ucd/EastAsianWidth.txt'):
chars, eaw = split_two(line)
if eaw == 'A':
ambiguous |= chars
seen |= chars
elif eaw == 'W' or eaw == 'F':
doublewidth |= chars
seen |= chars
doublewidth |= set(range(0x3400, 0x4DBF + 1)) - seen
doublewidth |= set(range(0x4E00, 0x9FFF + 1)) - seen
doublewidth |= set(range(0xF900, 0xFAFF + 1)) - seen
doublewidth |= set(range(0x20000, 0x2FFFD + 1)) - seen
doublewidth |= set(range(0x30000, 0x3FFFD + 1)) - seen
def get_ranges(items):
items.sort()
for k, g in groupby(enumerate(items), lambda m: m[0]-m[1]):
group = tuple(map(itemgetter(1), g))
a, b = group[0], group[-1]
if a == b:
yield a
else:
yield a, b
def write_case(spec, p):
if isinstance(spec, tuple):
p('\t\tcase 0x{:x} ... 0x{:x}:'.format(*spec))
else:
p('\t\tcase 0x{:x}:'.format(spec))
@contextmanager
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')
if include_data_types:
p('#include "data-types.h"\n')
p('START_ALLOW_CASE_RANGE')
p()
yield p
p()
if include_data_types:
p('END_ALLOW_CASE_RANGE')
f.close()
def gen_emoji():
with create_header('kitty/emoji.h') as p:
p('static inline bool\nis_emoji(char_type code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(all_emoji)):
write_case(spec, p)
p('\t\t\treturn true;')
p('\t\tdefault: return false;')
p('\t}')
p('\treturn false;\n}')
p('static inline bool\nis_emoji_modifier(char_type code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(emoji_categories['Emoji_Modifier'])):
write_case(spec, p)
p('\t\t\treturn true;')
p('\t\tdefault: return false;')
p('\t}')
p('\treturn false;\n}')
def category_test(name, p, classes, comment, static=False):
static = 'static inline ' if static else ''
chars = set()
for c in classes:
chars |= class_maps[c]
p(f'{static}bool\n{name}(char_type code) {{')
p(f'\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
p('\tswitch(code) {')
for spec in get_ranges(list(chars)):
write_case(spec, p)
p(f'\t\t\treturn true;')
p('\t} // }}}\n')
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, 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) {')
p(f'\tstatic char_type map[{len(mark_map)}] =', '{', ', '.join(map(str, mark_map)), '}; // {{{ mapping }}}')
p('\tif (m < arraysz(map)) return map[m];')
p('\treturn 0;')
p('}\n')
p('combining_type mark_for_codepoint(char_type c) {')
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('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')
def gen_wcwidth():
seen = set()
def add(p, comment, chars_, ret):
chars = chars_ - seen
seen.update(chars)
p(f'\t\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
for spec in get_ranges(list(chars)):
write_case(spec, p)
p(f'\t\t\treturn {ret};')
p('\t\t// }}}\n')
with create_header('kitty/wcwidth-std.h') as p:
p('static int\nwcwidth_std(int32_t code) {')
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, '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 Presentation', emoji_categories['Emoji_Presentation'], 2)
add(p, 'Not assigned in the unicode character database', not_assigned, -1)
p('\t\tdefault: return 1;')
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()
parse_eaw()
gen_ucd()
gen_wcwidth()
gen_emoji()
gen_names()

View File

@@ -264,7 +264,7 @@ void _glfwPollMonitorsNS(void)
const CGSize size = CGDisplayScreenSize(displays[i]);
char* name = getDisplayName(displays[i]);
if (!name)
name = strdup("Unknown");
name = _glfw_strdup("Unknown");
monitor = _glfwAllocMonitor(name, size.width, size.height);
monitor->ns.displayID = displays[i];
@@ -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

@@ -88,9 +88,10 @@ typedef struct _GLFWwindowNS
GLFWbool maximized;
// Cached window and framebuffer sizes used to filter out duplicate events
// Cached window properties to filter out duplicate events
int width, height;
int fbWidth, fbHeight;
float xscale, yscale;
// The total sum of the distances the cursor has been warped
// since the last cursor motion event was processed
@@ -163,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

@@ -43,6 +43,7 @@
#define NSEventModifierFlagControl NSControlKeyMask
#define NSEventModifierFlagOption NSAlternateKeyMask
#define NSEventModifierFlagShift NSShiftKeyMask
#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask
#define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask
#define NSEventMaskAny NSAnyEventMask
#define NSEventTypeApplicationDefined NSApplicationDefined
@@ -137,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),
@@ -149,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
@@ -177,6 +177,8 @@ static int translateFlags(NSUInteger flags)
mods |= GLFW_MOD_ALT;
if (flags & NSEventModifierFlagCommand)
mods |= GLFW_MOD_SUPER;
if (flags & NSEventModifierFlagCapsLock)
mods |= GLFW_MOD_CAPS_LOCK;
return mods;
}
@@ -571,6 +573,19 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
window->ns.fbHeight = fbRect.size.height;
_glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
}
const float xscale = fbRect.size.width / contentRect.size.width;
const float yscale = fbRect.size.height / contentRect.size.height;
if (xscale != window->ns.xscale || yscale != window->ns.yscale)
{
window->ns.xscale = xscale;
window->ns.yscale = yscale;
_glfwInputWindowContentScale(window, xscale, yscale);
if (window->ns.layer)
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
}
}
- (void)drawRect:(NSRect)rect
@@ -694,7 +709,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
NSUInteger i;
for (i = 0; i < count; i++)
paths[i] = strdup([[e nextObject] UTF8String]);
paths[i] = _glfw_strdup([[e nextObject] UTF8String]);
_glfwInputDrop(window, (int) count, (const char**) paths);
@@ -1087,8 +1102,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
[window->ns.object zoom:nil];
}
if (wndconfig->ns.frame)
[window->ns.object setFrameAutosaveName:[NSString stringWithUTF8String:wndconfig->title]];
if (strlen(wndconfig->ns.frameName))
[window->ns.object setFrameAutosaveName:[NSString stringWithUTF8String:wndconfig->ns.frameName]];
window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window];
@@ -1159,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;
@@ -1350,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;
@@ -1405,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)
@@ -1475,6 +1487,20 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return [window->ns.object isZoomed];
}
int _glfwPlatformWindowHovered(_GLFWwindow* window)
{
const NSPoint point = [NSEvent mouseLocation];
if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] !=
[window->ns.object windowNumber])
{
return GLFW_FALSE;
}
return NSPointInRect(point,
[window->ns.object convertRectToScreen:[window->ns.view bounds]]);
}
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return ![window->ns.object isOpaque] && ![window->ns.view isOpaque];
@@ -1511,6 +1537,9 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
void _glfwPlatformPollEvents(void)
{
if (!initializeAppKit())
return;
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
@@ -1786,7 +1815,7 @@ const char* _glfwPlatformGetClipboardString(void)
}
free(_glfw.ns.clipboardString);
_glfw.ns.clipboardString = strdup([object UTF8String]);
_glfw.ns.clipboardString = _glfw_strdup([object UTF8String]);
return _glfw.ns.clipboardString;
}
@@ -1845,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));
@@ -1876,4 +1906,3 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
return window->ns.object;
}

55
glfw/context.c vendored
View File

@@ -38,8 +38,24 @@
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
// Checks whether the desired context attributes are valid
//
// This function checks things like whether the specified client API version
// exists and whether all relevant options have supported and non-conflicting
// values
//
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)
@@ -155,6 +171,8 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
return GLFW_TRUE;
}
// Chooses the framebuffer config that best matches the desired one
//
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
const _GLFWfbconfig* alternatives,
unsigned int count)
@@ -321,10 +339,13 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
return closest;
}
GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
// Retrieves the attributes of the current context
//
GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig)
{
int i;
_GLFWwindow* window;
_GLFWwindow* previous;
const char* version;
const char* prefixes[] =
{
@@ -334,11 +355,12 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
NULL
};
window = _glfwPlatformGetTls(&_glfw.contextSlot);
window->context.source = ctxconfig->source;
window->context.client = GLFW_OPENGL_API;
previous = _glfwPlatformGetTls(&_glfw.contextSlot);;
glfwMakeContextCurrent((GLFWwindow*) window);
window->context.GetIntegerv = (PFNGLGETINTEGERVPROC)
window->context.getProcAddress("glGetIntegerv");
window->context.GetString = (PFNGLGETSTRINGPROC)
@@ -346,6 +368,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
if (!window->context.GetIntegerv || !window->context.GetString)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken");
glfwMakeContextCurrent((GLFWwindow*) previous);
return GLFW_FALSE;
}
@@ -363,6 +386,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
"OpenGL ES version string retrieval is broken");
}
glfwMakeContextCurrent((GLFWwindow*) previous);
return GLFW_FALSE;
}
@@ -394,6 +418,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
"No version found in OpenGL ES version string");
}
glfwMakeContextCurrent((GLFWwindow*) previous);
return GLFW_FALSE;
}
@@ -423,6 +448,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
window->context.major, window->context.minor);
}
glfwMakeContextCurrent((GLFWwindow*) previous);
return GLFW_FALSE;
}
@@ -438,6 +464,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Entry point retrieval is broken");
glfwMakeContextCurrent((GLFWwindow*) previous);
return GLFW_FALSE;
}
}
@@ -544,9 +571,12 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
window->context.swapBuffers(window);
}
glfwMakeContextCurrent((GLFWwindow*) previous);
return GLFW_TRUE;
}
// Searches an extension string for the specified extension
//
GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions)
{
const char* start = extensions;
@@ -587,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;
}
@@ -616,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;
}
@@ -632,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;
}
@@ -649,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;
}
@@ -714,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__))
@@ -52,7 +56,7 @@ def init_env(env, pkg_config, at_least_version, module='x11'):
ans.ldpaths.extend(('-framework', f))
elif module == 'wayland':
at_least_version('wayland-protocols', 1, 1)
at_least_version('wayland-protocols', *sinfo['wayland_protocols'])
ans.wayland_packagedir = os.path.abspath(pkg_config('wayland-protocols', '--variable=pkgdatadir')[0])
ans.wayland_scanner = os.path.abspath(pkg_config('wayland-scanner', '--variable=wayland_scanner')[0])
ans.wayland_protocols = tuple(sinfo[module]['protocols'])
@@ -69,6 +73,8 @@ def init_env(env, pkg_config, at_least_version, module='x11'):
def build_wayland_protocols(env, run_tool, emphasis, newer, dest_dir):
for protocol in env.wayland_protocols:
src = os.path.join(env.wayland_packagedir, protocol)
if not os.path.exists(src):
raise SystemExit('The wayland-protocols package on your system is missing the {} protocol definition file'.format(protocol))
for ext in 'hc':
dest = wayland_protocol_file_name(src, ext)
dest = os.path.join(dest_dir, dest)
@@ -80,6 +86,7 @@ def build_wayland_protocols(env, run_tool, emphasis, newer, dest_dir):
def collect_source_information():
raw = open('src/CMakeLists.txt').read()
mraw = open('CMakeLists.txt').read()
def extract_sources(group, start_pos=0):
for which in 'HEADERS SOURCES'.split():
@@ -91,16 +98,20 @@ def collect_source_information():
).group(1).strip().split()
)
wayland_protocols = re.search('WaylandProtocols\s+(\S+)\s+', mraw).group(1)
wayland_protocols = list(map(int, wayland_protocols.split('.')))
ans = {
'common': dict(extract_sources('common')),
'wayland_protocols': wayland_protocols,
}
joystick = 'null' if is_bsd else 'linux'
for group in 'cocoa win32 x11 wayland osmesa'.split():
m = re.search('_GLFW_' + group.upper(), raw)
ans[group] = dict(extract_sources('glfw', m.start()))
if group == 'x11':
ans[group]['headers'].append('linux_joystick.h')
ans[group]['sources'].append('linux_joystick.c')
elif group == 'wayland':
if group in ('x11', 'wayland'):
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
if group == 'wayland':
ans[group]['protocols'] = p = []
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
p.append(m.group(1))
@@ -242,9 +253,10 @@ def main():
sinfo = collect_source_information()
files_to_copy = set()
for x in sinfo.values():
headers, sources = x['headers'], x['sources']
for name in headers + sources:
files_to_copy.add(os.path.abspath(os.path.join('src', name)))
if isinstance(x, dict):
headers, sources = x['headers'], x['sources']
for name in headers + sources:
files_to_copy.add(os.path.abspath(os.path.join('src', name)))
glfw_header = os.path.abspath('include/GLFW/glfw3.h')
glfw_native_header = os.path.abspath('include/GLFW/glfw3native.h')
os.chdir(base)

377
glfw/glfw3.h vendored
View File

@@ -493,17 +493,37 @@ extern "C" {
* @{ */
/*! @brief If this bit is set one or more Shift keys were held down.
*
* If this bit is set one or more Shift keys were held down.
*/
#define GLFW_MOD_SHIFT 0x0001
/*! @brief If this bit is set one or more Control keys were held down.
*
* If this bit is set one or more Control keys were held down.
*/
#define GLFW_MOD_CONTROL 0x0002
/*! @brief If this bit is set one or more Alt keys were held down.
*
* If this bit is set one or more Alt keys were held down.
*/
#define GLFW_MOD_ALT 0x0004
/*! @brief If this bit is set one or more Super keys were held down.
*
* If this bit is set one or more Super keys were held down.
*/
#define GLFW_MOD_SUPER 0x0008
/*! @brief If this bit is set the Caps Lock key is enabled.
*
* If this bit is set the Caps Lock key is enabled and the @ref
* GLFW_LOCK_KEY_MODS input mode is set.
*/
#define GLFW_MOD_CAPS_LOCK 0x0010
/*! @brief If this bit is set the Num Lock key is enabled.
*
* If this bit is set the Num Lock key is enabled and the @ref
* GLFW_LOCK_KEY_MODS input mode is set.
*/
#define GLFW_MOD_NUM_LOCK 0x0020
/*! @} */
@@ -794,6 +814,11 @@ extern "C" {
* [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib).
*/
#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A
/*! @brief Mouse cursor hover window attribute.
*
* Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).
*/
#define GLFW_HOVERED 0x0002000B
/*! @brief Framebuffer bit depth hint.
*
@@ -944,8 +969,11 @@ extern "C" {
#define GLFW_CONTEXT_CREATION_API 0x0002200B
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
#define GLFW_COCOA_FRAME_AUTOSAVE 0x00023002
#define GLFW_COCOA_FRAME_NAME 0x00023002
#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003
#define GLFW_X11_CLASS_NAME 0x00024001
#define GLFW_X11_INSTANCE_NAME 0x00024002
/*! @} */
#define GLFW_NO_API 0
@@ -963,6 +991,7 @@ extern "C" {
#define GLFW_CURSOR 0x00033001
#define GLFW_STICKY_KEYS 0x00033002
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
#define GLFW_LOCK_KEY_MODS 0x00033004
#define GLFW_CURSOR_NORMAL 0x00034001
#define GLFW_CURSOR_HIDDEN 0x00034002
@@ -1025,9 +1054,6 @@ extern "C" {
#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001
#define GLFW_COCOA_MENUBAR 0x00051002
#define GLFW_X11_WM_CLASS_NAME 0x00052001
#define GLFW_X11_WM_CLASS_CLASS 0x00052002
/*! @} */
#define GLFW_DONT_CARE -1
@@ -1046,7 +1072,7 @@ extern "C" {
* @sa @ref glfwGetProcAddress
*
* @since Added in version 3.0.
*
* @ingroup context
*/
typedef void (*GLFWglproc)(void);
@@ -1257,6 +1283,24 @@ typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int);
*/
typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);
/*! @brief The function signature for window content scale callbacks.
*
* This is the function signature for window content scale callback
* functions.
*
* @param[in] window The window whose content scale changed.
* @param[in] xscale The new x-axis content scale of the window.
* @param[in] yscale The new y-axis content scale of the window.
*
* @sa @ref window_scale
* @sa @ref glfwSetWindowContentScaleCallback
*
* @since Added in version 3.3.
*
* @ingroup window
*/
typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float);
/*! @brief The function signature for mouse button callbacks.
*
* This is the function signature for mouse button callback functions.
@@ -1414,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
@@ -1431,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
@@ -1623,8 +1669,7 @@ GLFWAPI void glfwTerminate(void);
/*! @brief Sets the specified init hint to the desired value.
*
* This function sets hints for the next initialization of GLFW. Only integer
* type hints can be set with this function.
* This function sets hints for the next initialization of GLFW.
*
* The values you set hints to are never reset by GLFW, but they only take
* effect during initialization. Once GLFW has been initialized, any values
@@ -1633,7 +1678,7 @@ GLFWAPI void glfwTerminate(void);
*
* Some hints are platform specific. These may be set on any platform but they
* will only affect their specific platform. Other platforms will ignore them.
* Setting these hints requires no platform specific headers or functions.
* Setting these hints requires no platform specific headers or functions.
*
* @param[in] hint The [init hint](@ref init_hints) to set.
* @param[in] value The new value of the init hint.
@@ -1647,7 +1692,6 @@ GLFWAPI void glfwTerminate(void);
*
* @sa init_hints
* @sa glfwInit
* @sa glfwInitHintString
*
* @since Added in version 3.3.
*
@@ -1655,40 +1699,6 @@ GLFWAPI void glfwTerminate(void);
*/
GLFWAPI void glfwInitHint(int hint, int value);
/*! @brief Sets the specified init hint to the desired value.
*
* This function sets hints for the next initialization of GLFW. Only string
* type hints can be set with this function.
*
* The values you set hints to are never reset by GLFW, but they only take
* effect during initialization. Once GLFW has been initialized, any values
* you set will be ignored until the library is terminated and initialized
* again.
*
* Some hints are platform specific. These may be set on any platform but they
* will only affect their specific platform. Other platforms will ignore them.
* Setting these hints requires no platform specific headers or functions.
*
* @param[in] hint The [init hint](@ref init_hints) to set.
* @param[in] value The new value of the init hint.
*
* @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref
* GLFW_INVALID_VALUE.
*
* @remarks This function may be called before @ref glfwInit.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa init_hints
* @sa glfwInit
* @sa glfwInitHint
*
* @since Added in version 3.3.
*
* @ingroup init
*/
GLFWAPI void glfwInitHintString(int hint, const char* value);
/*! @brief Retrieves the version of the GLFW library.
*
* This function retrieves the major, minor and revision numbers of the GLFW
@@ -1985,6 +1995,56 @@ GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, flo
*/
GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor);
/*! @brief Sets the user pointer of the specified monitor.
*
* This function sets the user-defined pointer of the specified monitor. The
* current value is retained until the monitor is disconnected. The initial
* value is `NULL`.
*
* This function may be called from the monitor callback, even for a monitor
* that is being disconnected.
*
* @param[in] monitor The monitor whose pointer to set.
* @param[in] pointer The new value.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @sa @ref monitor_userptr
* @sa @ref glfwGetMonitorUserPointer
*
* @since Added in version 3.3.
*
* @ingroup monitor
*/
GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer);
/*! @brief Returns the user pointer of the specified monitor.
*
* This function returns the current value of the user-defined pointer of the
* specified monitor. The initial value is `NULL`.
*
* This function may be called from the monitor callback, even for a monitor
* that is being disconnected.
*
* @param[in] monitor The monitor whose pointer to return.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @sa @ref monitor_userptr
* @sa @ref glfwSetMonitorUserPointer
*
* @since Added in version 3.3.
*
* @ingroup monitor
*/
GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor);
/*! @brief Sets the monitor configuration callback.
*
* This function sets the monitor configuration callback, or removes the
@@ -2184,6 +2244,7 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp);
*
* @sa @ref window_hints
* @sa @ref glfwWindowHint
* @sa @ref glfwWindowHintString
*
* @since Added in version 3.0.
*
@@ -2194,14 +2255,20 @@ GLFWAPI void glfwDefaultWindowHints(void);
/*! @brief Sets the specified window hint to the desired value.
*
* This function sets hints for the next call to @ref glfwCreateWindow. The
* hints, once set, retain their values until changed by a call to @ref
* glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is
* terminated.
* hints, once set, retain their values until changed by a call to this
* function or @ref glfwDefaultWindowHints, or until the library is terminated.
*
* Only integer value hints can be set with this function. String value hints
* are set with @ref glfwWindowHintString.
*
* This function does not check whether the specified hint values are valid.
* If you set hints to invalid values this will instead be reported by the next
* call to @ref glfwCreateWindow.
*
* Some hints are platform specific. These may be set on any platform but they
* will only affect their specific platform. Other platforms will ignore them.
* Setting these hints requires no platform specific headers or functions.
*
* @param[in] hint The [window hint](@ref window_hints) to set.
* @param[in] value The new value of the window hint.
*
@@ -2211,6 +2278,7 @@ GLFWAPI void glfwDefaultWindowHints(void);
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_hints
* @sa @ref glfwWindowHintString
* @sa @ref glfwDefaultWindowHints
*
* @since Added in version 3.0. Replaces `glfwOpenWindowHint`.
@@ -2219,6 +2287,44 @@ GLFWAPI void glfwDefaultWindowHints(void);
*/
GLFWAPI void glfwWindowHint(int hint, int value);
/*! @brief Sets the specified window hint to the desired value.
*
* This function sets hints for the next call to @ref glfwCreateWindow. The
* hints, once set, retain their values until changed by a call to this
* function or @ref glfwDefaultWindowHints, or until the library is terminated.
*
* Only string type hints can be set with this function. Integer value hints
* are set with @ref glfwWindowHint.
*
* This function does not check whether the specified hint values are valid.
* If you set hints to invalid values this will instead be reported by the next
* call to @ref glfwCreateWindow.
*
* Some hints are platform specific. These may be set on any platform but they
* will only affect their specific platform. Other platforms will ignore them.
* Setting these hints requires no platform specific headers or functions.
*
* @param[in] hint The [window hint](@ref window_hints) to set.
* @param[in] value The new value of the window hint.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_INVALID_ENUM.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_hints
* @sa @ref glfwWindowHint
* @sa @ref glfwDefaultWindowHints
*
* @since Added in version 3.3.
*
* @ingroup window
*/
GLFWAPI void glfwWindowHintString(int hint, const char* value);
/*! @brief Creates a window and its associated context.
*
* This function creates a window and its associated OpenGL or OpenGL ES
@@ -2329,9 +2435,8 @@ GLFWAPI void glfwWindowHint(int hint, int value);
* `CMake/MacOSXBundleInfo.plist.in` in the source tree.
*
* @remark @macos When activating frame autosaving with
* [GLFW_COCOA_FRAME_AUTOSAVE](@ref GLFW_COCOA_FRAME_AUTOSAVE_hint), the
* specified window size may be overriden by a previously saved size and
* position.
* [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified
* window size and position may be overriden by previously saved values.
*
* @remark @x11 Some window managers will not respect the placement of
* initially hidden windows.
@@ -2341,28 +2446,22 @@ GLFWAPI void glfwWindowHint(int hint, int value);
* query the final size, position or other attributes directly after window
* creation.
*
* @remark @x11 The name and class of the `WM_CLASS` window property will by
* default be set to the window title passed to this function. Set the @ref
* GLFW_X11_WM_CLASS_NAME and @ref GLFW_X11_WM_CLASS_CLASS init hints before
* initialization to override this. You can also set the title int he following
* special format, which allows setting the two parts of the WM_CLASS property
* and the window title independently:
* <01> WM_CLASS name <30> WM_CLASS class <30> title
* Here <01> refers to the byte value 01 (ASCII start-of-header) and <30> refers
* to the byte value 30 (ASCII record separator).
* @remark @x11 The class part of the `WM_CLASS` window property will by
* default be set to the window title passed to this function. The instance
* part will use the contents of the `RESOURCE_NAME` environment variable, if
* present and not empty, or fall back to the window title. Set the @ref
* 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.
*
* @remark @wayland The wl_shell protocol does not support window
* icons, the window will inherit the one defined in the application's
* desktop file, so this function emits @ref GLFW_PLATFORM_ERROR.
*
* @remark @wayland Screensaver inhibition requires the idle-inhibit protocol
* to be implemented in the user's compositor.
*
@@ -2506,9 +2605,9 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
* [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
* in the Mac Developer Library.
*
* @remark @wayland The wl_shell protocol does not support icons, the window
* will inherit the one defined in the application's desktop file, so this
* function emits @ref GLFW_PLATFORM_ERROR.
* @remark @wayland There is no existing protocol to change an icon, the
* window will thus inherit the one defined in the application's desktop file.
* This function always emits @ref GLFW_PLATFORM_ERROR.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -2800,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
@@ -2836,6 +2931,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_scale
* @sa @ref glfwSetWindowContentScaleCallback
* @sa @ref glfwGetMonitorContentScale
*
* @since Added in version 3.3.
@@ -2915,7 +3011,8 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity);
* GLFW_PLATFORM_ERROR.
*
* @remark @wayland There is no concept of iconification in wl_shell, this
* function will always emit @ref GLFW_PLATFORM_ERROR.
* function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated
* protocol.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -3091,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
@@ -3112,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.
@@ -3237,7 +3330,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
* The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
* [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib),
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib).
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib).
*
* Some of these attributes are ignored for full screen windows. The new
* value will take effect if the window is later made windowed.
@@ -3467,7 +3560,7 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @remark @wayland The wl_shell protocol has no concept of iconification,
* this callback will never be called.
* this callback will never be called when using this deprecated protocol.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -3525,6 +3618,30 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window,
*/
GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun);
/*! @brief Sets the window content scale callback for the specified window.
*
* This function sets the window content scale callback of the specified window,
* which is called when the content scale of the specified window changes.
*
* @param[in] window The window whose callback to set.
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
* callback.
* @return The previously set callback, or `NULL` if no callback was set or the
* library had not been [initialized](@ref intro_init).
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_scale
* @sa @ref glfwGetWindowContentScale
*
* @since Added in version 3.3.
*
* @ingroup window
*/
GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun cbfun);
/*! @brief Processes all pending events.
*
* This function processes only those events that are already in the event
@@ -3689,12 +3806,12 @@ GLFWAPI void glfwPostEmptyEvent(void);
/*! @brief Returns the value of an input option for the specified window.
*
* This function returns the value of an input option for the specified window.
* The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS or
* @ref GLFW_STICKY_MOUSE_BUTTONS.
* The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
* @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS.
*
* @param[in] window The window to query.
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
* `GLFW_STICKY_MOUSE_BUTTONS`.
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_INVALID_ENUM.
@@ -3712,8 +3829,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
/*! @brief Sets an input option for the specified window.
*
* This function sets an input mode option for the specified window. The mode
* must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS or
* @ref GLFW_STICKY_MOUSE_BUTTONS.
* must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
* @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS.
*
* If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
* modes:
@@ -3739,9 +3856,15 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
* you are only interested in whether mouse buttons have been pressed but not
* when or in which order.
*
* If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to
* enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled,
* callbacks that receive modifier bits will also have the @ref
* GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on,
* and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on.
*
* @param[in] window The window whose input mode to set.
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
* `GLFW_STICKY_MOUSE_BUTTONS`.
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`.
* @param[in] value The new value of the specified input mode.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
@@ -4481,10 +4604,10 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count);
* it with the corresponding direction.
*
* @code
* if (hats[2] & GLFW_HAT_RIGHT)
* {
* // State of hat 2 could be right-up, right or right-down
* }
* if (hats[2] & GLFW_HAT_RIGHT)
* {
* // State of hat 2 could be right-up, right or right-down
* }
* @endcode
*
* If the specified joystick is not present this function will return `NULL`
@@ -4588,6 +4711,56 @@ GLFWAPI const char* glfwGetJoystickName(int jid);
*/
GLFWAPI const char* glfwGetJoystickGUID(int jid);
/*! @brief Sets the user pointer of the specified joystick.
*
* This function sets the user-defined pointer of the specified joystick. The
* current value is retained until the joystick is disconnected. The initial
* value is `NULL`.
*
* This function may be called from the joystick callback, even for a joystick
* that is being disconnected.
*
* @param[in] joystick The joystick whose pointer to set.
* @param[in] pointer The new value.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @sa @ref joystick_userptr
* @sa @ref glfwGetJoystickUserPointer
*
* @since Added in version 3.3.
*
* @ingroup input
*/
GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer);
/*! @brief Returns the user pointer of the specified joystick.
*
* This function returns the current value of the user-defined pointer of the
* specified joystick. The initial value is `NULL`.
*
* This function may be called from the joystick callback, even for a joystick
* that is being disconnected.
*
* @param[in] joystick The joystick whose pointer to return.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @sa @ref joystick_userptr
* @sa @ref glfwSetJoystickUserPointer
*
* @since Added in version 3.3.
*
* @ingroup input
*/
GLFWAPI void* glfwGetJoystickUserPointer(int jid);
/*! @brief Returns whether the specified joystick has a gamepad mapping.
*
* This function returns whether the specified joystick is both present and has
@@ -4724,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.
@@ -4864,7 +5037,7 @@ GLFWAPI void glfwSetTime(double time);
* 1&nbsp;/&nbsp;frequency seconds. To get the frequency, call @ref
* glfwGetTimerFrequency.
*
* @return The value of the timer, or zero if an
* @return The value of the timer, or zero if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
@@ -5290,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
@@ -5305,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
@@ -5364,4 +5542,3 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
#endif
#endif /* _glfw3_h_ */

36
glfw/glx_context.c vendored
View File

@@ -223,7 +223,7 @@ static GLFWglproc getProcAddressGLX(const char* procname)
else if (_glfw.glx.GetProcAddressARB)
return _glfw.glx.GetProcAddressARB((const GLubyte*) procname);
else
return dlsym(_glfw.glx.handle, procname);
return _glfw_dlsym(_glfw.glx.handle, procname);
}
// Destroy the OpenGL context
@@ -271,7 +271,7 @@ GLFWbool _glfwInitGLX(void)
for (i = 0; sonames[i]; i++)
{
_glfw.glx.handle = dlopen(sonames[i], RTLD_LAZY | RTLD_GLOBAL);
_glfw.glx.handle = _glfw_dlopen(sonames[i]);
if (_glfw.glx.handle)
break;
}
@@ -283,35 +283,35 @@ GLFWbool _glfwInitGLX(void)
}
_glfw.glx.GetFBConfigs =
dlsym(_glfw.glx.handle, "glXGetFBConfigs");
_glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs");
_glfw.glx.GetFBConfigAttrib =
dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib");
_glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib");
_glfw.glx.GetClientString =
dlsym(_glfw.glx.handle, "glXGetClientString");
_glfw_dlsym(_glfw.glx.handle, "glXGetClientString");
_glfw.glx.QueryExtension =
dlsym(_glfw.glx.handle, "glXQueryExtension");
_glfw_dlsym(_glfw.glx.handle, "glXQueryExtension");
_glfw.glx.QueryVersion =
dlsym(_glfw.glx.handle, "glXQueryVersion");
_glfw_dlsym(_glfw.glx.handle, "glXQueryVersion");
_glfw.glx.DestroyContext =
dlsym(_glfw.glx.handle, "glXDestroyContext");
_glfw_dlsym(_glfw.glx.handle, "glXDestroyContext");
_glfw.glx.MakeCurrent =
dlsym(_glfw.glx.handle, "glXMakeCurrent");
_glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent");
_glfw.glx.SwapBuffers =
dlsym(_glfw.glx.handle, "glXSwapBuffers");
_glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers");
_glfw.glx.QueryExtensionsString =
dlsym(_glfw.glx.handle, "glXQueryExtensionsString");
_glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString");
_glfw.glx.CreateNewContext =
dlsym(_glfw.glx.handle, "glXCreateNewContext");
_glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext");
_glfw.glx.CreateWindow =
dlsym(_glfw.glx.handle, "glXCreateWindow");
_glfw_dlsym(_glfw.glx.handle, "glXCreateWindow");
_glfw.glx.DestroyWindow =
dlsym(_glfw.glx.handle, "glXDestroyWindow");
_glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow");
_glfw.glx.GetProcAddress =
dlsym(_glfw.glx.handle, "glXGetProcAddress");
_glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress");
_glfw.glx.GetProcAddressARB =
dlsym(_glfw.glx.handle, "glXGetProcAddressARB");
_glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB");
_glfw.glx.GetVisualFromFBConfig =
dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig");
_glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig");
if (!_glfw.glx.GetFBConfigs ||
!_glfw.glx.GetFBConfigAttrib ||
@@ -428,7 +428,7 @@ void _glfwTerminateGLX(void)
if (_glfw.glx.handle)
{
dlclose(_glfw.glx.handle);
_glfw_dlclose(_glfw.glx.handle);
_glfw.glx.handle = NULL;
}
}

46
glfw/init.c vendored
View File

@@ -35,8 +35,9 @@
#include <assert.h>
// The global variables below comprise all global data in GLFW.
// Any other global variable is a bug.
// The global variables below comprise all mutable global data in GLFW
//
// Any other global variable is a bug
// Global state shared between compilation units of GLFW
//
@@ -53,10 +54,6 @@ static _GLFWinitconfig _glfwInitHints =
{
GLFW_TRUE, // macOS menu bar
GLFW_TRUE // macOS bundle chdir
},
{
"", // X11 WM_CLASS name
"" // X11 WM_CLASS class
}
};
@@ -141,10 +138,25 @@ static void terminate(void)
}
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
char* _glfw_strdup(const char* source)
{
const size_t length = strlen(source);
char* result = calloc(length + 1, 1);
strcpy(result, source);
return result;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW event API //////
//////////////////////////////////////////////////////////////////////////
// Notifies shared code of an error
//
void _glfwInputError(int code, const char* format, ...)
{
_GLFWerror* error;
@@ -260,27 +272,7 @@ GLFWAPI void glfwInitHint(int hint, int value)
}
_glfwInputError(GLFW_INVALID_ENUM,
"Invalid integer type init hint 0x%08X", hint);
}
GLFWAPI void glfwInitHintString(int hint, const char* value)
{
assert(value != NULL);
switch (hint)
{
case GLFW_X11_WM_CLASS_NAME:
strncpy(_glfwInitHints.x11.className, value,
sizeof(_glfwInitHints.x11.className) - 1);
return;
case GLFW_X11_WM_CLASS_CLASS:
strncpy(_glfwInitHints.x11.classClass, value,
sizeof(_glfwInitHints.x11.classClass) - 1);
return;
}
_glfwInputError(GLFW_INVALID_ENUM,
"Invalid string type init hint 0x%08X", hint);
"Invalid init hint 0x%08X", hint);
}
GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev)

212
glfw/input.c vendored
View File

@@ -57,6 +57,58 @@ static _GLFWmapping* findMapping(const char* guid)
return NULL;
}
// Checks whether a gamepad mapping element is present in the hardware
//
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
const _GLFWjoystick* js)
{
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount)
return GLFW_FALSE;
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount)
return GLFW_FALSE;
else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount)
return GLFW_FALSE;
return GLFW_TRUE;
}
// Finds a mapping based on joystick GUID and verifies element indices
//
static _GLFWmapping* findValidMapping(const _GLFWjoystick* js)
{
_GLFWmapping* mapping = findMapping(js->guid);
if (mapping)
{
int i;
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
{
if (!isValidElementForJoystick(mapping->buttons + i, js))
{
_glfwInputError(GLFW_INVALID_VALUE,
"Invalid button in gamepad mapping %s (%s)",
mapping->guid,
mapping->name);
return NULL;
}
}
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
{
if (!isValidElementForJoystick(mapping->axes + i, js))
{
_glfwInputError(GLFW_INVALID_VALUE,
"Invalid axis in gamepad mapping %s (%s)",
mapping->guid,
mapping->name);
return NULL;
}
}
}
return mapping;
}
// Parses an SDL_GameControllerDB line and adds it to the mapping list
//
static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
@@ -115,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);
@@ -125,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
{
@@ -172,6 +255,8 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
////// GLFW event API //////
//////////////////////////////////////////////////////////////////////////
// Notifies shared code of a physical key event
//
void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key >= 0 && key <= GLFW_KEY_LAST)
@@ -193,15 +278,24 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m
action = GLFW_REPEAT;
}
if (!window->lockKeyMods)
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
if (window->callbacks.key)
window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods);
}
// Notifies shared code of a Unicode codepoint input event
// The 'plain' parameter determines whether to emit a regular character event
//
void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain)
{
if (codepoint < 32 || (codepoint > 126 && codepoint < 160))
return;
if (!window->lockKeyMods)
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
if (window->callbacks.charmods)
window->callbacks.charmods((GLFWwindow*) window, codepoint, mods);
@@ -212,17 +306,24 @@ void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWb
}
}
// Notifies shared code of a scroll event
//
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
{
if (window->callbacks.scroll)
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset);
}
// Notifies shared code of a mouse button click event
//
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)
{
if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST)
return;
if (!window->lockKeyMods)
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
if (action == GLFW_RELEASE && window->stickyMouseButtons)
window->mouseButtons[button] = _GLFW_STICK;
else
@@ -232,6 +333,9 @@ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)
window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods);
}
// Notifies shared code of a cursor motion event
// The position is specified in client-area relative screen coordinates
//
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos)
{
if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos)
@@ -244,18 +348,24 @@ void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos)
window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos);
}
// Notifies shared code of a cursor enter/leave event
//
void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered)
{
if (window->callbacks.cursorEnter)
window->callbacks.cursorEnter((GLFWwindow*) window, entered);
}
// Notifies shared code of files or directories dropped on a window
//
void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
{
if (window->callbacks.drop)
window->callbacks.drop((GLFWwindow*) window, count, paths);
}
// Notifies shared code of a joystick connection or disconnection
//
void _glfwInputJoystick(_GLFWjoystick* js, int event)
{
const int jid = (int) (js - _glfw.joysticks);
@@ -264,16 +374,22 @@ void _glfwInputJoystick(_GLFWjoystick* js, int event)
_glfw.callbacks.joystick(jid, event);
}
// Notifies shared code of the new value of a joystick axis
//
void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value)
{
js->axes[axis] = value;
}
// Notifies shared code of the new value of a joystick button
//
void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value)
{
js->buttons[button] = value;
}
// Notifies shared code of the new value of a joystick hat
//
void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value)
{
const int base = js->buttonCount + hat * 4;
@@ -291,6 +407,8 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value)
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
// Returns an available joystick object with arrays and name allocated
//
_GLFWjoystick* _glfwAllocJoystick(const char* name,
const char* guid,
int axisCount,
@@ -311,20 +429,22 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name,
js = _glfw.joysticks + jid;
js->present = GLFW_TRUE;
js->name = strdup(name);
js->name = _glfw_strdup(name);
js->axes = calloc(axisCount, sizeof(float));
js->buttons = calloc(buttonCount + hatCount * 4, 1);
js->hats = calloc(hatCount, 1);
js->axisCount = axisCount;
js->buttonCount = buttonCount;
js->hatCount = hatCount;
js->mapping = findMapping(guid);
strcpy(js->guid, guid);
js->mapping = findValidMapping(js);
return js;
}
// Frees arrays and name and flags the joystick object as unused
//
void _glfwFreeJoystick(_GLFWjoystick* js)
{
free(js->name);
@@ -354,6 +474,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
return window->stickyKeys;
case GLFW_STICKY_MOUSE_BUTTONS:
return window->stickyMouseButtons;
case GLFW_LOCK_KEY_MODS:
return window->lockKeyMods;
}
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
@@ -409,7 +531,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
}
}
window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE;
window->stickyKeys = value;
}
else if (mode == GLFW_STICKY_MOUSE_BUTTONS)
{
@@ -429,8 +551,10 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
}
}
window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE;
window->stickyMouseButtons = value;
}
else if (mode == GLFW_LOCK_KEY_MODS)
window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE;
else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
}
@@ -914,6 +1038,38 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid)
return js->guid;
}
GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer)
{
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT();
js = _glfw.joysticks + jid;
if (!js->present)
return;
js->userPointer = pointer;
}
GLFWAPI void* glfwGetJoystickUserPointer(int jid)
{
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
return js->userPointer;
}
GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@@ -973,7 +1129,7 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
{
_GLFWjoystick* js = _glfw.joysticks + jid;
if (js->present)
js->mapping = findMapping(js->guid);
js->mapping = findValidMapping(js);
}
return GLFW_TRUE;
@@ -1063,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->buttons[i].value >> 4;
const unsigned int bit = js->mapping->buttons[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;

484
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;
@@ -196,38 +196,6 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
#error "No supported window creation API selected"
#endif
//========================================================================
// Doxygen group definitions
//========================================================================
/*! @defgroup platform Platform interface
* @brief The interface implemented by the platform-specific code.
*
* The platform API is the interface exposed by the platform-specific code for
* each platform and is called by the shared code of the public API It mirrors
* the public API except it uses objects instead of handles.
*/
/*! @defgroup event Event interface
* @brief The interface used by the platform-specific code to report events.
*
* The event API is used by the platform-specific code to notify the shared
* code of events that can be translated into state changes and/or callback
* calls.
*/
/*! @defgroup utility Utility functions
* @brief Various utility functions for internal use.
*
* These functions are shared code and may be used by any part of GLFW
* Each platform may add its own utility functions, but those must only be
* called by the platform-specific code
*/
//========================================================================
// Helper macros
//========================================================================
// Constructs a version number string from the public header macros
#define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r
#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r)
@@ -258,11 +226,8 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
y = t; \
}
//========================================================================
// Platform-independent structures
//========================================================================
// Per-thread error structure
//
struct _GLFWerror
{
_GLFWerror* next;
@@ -270,10 +235,10 @@ struct _GLFWerror
char description[_GLFW_MESSAGE_SIZE];
};
/*! @brief Initialization configuration.
*
* Parameters relating to the initialization of the library.
*/
// Initialization configuration
//
// Parameters relating to the initialization of the library
//
struct _GLFWinitconfig
{
GLFWbool hatButtons;
@@ -281,18 +246,14 @@ struct _GLFWinitconfig
GLFWbool menubar;
GLFWbool chdir;
} ns;
struct {
char className[256];
char classClass[256];
} x11;
};
/*! @brief Window configuration.
*
* Parameters relating to the creation of the window but not directly related
* to the framebuffer. This is used to pass window creation parameters from
* shared code to the platform API.
*/
// Window configuration
//
// Parameters relating to the creation of the window but not directly related
// to the framebuffer. This is used to pass window creation parameters from
// shared code to the platform API.
//
struct _GLFWwndconfig
{
int width;
@@ -308,16 +269,20 @@ struct _GLFWwndconfig
GLFWbool centerCursor;
struct {
GLFWbool retina;
GLFWbool frame;
char frameName[256];
} ns;
struct {
char className[256];
char instanceName[256];
} x11;
};
/*! @brief Context configuration.
*
* Parameters relating to the creation of the context but not directly related
* to the framebuffer. This is used to pass context creation parameters from
* shared code to the platform API.
*/
// Context configuration
//
// Parameters relating to the creation of the context but not directly related
// to the framebuffer. This is used to pass context creation parameters from
// shared code to the platform API.
//
struct _GLFWctxconfig
{
int client;
@@ -336,14 +301,14 @@ struct _GLFWctxconfig
} nsgl;
};
/*! @brief Framebuffer configuration.
*
* This describes buffers and their sizes. It also contains
* a platform-specific ID used to map back to the backend API object.
*
* It is used to pass framebuffer parameters from shared code to the platform
* API and also to enumerate and select available framebuffer configs.
*/
// Framebuffer configuration
//
// This describes buffers and their sizes. It also contains
// a platform-specific ID used to map back to the backend API object.
//
// It is used to pass framebuffer parameters from shared code to the platform
// API and also to enumerate and select available framebuffer configs.
//
struct _GLFWfbconfig
{
int redBits;
@@ -365,8 +330,8 @@ struct _GLFWfbconfig
uintptr_t handle;
};
/*! @brief Context structure.
*/
// Context structure
//
struct _GLFWcontext
{
int client;
@@ -396,8 +361,8 @@ struct _GLFWcontext
_GLFW_OSMESA_CONTEXT_STATE;
};
/*! @brief Window and context structure.
*/
// Window and context structure
//
struct _GLFWwindow
{
struct _GLFWwindow* next;
@@ -419,6 +384,7 @@ struct _GLFWwindow
GLFWbool stickyKeys;
GLFWbool stickyMouseButtons;
GLFWbool lockKeyMods;
int cursorMode;
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
char keys[GLFW_KEY_LAST + 1];
@@ -436,6 +402,7 @@ struct _GLFWwindow
GLFWwindowiconifyfun iconify;
GLFWwindowmaximizefun maximize;
GLFWframebuffersizefun fbsize;
GLFWwindowcontentscalefun scale;
GLFWmousebuttonfun mouseButton;
GLFWcursorposfun cursorPos;
GLFWcursorenterfun cursorEnter;
@@ -450,11 +417,12 @@ struct _GLFWwindow
_GLFW_PLATFORM_WINDOW_STATE;
};
/*! @brief Monitor structure.
*/
// Monitor structure
//
struct _GLFWmonitor
{
char* name;
void* userPointer;
// Physical dimensions in millimeters.
int widthMM, heightMM;
@@ -473,8 +441,8 @@ struct _GLFWmonitor
_GLFW_PLATFORM_MONITOR_STATE;
};
/*! @brief Cursor structure
*/
// Cursor structure
//
struct _GLFWcursor
{
_GLFWcursor* next;
@@ -483,16 +451,18 @@ struct _GLFWcursor
_GLFW_PLATFORM_CURSOR_STATE;
};
/*! @brief Gamepad mapping element structure
*/
// Gamepad mapping element structure
//
struct _GLFWmapelement
{
uint8_t type;
uint8_t value;
uint8_t index;
int8_t axisScale;
int8_t axisOffset;
};
/*! @brief Gamepad mapping structure
*/
// Gamepad mapping structure
//
struct _GLFWmapping
{
char name[128];
@@ -501,8 +471,8 @@ struct _GLFWmapping
_GLFWmapelement axes[6];
};
/*! @brief Joystick structure
*/
// Joystick structure
//
struct _GLFWjoystick
{
GLFWbool present;
@@ -513,6 +483,7 @@ struct _GLFWjoystick
unsigned char* hats;
int hatCount;
char* name;
void* userPointer;
char guid[33];
_GLFWmapping* mapping;
@@ -520,24 +491,24 @@ struct _GLFWjoystick
_GLFW_PLATFORM_JOYSTICK_STATE;
};
/*! @brief Thread local storage structure.
*/
// Thread local storage structure
//
struct _GLFWtls
{
// This is defined in the platform's thread.h
_GLFW_PLATFORM_TLS_STATE;
};
/*! @brief Mutex structure.
*/
// Mutex structure
//
struct _GLFWmutex
{
// This is defined in the platform's thread.h
_GLFW_PLATFORM_MUTEX_STATE;
};
/*! @brief Library global data.
*/
// Library global data
//
struct _GLFWlibrary
{
GLFWbool initialized;
@@ -611,21 +582,14 @@ struct _GLFWlibrary
_GLFW_OSMESA_LIBRARY_CONTEXT_STATE;
};
//========================================================================
// Global state shared between compilation units of GLFW
//========================================================================
/*! @brief All global data shared between compilation units.
*/
//
extern _GLFWlibrary _glfw;
//========================================================================
// Platform API functions
//========================================================================
/*! @addtogroup platform @{ */
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformInit(void);
void _glfwPlatformTerminate(void);
@@ -634,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);
@@ -642,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);
@@ -664,29 +631,38 @@ 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);
int _glfwPlatformWindowMaximized(_GLFWwindow* window);
int _glfwPlatformWindowHovered(_GLFWwindow* window);
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window);
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window);
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled);
@@ -700,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);
@@ -713,307 +694,78 @@ void _glfwPlatformDestroyMutex(_GLFWmutex* mutex);
void _glfwPlatformLockMutex(_GLFWmutex* mutex);
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex);
/*! @} */
//////////////////////////////////////////////////////////////////////////
////// GLFW event API //////
//////////////////////////////////////////////////////////////////////////
//========================================================================
// Event API functions
//========================================================================
/*! @brief Notifies shared code that a window has lost or received input focus.
* @param[in] window The window that received the event.
* @param[in] focused `GLFW_TRUE` if the window received focus, or `GLFW_FALSE`
* if it lost focus.
* @ingroup event
*/
void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused);
/*! @brief Notifies shared code that a window has moved.
* @param[in] window The window that received the event.
* @param[in] xpos The new x-coordinate of the client area of the window.
* @param[in] ypos The new y-coordinate of the client area of the window.
* @ingroup event
*/
void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos);
/*! @brief Notifies shared code that a window has been resized.
* @param[in] window The window that received the event.
* @param[in] width The new width of the client area of the window.
* @param[in] height The new height of the client area of the window.
* @ingroup event
*/
void _glfwInputWindowSize(_GLFWwindow* window, int width, int height);
/*! @brief Notifies shared code that a window framebuffer has been resized.
* @param[in] window The window that received the event.
* @param[in] width The new width, in pixels, of the framebuffer.
* @param[in] height The new height, in pixels, of the framebuffer.
* @ingroup event
*/
void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height);
/*! @brief Notifies shared code that a window has been iconified or restored.
* @param[in] window The window that received the event.
* @param[in] iconified `GLFW_TRUE` if the window was iconified, or
* `GLFW_FALSE` if it was restored.
* @ingroup event
*/
void _glfwInputWindowContentScale(_GLFWwindow* window,
float xscale, float yscale);
void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified);
/*! @brief Notifies shared code that a window has been maximized or restored.
* @param[in] window The window that received the event.
* @param[in] maximized `GLFW_TRUE` if the window was maximized, or
* `GLFW_FALSE` if it was restored.
* @ingroup event
*/
void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized);
/*! @brief Notifies shared code that a window's contents needs updating.
* @param[in] window The window that received the event.
*/
void _glfwInputWindowDamage(_GLFWwindow* window);
/*! @brief Notifies shared code that the user wishes to close a window.
* @param[in] window The window that received the event.
* @ingroup event
*/
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
/*! @brief Notifies shared code that a window has changed its desired monitor.
* @param[in] window The window that received the event.
* @param[in] monitor The new desired monitor, or `NULL`.
* @ingroup event
*/
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
/*! @brief Notifies shared code of a physical key event.
* @param[in] window The window that received the event.
* @param[in] key The key that was pressed or released.
* @param[in] scancode The system-specific scan code of the key.
* @param[in] action @ref GLFW_PRESS or @ref GLFW_RELEASE.
* @param[in] mods The modifiers pressed when the event was generated.
* @ingroup event
*/
void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods);
/*! @brief Notifies shared code of a Unicode character input event.
* @param[in] window The window that received the event.
* @param[in] codepoint The Unicode code point of the input character.
* @param[in] mods Bit field describing which modifier keys were held down.
* @param[in] plain `GLFW_TRUE` if the character is regular text input, or
* `GLFW_FALSE` otherwise.
* @ingroup event
*/
void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain);
/*! @brief Notifies shared code of a scroll event.
* @param[in] window The window that received the event.
* @param[in] xoffset The scroll offset along the x-axis.
* @param[in] yoffset The scroll offset along the y-axis.
* @ingroup event
*/
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);
/*! @brief Notifies shared code of a mouse button click event.
* @param[in] window The window that received the event.
* @param[in] button The button that was pressed or released.
* @param[in] action @ref GLFW_PRESS or @ref GLFW_RELEASE.
* @param[in] mods The modifiers pressed when the event was generated.
* @ingroup event
*/
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
/*! @brief Notifies shared code of a cursor motion event.
* @param[in] window The window that received the event.
* @param[in] xpos The new x-coordinate of the cursor, relative to the left
* edge of the client area of the window.
* @param[in] ypos The new y-coordinate of the cursor, relative to the top edge
* of the client area of the window.
* @ingroup event
*/
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
/*! @brief Notifies shared code of a cursor enter/leave event.
* @param[in] window The window that received the event.
* @param[in] entered `GLFW_TRUE` if the cursor entered the client area of the
* window, or `GLFW_FALSE` if it left it.
* @ingroup event
*/
void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered);
void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
void _glfwInputJoystick(_GLFWjoystick* js, int event);
void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value);
void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value);
void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value);
/*! @brief Notifies shared code of a monitor connection or disconnection.
* @param[in] monitor The monitor that was connected or disconnected.
* @param[in] action One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @param[in] placement `_GLFW_INSERT_FIRST` or `_GLFW_INSERT_LAST`.
* @ingroup event
*/
void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement);
/*! @brief Notifies shared code that a full screen window has acquired or
* released a monitor.
* @param[in] monitor The monitor that was acquired or released.
* @param[in] window The window that acquired the monitor, or `NULL`.
* @ingroup event
*/
void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window);
/*! @brief Notifies shared code of an error.
* @param[in] code The error code most suitable for the error.
* @param[in] format The `printf` style format string of the error
* description.
* @ingroup event
*/
#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
/*! @brief Notifies shared code of files or directories dropped on a window.
* @param[in] window The window that received the event.
* @param[in] count The number of dropped objects.
* @param[in] names The names of the dropped objects.
* @ingroup event
*/
void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
/*! @brief Notifies shared code of a joystick connection or disconnection.
* @param[in] js The joystick that was connected or disconnected.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @ingroup event
*/
void _glfwInputJoystick(_GLFWjoystick* js, int event);
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
/*! @brief Notifies shared code of the new value of a joystick axis.
* @param[in] js The joystick whose axis to update.
* @param[in] axis The index of the axis to update.
* @param[in] value The new value of the axis.
*/
void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value);
/*! @brief Notifies shared code of the new value of a joystick button.
* @param[in] js The joystick whose button to update.
* @param[in] button The index of the button to update.
* @param[in] value The new value of the button.
*/
void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value);
/*! @brief Notifies shared code of the new value of a joystick hat.
* @param[in] js The joystick whose hat to update.
* @param[in] button The index of the hat to update.
* @param[in] value The new value of the hat.
*/
void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value);
//========================================================================
// Utility functions
//========================================================================
/*! @brief Chooses the video mode most closely matching the desired one.
* @ingroup utility
*/
const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
const GLFWvidmode* desired);
/*! @brief Performs lexical comparison between two @ref GLFWvidmode structures.
* @ingroup utility
*/
int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second);
/*! @brief Splits a color depth into red, green and blue bit depths.
* @ingroup utility
*/
void _glfwSplitBPP(int bpp, int* red, int* green, int* blue);
/*! @brief Searches an extension string for the specified extension.
* @param[in] string The extension string to search.
* @param[in] extensions The extension to search for.
* @return `GLFW_TRUE` if the extension was found, or `GLFW_FALSE` otherwise.
* @ingroup utility
*/
GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions);
/*! @brief Chooses the framebuffer config that best matches the desired one.
* @param[in] desired The desired framebuffer config.
* @param[in] alternatives The framebuffer configs supported by the system.
* @param[in] count The number of entries in the alternatives array.
* @return The framebuffer config most closely matching the desired one, or @c
* NULL if none fulfilled the hard constraints of the desired values.
* @ingroup utility
*/
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
const _GLFWfbconfig* alternatives,
unsigned int count);
/*! @brief Retrieves the attributes of the current context.
* @param[in] ctxconfig The desired context attributes.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if the context is
* unusable.
* @ingroup utility
*/
GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig);
/*! @brief Checks whether the desired context attributes are valid.
* @param[in] ctxconfig The context attributes to check.
* @return `GLFW_TRUE` if the context attributes are valid, or `GLFW_FALSE`
* otherwise.
* @ingroup utility
*
* This function checks things like whether the specified client API version
* exists and whether all relevant options have supported and non-conflicting
* values.
*/
GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig);
GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig);
/*! @brief Allocates red, green and blue value arrays of the specified size.
* @ingroup utility
*/
void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size);
/*! @brief Frees the red, green and blue value arrays and clears the struct.
* @ingroup utility
*/
void _glfwFreeGammaArrays(GLFWgammaramp* ramp);
/*! @brief Allocates and returns a monitor object with the specified name
* and dimensions.
* @param[in] name The name of the monitor.
* @param[in] widthMM The width, in mm, of the monitor's display area.
* @param[in] heightMM The height, in mm, of the monitor's display area.
* @return The newly created object.
* @ingroup utility
*/
const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
const GLFWvidmode* desired);
int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second);
_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM);
/*! @brief Frees a monitor object and any data associated with it.
* @ingroup utility
*/
void _glfwFreeMonitor(_GLFWmonitor* monitor);
void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size);
void _glfwFreeGammaArrays(GLFWgammaramp* ramp);
void _glfwSplitBPP(int bpp, int* red, int* green, int* blue);
/*! @brief Returns an available joystick object with arrays and name allocated.
* @ingroup utility
*/
_GLFWjoystick* _glfwAllocJoystick(const char* name,
const char* guid,
int axisCount,
int buttonCount,
int hatCount);
/*! @brief Frees arrays and name and flags the joystick object as unused.
* @ingroup utility
*/
void _glfwFreeJoystick(_GLFWjoystick* js);
/*! @ingroup utility
*/
GLFWbool _glfwInitVulkan(int mode);
/*! @ingroup utility
*/
void _glfwTerminateVulkan(void);
/*! @ingroup utility
*/
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)

626
glfw/mappings.h vendored
View File

@@ -51,7 +51,7 @@
// 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.
// 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.
@@ -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,",

51
glfw/monitor.c vendored
View File

@@ -86,6 +86,8 @@ static GLFWbool refreshVideoModes(_GLFWmonitor* monitor)
////// GLFW event API //////
//////////////////////////////////////////////////////////////////////////
// Notifies shared code of a monitor connection or disconnection
//
void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)
{
if (action == GLFW_CONNECTED)
@@ -141,6 +143,9 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)
_glfwFreeMonitor(monitor);
}
// Notifies shared code that a full screen window has acquired or released
// a monitor
//
void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window)
{
monitor->window = window;
@@ -151,6 +156,8 @@ void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window)
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
// Allocates and returns a monitor object with the specified name and dimensions
//
_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM)
{
_GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor));
@@ -158,16 +165,20 @@ _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM)
monitor->heightMM = heightMM;
if (name)
monitor->name = strdup(name);
monitor->name = _glfw_strdup(name);
return monitor;
}
// Frees a monitor object and any data associated with it
//
void _glfwFreeMonitor(_GLFWmonitor* monitor)
{
if (monitor == NULL)
return;
_glfwPlatformFreeMonitor(monitor);
_glfwFreeGammaArrays(&monitor->originalRamp);
_glfwFreeGammaArrays(&monitor->currentRamp);
@@ -176,6 +187,8 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor)
free(monitor);
}
// Allocates red, green and blue value arrays of the specified size
//
void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size)
{
ramp->red = calloc(size, sizeof(unsigned short));
@@ -184,6 +197,8 @@ void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size)
ramp->size = size;
}
// Frees the red, green and blue value arrays and clears the struct
//
void _glfwFreeGammaArrays(GLFWgammaramp* ramp)
{
free(ramp->red);
@@ -193,6 +208,8 @@ void _glfwFreeGammaArrays(GLFWgammaramp* ramp)
memset(ramp, 0, sizeof(GLFWgammaramp));
}
// Chooses the video mode most closely matching the desired one
//
const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
const GLFWvidmode* desired)
{
@@ -243,11 +260,15 @@ const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
return closest;
}
// Performs lexical comparison between two @ref GLFWvidmode structures
//
int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm)
{
return compareVideoModes(fm, sm);
}
// Splits a color depth into red, green and blue bit depths
//
void _glfwSplitBPP(int bpp, int* red, int* green, int* blue)
{
int delta;
@@ -351,6 +372,24 @@ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)
return monitor->name;
}
GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
assert(monitor != NULL);
_GLFW_REQUIRE_INIT();
monitor->userPointer = pointer;
}
GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
assert(monitor != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return monitor->userPointer;
}
GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@@ -406,16 +445,16 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
for (i = 0; i < 256; i++)
{
double value;
float value;
// Calculate intensity
value = i / 255.0;
value = i / 255.f;
// Apply gamma curve
value = pow(value, 1.0 / gamma) * 65535.0 + 0.5;
value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
// Clamp to value range
if (value > 65535.0)
value = 65535.0;
if (value > 65535.f)
value = 65535.f;
values[i] = (unsigned short) value;
}

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)
{
}

8
glfw/null_window.c vendored
View File

@@ -165,6 +165,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return GLFW_FALSE;
}
int _glfwPlatformWindowHovered(_GLFWwindow* window)
{
return GLFW_FALSE;
}
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return GLFW_FALSE;
@@ -200,7 +205,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
}
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
int _glfwPlatformWindowBell(_GLFWwindow* window)
{
return GLFW_FALSE;
}
@@ -318,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,14 +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"
@@ -71,14 +74,18 @@
"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": [
1,
12
],
"win32": {
"headers": [
"win32_platform.h",

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);
}

91
glfw/wgl_context.c vendored
View File

@@ -329,21 +329,52 @@ static void destroyContextWGL(_GLFWwindow* window)
}
}
// Initialize WGL-specific extensions
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
// Initialize WGL
//
static void loadWGLExtensions(void)
GLFWbool _glfwInitWGL(void)
{
PIXELFORMATDESCRIPTOR pfd;
HGLRC rc;
HDC dc = GetDC(_glfw.win32.helperWindowHandle);;
HGLRC prc, rc;
HDC pdc, dc;
_glfw.wgl.extensionsLoaded = GLFW_TRUE;
if (_glfw.wgl.instance)
return GLFW_TRUE;
_glfw.wgl.instance = LoadLibraryA("opengl32.dll");
if (!_glfw.wgl.instance)
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to load opengl32.dll");
return GLFW_FALSE;
}
_glfw.wgl.CreateContext = (PFN_wglCreateContext)
GetProcAddress(_glfw.wgl.instance, "wglCreateContext");
_glfw.wgl.DeleteContext = (PFN_wglDeleteContext)
GetProcAddress(_glfw.wgl.instance, "wglDeleteContext");
_glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress)
GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress");
_glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)
GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC");
_glfw.wgl.GetCurrentContext = (PFN_wglGetCurrentContext)
GetProcAddress(_glfw.wgl.instance, "wglGetCurrentContext");
_glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent)
GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent");
_glfw.wgl.ShareLists = (PFN_wglShareLists)
GetProcAddress(_glfw.wgl.instance, "wglShareLists");
// NOTE: A dummy context has to be created for opengl32.dll to load the
// OpenGL ICD, from which we can then query WGL extensions
// NOTE: This code will accept the Microsoft GDI ICD; accelerated context
// creation failure occurs during manual pixel format enumeration
dc = GetDC(_glfw.win32.helperWindowHandle);;
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
@@ -355,7 +386,7 @@ static void loadWGLExtensions(void)
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to set pixel format for dummy context");
return;
return GLFW_FALSE;
}
rc = wglCreateContext(dc);
@@ -363,15 +394,19 @@ static void loadWGLExtensions(void)
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to create dummy context");
return;
return GLFW_FALSE;
}
pdc = wglGetCurrentDC();
prc = wglGetCurrentContext();
if (!wglMakeCurrent(dc, rc))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to make dummy context current");
wglMakeCurrent(pdc, prc);
wglDeleteContext(rc);
return;
return GLFW_FALSE;
}
// NOTE: Functions must be loaded first as they're needed to retrieve the
@@ -414,43 +449,8 @@ static void loadWGLExtensions(void)
_glfw.wgl.ARB_context_flush_control =
extensionSupportedWGL("WGL_ARB_context_flush_control");
wglMakeCurrent(dc, NULL);
wglMakeCurrent(pdc, prc);
wglDeleteContext(rc);
}
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
// Initialize WGL
//
GLFWbool _glfwInitWGL(void)
{
if (_glfw.wgl.instance)
return GLFW_TRUE;
_glfw.wgl.instance = LoadLibraryA("opengl32.dll");
if (!_glfw.wgl.instance)
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to load opengl32.dll");
return GLFW_FALSE;
}
_glfw.wgl.CreateContext = (PFN_wglCreateContext)
GetProcAddress(_glfw.wgl.instance, "wglCreateContext");
_glfw.wgl.DeleteContext = (PFN_wglDeleteContext)
GetProcAddress(_glfw.wgl.instance, "wglDeleteContext");
_glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress)
GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress");
_glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)
GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC");
_glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent)
GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent");
_glfw.wgl.ShareLists = (PFN_wglShareLists)
GetProcAddress(_glfw.wgl.instance, "wglShareLists");
return GLFW_TRUE;
}
@@ -480,9 +480,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
PIXELFORMATDESCRIPTOR pfd;
HGLRC share = NULL;
if (!_glfw.wgl.extensionsLoaded)
loadWGLExtensions();
if (ctxconfig->share)
share = ctxconfig->share->context.wgl.handle;

5
glfw/wgl_context.h vendored
View File

@@ -86,6 +86,7 @@ typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC);
typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC);
typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR);
typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void);
typedef HGLRC (WINAPI * PFN_wglGetCurrentContext)(void);
typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC);
typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC);
@@ -94,6 +95,7 @@ typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC);
#define wglDeleteContext _glfw.wgl.DeleteContext
#define wglGetProcAddress _glfw.wgl.GetProcAddress
#define wglGetCurrentDC _glfw.wgl.GetCurrentDC
#define wglGetCurrentContext _glfw.wgl.GetCurrentContext
#define wglMakeCurrent _glfw.wgl.MakeCurrent
#define wglShareLists _glfw.wgl.ShareLists
@@ -124,11 +126,10 @@ typedef struct _GLFWlibraryWGL
PFN_wglDeleteContext DeleteContext;
PFN_wglGetProcAddress GetProcAddress;
PFN_wglGetCurrentDC GetCurrentDC;
PFN_wglGetCurrentContext GetCurrentContext;
PFN_wglMakeCurrent MakeCurrent;
PFN_wglShareLists ShareLists;
GLFWbool extensionsLoaded;
PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT;
PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB;
PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT;

10
glfw/win32_init.c vendored
View File

@@ -351,9 +351,10 @@ static HWND createHelperWindow(void)
dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
dbi.dbcc_classguid = GUID_DEVINTERFACE_HID;
RegisterDeviceNotificationW(window,
(DEV_BROADCAST_HDR*) &dbi,
DEVICE_NOTIFY_WINDOW_HANDLE);
_glfw.win32.deviceNotificationHandle =
RegisterDeviceNotificationW(window,
(DEV_BROADCAST_HDR*) &dbi,
DEVICE_NOTIFY_WINDOW_HANDLE);
}
while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE))
@@ -543,6 +544,9 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void)
{
if (_glfw.win32.deviceNotificationHandle)
UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle);
if (_glfw.win32.helperWindowHandle)
DestroyWindow(_glfw.win32.helperWindowHandle);

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;

19
glfw/win32_platform.h vendored
View File

@@ -67,11 +67,6 @@
#include <xinput.h>
#include <dbt.h>
#if defined(_MSC_VER)
#include <malloc.h>
#define strdup _strdup
#endif
// HACK: Define macros that some windows.h variants don't
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
@@ -105,12 +100,11 @@
#endif
#if WINVER < 0x0601
typedef struct tagCHANGEFILTERSTRUCT
typedef struct
{
DWORD cbSize;
DWORD ExtStatus;
} CHANGEFILTERSTRUCT, *PCHANGEFILTERSTRUCT;
} CHANGEFILTERSTRUCT;
#ifndef MSGFLT_ALLOW
#define MSGFLT_ALLOW 1
#endif
@@ -129,13 +123,13 @@ typedef struct
#endif /*Windows Vista*/
#ifndef DPI_ENUMS_DECLARED
typedef enum PROCESS_DPI_AWARENESS
typedef enum
{
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS;
typedef enum MONITOR_DPI_TYPE
typedef enum
{
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,
@@ -209,7 +203,7 @@ typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*
// user32.dll function pointer typedefs
typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void);
typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT);
typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*);
#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_
#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_
@@ -289,6 +283,7 @@ typedef struct _GLFWwindowWin32
typedef struct _GLFWlibraryWin32
{
HWND helperWindowHandle;
HDEVNOTIFY deviceNotificationHandle;
DWORD foregroundLockTimeout;
int acquiredMonitorCount;
char* clipboardString;
@@ -403,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);

106
glfw/win32_window.c vendored
View File

@@ -384,14 +384,18 @@ static int getKeyMods(void)
{
int mods = 0;
if (GetKeyState(VK_SHIFT) & (1 << 31))
if (GetKeyState(VK_SHIFT) & 0x8000)
mods |= GLFW_MOD_SHIFT;
if (GetKeyState(VK_CONTROL) & (1 << 31))
if (GetKeyState(VK_CONTROL) & 0x8000)
mods |= GLFW_MOD_CONTROL;
if (GetKeyState(VK_MENU) & (1 << 31))
if (GetKeyState(VK_MENU) & 0x8000)
mods |= GLFW_MOD_ALT;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1 << 31))
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
mods |= GLFW_MOD_SUPER;
if (GetKeyState(VK_CAPITAL) & 1)
mods |= GLFW_MOD_CAPS_LOCK;
if (GetKeyState(VK_NUMLOCK) & 1)
mods |= GLFW_MOD_NUM_LOCK;
return mods;
}
@@ -402,14 +406,18 @@ static int getAsyncKeyMods(void)
{
int mods = 0;
if (GetAsyncKeyState(VK_SHIFT) & (1 << 31))
if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
mods |= GLFW_MOD_SHIFT;
if (GetAsyncKeyState(VK_CONTROL) & (1 << 31))
if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
mods |= GLFW_MOD_CONTROL;
if (GetAsyncKeyState(VK_MENU) & (1 << 31))
if (GetAsyncKeyState(VK_MENU) & 0x8000)
mods |= GLFW_MOD_ALT;
if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & (1 << 31))
if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & 0x8000)
mods |= GLFW_MOD_SUPER;
if (GetAsyncKeyState(VK_CAPITAL) & 1)
mods |= GLFW_MOD_CAPS_LOCK;
if (GetAsyncKeyState(VK_NUMLOCK) & 1)
mods |= GLFW_MOD_NUM_LOCK;
return mods;
}
@@ -463,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
@@ -893,7 +900,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
if (iconified)
releaseMonitor(window);
else
{
acquireMonitor(window);
fitToMonitor(window);
}
}
window->win32.iconified = iconified;
@@ -988,6 +998,14 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
return 0;
}
case WM_DPICHANGED:
{
const float xscale = HIWORD(wParam) / 96.f;
const float yscale = LOWORD(wParam) / 96.f;
_glfwInputWindowContentScale(window, xscale, yscale);
break;
}
case WM_SETCURSOR:
{
if (LOWORD(lParam) == HTCLIENT)
@@ -1224,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;
@@ -1344,7 +1359,10 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
if (window->monitor)
{
if (window->monitor->window == window)
{
acquireMonitor(window);
fitToMonitor(window);
}
}
else
{
@@ -1443,7 +1461,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
ShowWindow(window->win32.handle, SW_SHOW);
ShowWindow(window->win32.handle, SW_SHOWNA);
}
void _glfwPlatformHideWindow(_GLFWwindow* window)
@@ -1456,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)
@@ -1479,7 +1497,10 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
if (monitor)
{
if (monitor->window == window)
{
acquireMonitor(window);
fitToMonitor(window);
}
}
else
{
@@ -1502,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
{
@@ -1567,6 +1595,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return IsZoomed(window->win32.handle);
}
int _glfwPlatformWindowHovered(_GLFWwindow* window)
{
return cursorInClientArea(window);
}
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return window->win32.transparent && _glfwIsCompositionEnabledWin32();
@@ -1981,4 +2014,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return window->win32.handle;
}

113
glfw/window.c vendored
View File

@@ -38,6 +38,8 @@
////// GLFW event API //////
//////////////////////////////////////////////////////////////////////////
// Notifies shared code that a window has lost or received input focus
//
void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused)
{
if (window->callbacks.focus)
@@ -64,42 +66,68 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused)
}
}
// Notifies shared code that a window has moved
// The position is specified in client-area relative screen coordinates
//
void _glfwInputWindowPos(_GLFWwindow* window, int x, int y)
{
if (window->callbacks.pos)
window->callbacks.pos((GLFWwindow*) window, x, y);
}
// Notifies shared code that a window has been resized
// The size is specified in screen coordinates
//
void _glfwInputWindowSize(_GLFWwindow* window, int width, int height)
{
if (window->callbacks.size)
window->callbacks.size((GLFWwindow*) window, width, height);
}
// Notifies shared code that a window has been iconified or restored
//
void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified)
{
if (window->callbacks.iconify)
window->callbacks.iconify((GLFWwindow*) window, iconified);
}
// Notifies shared code that a window has been maximized or restored
//
void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized)
{
if (window->callbacks.maximize)
window->callbacks.maximize((GLFWwindow*) window, maximized);
}
// Notifies shared code that a window framebuffer has been resized
// The size is specified in pixels
//
void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height)
{
if (window->callbacks.fbsize)
window->callbacks.fbsize((GLFWwindow*) window, width, height);
}
// Notifies shared code that a window content scale has changed
// The scale is specified as the ratio between the current and default DPI
//
void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale)
{
if (window->callbacks.scale)
window->callbacks.scale((GLFWwindow*) window, xscale, yscale);
}
// Notifies shared code that the window contents needs updating
//
void _glfwInputWindowDamage(_GLFWwindow* window)
{
if (window->callbacks.refresh)
window->callbacks.refresh((GLFWwindow*) window);
}
// Notifies shared code that the user wishes to close a window
//
void _glfwInputWindowCloseRequest(_GLFWwindow* window)
{
window->shouldClose = GLFW_TRUE;
@@ -108,6 +136,8 @@ void _glfwInputWindowCloseRequest(_GLFWwindow* window)
window->callbacks.close((GLFWwindow*) window);
}
// Notifies shared code that a window has changed its desired monitor
//
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor)
{
window->monitor = monitor;
@@ -127,7 +157,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
_GLFWctxconfig ctxconfig;
_GLFWwndconfig wndconfig;
_GLFWwindow* window;
_GLFWwindow* previous;
assert(title != NULL);
assert(width >= 0);
@@ -153,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;
@@ -191,36 +210,32 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->numer = GLFW_DONT_CARE;
window->denom = GLFW_DONT_CARE;
// Save the currently current context so it can be restored later
previous = _glfwPlatformGetTls(&_glfw.contextSlot);
if (ctxconfig.client != GLFW_NO_API)
glfwMakeContextCurrent(NULL);
// Open the actual window and create its context
if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig))
{
glfwMakeContextCurrent((GLFWwindow*) previous);
glfwDestroyWindow((GLFWwindow*) window);
return NULL;
}
if (ctxconfig.client != GLFW_NO_API)
{
window->context.makeCurrent(window);
// Retrieve the actual (as opposed to requested) context attributes
if (!_glfwRefreshContextAttribs(&ctxconfig))
if (!_glfwRefreshContextAttribs(window, &ctxconfig))
{
glfwMakeContextCurrent((GLFWwindow*) previous);
glfwDestroyWindow((GLFWwindow*) window);
return NULL;
}
// Restore the previously current context (or NULL)
glfwMakeContextCurrent((GLFWwindow*) previous);
}
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)
{
@@ -349,9 +364,6 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_COCOA_RETINA_FRAMEBUFFER:
_glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_COCOA_FRAME_AUTOSAVE:
_glfw.hints.window.ns.frame = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_COCOA_GRAPHICS_SWITCHING:
_glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE;
return;
@@ -396,6 +408,31 @@ GLFWAPI void glfwWindowHint(int hint, int value)
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint);
}
GLFWAPI void glfwWindowHintString(int hint, const char* value)
{
assert(value != NULL);
_GLFW_REQUIRE_INIT();
switch (hint)
{
case GLFW_COCOA_FRAME_NAME:
strncpy(_glfw.hints.window.ns.frameName, value,
sizeof(_glfw.hints.window.ns.frameName) - 1);
return;
case GLFW_X11_CLASS_NAME:
strncpy(_glfw.hints.window.x11.className, value,
sizeof(_glfw.hints.window.x11.className) - 1);
return;
case GLFW_X11_INSTANCE_NAME:
strncpy(_glfw.hints.window.x11.instanceName, value,
sizeof(_glfw.hints.window.x11.instanceName) - 1);
return;
}
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint);
}
GLFWAPI void glfwDestroyWindow(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
@@ -731,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)
@@ -781,6 +818,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowVisible(window);
case GLFW_MAXIMIZED:
return _glfwPlatformWindowMaximized(window);
case GLFW_HOVERED:
return _glfwPlatformWindowHovered(window);
case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE:
@@ -1015,6 +1054,17 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle
return cbfun;
}
GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* handle,
GLFWwindowcontentscalefun cbfun)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun);
return cbfun;
}
GLFWAPI void glfwPollEvents(void)
{
_GLFW_REQUIRE_INIT();
@@ -1056,4 +1106,3 @@ GLFWAPI void glfwPostEmptyEvent(void)
_glfwPlatformPostEmptyEvent();
}

683
glfw/wl_init.c vendored
View File

@@ -34,7 +34,6 @@
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
static inline int min(int n1, int n2)
@@ -42,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,
@@ -49,11 +81,25 @@ static void pointerHandleEnter(void* data,
wl_fixed_t sx,
wl_fixed_t sy)
{
_GLFWwindow* window = wl_surface_get_user_data(surface);
// Happens in the case we just destroyed the surface.
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;
window->wl.hovered = GLFW_TRUE;
_glfwPlatformSetCursor(window, window->wl.currentCursor);
_glfwInputCursorEnter(window, GLFW_TRUE);
}
@@ -68,11 +114,46 @@ static void pointerHandleLeave(void* data,
if (!window)
return;
window->wl.hovered = GLFW_FALSE;
_glfw.wl.pointerSerial = serial;
_glfw.wl.pointerFocus = NULL;
_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,
@@ -80,6 +161,7 @@ static void pointerHandleMotion(void* data,
wl_fixed_t sy)
{
_GLFWwindow* window = _glfw.wl.pointerFocus;
const char* cursorName;
if (!window)
return;
@@ -92,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,
@@ -107,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;
@@ -125,37 +309,28 @@ 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.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;
/* 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;
assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL ||
axis == WL_POINTER_AXIS_VERTICAL_SCROLL);
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;
default:
assert(GLFW_FALSE);
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);
}
@@ -174,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)
{
@@ -196,74 +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");
}
static void keyboardHandleEnter(void* data,
@@ -272,7 +374,17 @@ static void keyboardHandleEnter(void* data,
struct wl_surface* surface,
struct wl_array* keys)
{
// Happens in the case we just destroyed the surface.
if (!surface)
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);
@@ -292,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)
@@ -320,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;
@@ -331,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);
}
}
@@ -363,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,
@@ -382,34 +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;
_glfw.wl.xkb.modifiers = modifiers;
_glfw.wl.keyboardRepeatRate = rate;
_glfw.wl.keyboardRepeatDelay = delay;
}
static const struct wl_keyboard_listener keyboardListener = {
@@ -418,6 +519,7 @@ static const struct wl_keyboard_listener keyboardListener = {
keyboardHandleLeave,
keyboardHandleKey,
keyboardHandleModifiers,
keyboardHandleRepeatInfo,
};
static void seatHandleCapabilities(void* data,
@@ -447,8 +549,26 @@ 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,
struct xdg_wm_base* wmBase,
uint32_t serial)
{
xdg_wm_base_pong(wmBase, serial);
}
static const struct xdg_wm_base_listener wmBaseListener = {
wmBaseHandlePing
};
static void registryHandleGlobal(void* data,
@@ -464,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 =
@@ -482,11 +607,24 @@ 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);
}
}
else if (strcmp(interface, "xdg_wm_base") == 0)
{
_glfw.wl.wmBase =
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 =
@@ -514,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;
}
}
}
@@ -522,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 //////
@@ -663,51 +679,39 @@ static void createKeyTables(void)
int _glfwPlatformInit(void)
{
_glfw.wl.xkb.handle = dlopen("libxkbcommon.so.0", RTLD_LAZY | RTLD_GLOBAL);
if (!_glfw.wl.xkb.handle)
_glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0");
if (!_glfw.wl.cursor.handle)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to open libxkbcommon.");
"Wayland: Failed to open libwayland-cursor");
return GLFW_FALSE;
}
_glfw.wl.xkb.context_new = (PFN_xkb_context_new)
dlsym(_glfw.wl.xkb.handle, "xkb_context_new");
_glfw.wl.xkb.context_unref = (PFN_xkb_context_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_context_unref");
_glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string)
dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string");
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref");
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
_glfw.wl.xkb.state_new = (PFN_xkb_state_new)
dlsym(_glfw.wl.xkb.handle, "xkb_state_new");
_glfw.wl.xkb.state_unref = (PFN_xkb_state_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_state_unref");
_glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms)
dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms");
_glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask)
dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
_glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods)
dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
_glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load)
_glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load");
_glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy)
_glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy");
_glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor)
_glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor");
_glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer)
_glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer");
#ifdef HAVE_XKBCOMMON_COMPOSE_H
_glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale");
_glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref");
_glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new");
_glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref");
_glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed");
_glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status)
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)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
#endif
_glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1");
if (!_glfw.wl.egl.handle)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to open libwayland-egl");
return GLFW_FALSE;
}
_glfw.wl.egl.window_create = (PFN_wl_egl_window_create)
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create");
_glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy)
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy");
_glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize)
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
load_glfw_xkb();
_glfw.wl.display = wl_display_connect(NULL);
if (!_glfw.wl.display)
@@ -720,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);
@@ -736,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();
@@ -747,7 +745,7 @@ int _glfwPlatformInit(void)
if (!_glfw.wl.cursorTheme)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Unable to load default cursor theme\n");
"Wayland: Unable to load default cursor theme");
return GLFW_FALSE;
}
_glfw.wl.cursorSurface =
@@ -759,30 +757,40 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void)
{
_glfwTerminateEGL();
#ifdef __linux__
_glfwTerminateJoysticksLinux();
#ifdef HAVE_XKBCOMMON_COMPOSE_H
xkb_compose_state_unref(_glfw.wl.xkb.composeState);
#endif
_glfwTerminateEGL();
if (_glfw.wl.egl.handle)
{
_glfw_dlclose(_glfw.wl.egl.handle);
_glfw.wl.egl.handle = NULL;
}
xkb_keymap_unref(_glfw.wl.xkb.keymap);
xkb_state_unref(_glfw.wl.xkb.state);
xkb_context_unref(_glfw.wl.xkb.context);
dlclose(_glfw.wl.xkb.handle);
_glfw.wl.xkb.handle = NULL;
release_glfw_xkb();
if (_glfw.wl.cursorTheme)
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
if (_glfw.wl.cursor.handle)
{
_glfw_dlclose(_glfw.wl.cursor.handle);
_glfw.wl.cursor.handle = NULL;
}
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)
wl_pointer_destroy(_glfw.wl.pointer);
if (_glfw.wl.keyboard)
@@ -818,4 +826,3 @@ const char* _glfwPlatformGetVersionString(void)
#endif
;
}

9
glfw/wl_monitor.c vendored
View File

@@ -52,7 +52,7 @@ static void geometry(void* data,
monitor->heightMM = physicalHeight;
snprintf(name, sizeof(name), "%s %s", make, model);
monitor->name = strdup(name);
monitor->name = _glfw_strdup(name);
}
static void mode(void* data,
@@ -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)

183
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,11 +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"
@@ -71,44 +75,56 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#define _GLFW_PLATFORM_CONTEXT_STATE
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
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
struct wl_cursor_image {
uint32_t width;
uint32_t height;
uint32_t hotspot_x;
uint32_t hotspot_y;
uint32_t delay;
};
struct wl_cursor {
unsigned int image_count;
struct wl_cursor_image** images;
char* name;
};
typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*);
typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*);
typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*);
typedef struct wl_buffer* (* PFN_wl_cursor_image_get_buffer)(struct wl_cursor_image*);
#define wl_cursor_theme_load _glfw.wl.cursor.theme_load
#define wl_cursor_theme_destroy _glfw.wl.cursor.theme_destroy
#define wl_cursor_theme_get_cursor _glfw.wl.cursor.theme_get_cursor
#define wl_cursor_image_get_buffer _glfw.wl.cursor.image_get_buffer
#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 struct wl_egl_window* (* PFN_wl_egl_window_create)(struct wl_surface*, int, int);
typedef void (* PFN_wl_egl_window_destroy)(struct wl_egl_window*);
typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int, int);
#define wl_egl_window_create _glfw.wl.egl.window_create
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
#define wl_egl_window_resize _glfw.wl.egl.window_resize
#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)
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
//
@@ -117,12 +133,18 @@ typedef struct _GLFWwindowWayland
int width, height;
GLFWbool visible;
GLFWbool maximized;
GLFWbool hovered;
GLFWbool transparent;
struct wl_surface* surface;
struct wl_egl_window* native;
struct wl_shell_surface* shellSurface;
struct wl_callback* callback;
struct {
struct xdg_surface* surface;
struct xdg_toplevel* toplevel;
} xdg;
_GLFWcursor* currentCursor;
double cursorPosX, cursorPosY;
@@ -142,6 +164,15 @@ typedef struct _GLFWwindowWayland
struct zwp_idle_inhibitor_v1* idleInhibitor;
// 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
@@ -151,65 +182,57 @@ 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;
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;
struct {
void* handle;
PFN_wl_cursor_theme_load theme_load;
PFN_wl_cursor_theme_destroy theme_destroy;
PFN_wl_cursor_theme_get_cursor theme_get_cursor;
PFN_wl_cursor_image_get_buffer image_get_buffer;
} cursor;
struct {
void* handle;
PFN_wl_egl_window_create window_create;
PFN_wl_egl_window_destroy window_destroy;
PFN_wl_egl_window_resize window_resize;
} egl;
} _GLFWlibraryWayland;
// Wayland-specific per-monitor data
@@ -217,6 +240,7 @@ typedef struct _GLFWlibraryWayland
typedef struct _GLFWmonitorWayland
{
struct wl_output* output;
int name;
int currentMode;
int x;
@@ -237,4 +261,3 @@ typedef struct _GLFWcursorWayland
void _glfwAddOutputWayland(uint32_t name, uint32_t version);

812
glfw/wl_window.c vendored

File diff suppressed because it is too large Load Diff

90
glfw/x11_init.c vendored
View File

@@ -461,17 +461,17 @@ static void detectEWMH(void)
//
static GLFWbool initExtensions(void)
{
_glfw.x11.vidmode.handle = dlopen("libXxf86vm.so.1", RTLD_LAZY | RTLD_GLOBAL);
_glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1");
if (_glfw.x11.vidmode.handle)
{
_glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension)
dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension");
_glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension");
_glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp)
dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp");
_glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp");
_glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp)
dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp");
_glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp");
_glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize)
dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize");
_glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize");
_glfw.x11.vidmode.available =
XF86VidModeQueryExtension(_glfw.x11.display,
@@ -479,13 +479,13 @@ static GLFWbool initExtensions(void)
&_glfw.x11.vidmode.errorBase);
}
_glfw.x11.xi.handle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_GLOBAL);
_glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6");
if (_glfw.x11.xi.handle)
{
_glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion)
dlsym(_glfw.x11.xi.handle, "XIQueryVersion");
_glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion");
_glfw.x11.xi.SelectEvents = (PFN_XISelectEvents)
dlsym(_glfw.x11.xi.handle, "XISelectEvents");
_glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents");
if (XQueryExtension(_glfw.x11.display,
"XInputExtension",
@@ -505,45 +505,45 @@ static GLFWbool initExtensions(void)
}
}
_glfw.x11.randr.handle = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_GLOBAL);
_glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2");
if (_glfw.x11.randr.handle)
{
_glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma)
dlsym(_glfw.x11.randr.handle, "XRRAllocGamma");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRAllocGamma");
_glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma)
dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
_glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo)
dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo");
_glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma)
dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
_glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo)
dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo");
_glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources)
dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources");
_glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma)
dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma");
_glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize)
dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize");
_glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo)
dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo");
_glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo)
dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo");
_glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary)
dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary");
_glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent)
dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent");
_glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension)
dlsym(_glfw.x11.randr.handle, "XRRQueryExtension");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryExtension");
_glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion)
dlsym(_glfw.x11.randr.handle, "XRRQueryVersion");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryVersion");
_glfw.x11.randr.SelectInput = (PFN_XRRSelectInput)
dlsym(_glfw.x11.randr.handle, "XRRSelectInput");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRSelectInput");
_glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig)
dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig");
_glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma)
dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma");
_glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration)
dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration");
_glfw_dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration");
if (XRRQueryExtension(_glfw.x11.display,
&_glfw.x11.randr.eventBase,
@@ -593,26 +593,26 @@ static GLFWbool initExtensions(void)
RROutputChangeNotifyMask);
}
_glfw.x11.xcursor.handle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_GLOBAL);
_glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1");
if (_glfw.x11.xcursor.handle)
{
_glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate)
dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate");
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate");
_glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy)
dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy");
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy");
_glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor)
dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
}
_glfw.x11.xinerama.handle = dlopen("libXinerama.so.1", RTLD_LAZY | RTLD_GLOBAL);
_glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1");
if (_glfw.x11.xinerama.handle)
{
_glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive)
dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive");
_glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive");
_glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension)
dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension");
_glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension");
_glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens)
dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens");
_glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens");
if (XineramaQueryExtension(_glfw.x11.display,
&_glfw.x11.xinerama.major,
@@ -644,22 +644,22 @@ static GLFWbool initExtensions(void)
}
}
_glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so.1", RTLD_LAZY | RTLD_GLOBAL);
_glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1");
if (_glfw.x11.x11xcb.handle)
{
_glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection)
dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
_glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
}
_glfw.x11.xrender.handle = dlopen("libXrender.so.1", RTLD_LAZY | RTLD_GLOBAL);
_glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1");
if (_glfw.x11.xrender.handle)
{
_glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension)
dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension");
_glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension");
_glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion)
dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion");
_glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion");
_glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat)
dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat");
_glfw_dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat");
if (XRenderQueryExtension(_glfw.x11.display,
&_glfw.x11.xrender.errorBase,
@@ -1002,25 +1002,25 @@ void _glfwPlatformTerminate(void)
if (_glfw.x11.x11xcb.handle)
{
dlclose(_glfw.x11.x11xcb.handle);
_glfw_dlclose(_glfw.x11.x11xcb.handle);
_glfw.x11.x11xcb.handle = NULL;
}
if (_glfw.x11.xcursor.handle)
{
dlclose(_glfw.x11.xcursor.handle);
_glfw_dlclose(_glfw.x11.xcursor.handle);
_glfw.x11.xcursor.handle = NULL;
}
if (_glfw.x11.randr.handle)
{
dlclose(_glfw.x11.randr.handle);
_glfw_dlclose(_glfw.x11.randr.handle);
_glfw.x11.randr.handle = NULL;
}
if (_glfw.x11.xinerama.handle)
{
dlclose(_glfw.x11.xinerama.handle);
_glfw_dlclose(_glfw.x11.xinerama.handle);
_glfw.x11.xinerama.handle = NULL;
}

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);

100
glfw/x11_window.c vendored
View File

@@ -212,6 +212,10 @@ static int translateState(int state)
mods |= GLFW_MOD_ALT;
if (state & Mod4Mask)
mods |= GLFW_MOD_SUPER;
if (state & LockMask)
mods |= GLFW_MOD_CAPS_LOCK;
if (state & Mod2Mask)
mods |= GLFW_MOD_NUM_LOCK;
return mods;
}
@@ -703,49 +707,34 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
updateNormalHints(window, wndconfig->width, wndconfig->height);
// Set ICCCM WM_CLASS property and window title
// Set ICCCM WM_CLASS property
{
XClassHint* hint = XAllocClassHint();
char *wm_cclass = NULL, *wm_cname = NULL;
const char *real_title = wndconfig->title;
if (strlen(_glfw.hints.init.x11.className) &&
strlen(_glfw.hints.init.x11.classClass))
if (strlen(wndconfig->x11.instanceName) &&
strlen(wndconfig->x11.className))
{
hint->res_name = (char*) _glfw.hints.init.x11.className;
hint->res_class = (char*) _glfw.hints.init.x11.classClass;
}
else if (strlen(real_title))
{
if (*real_title == 1) {
char *p = strchr(real_title, 30);
if (p && p > real_title + 1) {
wm_cname = calloc(p - real_title + 1, 1);
if (wm_cname) memcpy(wm_cname, real_title + 1, p - real_title - 1);
hint->res_name = wm_cname;
char *q = strchr(p + 1, 30);
if (q && q > p + 1) {
wm_cclass = calloc(q - p + 1, 1);
if (wm_cclass) memcpy(wm_cclass, p + 1, q - p - 1);
hint->res_class = wm_cclass;
real_title = q + 1;
}
}
} else {
hint->res_name = (char*) real_title;
hint->res_class = (char*) real_title;
}
hint->res_name = (char*) wndconfig->x11.instanceName;
hint->res_class = (char*) wndconfig->x11.className;
}
else
{
hint->res_name = (char*) "glfw-application";
hint->res_class = (char*) "GLFW-Application";
const char* resourceName = getenv("RESOURCE_NAME");
if (resourceName && strlen(resourceName))
hint->res_name = (char*) resourceName;
else if (strlen(wndconfig->title))
hint->res_name = (char*) wndconfig->title;
else
hint->res_name = (char*) "glfw-application";
if (strlen(wndconfig->title))
hint->res_class = (char*) wndconfig->title;
else
hint->res_class = (char*) "GLFW-Application";
}
XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
XFree(hint);
free(wm_cclass); free(wm_cname);
_glfwPlatformSetWindowTitle(window, real_title);
}
// Announce support for Xdnd (drag and drop)
@@ -756,6 +745,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
PropModeReplace, (unsigned char*) &version, 1);
}
_glfwPlatformSetWindowTitle(window, wndconfig->title);
if (_glfw.x11.im)
{
@@ -1066,7 +1056,7 @@ static const char* getSelectionString(Atom selection)
if (targets[i] == XA_STRING)
*selectionString = convertLatin1toUTF8(data);
else
*selectionString = strdup(data);
*selectionString = _glfw_strdup(data);
}
XFree(data);
@@ -1086,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
@@ -1107,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)
{
@@ -1123,7 +1111,6 @@ static GLFWbool acquireMonitor(_GLFWwindow* window)
}
_glfwInputMonitorWindow(window->monitor, window);
return status;
}
// Remove the window and restore the original video mode
@@ -1987,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);
@@ -2357,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)
@@ -2468,6 +2451,28 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return maximized;
}
int _glfwPlatformWindowHovered(_GLFWwindow* window)
{
Window w = _glfw.x11.root;
while (w)
{
Window root;
int rootX, rootY, childX, childY;
unsigned int mask;
if (!XQueryPointer(_glfw.x11.display, w,
&root, &w, &rootX, &rootY, &childX, &childY, &mask))
{
return GLFW_FALSE;
}
if (w == window->x11.handle)
return GLFW_TRUE;
}
return GLFW_FALSE;
}
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
if (!window->x11.transparent)
@@ -2827,7 +2832,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
void _glfwPlatformSetClipboardString(const char* string)
{
free(_glfw.x11.clipboardString);
_glfw.x11.clipboardString = strdup(string);
_glfw.x11.clipboardString = _glfw_strdup(string);
XSetSelectionOwner(_glfw.x11.display,
_glfw.x11.CLIPBOARD,
@@ -3019,7 +3024,7 @@ GLFWAPI void glfwSetX11SelectionString(const char* string)
_GLFW_REQUIRE_INIT();
free(_glfw.x11.primarySelectionString);
_glfw.x11.primarySelectionString = strdup(string);
_glfw.x11.primarySelectionString = _glfw_strdup(string);
XSetSelectionOwner(_glfw.x11.display,
_glfw.x11.PRIMARY,
@@ -3039,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

@@ -8,7 +8,7 @@ to render arbitrary pixel (raster) graphics to the screen of the terminal
emulator. The major design goals are
* Should not require terminal emulators to understand image formats.
* Should allow specifying graphics to be drawn at individual pixel positions.
* Should allow specifying graphics to be drawn at individual pixel positions.
* The graphics should integrate with the text, in particular it should be possible to draw graphics
below as well as above the text, with alpha blending. The graphics should also scroll with the text, automatically.
* Should use optimizations when the client is running on the same computer as the terminal emulator.
@@ -21,23 +21,37 @@ To see a quick demo, inside a kitty terminal run:
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
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;
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`
@@ -50,7 +64,7 @@ All graphics escape codes are of the form:
```
This is a so-called _Application Programming Command (APC)_. Most terminal
emulators ignore APC codes, making it safe to use.
emulators ignore APC codes, making it safe to use.
The control data is a comma-separated list of `key=value` pairs. The payload
is arbitrary binary data, base64-encoded to prevent interoperation problems
@@ -124,12 +138,12 @@ The transmission medium is specified using the `t` key. The `t` key defaults to
and can take the values:
|===
| Value of `t` | Meaning
| Value of `t` | Meaning
| d | Direct (the data is transmitted within the escape code itself)
| f | A simple file
| t | A temporary file, the terminal emulator will delete the file after reading the pixel data
| s | A http://man7.org/linux/man-pages/man7/shm_overview.7.html[POSIX shared memory object]. The terminal emulator will delete it after reading the pixel data
| s | A http://man7.org/linux/man-pages/man7/shm_overview.7.html[POSIX shared memory object]. The terminal emulator will delete it after reading the pixel data
|===
==== Local client
@@ -175,9 +189,9 @@ if the data is split into three chunks, the client would send the following
sequence of escape codes to the terminal emulator:
```
<ESC>_Gs=100,v=30,m=1;<encoded pixel data first chunk><ESC>\
<ESC>_Gm=1;<encoded pixel data second chunk><ESC>\
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
<ESC>_Gs=100,v=30,m=1;<encoded pixel data first chunk><ESC>\
<ESC>_Gm=1;<encoded pixel data second chunk><ESC>\
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
```
Note that only the first escape code needs to have the full set of control
@@ -189,7 +203,7 @@ before sending any other graphics related escape codes.
=== Detecting available transmission mediums
Since a client has no a-priori knowledge of whether it shares a filesystem/shared memory
with the terminal emulator, it can send an id with the control data, using the `i` key
with the terminal emulator, it can send an id with the control data, using the `i` key
(which can be an arbitrary positive integer up to 4294967295, it must not be zero).
If it does so, the terminal emulator will reply after trying to load the image, saying
whether loading was successful or not. For example:
@@ -207,7 +221,7 @@ to which the terminal emulator will reply (after trying to load the data):
Here the `i` value will be the same as was sent by the client in the original
request. The message data will be a ASCII encoded string containing only
printable characters and spaces. The string will be `OK` if reading the pixel
data succeeded or an error message.
data succeeded or an error message.
Sometimes, using an id is not appropriate, for example, if you do not want to
replace a previously sent image with the same id, or if you are sending a dummy
@@ -245,7 +259,7 @@ be re-transmitted.
=== Controlling displayed image layout
The image is rendered at the current cursor position, from the upper left corner of
the current cell. You can also specify extra `X=3` and `Y=4` pixel offsets to display from
the current cell. You can also specify extra `X=3` and `Y=4` pixel offsets to display from
a different origin within the cell. Note that the offsets must be smaller that the size of the cell.
By default, the entire image will be displayed (images wider than the available
@@ -255,7 +269,7 @@ the top-left corner, width and height of the source rectangle.
You can also ask the terminal emulator to display the image in a specified rectangle
(num of columns / num of lines), using the control codes `c,r`. `c` is the number of columns
and `r` the number of rows. The image will be scaled (enlarged/shrunk) as needed to fit
and `r` the number of rows. The image will be scaled (enlarged/shrunk) as needed to fit
the specified area. Note that if you specify a start cell offset via the `X,Y` keys, it is not
added to the number of rows/columns.
@@ -269,7 +283,7 @@ allows rendering of text on top of images.
Images can be deleted by using the delete action `a=d`. If specified without any
other keys, it will delete all images visible on screen. To delete specific images,
use the `d` key as described in the table below. Note that each value of d has
use the `d` key as described in the table below. Note that each value of d has
both a lowercase and an uppercase variant. The lowercase variant only deletes the
images without necessarily freeing up the stored image data, so that the images can be
re-displayed without needing to resend the data. The uppercase variants will delete
@@ -278,7 +292,7 @@ scrollback buffer. The values of the `x` and `y` keys are the same as cursor pos
x=1, y=1 is the top left cell).
|===
| Value of `d` | Meaning
| Value of `d` | Meaning
| `a` or `A` | Delete all images visible on screen
| `i` or `I` | Delete all images with the specified id, specified using the `i` key.
@@ -313,7 +327,7 @@ delete older images to make space for the new one.
== Control data reference
The table below shows all the control data keys as well as what values they can
take, and the default value they take when missing. All integers are 32-bit.
take, and the default value they take when missing. All integers are 32-bit.
[cols="^1,<3,^1,<6"]
|===

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

@@ -5,10 +5,10 @@
#define bottom -1.0f
const vec2 pos_map[] = vec2[4](
vec2(right, top),
vec2(right, bottom),
vec2(left, bottom),
vec2(left, top)
vec2(right, top),
vec2(right, bottom),
vec2(left, bottom),
vec2(left, top)
);
out vec2 texcoord;

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,12 +12,13 @@ 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),
uvec2(RIGHT, BOTTOM),
uvec2(LEFT, BOTTOM),
uvec2(LEFT, TOP)
uvec2(RIGHT, TOP),
uvec2(RIGHT, BOTTOM),
uvec2(LEFT, BOTTOM),
uvec2(LEFT, TOP)
);
vec2 to_opengl(uint x, uint y) {
@@ -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,26 +2,35 @@
# 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 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,
layout_sprite_map, mark_os_window_for_close, show_window,
toggle_fullscreen, viewport_for_window
layout_sprite_map, mark_os_window_for_close, set_clipboard_string,
set_dpi_from_os_window, show_window, toggle_fullscreen,
viewport_for_window
)
from .fonts.render import prerender, resize_fonts, set_font_family
from .keys import get_shortcut
from .remote_control import handle_cmd
from .session import create_session
from .tabs import SpecialWindow, TabManager
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
from .utils import (
encode_wm_class, end_startup_notification, get_primary_selection,
init_startup_notification, open_url, safe_print, set_primary_selection,
single_instance
end_startup_notification, get_primary_selection, init_startup_notification,
log_error, open_url, parse_address_spec, remove_socket_file, safe_print,
set_primary_selection, single_instance
)
@@ -30,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):
@@ -59,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
@@ -79,46 +101,146 @@ class Boss:
startup_session = create_session(opts, args)
self.add_os_window(startup_session, os_window_id=os_window_id)
def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, size=None, visible=True):
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
os_window_id = create_os_window(w, h, encode_wm_class(wname or self.args.name, wclass or self.args.cls), visible)
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:
ctx = init_startup_notification(os_window_id, startup_id)
dpi_changed = show_window(os_window_id)
if startup_id:
end_startup_notification(ctx)
tm = TabManager(os_window_id, self.opts, self.args, startup_session)
self.os_window_map[os_window_id] = tm
return os_window_id
if dpi_changed:
self.on_dpi_change(os_window_id)
def list_os_windows(self):
for os_window_id, tm in self.os_window_map.items():
yield {
'id': os_window_id,
'tabs': list(tm.list_tabs()),
}
def match_windows(self, match):
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:
for window in tab:
if window.matches(field, pat):
yield window
def tab_for_window(self, window):
for tm in self.os_window_map.values():
for tab in tm:
for w in tab:
if w.id == window.id:
return tab
def match_tabs(self, match):
try:
field, exp = match.split(':', 1)
except ValueError:
return
pat = re.compile(exp)
tms = tuple(self.os_window_map.values())
found = False
if field in ('title', 'id'):
for tm in tms:
for tab in tm:
if tab.matches(field, pat):
yield tab
found = True
if not found:
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
for tab in tabs:
if tab:
yield tab
def set_active_window(self, window):
for tm in self.os_window_map.values():
for tab in tm:
for w in tab:
if w.id == window.id:
if tab is not self.active_tab:
tm.set_active_tab(tab)
tab.set_active_window(w)
return
def _new_os_window(self, args, cwd_from=None):
sw = self.args_to_special_window(args, cwd_from) if args else None
startup_session = create_session(self.opts, special_window=sw, cwd_from=cwd_from)
self.add_os_window(startup_session)
def new_os_window(self, *args):
sw = self.args_to_special_window(args) if args else None
startup_session = create_session(self.opts, special_window=sw)
return self.add_os_window(startup_session)
self._new_os_window(args)
def new_os_window_with_cwd(self, *args):
w = self.active_window
cwd_from = w.child.pid if w is not None else None
self._new_os_window(args, cwd_from)
def add_child(self, window):
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
self.window_id_map[window.id] = window
def peer_messages_received(self, messages):
import json
for msg in messages:
msg = json.loads(msg.decode('utf-8'))
def _handle_remote_command(self, cmd, window=None):
response = None
if self.opts.allow_remote_control:
try:
response = handle_cmd(self, window, cmd)
except Exception as err:
import traceback
response = {'ok': False, 'error': str(err)}
if not getattr(err, 'hide_traceback', False):
response['tb'] = traceback.format_exc()
else:
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control yes to your kitty.conf'}
return response
def peer_message_received(self, msg):
msg = msg.decode('utf-8')
cmd_prefix = '\x1bP@kitty-cmd'
if msg.startswith(cmd_prefix):
cmd = msg[len(cmd_prefix):-2]
response = self._handle_remote_command(cmd)
if response is not None:
response = (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8')
return response
else:
msg = json.loads(msg)
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
startup_id = msg.get('startup_id')
args, rest = parse_args(msg['args'][1:])
args.args = rest
opts = create_opts(args)
session = create_session(opts, args)
os_window_id = self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts), visible=False)
if startup_id:
ctx = init_startup_notification(os_window_id, startup_id)
show_window(os_window_id)
if startup_id:
end_startup_notification(ctx)
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id)
else:
safe_print('Unknown message received from peer, ignoring')
log_error('Unknown message received from peer, ignoring')
def handle_remote_cmd(self, cmd, window=None):
response = self._handle_remote_command(cmd, window)
if response is not None:
if window is not None:
window.send_cmd_response(response)
def on_child_death(self, window_id):
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)
@@ -162,33 +284,35 @@ class Boss:
if tm is not None:
tm.activate_tab_at(x)
def on_window_resize(self, os_window_id, w, h):
def on_window_resize(self, os_window_id, w, h, dpi_changed):
tm = self.os_window_map.get(os_window_id)
if tm is not None:
tm.resize()
if dpi_changed:
if set_dpi_from_os_window(os_window_id):
self.on_dpi_change(os_window_id)
else:
tm.resize()
else:
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):
if new_size == self.current_font_size:
return
self.current_font_size = new_size
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:
@@ -199,6 +323,15 @@ class Boss:
tm.refresh_sprite_positions()
glfw_post_empty_event()
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)
def on_dpi_change(self, os_window_id):
self._change_font_size()
@property
def active_tab_manager(self):
os_window_id = current_os_window()
@@ -222,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)
@@ -262,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
@@ -297,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()
@@ -326,12 +543,19 @@ class Boss:
text = w.text_for_selection()
if text:
set_primary_selection(text)
if self.opts.copy_on_select:
set_clipboard_string(text)
def goto_tab(self, tab_num):
tm = self.active_tab_manager
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:
@@ -342,7 +566,7 @@ class Boss:
if tm is not None:
tm.next_tab(-1)
def args_to_special_window(self, args):
def args_to_special_window(self, args, cwd_from=None):
args = list(args)
stdin = None
w = self.active_window
@@ -351,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
@@ -368,23 +596,44 @@ class Boss:
if not arg:
continue
cmd.append(arg)
return SpecialWindow(cmd, stdin)
return SpecialWindow(cmd, stdin, cwd_from=cwd_from)
def new_tab(self, *args):
def _new_tab(self, args, cwd_from=None):
special_window = None
if args:
special_window = self.args_to_special_window(args)
if isinstance(args, SpecialWindowInstance):
special_window = args
else:
special_window = self.args_to_special_window(args, cwd_from=cwd_from)
tm = self.active_tab_manager
if tm is not None:
tm.new_tab(special_window=special_window)
tm.new_tab(special_window=special_window, cwd_from=cwd_from)
def new_window(self, *args):
def new_tab(self, *args):
self._new_tab(args)
def new_tab_with_cwd(self, *args):
w = self.active_window
cwd_from = w.child.pid if w is not None else None
self._new_tab(args, cwd_from=cwd_from)
def _new_window(self, args, cwd_from=None):
tab = self.active_tab
if tab is not None:
if args:
tab.new_special_window(self.args_to_special_window(args))
tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from))
else:
tab.new_window()
tab.new_window(cwd_from=cwd_from)
def new_window(self, *args):
self._new_window(args)
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)
def move_tab_forward(self):
tm = self.active_tab_manager
@@ -395,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

@@ -19,6 +19,7 @@ in float bg_alpha;
#ifdef NEEDS_FOREGROUND
uniform sampler2DArray sprites;
uniform float inactive_text_alpha;
in vec3 sprite_pos;
in vec3 underline_pos;
in vec3 strike_pos;
@@ -34,7 +35,7 @@ vec4 alpha_blend(vec3 over, float over_alpha, vec3 under, float under_alpha) {
// Alpha blend two colors returning the resulting color pre-multiplied by its alpha
// and its alpha.
// See https://en.wikipedia.org/wiki/Alpha_compositing
float alpha = mix(under_alpha, 1.0f, over_alpha);
float alpha = mix(under_alpha, 1.0f, over_alpha);
vec3 combined_color = mix(under * under_alpha, over, over_alpha);
return vec4(combined_color, alpha);
}
@@ -45,7 +46,7 @@ vec3 premul_blend(vec3 over, float over_alpha, vec3 under) {
vec4 alpha_blend_premul(vec3 over, float over_alpha, vec3 under, float under_alpha) {
// Same as alpha_blend() except that it assumes over and under are both premultiplied.
float alpha = mix(under_alpha, 1.0f, over_alpha);
float alpha = mix(under_alpha, 1.0f, over_alpha);
return vec4(premul_blend(over, over_alpha, under), alpha);
}
// }}}
@@ -60,7 +61,7 @@ vec4 calculate_foreground() {
// Since strike and text are the same color, we simply add the alpha values
float combined_alpha = min(text_alpha + strike_alpha, 1.0f);
// Underline color might be different, so alpha blend
return alpha_blend(decoration_fg, underline_alpha, fg, combined_alpha);
return alpha_blend(decoration_fg, underline_alpha * inactive_text_alpha, fg, combined_alpha * inactive_text_alpha);
}
#endif
@@ -70,7 +71,7 @@ void main() {
final_color = vec4(background.rgb * bg_alpha, bg_alpha);
#else
final_color = vec4(background.rgb, 1.0f);
#endif
#endif
#endif
#ifdef SPECIAL
@@ -78,10 +79,10 @@ void main() {
final_color = vec4(background.rgb * bg_alpha, bg_alpha);
#else
final_color = vec4(background.rgb, bg_alpha);
#endif
#endif
#endif
#if defined(FOREGROUND) || defined(SIMPLE)
#if defined(FOREGROUND) || defined(SIMPLE)
// FOREGROUND or SIMPLE
vec4 fg = calculate_foreground(); // pre-multiplied foreground
@@ -99,7 +100,7 @@ void main() {
final_color = alpha_blend_premul(fg.rgb, fg.a, background * bg_alpha, bg_alpha);
final_color = vec4(final_color.rgb / final_color.a, final_color.a);
#else
// since background alpha is 1.0, it is effectively premultipled
// since background alpha is 1.0, it is effectively pre-multiplied
final_color = vec4(premul_blend(fg.rgb, fg.a, background), 1.0f);
final_color = vec4(final_color.rgb / final_color.a, final_color.a);
#endif

View File

@@ -6,13 +6,11 @@
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;
uint color_table[256];
uint color_table[256];
};
// Have to use fixed locations here as all variants of the cell program share the same VAO
@@ -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
@@ -118,27 +116,27 @@ float is_cursor(uint x, uint y) {
void main() {
// set cell vertex position {{{
uint instance_id = uint(gl_InstanceID);
uint instance_id = uint(gl_InstanceID);
/* The current cell being rendered */
uint r = instance_id / xnum;
uint c = instance_id - r * xnum;
uint r = instance_id / xnum;
uint c = instance_id - r * xnum;
/* The position of this vertex, at a corner of the cell */
float left = xstart + c * dx;
float top = ystart - r * dy;
vec2 xpos = vec2(left, left + dx);
vec2 ypos = vec2(top, top - dy);
uvec2 pos = cell_pos_map[gl_VertexID];
/* The position of this vertex, at a corner of the cell */
float left = xstart + c * dx;
float top = ystart - r * dy;
vec2 xpos = vec2(left, left + dx);
vec2 ypos = vec2(top, top - dy);
uvec2 pos = cell_pos_map[gl_VertexID];
gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1);
// }}}
// 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];
uvec2 default_colors = uvec2(default_fg, default_bg);
uint text_attrs = sprite_coords[3];
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]);
// }}}
@@ -150,7 +148,7 @@ void main() {
sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & Z_MASK);
colored_sprite = float((sprite_coords.z & COLOR_MASK) >> 14);
// Foreground
// Foreground
uint resolved_fg = resolve_color(colors[fg_index], default_colors[fg_index]);
foreground = color_to_vec(resolved_fg);
// Selection
@@ -165,7 +163,7 @@ void main() {
foreground = choose_color(cursor, bg, foreground);
decoration_fg = choose_color(cursor, bg, decoration_fg);
#endif
// }}}
// }}}
// Background {{{
#ifdef NEEDS_BACKROUND

18
kitty/charsets.c generated
View File

@@ -43,7 +43,7 @@ static uint32_t charset_translations[5][256] = {
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
},
},
/* VT100 graphics mapped to Unicode */
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
@@ -81,7 +81,7 @@ static uint32_t charset_translations[5][256] = {
},
/* IBM Codepage 437 mapped to Unicode */
{
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
@@ -113,7 +113,7 @@ static uint32_t charset_translations[5][256] = {
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
},
},
// VAX 42 map
{
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
@@ -183,13 +183,13 @@ static uint32_t charset_translations[5][256] = {
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
},
},
};
uint32_t*
uint32_t*
translation_table(uint32_t which) {
switch(which){
switch(which){
case 'B':
return charset_translations[0];
case '0':
@@ -208,7 +208,7 @@ translation_table(uint32_t which) {
uint32_t *latin1_charset = charset_translations[0];
// UTF-8 decode taken from: http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
static const uint8_t utf8_data[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
@@ -226,7 +226,7 @@ static const uint8_t utf8_data[] = {
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
};
uint32_t
uint32_t
decode_utf8(uint32_t* state, uint32_t* codep, uint8_t byte) {
uint32_t type = utf8_data[byte];
@@ -278,7 +278,7 @@ base64_decode(const uint32_t *src, size_t src_sz, uint8_t *dest, size_t dest_cap
if (!src_sz) { *dest_sz = 0; return NULL; }
if (src_sz % 4 != 0) return "base64 encoded data must have a length that is a multiple of four";
*dest_sz = (src_sz / 4) * 3;
if (src[src_sz - 1] == '=') (*dest_sz)--;
if (src[src_sz - 1] == '=') (*dest_sz)--;
if (src[src_sz - 2] == '=') (*dest_sz)--;
if (*dest_sz > dest_capacity) return "output buffer too small";
for (size_t i = 0, j = 0; i < src_sz;) {

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,30 +71,19 @@ 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 {{{
#define FREE_CHILD(x) \
Py_CLEAR((x).screen); x = EMPTY_CHILD;
#define XREF_CHILD(x, OP) OP(x.screen);
#define XREF_CHILD(x, OP) OP(x.screen);
#define INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF)
#define DECREF_CHILD(x) XREF_CHILD(x, Py_DECREF)
@@ -138,12 +126,12 @@ self_pipe(int fds[2]) {
static PyObject *
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
ChildMonitor *self;
PyObject *dump_callback, *death_notify;
int talk_fd = -1;
PyObject *dump_callback, *death_notify;
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,19 +139,19 @@ 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) {
self->dump_callback = dump_callback; Py_INCREF(dump_callback);
parse_func = parse_worker_dump;
} else parse_func = parse_worker;
self->count = 0;
self->count = 0;
fds[0].fd = wakeup_fds[0]; fds[1].fd = signal_fds[0];
fds[0].events = POLLIN; fds[1].events = POLLIN;
the_monitor = self;
@@ -187,7 +175,7 @@ dealloc(ChildMonitor* self) {
}
close(wakeup_fds[0]);
close(wakeup_fds[1]);
close(signal_fds[0]);
close(signal_fds[0]);
close(signal_fds[1]);
}
@@ -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;
@@ -243,7 +228,7 @@ add_child(ChildMonitor *self, PyObject *args) {
#define A(attr) &add_queue[add_queue_count].attr
if (!PyArg_ParseTuple(args, "kiiO", A(id), A(pid), A(fd), A(screen))) {
children_mutex(unlock);
return NULL;
return NULL;
}
#undef A
INCREF_CHILD(add_queue[add_queue_count]);
@@ -259,14 +244,14 @@ schedule_write_to_child(unsigned long id, const char *data, size_t sz) {
bool found = false;
children_mutex(lock);
for (size_t i = 0; i < self->count; i++) {
if (children[i].id == id) {
if (children[i].id == id) {
found = true;
Screen *screen = children[i].screen;
screen_mutex(lock, write);
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
if (space_left < sz) {
if (space_left < sz) {
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
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;
}
@@ -295,17 +280,27 @@ needs_write(ChildMonitor UNUSED *self, PyObject *args) {
#define needs_write_doc "needs_write(id, data) -> Queue data to be written to child."
unsigned long id, sz;
const char *data;
if (!PyArg_ParseTuple(args, "ks#", &id, &data, &sz)) return NULL;
if (!PyArg_ParseTuple(args, "ks#", &id, &data, &sz)) return NULL;
if (schedule_write_to_child(id, data, sz)) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;
}
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;
}
@@ -332,7 +327,7 @@ parse_input(ChildMonitor *self) {
PyObject *msg = NULL;
children_mutex(lock);
while (remove_queue_count) {
remove_queue_count--;
remove_queue_count--;
remove_notify[remove_count] = remove_queue[remove_queue_count].id;
remove_count++;
FREE_CHILD(remove_queue[remove_queue_count]);
@@ -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;
@@ -517,13 +518,23 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, double now, OSWindow *os_w
ColorProfile *cp = rd->screen->color_profile;
ans->shape = cursor->shape ? cursor->shape : OPT(cursor_shape);
ans->color = colorprofile_to_color(cp, cp->overridden.cursor_color, cp->configured.cursor_color);
if (ans->shape == CURSOR_BLOCK) return;
ans->is_focused = os_window->is_focused;
if (ans->shape == CURSOR_BLOCK && ans->is_focused) return;
double left = rd->xstart + cursor->x * rd->dx;
double top = rd->ystart - cursor->y * rd->dy;
unsigned long mult = MAX(1, screen_current_char_width(rd->screen));
double right = left + (ans->shape == CURSOR_BEAM ? cursor_width(1.5, true, os_window) : rd->dx * mult);
double bottom = top - rd->dy;
if (ans->shape == CURSOR_UNDERLINE) top = bottom + cursor_width(2.0, false, os_window);
switch (ans->shape) {
case CURSOR_UNDERLINE:
top = bottom + cursor_width(2.0, false, os_window);
break;
case CURSOR_BLOCK:
top -= 2.0 / os_window->viewport_height; // 1px adjustment for width of line
break;
default:
break;
}
ans->left = left; ans->right = right; ans->top = top; ans->bottom = bottom;
}
@@ -544,29 +555,39 @@ update_window_title(Window *w, OSWindow *os_window) {
static PyObject*
simple_render_screen(PyObject UNUSED *self, PyObject *args) {
#define simple_render_screen_doc "Render a Screen object, with no cursor"
Screen *screen;
Screen *screen;
float xstart, ystart, dx, dy;
static ssize_t vao_idx = -1, gvao_idx = -1;
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;
draw_cells(vao_idx, gvao_idx, xstart, ystart, dx, dy, screen, current_os_window());
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 (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);
}
@@ -574,48 +595,71 @@ 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;
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window);
if (is_active_window && WD.screen->cursor_render_info.is_visible && WD.screen->cursor_render_info.shape != CURSOR_BLOCK) {
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);
}
if (WD.screen->start_visual_bell_at != 0) {
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
render(double now) {
double time_since_last_render = now - last_render_at;
if (time_since_last_render < OPT(repaint_delay)) {
if (time_since_last_render < OPT(repaint_delay)) {
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;
render_os_window(w, now, &active_window_id);
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);
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
@@ -647,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);
@@ -663,7 +707,7 @@ cm_thread_write(PyObject UNUSED *self, PyObject *args) {
if (!PyArg_ParseTuple(args, "is#", &fd, &buf, &sz)) return NULL;
ThreadWriteData *data = alloc_twd(sz);
if (data == NULL) return PyErr_NoMemory();
data->fd = fd;
data->fd = fd;
memcpy(data->buf, buf, data->sz);
int ret = pthread_create(&thread, NULL, thread_write, data);
if (ret != 0) { free_twd(data); return PyErr_SetFromErrno(PyExc_OSError); }
@@ -682,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;
@@ -711,12 +755,12 @@ 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);
}
remove_os_window(os_window->id);
remove_os_window(os_window->id);
} else has_open_windows = true;
}
}
@@ -748,7 +792,7 @@ hangup(pid_t pid) {
if (errno == ESRCH) return;
if (errno != 0) { perror("Failed to get process group id for child"); return; }
if (killpg(pgid, SIGHUP) != 0) {
if (errno != ESRCH) perror("Failed to kill child");
if (errno != ESRCH) perror("Failed to kill child");
}
}
@@ -763,7 +807,7 @@ cleanup_child(ssize_t i) {
static inline void
remove_children(ChildMonitor *self) {
if (self->count > 0) {
size_t count = 0;
size_t count = 0;
for (ssize_t i = self->count - 1; i >= 0; i--) {
if (children[i].needs_removal) {
count++;
@@ -831,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;
@@ -839,7 +935,7 @@ write_to_child(int fd, Screen *screen) {
while (written < screen->write_buf_used) {
ret = write(fd, screen->write_buf + written, screen->write_buf_used - written);
if (ret > 0) { written += ret; }
else if (ret == 0) {
else if (ret == 0) {
// could mean anything, ignore
break;
} else {
@@ -858,7 +954,7 @@ io_loop(void *data) {
// The I/O thread loop
size_t i;
int ret;
bool has_more, data_received;
bool has_more, data_received;
Screen *screen;
ChildMonitor *self = (ChildMonitor*)data;
set_thread_name("KittyChildMon");
@@ -880,18 +976,18 @@ io_loop(void *data) {
ret = poll(fds, self->count + EXTRA_FDS, -1);
if (ret > 0) {
if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup
if (fds[1].revents && POLLIN) {
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)) {
data_received = true;
has_more = read_bytes(fds[EXTRA_FDS + i].fd, children[i].screen);
if (!has_more) {
if (!has_more) {
// child is dead
children_mutex(lock);
children[i].needs_removal = true;
@@ -906,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
@@ -933,66 +1029,265 @@ 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 {{{
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)
@@ -1007,15 +1302,23 @@ PyTypeObject ChildMonitor_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.ChildMonitor",
.tp_basicsize = sizeof(ChildMonitor),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "ChildMonitor",
.tp_methods = methods,
.tp_new = new,
.tp_new = new,
};
static PyObject*
safe_pipe(PYNOARG) {
int fds[2] = {0};
if (!self_pipe(fds)) return PyErr_SetFromErrno(PyExc_OSError);
return Py_BuildValue("ii", fds[0], fds[1]);
}
static PyMethodDef module_methods[] = {
METHOD(simple_render_screen, METH_VARARGS)
METHODB(safe_pipe, METH_NOARGS),
{NULL} /* Sentinel */
};
@@ -1023,10 +1326,9 @@ bool
init_child_monitor(PyObject *module) {
if (PyType_Ready(&ChildMonitor_Type) < 0) return false;
if (PyModule_AddObject(module, "ChildMonitor", (PyObject *)&ChildMonitor_Type) != 0) return false;
Py_INCREF(&ChildMonitor_Type);
Py_INCREF(&ChildMonitor_Type);
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
return true;
}
// }}}

113
kitty/child.c Normal file
View File

@@ -0,0 +1,113 @@
/*
* child.c
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "data-types.h"
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
static inline char**
serialize_string_tuple(PyObject *src) {
Py_ssize_t sz = PyTuple_GET_SIZE(src);
char **ans = calloc(sz + 1, sizeof(char*));
if (!ans) fatal("Out of memory");
for (Py_ssize_t i = 0; i < sz; i++) ans[i] = PyUnicode_AsUTF8(PyTuple_GET_ITEM(src, i));
return ans;
}
extern char **environ;
static inline void
write_to_stderr(const char *text) {
size_t sz = strlen(text);
size_t written = 0;
while(written < sz) {
ssize_t amt = write(2, text + written, sz - written);
if (amt == 0) break;
if (amt < 0) {
if (errno == EAGAIN || errno == EINTR) continue;
break;
}
written += amt;
}
}
static PyObject*
spawn(PyObject *self UNUSED, PyObject *args) {
PyObject *argv_p, *env_p;
int master, slave, stdin_read_fd, stdin_write_fd;
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);
char **env = serialize_string_tuple(env_p);
#define exit_on_err(m) { write_to_stderr(m); write_to_stderr(": "); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); }
pid_t pid = fork();
switch(pid) {
case 0:
// child
// Use only signal-safe functions (man 7 signal-safety)
if (chdir(cwd) != 0) { if (chdir("/") != 0) {} }; // ignore failure to chdir to /
if (setsid() == -1) exit_on_err("setsid() in child process failed");
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");
if (dup2(slave, 2) == -1) exit_on_err("dup2() failed for fd number 2");
if (stdin_read_fd > -1) {
if (dup2(stdin_read_fd, 0) == -1) exit_on_err("dup2() failed for fd number 0");
close(stdin_read_fd);
close(stdin_write_fd);
} else {
if (dup2(slave, 0) == -1) exit_on_err("dup2() failed for fd number 0");
}
close(slave);
close(master);
for (int c = 3; c < 201; c++) close(c);
// Establish the controlling terminal (see man 7 credentials)
int tfd = open(name, O_RDWR);
if (tfd == -1) exit_on_err("Failed to open controlling terminal");
close(tfd);
environ = env;
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: ");
write_to_stderr(argv[0]);
write_to_stderr("\nWith error: ");
write_to_stderr(strerror(errno));
write_to_stderr("\nPress Enter to exit.\n");
execlp("sh", "sh", "-c", "read w", NULL);
exit(EXIT_FAILURE);
break;
case -1:
PyErr_SetFromErrno(PyExc_OSError);
break;
default:
break;
}
#undef exit_on_err
free(argv);
free(env);
if (PyErr_Occurred()) return NULL;
return PyLong_FromLong(pid);
}
static PyMethodDef module_methods[] = {
METHODB(spawn, METH_VARARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
};
bool
init_child(PyObject *module) {
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
return true;
}

View File

@@ -4,11 +4,26 @@
import fcntl
import os
import sys
import kitty.fast_data_types as fast_data_types
from .constants import terminfo_dir
from .constants import is_macos, shell_path, terminfo_dir
def cwd_of_process(pid):
if is_macos:
from kitty.fast_data_types import cwd_of_process
ans = cwd_of_process(pid)
else:
ans = '/proc/{}/cwd'.format(pid)
return os.path.realpath(ans)
def cmdline_of_process(pid):
if is_macos:
# TODO: macOS implementation, see DarwinProcess.c in htop for inspiration
raise NotImplementedError()
return open('/proc/{}/cmdline'.format(pid), 'rb').read().decode('utf-8').split('\0')
def remove_cloexec(fd):
@@ -20,9 +35,17 @@ class Child:
child_fd = pid = None
forked = False
def __init__(self, argv, cwd, opts, stdin=None, env=None):
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None):
self.argv = argv
self.cwd = os.path.abspath(os.path.expandvars(os.path.expanduser(cwd or os.getcwd())))
if cwd_from is not None:
try:
cwd = cwd_of_process(cwd_from)
except Exception:
import traceback
traceback.print_exc()
else:
cwd = os.path.expandvars(os.path.expanduser(cwd or os.getcwd()))
self.cwd = os.path.abspath(cwd)
self.opts = opts
self.stdin = stdin
self.env = env or {}
@@ -38,44 +61,40 @@ class Child:
if stdin is not None:
stdin_read_fd, stdin_write_fd = os.pipe()
remove_cloexec(stdin_read_fd)
env = self.env
pid = os.fork()
if pid == 0: # child
try:
os.chdir(self.cwd)
except EnvironmentError:
os.chdir('/')
os.setsid()
for i in range(3):
if stdin is not None and i == 0:
os.dup2(stdin_read_fd, i)
os.close(stdin_read_fd), os.close(stdin_write_fd)
else:
os.dup2(slave, i)
os.close(slave), os.close(master)
os.closerange(3, 200)
# Establish the controlling terminal (see man 7 credentials)
os.close(os.open(os.ttyname(1), os.O_RDWR))
os.environ.update(env)
os.environ['TERM'] = self.opts.term
os.environ['COLORTERM'] = 'truecolor'
if os.path.isdir(terminfo_dir):
os.environ['TERMINFO'] = terminfo_dir
try:
os.execvp(self.argv[0], self.argv)
except Exception as err:
# Report he failure and exec a shell instead so that
# we are not left with a forked but not execed process
print('Could not launch:', self.argv[0])
print('\t', err)
print('\nPress Enter to exit:', end=' ')
sys.stdout.flush()
os.execvp('/bin/sh', ['/bin/sh', '-c', 'read w'])
else: # master
os.close(slave)
self.pid = pid
self.child_fd = master
if stdin is not None:
os.close(stdin_read_fd)
fast_data_types.thread_write(stdin_write_fd, stdin)
return pid
else:
stdin_read_fd = stdin_write_fd = -1
env = os.environ.copy()
env.update(self.env)
env['TERM'] = self.opts.term
env['COLORTERM'] = 'truecolor'
if os.path.isdir(terminfo_dir):
env['TERMINFO'] = terminfo_dir
env = tuple('{}={}'.format(k, v) for k, v in env.items())
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
if stdin is not None:
os.close(stdin_read_fd)
fast_data_types.thread_write(stdin_write_fd, stdin)
return pid
@property
def cmdline(self):
try:
return cmdline_of_process(self.pid)
except Exception:
return list(self.argv)
@property
def current_cwd(self):
try:
return cwd_of_process(self.pid)
except Exception:
pass

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)
@@ -206,7 +238,7 @@ def prettify(text):
def sub(m):
t = m.group(2)
for ch in m.group(1):
t = {'C': cyan, '_': italic, '*': bold}[ch](t)
t = {'C': cyan, '_': italic, '*': bold, 'G': green, 'T': title}[ch](t)
return t
text = re.sub(r'[|]([a-zA-Z_*]+?) (.+?)[|]', sub, text)
@@ -263,18 +295,26 @@ def print_help_for_seq(seq, usage, message, appname):
if leading_indent is None:
leading_indent = indent
j = '\n' + (' ' * indent)
a((' ' * leading_indent) + j.join(wrap(text, limit=linesz - indent)))
lines = []
for l in text.splitlines():
if l:
lines.extend(wrap(l, limit=linesz - indent))
else:
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 |_ {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
' arguments following the |_ options|. For example: {appname} /bin/sh'
).format(appname=appname)
wa(prettify(message))
a('')
a('{}:'.format(title('Options')))
if seq:
a('{}:'.format(title('Options')))
for opt in seq:
if isinstance(opt, str):
a('{}:'.format(title(opt)))
@@ -430,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
@@ -443,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,12 +6,14 @@
*/
#include "data-types.h"
#include "state.h"
#include <Cocoa/Cocoa.h>
#include <AvailabilityMacros.h>
// Needed for _NSGetProgname
#include <crt_externs.h>
typedef void* rusage_info_t; // needed for libproc.h
#include <libproc.h>
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101200)
#define NSWindowStyleMaskResizable NSResizableWindowMask
@@ -21,8 +23,11 @@
#endif
static NSMenuItem* title_menu = NULL;
static bool change_titlebar_color = false;
static color_type titlebar_color = 0;
static NSString*
static NSString*
find_app_name(void) {
size_t i;
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
@@ -54,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 =
@@ -70,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"];
@@ -119,7 +151,7 @@ cocoa_create_global_menu(void) {
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
[NSApp setWindowsMenu:windowMenu];
[windowMenu release];
[preferences_menu_item release];
[bar release];
}
@@ -141,7 +173,7 @@ cocoa_update_title(PyObject *pytitle) {
bool
cocoa_make_window_resizable(void *w) {
NSWindow *window = (NSWindow*)w;
@try {
[window setStyleMask:
[window styleMask] | NSWindowStyleMaskResizable];
@@ -152,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];
@@ -166,8 +198,68 @@ cocoa_get_lang(PyObject UNUSED *self) {
return Py_BuildValue("s", [locale UTF8String]);
}
static PyObject*
cwd_of_process(PyObject *self UNUSED, PyObject *pid_) {
long pid = PyLong_AsLong(pid_);
struct proc_vnodepathinfo vpi;
int ret = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
if (ret < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
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, ""},
{"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

@@ -42,7 +42,7 @@ PyObject* create_256_color_table() {
uint8_t v = 8 + i * 10;
FG_BG_256[j] = (v << 16) | (v << 8) | v;
}
PyObject *ans = PyTuple_New(255);
if (ans == NULL) return PyErr_NoMemory();
for (i=0; i < 255; i++) {
@@ -62,7 +62,7 @@ new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
if (FG_BG_256[255] == 0) create_256_color_table();
memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256));
memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
self->dirty = true;
self->dirty = true;
}
return (PyObject*) self;
}
@@ -93,7 +93,7 @@ update_ansi_color_table(ColorProfile *self, PyObject *val) {
Py_RETURN_NONE;
}
color_type
color_type
colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) {
color_type t = entry & 0xFF, r;
switch(t) {
@@ -128,12 +128,12 @@ as_color(ColorProfile *self, PyObject *val) {
default:
ans = Py_None; Py_INCREF(Py_None);
}
if (ans == NULL) ans = Py_BuildValue("BBB", (unsigned char)(col >> 16), (unsigned char)((col >> 8) & 0xFF), (unsigned char)(col & 0xFF));
if (ans == NULL) ans = Py_BuildValue("BBB", (unsigned char)(col >> 16), (unsigned char)((col >> 8) & 0xFF), (unsigned char)(col & 0xFF));
return ans;
}
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;
@@ -177,13 +177,13 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size
}
self->dirty = false;
}
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);
}
// Boilerplate {{{
@@ -227,13 +227,13 @@ PyTypeObject ColorProfile_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.ColorProfile",
.tp_basicsize = sizeof(ColorProfile),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "ColorProfile",
.tp_members = members,
.tp_methods = methods,
.tp_getset = getsetters,
.tp_new = new,
.tp_new = new,
};
INIT_TYPE(ColorProfile)

View File

@@ -10,15 +10,18 @@ import shlex
import sys
import tempfile
from collections import namedtuple
from contextlib import contextmanager
from . import fast_data_types as defines
from .constants import config_dir
from .config_utils import (
init_config, parse_config_base, positive_float, positive_int, to_bool,
to_color, unit_float
)
from .constants import cache_dir, defconf
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .rgb import to_color
from .utils import safe_print
from .utils import log_error
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
MINIMUM_FONT_SIZE = 4
@@ -44,26 +47,18 @@ def to_cursor_shape(x):
)
def to_bool(x):
return x.lower() in 'y yes true'.split()
def parse_mods(parts):
def map_mod(m):
return {'CTRL': 'CONTROL', 'CMD': 'CONTROL'}.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
@@ -94,7 +89,10 @@ def parse_shortcut(sc):
KeyAction = namedtuple('KeyAction', 'func args')
shlex_actions = {'pass_selection_to_program', 'new_window', 'new_tab'}
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):
@@ -109,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)
@@ -123,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
@@ -145,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:
@@ -174,17 +173,16 @@ def parse_symbol_map(val):
def parse_send_text_bytes(text):
return ast.literal_eval("'''" + text + "'''").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:
@@ -195,7 +193,7 @@ def parse_send_text(val, keymap):
return parse_key(key_str, keymap)
def to_open_url_modifiers(val):
def to_modifiers(val):
return parse_mods(val.split('+'))
@@ -209,24 +207,21 @@ def to_layout_names(raw):
return parts
def positive_int(x):
return max(0, int(x))
def positive_float(x):
return max(0, float(x))
def unit_float(x):
return max(0, min(float(x), 1))
def adjust_line_height(x):
if x.endswith('%'):
return float(x[:-1].strip()) / 100.0
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:
@@ -240,33 +235,46 @@ def tab_separator(x):
x = x[1:-1]
break
if not x.strip():
x = defaults.tab_separator
x = ('\xa0' * len(x)) if x else defaults.tab_separator
return 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):
return {'top': 1, 'bottom': 3}.get(x.lower(), 3)
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,
'adjust_line_height': adjust_line_height,
'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,
'cursor_shape': to_cursor_shape,
'open_url_modifiers': to_open_url_modifiers,
'open_url_modifiers': to_modifiers,
'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,
@@ -281,29 +289,49 @@ 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,
}
for name in (
'foreground background cursor active_border_color inactive_border_color'
' selection_foreground selection_background url_color'
).split():
type_map[name] = lambda x: to_color(x, validate=True)
type_map[name] = to_color
for i in range(16):
type_map['color%d' % i] = lambda x: to_color(x, validate=True)
type_map['color%d' % i] = to_color
for a in ('active', 'inactive'):
for b in ('foreground', 'background'):
type_map['%s_tab_%s' % (a, b)] = lambda x: to_color(x, validate=True)
type_map['%s_tab_%s' % (a, b)] = to_color
def special_handling(key, val, ans):
if key == 'map':
parse_key(val, ans['keymap'])
return True
if key == 'symbol_map':
ans['symbol_map'].update(parse_symbol_map(val))
return True
if key == 'send_text':
# For legacy compatibility
parse_send_text(val, ans['keymap'])
return True
defaults = None
default_config_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'kitty.conf'
)
def parse_config(lines, check_keys=True):
@@ -311,46 +339,22 @@ def parse_config(lines, check_keys=True):
'keymap': {},
'symbol_map': {},
}
if check_keys:
all_keys = defaults._asdict()
for line in lines:
line = line.strip()
if not line or line.startswith('#'):
continue
m = key_pat.match(line)
if m is not None:
key, val = m.groups()
if key == 'map':
parse_key(val, ans['keymap'])
continue
if key == 'symbol_map':
ans['symbol_map'].update(parse_symbol_map(val))
continue
if key == 'send_text':
# For legacy compatibility
parse_send_text(val, ans['keymap'])
continue
if check_keys:
if key not in all_keys:
safe_print(
'Ignoring unknown config key: {}'.format(key),
file=sys.stderr
)
continue
tm = type_map.get(key)
if tm is not None:
val = tm(val)
ans[key] = val
parse_config_base(
lines,
defaults,
type_map,
special_handling,
ans,
check_keys=check_keys
)
return ans
with open(
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty.conf'), 'rb'
) as f:
defaults = parse_config(f.read().decode('utf-8').splitlines(), check_keys=False)
Options = namedtuple('Defaults', ','.join(defaults.keys()))
defaults = Options(**defaults)
actions = frozenset(a.func for a in defaults.keymap.values()) | frozenset({'combine', 'send_text', 'goto_tab'})
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 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'})
@@ -392,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:
@@ -415,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

63
kitty/config_utils.py Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import re
from collections import namedtuple
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+(.+)$')
def to_color(x):
return as_color(x, validate=True)
def positive_int(x):
return max(0, int(x))
def positive_float(x):
return max(0, float(x))
def unit_float(x):
return max(0, min(float(x), 1))
def to_bool(x):
return x.lower() in 'y yes true'.split()
def parse_config_base(
lines, defaults, type_map, special_handling, ans, check_keys=True
):
if check_keys:
all_keys = defaults._asdict()
for line in lines:
line = line.strip()
if not line or line.startswith('#'):
continue
m = key_pat.match(line)
if m is not None:
key, val = m.groups()
if special_handling(key, val, ans):
continue
if check_keys:
if key not in all_keys:
log_error('Ignoring unknown config key: {}'.format(key))
continue
tm = type_map.get(key)
if tm is not None:
val = tm(val)
ans[key] = val
def init_config(defaults_path, parse_config):
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)
return 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, 6, 0)
version = (0, 8, 3)
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')
@@ -22,11 +24,25 @@ WindowGeometry = namedtuple('WindowGeometry', 'left top right bottom xnum ynum')
def _get_config_dir():
# This must be called before calling setApplicationName
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
return os.path.abspath(os.path.expanduser(os.environ['VISE_CONFIG_DIRECTORY']))
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or ('~/Library/Preferences' if is_macos else '~/.config')))
locations = []
if 'XDG_CONFIG_HOME' in os.environ:
locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME'])))
locations.append(os.path.expanduser('~/.config'))
if is_macos:
locations.append(os.path.expanduser('~/Library/Preferences'))
if 'XDG_CONFIG_DIRS' in os.environ:
for loc in os.environ['XDG_CONFIG_DIRS'].split(os.pathsep):
locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME'])))
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
@@ -37,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
@@ -56,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

@@ -69,7 +69,7 @@
// Sharp control codes
// -------------------
// Align display
// Align display
#define DECALN '8'
// Esc control codes
@@ -180,7 +180,7 @@
// Scroll down by the specified number of lines
#define SD 'T'
// *Erase character*: Erase the indicated # of characters on the
// current line.
#define ECH 'X'
@@ -212,7 +212,7 @@
// *Select graphics rendition*: The terminal can display the following
// character attributes that change the character display without
// changing the character
// changing the character
#define SGR 'm'
// *Device status report*.
@@ -230,12 +230,6 @@
// Forward tab
#define CHT 'I'
// Save cursor
#define SC 's'
// Restore cursor
#define RC 'u'
// Misc sequences
// ----------------

View File

@@ -86,7 +86,7 @@ dealloc(CTFace* self) {
static PyObject*
font_descriptor_to_python(CTFontDescriptorRef descriptor) {
NSURL *url = (NSURL *) CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
NSString *psName = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute);
NSString *psName = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute);
NSString *family = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontFamilyNameAttribute);
NSString *style = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute);
NSDictionary *traits = (NSDictionary *) CTFontDescriptorCopyAttribute(descriptor, kCTFontTraitsAttribute);
@@ -94,8 +94,8 @@ font_descriptor_to_python(CTFontDescriptorRef descriptor) {
NSNumber *weightVal = traits[(id)kCTFontWeightTrait];
NSNumber *widthVal = traits[(id)kCTFontWidthTrait];
PyObject *ans = Py_BuildValue("{ssssssss sOsOsO sfsfsI}",
"path", [[url path] UTF8String],
PyObject *ans = Py_BuildValue("{ssssssss sOsOsO sfsfsI}",
"path", [[url path] UTF8String],
"postscript_name", [psName UTF8String],
"family", [family UTF8String],
"style", [style UTF8String],
@@ -148,7 +148,7 @@ static PyObject*
coretext_all_fonts(PyObject UNUSED *_self) {
static CTFontCollectionRef collection = NULL;
if (collection == NULL) collection = CTFontCollectionCreateFromAvailableFonts(NULL);
NSArray *matches = (NSArray *) CTFontCollectionCreateMatchingFontDescriptors(collection);
NSArray *matches = (NSArray *) CTFontCollectionCreateMatchingFontDescriptors(collection);
PyObject *ans = PyTuple_New([matches count]), *temp;
if (ans == NULL) return PyErr_NoMemory();
for (unsigned int i = 0; i < [matches count]; i++) {
@@ -173,24 +173,28 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
if (new_font == old_font) { CFRelease(new_font); continue; }
return new_font;
}
PyErr_SetString(PyExc_ValueError, "CoreText returned the same font as a fallback font");
PyErr_SetString(PyExc_ValueError, "CoreText returned the same font as a fallback font");
return NULL;
}
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[128] = {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);
}
unsigned int
unsigned int
glyph_id_for_codepoint(PyObject *s, char_type ch) {
CTFace *self = (CTFace*)s;
unichar chars[2] = {0};
@@ -200,6 +204,15 @@ glyph_id_for_codepoint(PyObject *s, char_type ch) {
return glyphs[0];
}
bool
is_glyph_empty(PyObject *s, glyph_index g) {
CTFace *self = (CTFace*)s;
CGGlyph gg = g;
CGRect bounds;
CTFontGetBoundingRectsForGlyphs(self->ct_font, kCTFontOrientationHorizontal, &gg, &bounds, 1);
return bounds.size.width <= 0;
}
static inline float
scaled_point_sz() {
return ((global_state.logical_dpi_x + global_state.logical_dpi_y) / 144.0) * global_state.font_sz_in_pts;
@@ -216,7 +229,7 @@ set_size_for_face(PyObject *s, unsigned int UNUSED desired_height, bool force) {
return true;
}
hb_font_t*
hb_font_t*
harfbuzz_font_for_face(PyObject* s) {
CTFace *self = (CTFace*)s;
if (!self->hb_font) {
@@ -227,7 +240,7 @@ harfbuzz_font_for_face(PyObject* s) {
return self->hb_font;
}
void
void
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
// See https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html
CTFace *self = (CTFace*)s;
@@ -241,10 +254,10 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
if (glyphs[i]) {
w = (unsigned int)(ceilf(
CTFontGetAdvancesForGlyphs(self->ct_font, kCTFontOrientationHorizontal, glyphs+i, NULL, 1)));
if (w > width) width = w;
if (w > width) width = w;
}
}
*cell_width = MAX(1, width);
*cell_width = MAX(1, width);
*underline_position = floor(self->ascent - self->underline_position + 0.5);
*underline_thickness = (unsigned int)ceil(MAX(0.1, self->underline_thickness));
*baseline = (unsigned int)self->ascent;
@@ -252,7 +265,7 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
// Let CoreText's layout engine calculate the line height. Slower, but hopefully more accurate.
CFStringRef ts = CFSTR("test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test");
CFMutableAttributedStringRef test_string = CFAttributedStringCreateMutable(kCFAllocatorDefault, CFStringGetLength(ts));
CFAttributedStringReplaceString(test_string, CFRangeMake(0, 0), ts);
CFAttributedStringReplaceString(test_string, CFRangeMake(0, 0), ts);
CFAttributedStringSetAttribute(test_string, CFRangeMake(0, CFStringGetLength(ts)), kCTFontAttributeName, self->ct_font);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(10, 10, 200, 200));
@@ -263,7 +276,7 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
CTFrameGetLineOrigins(test_frame, CFRangeMake(0, 1), &origin1);
CTFrameGetLineOrigins(test_frame, CFRangeMake(1, 1), &origin2);
CGFloat line_height = origin1.y - origin2.y;
CFRelease(test_frame); CFRelease(path); CFRelease(framesetter);
CFRelease(test_frame); CFRelease(path); CFRelease(framesetter);
*cell_height = MAX(4, (unsigned int)ceilf(line_height));
#undef count
}
@@ -278,7 +291,7 @@ face_from_descriptor(PyObject *descriptor) {
return (PyObject*) ct_face(font);
}
PyObject*
PyObject*
face_from_path(const char *path, int UNUSED index) {
CFStringRef s = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, s, kCFURLPOSIXPathStyle, false);
@@ -315,7 +328,7 @@ render_color_glyph(CTFontRef font, uint8_t *buf, int glyph_id, unsigned int widt
if (ctx == NULL) fatal("Out of memory");
CGContextSetShouldAntialias(ctx, true);
CGContextSetShouldSmoothFonts(ctx, true); // sub-pixel antialias
CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
CGAffineTransform transform = CGAffineTransformIdentity;
CGContextSetTextDrawingMode(ctx, kCGTextFill);
CGGlyph glyph = glyph_id;
@@ -353,7 +366,7 @@ render_glyphs(CTFontRef font, unsigned int width, unsigned int height, unsigned
CGContextSetGrayFillColor(render_ctx, 1, 1); // white glyphs
CGContextSetTextDrawingMode(render_ctx, kCGTextFill);
CGContextSetTextMatrix(render_ctx, CGAffineTransformIdentity);
CGContextSetTextPosition(render_ctx, 0, height - baseline);
CGContextSetTextPosition(render_ctx, 0, height - baseline);
CTFontDrawGlyphs(font, glyphs, positions, num_glyphs, render_ctx);
}
@@ -420,7 +433,7 @@ static PyMethodDef methods[] = {
static PyObject *
repr(CTFace *self) {
char buf[1024] = {0};
snprintf(buf, sizeof(buf)/sizeof(buf[0]), "ascent=%.1f, descent=%.1f, leading=%.1f, point_sz=%.1f, scaled_point_sz=%.1f, underline_position=%.1f underline_thickness=%.1f",
snprintf(buf, sizeof(buf)/sizeof(buf[0]), "ascent=%.1f, descent=%.1f, leading=%.1f, point_sz=%.1f, scaled_point_sz=%.1f, underline_position=%.1f underline_thickness=%.1f",
(self->ascent), (self->descent), (self->leading), (self->point_sz), (self->scaled_point_sz), (self->underline_position), (self->underline_thickness));
return PyUnicode_FromFormat(
"Face(family=%U, full_name=%U, postscript_name=%U, path=%U, units_per_em=%u, %s)",
@@ -455,8 +468,8 @@ PyTypeObject CTFace_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.CTFace",
.tp_basicsize = sizeof(CTFace),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "CoreText Font face",
.tp_methods = methods,
.tp_members = members,
@@ -465,10 +478,10 @@ PyTypeObject CTFace_Type = {
int
int
init_CoreText(PyObject *module) {
if (PyType_Ready(&CTFace_Type) < 0) return 0;
if (PyModule_AddObject(module, "CTFace", (PyObject *)&CTFace_Type) != 0) return 0;
if (PyModule_AddObject(module, "CTFace", (PyObject *)&CTFace_Type) != 0) return 0;
if (PyModule_AddFunctions(module, module_methods) != 0) return 0;
if (Py_AtExit(finalize) != 0) {
PyErr_SetString(PyExc_RuntimeError, "Failed to register the CoreText at exit handler");

View File

@@ -39,7 +39,7 @@ repr(Cursor *self) {
);
}
void
void
cursor_reset_display_attrs(Cursor *self) {
self->bg = 0; self->fg = 0; self->decoration_fg = 0;
self->decoration = 0; self->bold = false; self->italic = false; self->reverse = false; self->strikethrough = false;
@@ -50,25 +50,25 @@ static inline void
parse_color(unsigned int *params, unsigned int *i, unsigned int count, uint32_t *result) {
unsigned int attr;
uint8_t r, g, b;
if (*i < count) {
if (*i < count) {
attr = params[(*i)++];
switch(attr) {
case 5:
if (*i < count) *result = (params[(*i)++] & 0xFF) << 8 | 1;
break;
switch(attr) {
case 5:
if (*i < count) *result = (params[(*i)++] & 0xFF) << 8 | 1;
break;
case 2: \
if (*i < count - 2) {
/* Ignore the first parameter in a four parameter RGB */
/* sequence (unused color space id), see https://github.com/kovidgoyal/kitty/issues/227 */
if (*i < count - 3) (*i)++;
r = params[(*i)++] & 0xFF;
g = params[(*i)++] & 0xFF;
b = params[(*i)++] & 0xFF;
*result = r << 24 | g << 16 | b << 8 | 2;
if (*i < count - 2) {
/* Ignore the first parameter in a four parameter RGB */
/* sequence (unused color space id), see https://github.com/kovidgoyal/kitty/issues/227 */
if (*i < count - 3) (*i)++;
r = params[(*i)++] & 0xFF;
g = params[(*i)++] & 0xFF;
b = params[(*i)++] & 0xFF;
*result = r << 24 | g << 16 | b << 8 | 2;
}
break;
}
}
break;
}
}
}
@@ -109,13 +109,13 @@ START_ALLOW_CASE_RANGE
self->strikethrough = false; break;
case 30 ... 37:
self->fg = ((attr - 30) << 8) | 1; break;
case 38:
case 38:
SET_COLOR(fg);
case 39:
self->fg = 0; break;
case 40 ... 47:
self->bg = ((attr - 40) << 8) | 1; break;
case 48:
case 48:
SET_COLOR(bg);
case 49:
self->bg = 0; break;
@@ -124,7 +124,7 @@ START_ALLOW_CASE_RANGE
case 100 ... 107:
self->bg = ((attr - 100 + 8) << 8) | 1; break;
case DECORATION_FG_CODE:
SET_COLOR(decoration_fg);
SET_COLOR(decoration_fg);
case DECORATION_FG_CODE + 1:
self->decoration_fg = 0; break;
}
@@ -149,7 +149,7 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para
attr = params[i++];
switch(attr) {
case 0:
RANGE { cell->attrs &= WIDTH_MASK; cell->fg = 0; cell->bg = 0; cell->decoration_fg = 0; }
RANGE { cell->attrs &= WIDTH_MASK; cell->fg = 0; cell->bg = 0; cell->decoration_fg = 0; }
break;
case 1:
SET(BOLD_SHIFT);
@@ -177,13 +177,13 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para
START_ALLOW_CASE_RANGE
case 30 ... 37:
SIMPLE(fg, ((attr - 30) << 8) | 1);
case 38:
case 38:
SET_COLOR(fg);
case 39:
SIMPLE(fg, 0);
case 40 ... 47:
SIMPLE(bg, ((attr - 40) << 8) | 1);
case 48:
case 48:
SET_COLOR(bg);
case 49:
SIMPLE(bg, 0);
@@ -224,14 +224,14 @@ color_as_sgr(char *buf, size_t sz, unsigned long val, unsigned simple_code, unsi
static inline const char*
decoration_as_sgr(uint8_t decoration) {
switch(decoration) {
case 1: return "4";
case 2: return "4:2";
case 1: return "4";
case 2: return "4:2";
case 3: return "4:3";
default: return "24";
}
}
const char*
const char*
cursor_as_sgr(Cursor *self, Cursor *prev) {
static char buf[128];
#define SZ sizeof(buf) - (p - buf) - 2
@@ -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;
@@ -267,12 +267,12 @@ void cursor_reset(Cursor *self) {
void cursor_copy_to(Cursor *src, Cursor *dest) {
#define CCY(x) dest->x = src->x;
CCY(x); CCY(y); CCY(shape); CCY(blink);
CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
CCY(x); CCY(y); CCY(shape); CCY(blink);
CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
}
static PyObject*
copy(Cursor *self);
copy(Cursor *self, PyObject*);
#define copy_doc "Create a clone of this cursor"
// Boilerplate {{{
@@ -317,21 +317,21 @@ PyTypeObject Cursor_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.Cursor",
.tp_basicsize = sizeof(Cursor),
.tp_dealloc = (destructor)dealloc,
.tp_dealloc = (destructor)dealloc,
.tp_repr = (reprfunc)repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "Cursors",
.tp_richcompare = richcmp,
.tp_richcompare = richcmp,
.tp_methods = methods,
.tp_members = members,
.tp_members = members,
.tp_getset = getseters,
.tp_new = new,
.tp_new = new,
};
RICHCMP(Cursor)
// }}}
Cursor*
cursor_copy(Cursor *self) {
Cursor* ans;
@@ -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
@@ -163,8 +123,8 @@ static PyMethodDef module_methods[] = {
static struct PyModuleDef module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "fast_data_types", /* name of module */
.m_doc = NULL,
.m_size = -1,
.m_doc = NULL,
.m_size = -1,
.m_methods = module_methods
};
@@ -180,10 +140,14 @@ extern bool init_fontconfig_library(PyObject*);
extern bool init_desktop(PyObject*);
extern bool init_fonts(PyObject*);
extern bool init_glfw(PyObject *m);
extern bool init_child(PyObject *m);
extern bool init_state(PyObject *module);
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);
@@ -203,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;
@@ -211,10 +176,13 @@ PyInit_fast_data_types(void) {
if (!init_ColorProfile(m)) return NULL;
if (!init_Screen(m)) return NULL;
if (!init_glfw(m)) return NULL;
if (!init_child(m)) return NULL;
if (!init_state(m)) return NULL;
if (!init_keys(m)) return NULL;
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;
@@ -227,7 +195,7 @@ PyInit_fast_data_types(void) {
#define OOF(n) #n, offsetof(Cell, n)
if (PyModule_AddObject(m, "CELL", Py_BuildValue("{sI sI sI sI sI sI sI sI sI}",
OOF(ch), OOF(fg), OOF(bg), OOF(decoration_fg), OOF(cc), OOF(sprite_x), OOF(sprite_y), OOF(sprite_z), "size", sizeof(Cell))) != 0) return NULL;
OOF(ch), OOF(fg), OOF(bg), OOF(decoration_fg), OOF(cc_idx), OOF(sprite_x), OOF(sprite_y), OOF(sprite_z), "size", sizeof(Cell))) != 0) return NULL;
#undef OOF
PyModule_AddIntConstant(m, "BOLD", BOLD_SHIFT);
PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT);

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)
@@ -25,12 +26,14 @@
#define MIN(x, y) (((x) > (y)) ? (y) : (x))
#define xstr(s) str(s)
#define str(s) #s
#define fatal(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); }
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
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;
typedef uint32_t color_type;
typedef uint32_t combining_type;
typedef uint16_t combining_type;
typedef uint32_t pixel;
typedef unsigned int index_type;
typedef uint16_t sprite_index;
@@ -55,8 +58,6 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
#define REVERSE_SHIFT 6
#define STRIKE_SHIFT 7
#define COL_MASK 0xFFFFFFFF
#define CC_MASK 0xFFFF
#define CC_SHIFT 16
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
#define DECORATION_FG_CODE 58
@@ -69,7 +70,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
#define CURSOR_TO_ATTRS(c, w) \
((w) | (((c->decoration & 3) << DECORATION_SHIFT) | ((c->bold & 1) << BOLD_SHIFT) | \
((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | ((c->strikethrough & 1) << STRIKE_SHIFT)))
((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | ((c->strikethrough & 1) << STRIKE_SHIFT)))
#define ATTRS_TO_CURSOR(a, c) \
(c)->decoration = (a >> DECORATION_SHIFT) & 3; (c)->bold = (a >> BOLD_SHIFT) & 1; (c)->italic = (a >> ITALIC_SHIFT) & 1; \
@@ -133,11 +134,12 @@ typedef struct {
} Region;
typedef struct {
char_type ch;
color_type fg, bg, decoration_fg;
combining_type cc;
sprite_index sprite_x, sprite_y, sprite_z;
attrs_type attrs;
// The following are only needed on the CPU, not the GPU
char_type ch;
combining_type cc_idx[2];
} Cell;
typedef struct {
@@ -181,7 +183,7 @@ typedef struct {
} Cursor;
typedef struct {
bool is_visible;
bool is_visible, is_focused;
CursorShape shape;
unsigned int x, y;
double left, right, top, bottom;
@@ -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 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) { \
@@ -243,7 +230,7 @@ typedef struct {
}
// Global functions
// Global functions
const char* base64_decode(const uint32_t *src, size_t src_sz, uint8_t *dest, size_t dest_capacity, size_t *dest_sz);
Line* alloc_line();
Cursor* alloc_cursor();
@@ -271,14 +258,12 @@ 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();
void wakeup_io_loop(bool);
void scroll_event(double, double);
void fake_scroll(bool);
void fake_scroll(int, bool);
void set_special_key_combo(int glfw_key, int mods);
void on_text_input(unsigned int codepoint, int mods);
void on_key_input(int key, int scancode, int action, int mods);

232
kitty/emoji.h generated
View File

@@ -1,13 +1,45 @@
// unicode data, built from the unicode standard on: 2018-02-09
// see gen-wcwidth.py
#pragma once
#include "data-types.h"
START_ALLOW_CASE_RANGE
static inline bool is_emoji(uint32_t code) {
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:
return true;
case 0x231a ... 0x231b:
return true;
case 0x2328:
return true;
case 0x2388:
return true;
case 0x23cf:
return true;
case 0x23e9 ... 0x23f3:
@@ -24,89 +56,15 @@ static inline bool is_emoji(uint32_t 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;
@@ -132,7 +90,7 @@ static inline bool is_emoji(uint32_t code) {
return true;
case 0x2757:
return true;
case 0x2763 ... 0x2764:
case 0x2763 ... 0x2767:
return true;
case 0x2795 ... 0x2797:
return true;
@@ -160,11 +118,15 @@ static inline bool is_emoji(uint32_t 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;
@@ -172,9 +134,9 @@ static inline bool is_emoji(uint32_t 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;
@@ -182,106 +144,48 @@ static inline bool is_emoji(uint32_t 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;
}
return false;
return false;
}
static inline bool is_emoji_modifier(uint32_t code) {
static inline bool
is_emoji_modifier(char_type code) {
switch(code) {
case 0x1f3fb ... 0x1f3ff:
return true;
default: return false;
}
return false;
return false;
}
END_ALLOW_CASE_RANGE

View File

@@ -131,9 +131,9 @@ add_charset(FcPattern *pat, size_t num) {
charset = FcCharSetCreate();
if (charset == NULL) { PyErr_NoMemory(); goto end; }
for (size_t i = 0; i < num; i++) {
if (!FcCharSetAddChar(charset, char_buf[i])) {
PyErr_SetString(PyExc_RuntimeError, "Failed to add character to fontconfig charset");
goto end;
if (!FcCharSetAddChar(charset, char_buf[i])) {
PyErr_SetString(PyExc_RuntimeError, "Failed to add character to fontconfig charset");
goto end;
}
}
AP(FcPatternAddCharSet, FC_CHARSET, charset, "charset");
@@ -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);
@@ -214,7 +213,7 @@ static PyMethodDef module_methods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};
bool
bool
init_fontconfig_library(PyObject *module) {
if (!FcInit()) {
PyErr_SetString(PyExc_RuntimeError, "Failed to initialize the fontconfig library");

View File

@@ -1,4 +1,5 @@
/*
* vim:fileencoding=utf-8
* fonts.c
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
*
@@ -8,11 +9,11 @@
#include "fonts.h"
#include "state.h"
#include "emoji.h"
#include "unicode-data.h"
#define MISSING_GLYPH 4
#define MAX_NUM_EXTRA_GLYPHS 8
typedef uint16_t glyph_index;
typedef void (*send_sprite_to_gpu_func)(unsigned int, unsigned int, unsigned int, pixel*);
send_sprite_to_gpu_func current_send_sprite_to_gpu = NULL;
static PyObject *python_send_to_gpu_impl = NULL;
@@ -36,11 +37,16 @@ struct SpritePosition {
ExtraGlyphs extra_glyphs;
};
#define SPECIAL_FILLED_MASK 1
#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;
glyph_index glyph;
bool is_special, filled;
uint8_t data;
};
typedef struct {
@@ -51,15 +57,15 @@ typedef struct {
static GPUSpriteTracker sprite_tracker = {0};
static hb_buffer_t *harfbuzz_buffer = NULL;
static char_type shape_buffer[2048] = {0};
static char_type shape_buffer[4096] = {0};
typedef struct {
PyObject *face;
// Map glyphs to sprite map co-ords
SpritePosition sprite_map[1024];
SpecialGlyphCache special_glyph_cache[1024];
bool bold, italic;
SpritePosition sprite_map[1024];
SpecialGlyphCache special_glyph_cache[SPECIAL_GLYPH_CACHE_SIZE];
bool bold, italic, emoji_presentation;
} Font;
typedef struct {
@@ -79,7 +85,7 @@ static Fonts fonts = {0};
// Sprites {{{
static inline void
static inline void
sprite_map_set_error(int error) {
switch(error) {
case 1:
@@ -123,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
@@ -150,20 +156,21 @@ sprite_position_for(Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, ui
return s;
}
static SpecialGlyphCache*
special_glyph_cache_for(Font *font, glyph_index glyph) {
static inline SpecialGlyphCache*
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->filled)) 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->filled) {
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));
if (s->next == NULL) return NULL;
if (s->next == NULL) return NULL;
}
s = s->next;
}
@@ -211,7 +218,7 @@ clear_sprite_map(Font *font) {
void
clear_special_glyph_cache(Font *font) {
#define CLEAR(s) s->filled = false; s->glyph = 0;
#define CLEAR(s) s->data = 0; s->glyph = 0;
SpecialGlyphCache *s;
for (size_t i = 0; i < sizeof(font->special_glyph_cache)/sizeof(font->special_glyph_cache[0]); i++) {
s = font->special_glyph_cache + i;
@@ -243,18 +250,18 @@ 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->face = face;
f->bold = bold; f->italic = italic; f->emoji_presentation = emoji_presentation;
return true;
}
static inline void
del_font(Font *f) {
Py_CLEAR(f->face);
del_font(Font *f) {
Py_CLEAR(f->face);
free_maps(f);
f->bold = false; f->italic = false;
}
@@ -262,7 +269,7 @@ del_font(Font *f) {
static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_position = 0, underline_thickness = 0;
static pixel *canvas = NULL;
#define CELLS_IN_CANVAS ((MAX_NUM_EXTRA_GLYPHS + 1) * 3)
static inline void
static inline void
clear_canvas(void) { memset(canvas, 0, CELLS_IN_CANVAS * cell_width * cell_height * sizeof(pixel)); }
static void
@@ -276,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);
@@ -285,10 +292,14 @@ update_cell_metrics() {
unsigned int before_cell_height = cell_height;
if (OPT(adjust_line_height_px) != 0) cell_height += OPT(adjust_line_height_px);
if (OPT(adjust_line_height_frac) != 0.f) cell_height *= OPT(adjust_line_height_frac);
if (OPT(adjust_column_width_px != 0)) cell_width += OPT(adjust_column_width_px);
if (OPT(adjust_column_width_frac) != 0.f) cell_height *= OPT(adjust_column_width_frac);
int line_height_adjustment = cell_height - before_cell_height;
if (cell_height < 4) { PyErr_SetString(PyExc_ValueError, "line height too small after adjustment"); return NULL; }
if (cell_height > 1000) { PyErr_SetString(PyExc_ValueError, "line height too large after adjustment"); return NULL; }
underline_position = MIN(cell_height - 1, underline_position);
// ensure there is at least a couple of pixels available to render styled underlines
while (underline_position > baseline + 1 && cell_height - underline_position < 2) underline_position--;
if (line_height_adjustment > 1) {
baseline += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2);
underline_position += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2);
@@ -309,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
@@ -318,49 +330,77 @@ face_has_codepoint(PyObject* face, char_type cp) {
return glyph_id_for_codepoint(face, cp) > 0;
}
static inline bool
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;
if (cell->cc) {
if (!face_has_codepoint(self->face, cell->cc & CC_MASK)) return false;
char_type cc = cell->cc >> 16;
if (cc && !face_has_codepoint(self->face, cc)) return false;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
combining_type cc_idx = cell->cc_idx[i];
if (cc_idx == VS15 || cc_idx == VS16) continue;
if (!face_has_codepoint(self->face, codepoint_for_mark(cc_idx))) return false;
}
return true;
}
static inline void
output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
printf("U+%x ", cell->ch);
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
}
if (bold) printf("bold ");
if (italic) printf("italic ");
if (emoji_presentation) printf("emoji_presentation ");
PyObject_Print(face, stdout, 0);
if (new_face) printf(" (new face)");
printf("\n");
}
static inline ssize_t
load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation) {
if (fonts.fallback_fonts_count > 100) { 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, 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, 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;
ssize_t f;
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;
}
}
if (fonts.fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return MISSING_FONT; }
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);
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
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");
Py_DECREF(face);
fonts.fallback_fonts_count++;
fonts.fonts_count++;
return ans;
return load_fallback_font(cell, bold, italic, emoji_presentation);
}
static inline ssize_t
@@ -399,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
@@ -441,7 +481,7 @@ render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *de
}
}
static void
static void
render_box_cell(Cell *cell) {
int error = 0;
glyph_index glyph = box_glyph_id(cell->ch);
@@ -467,19 +507,17 @@ render_box_cell(Cell *cell) {
}
static inline void
load_hb_buffer(Cell *first_cell, index_type num_cells) {
load_hb_buffer(Cell *first_cell, index_type num_cells) {
index_type num;
hb_buffer_clear_contents(harfbuzz_buffer);
while (num_cells) {
attrs_type prev_width = 0;
for (num = 0; num_cells && num < sizeof(shape_buffer)/sizeof(shape_buffer[0]) - 20; first_cell++, num_cells--) {
for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cell->cc_idx); first_cell++, num_cells--) {
if (prev_width == 2) { prev_width = 0; continue; }
shape_buffer[num++] = first_cell->ch;
prev_width = first_cell->attrs & WIDTH_MASK;
if (first_cell->cc) {
shape_buffer[num++] = first_cell->cc & CC_MASK;
combining_type cc2 = first_cell->cc >> 16;
if (cc2) shape_buffer[num++] = cc2 & CC_MASK;
for (unsigned i = 0; i < arraysz(first_cell->cc_idx) && first_cell->cc_idx[i]; i++) {
shape_buffer[num++] = codepoint_for_mark(first_cell->cc_idx[i]);
}
}
hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num);
@@ -517,14 +555,14 @@ 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();
for (unsigned int i = 0; i < num_cells; i++) {
for (unsigned int i = 0; i < num_cells; i++) {
sprite_position[i]->rendered = true;
sprite_position[i]->colored = was_colored;
set_cell_sprite(cells + i, sprite_position[i]);
set_cell_sprite(cells + i, sprite_position[i]);
pixel *buf = num_cells == 1 ? canvas : extract_cell_from_canvas(i, num_cells);
current_send_sprite_to_gpu(sprite_position[i]->x, sprite_position[i]->y, sprite_position[i]->z, buf);
}
@@ -538,28 +576,84 @@ typedef struct {
char_type current_codepoint;
} CellData;
typedef struct {
unsigned int first_glyph_idx, first_cell_idx, num_glyphs, num_cells;
} Group;
typedef struct {
uint32_t previous_cluster;
bool prev_was_special, prev_was_empty;
CellData current_cell_data;
Group *groups;
size_t groups_capacity, group_idx, glyph_idx, cell_idx, num_cells, num_glyphs;
Cell *first_cell, *last_cell;
hb_glyph_info_t *info;
hb_glyph_position_t *positions;
} GroupState;
static GroupState group_state = {0};
static inline unsigned int
num_codepoints_in_cell(Cell *cell) {
unsigned int ans = 1;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++;
return ans;
}
static inline void
shape(Cell *first_cell, index_type num_cells, hb_font_t *font) {
if (group_state.groups_capacity <= 2 * num_cells) {
group_state.groups_capacity = MAX(128, 2 * num_cells); // avoid unnecessary reallocs
group_state.groups = realloc(group_state.groups, sizeof(Group) * group_state.groups_capacity);
if (!group_state.groups) fatal("Out of memory");
}
group_state.previous_cluster = UINT32_MAX;
group_state.prev_was_special = false;
group_state.prev_was_empty = false;
group_state.current_cell_data.cell = first_cell; group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cell); group_state.current_cell_data.codepoints_consumed = 0; group_state.current_cell_data.current_codepoint = first_cell->ch;
memset(group_state.groups, 0, sizeof(Group) * group_state.groups_capacity);
group_state.group_idx = 0;
group_state.glyph_idx = 0;
group_state.cell_idx = 0;
group_state.num_cells = num_cells;
group_state.first_cell = first_cell;
group_state.last_cell = first_cell + (num_cells ? num_cells - 1 : 0);
load_hb_buffer(first_cell, num_cells);
hb_shape(font, harfbuzz_buffer, NULL, 0);
unsigned int info_length, positions_length;
group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length);
group_state.positions = hb_buffer_get_glyph_positions(harfbuzz_buffer, &positions_length);
if (!group_state.info || !group_state.positions) group_state.num_glyphs = 0;
else group_state.num_glyphs = MIN(info_length, positions_length);
}
static inline bool
is_special_glyph(glyph_index glyph_id, Font *font, CellData* cell_data) {
// A glyph is special if the codepoint it corresponds to matches a
// different glyph in the font
SpecialGlyphCache *s = special_glyph_cache_for(font, glyph_id);
SpecialGlyphCache *s = special_glyph_cache_for(font, glyph_id, SPECIAL_FILLED_MASK);
if (s == NULL) return false;
if (!s->filled) {
s->is_special = cell_data->current_codepoint ? (
glyph_id != glyph_id_for_codepoint(font->face, cell_data->current_codepoint) ? true : false)
if (!(s->data & SPECIAL_FILLED_MASK)) {
bool is_special = cell_data->current_codepoint ? (
glyph_id != glyph_id_for_codepoint(font->face, cell_data->current_codepoint) ? true : false)
:
false;
s->filled = true;
}
return s->is_special;
uint8_t val = is_special ? SPECIAL_VALUE_MASK : 0;
s->data |= val | SPECIAL_FILLED_MASK;
}
return s->data & SPECIAL_VALUE_MASK;
}
static inline unsigned int
num_codepoints_in_cell(Cell *cell) {
unsigned int ans = 1;
if (cell->cc) ans += ((cell->cc >> CC_SHIFT) & CC_MASK) ? 2 : 1;
return ans;
static inline bool
is_empty_glyph(glyph_index glyph_id, Font *font) {
// A glyph is empty if its metrics have a width of zero
SpecialGlyphCache *s = special_glyph_cache_for(font, glyph_id, EMPTY_FILLED_MASK);
if (s == NULL) return false;
if (!(s->data & EMPTY_FILLED_MASK)) {
uint8_t val = is_glyph_empty(font->face, glyph_id) ? EMPTY_VALUE_MASK : 0;
s->data |= val | EMPTY_FILLED_MASK;
}
return s->data & EMPTY_VALUE_MASK;
}
static inline unsigned int
@@ -579,100 +673,154 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) {
case 0:
cell_data->current_codepoint = cell_data->cell->ch;
break;
case 1:
cell_data->current_codepoint = cell_data->cell->cc & CC_MASK;
break;
case 2:
cell_data->current_codepoint = (cell_data->cell->cc >> CC_SHIFT) & CC_MASK;
break;
default:
cell_data->current_codepoint = 0;
cell_data->current_codepoint = codepoint_for_mark(cell_data->cell->cc_idx[cell_data->codepoints_consumed - 1]);
break;
}
}
return 0;
}
static inline glyph_index
next_group(Font *font, unsigned int *num_group_cells, unsigned int *num_group_glyphs, Cell *cells, hb_glyph_info_t *info, unsigned int max_num_glyphs, unsigned int max_num_cells, ExtraGlyphs *extra_glyphs) {
// See https://github.com/behdad/harfbuzz/issues/615 for a discussion of
// how to break text into cells. In addition, we have to deal with
// monospace ligature fonts that use dummy glyphs of zero size to implement
// their ligatures.
CellData cell_data;
cell_data.cell = cells; cell_data.num_codepoints = num_codepoints_in_cell(cells); cell_data.codepoints_consumed = 0; cell_data.current_codepoint = cells->ch;
#define LIMIT (MAX_NUM_EXTRA_GLYPHS + 1)
glyph_index glyphs_in_group[LIMIT];
unsigned int ncells = 0, nglyphs = 0, n;
uint32_t previous_cluster = UINT32_MAX, cluster;
Cell *last_cell = cells + max_num_cells;
unsigned int cell_limit = MIN(max_num_cells, LIMIT + 1), glyph_limit = MIN(LIMIT, max_num_glyphs);
bool is_special, prev_was_special = false;
while(nglyphs < glyph_limit && ncells < cell_limit) {
glyph_index glyph_id = info[nglyphs].codepoint;
cluster = info[nglyphs].cluster;
is_special = is_special_glyph(glyph_id, font, &cell_data);
if (prev_was_special && !is_special) break;
glyphs_in_group[nglyphs++] = glyph_id;
// Soak up a number of codepoints indicated by the difference in cluster numbers.
if (cluster > previous_cluster || nglyphs == 1) {
n = nglyphs == 1 ? 1 : cluster - previous_cluster;
unsigned int before = ncells;
while(n-- && ncells < max_num_cells) ncells += check_cell_consumed(&cell_data, last_cell);
if (ncells > before && !is_special) break;
}
previous_cluster = cluster;
prev_was_special = is_special;
}
*num_group_cells = MAX(1, MIN(ncells, cell_limit));
*num_group_glyphs = MAX(1, MIN(nglyphs, glyph_limit));
memset(extra_glyphs, 0, sizeof(ExtraGlyphs));
switch(nglyphs) {
case 0:
case 1:
break;
default:
memcpy(&extra_glyphs->data, glyphs_in_group + 1, sizeof(glyph_index) * MIN(MAX_NUM_EXTRA_GLYPHS, nglyphs - 1));
break;
}
#undef LIMIT
return glyphs_in_group[0];
}
static inline unsigned int
shape(Cell *first_cell, index_type num_cells, hb_font_t *font, hb_glyph_info_t **info, hb_glyph_position_t **positions) {
load_hb_buffer(first_cell, num_cells);
hb_shape(font, harfbuzz_buffer, NULL, 0);
unsigned int info_length, positions_length;
*info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length);
*positions = hb_buffer_get_glyph_positions(harfbuzz_buffer, &positions_length);
if (!info || !positions) return 0;
return MIN(info_length, positions_length);
}
static inline void
shape_run(Cell *first_cell, index_type num_cells, Font *font) {
hb_glyph_info_t *info;
hb_glyph_position_t *positions;
unsigned int num_glyphs = shape(first_cell, num_cells, harfbuzz_font_for_face(font->face), &info, &positions);
shape(first_cell, num_cells, harfbuzz_font_for_face(font->face));
#if 0
// You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text
hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, num_glyphs, (char*)canvas, sizeof(pixel) * CELLS_IN_CANVAS * cell_width * cell_height, NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS);
hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, group_state.num_glyphs, (char*)canvas, sizeof(pixel) * CELLS_IN_CANVAS * cell_width * cell_height, NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS);
printf("\n%s\n", (char*)canvas);
clear_canvas();
#endif
unsigned int run_pos = 0, cell_pos = 0, num_group_glyphs, num_group_cells;
ExtraGlyphs extra_glyphs;
glyph_index first_glyph;
while(run_pos < num_glyphs && cell_pos < num_cells) {
first_glyph = next_group(font, &num_group_cells, &num_group_glyphs, first_cell + cell_pos, info + run_pos, num_glyphs - run_pos, num_cells - cell_pos, &extra_glyphs);
/* printf("Group: num_group_cells: %u num_group_glyphs: %u\n", num_group_cells, num_group_glyphs); */
render_group(num_group_cells, num_group_glyphs, first_cell + cell_pos, info + run_pos, positions + run_pos, font, first_glyph, &extra_glyphs);
run_pos += num_group_glyphs; cell_pos += num_group_cells;
/* Now distribute the glyphs into groups of cells
* Considerations to keep in mind:
* Group sizes should be as small as possible for best performance
* Combining chars can result in multiple glyphs rendered into a single cell
* Emoji and East Asian wide chars can cause a single glyph to be rendered over multiple cells
* Ligature fonts, take two common approaches:
* 1. ABC becomes EMPTY, EMPTY, WIDE GLYPH this means we have to render N glyphs in N cells (example Fira Code)
* 2. ABC becomes WIDE GLYPH this means we have to render one glyph in N cells (example Operator Mono Lig)
*
* We rely on the cluster numbers from harfbuzz to tell us how many unicode codepoints a glyph corresponds to.
* Then we check if the glyph is a ligature glyph (is_special_glyph) and if it is an empty glyph. These three
* datapoints give us enough information to satisfy the constraint above, for a wide variety of fonts.
*/
uint32_t cluster, next_cluster;
bool add_to_current_group;
#define G(x) (group_state.x)
#define MAX_GLYPHS_IN_GROUP (MAX_NUM_EXTRA_GLYPHS + 1)
while (G(glyph_idx) < G(num_glyphs) && G(cell_idx) < G(num_cells)) {
glyph_index glyph_id = G(info)[G(glyph_idx)].codepoint;
cluster = G(info)[G(glyph_idx)].cluster;
bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));
bool is_empty = is_special && is_empty_glyph(glyph_id, font);
uint32_t num_codepoints_used_by_glyph = 0;
bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1;
Group *current_group = G(groups) + G(group_idx);
if (is_last_glyph) {
num_codepoints_used_by_glyph = UINT32_MAX;
} else {
next_cluster = G(info)[G(glyph_idx) + 1].cluster;
if (next_cluster > cluster) num_codepoints_used_by_glyph = next_cluster - cluster;
}
if (!current_group->num_glyphs) {
add_to_current_group = true;
} else {
if (is_special) {
add_to_current_group = G(prev_was_empty);
} else {
add_to_current_group = !G(prev_was_special);
}
}
if (current_group->num_glyphs >= MAX_GLYPHS_IN_GROUP || current_group->num_cells >= MAX_GLYPHS_IN_GROUP) add_to_current_group = false;
if (!add_to_current_group) { G(group_idx)++; current_group = G(groups) + G(group_idx); }
if (!current_group->num_glyphs++) {
current_group->first_glyph_idx = G(glyph_idx);
current_group->first_cell_idx = G(cell_idx);
}
#define MOVE_GLYPH_TO_NEXT_GROUP(start_cell_idx) { \
current_group->num_glyphs--; \
G(group_idx)++; current_group = G(groups) + G(group_idx); \
current_group->first_cell_idx = start_cell_idx; \
current_group->num_glyphs = 1; \
current_group->first_glyph_idx = G(glyph_idx); \
}
if (is_last_glyph) {
// soak up all remaining cells
if (G(cell_idx) < G(num_cells)) {
unsigned int num_left = G(num_cells) - G(cell_idx);
if (current_group->num_cells + num_left > MAX_GLYPHS_IN_GROUP) MOVE_GLYPH_TO_NEXT_GROUP(G(cell_idx));
current_group->num_cells += num_left;
if (current_group->num_cells > MAX_GLYPHS_IN_GROUP) current_group->num_cells = MAX_GLYPHS_IN_GROUP; // leave any trailing cells empty
G(cell_idx) += num_left;
}
} else {
unsigned int num_cells_consumed = 0, start_cell_idx = G(cell_idx);
while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {
unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cell));
G(cell_idx) += w;
num_cells_consumed += w;
num_codepoints_used_by_glyph--;
}
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
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);
current_group->num_glyphs = 1; current_group->first_glyph_idx = G(glyph_idx);
current_group->num_cells = MIN(num_cells_consumed, MAX_GLYPHS_IN_GROUP);
current_group->first_cell_idx = start_cell_idx;
start_cell_idx += current_group->num_cells;
num_cells_consumed -= current_group->num_cells;
}
} else {
if (num_cells_consumed + current_group->num_cells > MAX_GLYPHS_IN_GROUP) MOVE_GLYPH_TO_NEXT_GROUP(start_cell_idx);
current_group->num_cells += num_cells_consumed;
if (!is_special) { // not a ligature, end the group
G(group_idx)++; current_group = G(groups) + G(group_idx);
}
}
}
}
G(prev_was_special) = is_special;
G(prev_was_empty) = is_empty;
G(previous_cluster) = cluster;
G(glyph_idx)++;
}
#undef MOVE_GLYPH_TO_NEXT_GROUP
#undef MAX_GLYPHS_IN_GROUP
}
static inline void
merge_groups_for_pua_space_ligature() {
if (G(group_idx) == 1) {
Group *g = G(groups), *g1 = G(groups) + 1;
g->num_cells += g1->num_cells;
g->num_glyphs += g1->num_glyphs;
g->num_glyphs = MIN(g->num_glyphs, MAX_NUM_EXTRA_GLYPHS + 1);
G(group_idx) = 0;
}
}
static inline void
render_groups(Font *font) {
unsigned idx = 0;
ExtraGlyphs ed;
while (idx <= G(group_idx)) {
Group *group = G(groups) + idx;
if (!group->num_cells) break;
/* 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;
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++;
}
}
@@ -691,31 +839,34 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
if (face == NULL) return NULL;
font = calloc(1, sizeof(Font));
font->face = face;
}
hb_glyph_info_t *info;
hb_glyph_position_t *positions;
unsigned int num_glyphs = shape(line->cells, num, harfbuzz_font_for_face(font->face), &info, &positions);
}
shape_run(line->cells, num, font);
PyObject *ans = PyList_New(0);
unsigned int run_pos = 0, cell_pos = 0, num_group_glyphs, num_group_cells;
ExtraGlyphs extra_glyphs;
unsigned int idx = 0;
glyph_index first_glyph;
while(run_pos < num_glyphs && cell_pos < num) {
first_glyph = next_group(font, &num_group_cells, &num_group_glyphs, line->cells + cell_pos, info + run_pos, num_glyphs - run_pos, num - cell_pos, &extra_glyphs);
while (idx <= G(group_idx)) {
Group *group = G(groups) + idx;
if (!group->num_cells) break;
first_glyph = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;
PyObject *eg = PyTuple_New(MAX_NUM_EXTRA_GLYPHS);
for (size_t g = 0; g < MAX_NUM_EXTRA_GLYPHS; g++) PyTuple_SET_ITEM(eg, g, Py_BuildValue("H", extra_glyphs.data[g]));
PyList_Append(ans, Py_BuildValue("IIIN", num_group_cells, num_group_glyphs, first_glyph, eg));
run_pos += num_group_glyphs; cell_pos += num_group_cells;
for (size_t g = 0; g < MAX_NUM_EXTRA_GLYPHS; g++) PyTuple_SET_ITEM(eg, g, Py_BuildValue("H", g + 1 < group->num_glyphs ? G(info)[group->first_glyph_idx + g].codepoint : 0));
PyList_Append(ans, Py_BuildValue("IIIN", group->num_cells, group->num_glyphs, first_glyph, eg));
idx++;
}
if (face) { Py_CLEAR(face); free(font); }
return ans;
}
#undef G
static inline void
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
static inline void
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:
while(num_cells--) set_sprite(first_cell++, 0, 0, 0);
@@ -729,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;
@@ -739,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;
@@ -761,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
@@ -773,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;
@@ -782,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
@@ -793,6 +959,7 @@ finalize(void) {
for (size_t i = 0; i < fonts.fonts_count; i++) del_font(fonts.fonts + i);
free(fonts.symbol_maps); free(fonts.fonts);
if (harfbuzz_buffer) hb_buffer_destroy(harfbuzz_buffer);
free(group_state.groups);
}
static PyObject*
@@ -899,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; }}
@@ -923,15 +1090,14 @@ error:
static PyObject*
get_fallback_font(PyObject UNUSED *self, PyObject *args) {
PyObject *text;
PyObject *text;
int bold, italic;
if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL;
static Py_UCS4 char_buf[16];
if (!PyUnicode_AsUCS4(text, char_buf, sizeof(char_buf)/sizeof(char_buf[0]), 1)) return NULL;
Cell cell = {0};
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];
if (PyUnicode_GetLength(text) > 1) cell.cc |= char_buf[1] & CC_MASK;
if (PyUnicode_GetLength(text) > 2) cell.cc |= (char_buf[2] & CC_MASK) << 16;
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);
@@ -956,7 +1122,7 @@ static PyMethodDef module_methods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};
bool
bool
init_fonts(PyObject *module) {
if (Py_AtExit(finalize) != 0) {
PyErr_SetString(PyExc_RuntimeError, "Failed to register the fonts module at exit handler");

View File

@@ -14,12 +14,14 @@
// API that font backends need to implement
typedef uint16_t glyph_index;
unsigned int glyph_id_for_codepoint(PyObject *, char_type);
bool is_glyph_empty(PyObject *, glyph_index);
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*);
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
@@ -73,7 +73,7 @@ def resize_fonts(new_sz):
def add_line(buf, cell_width, position, thickness, cell_height):
y = position - thickness // 2
while thickness > 0 and y > -1 and y < cell_height - 1:
while thickness > 0 and y > -1 and y < cell_height:
thickness -= 1
ctypes.memset(ctypes.addressof(buf) + (cell_width * y), 255, cell_width)
y += 1
@@ -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

@@ -38,7 +38,7 @@ PyTypeObject Face_Type;
static PyObject* FreeType_Exception = NULL;
void
void
set_freetype_error(const char* prefix, int err_code) {
int i = 0;
#undef FTERRORS_H_
@@ -54,7 +54,7 @@ set_freetype_error(const char* prefix, int err_code) {
#ifdef FT_ERRORS_H
#include FT_ERRORS_H
#else
#else
FT_ERROR_START_LIST FT_ERROR_END_LIST
#endif
@@ -121,8 +121,8 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
return true;
}
}
set_freetype_error("Failed to set char size, with error:", error);
return false;
set_freetype_error("Failed to set char size, with error:", error);
return false;
}
return !error;
}
@@ -170,7 +170,7 @@ init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle) {
PyObject*
ft_face_from_data(const uint8_t* data, size_t sz, void *extra_data, free_extra_data_func fed, PyObject *path, int hinting, int hintstyle, float apple_leading) {
Face *ans = (Face*)Face_Type.tp_alloc(&Face_Type, 0);
if (ans == NULL) return NULL;
if (ans == NULL) return NULL;
int error = FT_New_Memory_Face(library, data, sz, 0, &ans->face);
if(error) { set_freetype_error("Failed to load memory face, with error:", error); Py_CLEAR(ans); return NULL; }
if (!init_ft_face(ans, path, hinting, hintstyle)) { Py_CLEAR(ans); return NULL; }
@@ -232,7 +232,7 @@ face_from_descriptor(PyObject *descriptor) {
}
return (PyObject*)self;
}
PyObject*
face_from_path(const char *path, int index) {
Face *ans = (Face*)Face_Type.tp_alloc(&Face_Type, 0);
@@ -265,7 +265,7 @@ repr(Face *self) {
}
static inline bool
static inline bool
load_glyph(Face *self, int glyph_index, int load_type) {
int flags = get_load_flags(self->hinting, self->hintstyle, load_type);
int error = FT_Load_Glyph(self->face, glyph_index, flags);
@@ -278,14 +278,18 @@ calc_cell_width(Face *self) {
unsigned int ans = 0;
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));
// We actually render the bitmap and get its width even though this is very slow, because
// there are fonts for which the horizontal advance is incorrect, see https://github.com/kovidgoyal/kitty/issues/352
if (load_glyph(self, glyph_index, FT_LOAD_RENDER)) {
unsigned int horizontal_advance = (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f);
ans = MAX(ans, self->face->glyph->bitmap.width);
ans = MAX(ans, horizontal_advance);
}
}
return ans;
}
void
void
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
Face *self = (Face*)s;
*cell_width = calc_cell_width(self);
@@ -295,11 +299,22 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
*underline_thickness = MAX(1, font_units_to_pixels(self, self->underline_thickness));
}
unsigned int
unsigned int
glyph_id_for_codepoint(PyObject *s, char_type cp) {
return FT_Get_Char_Index(((Face*)s)->face, cp);
}
bool
is_glyph_empty(PyObject *s, glyph_index g) {
Face *self = (Face*)s;
if (!load_glyph(self, g, FT_LOAD_DEFAULT)) { PyErr_Print(); return false; }
#define M self->face->glyph->metrics
/* printf("glyph: %u horiBearingX: %ld horiBearingY: %ld width: %ld height: %ld\n", g, M.horiBearingX, M.horiBearingY, M.width, M.height); */
return M.width == 0;
#undef M
}
hb_font_t*
harfbuzz_font_for_face(PyObject *self) { return ((Face*)self)->harfbuzz_font; }
@@ -326,6 +341,7 @@ trim_borders(ProcessedBitmap *ans, size_t extra) {
if (!column_has_text) { ans->width--; extra--; }
}
// Remove any remaining extra columns from the left edge of the bitmap
ans->start_x = extra;
ans->width -= extra;
}
@@ -345,7 +361,7 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
size_t extra = bitmap->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 (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)) {
@@ -376,12 +392,12 @@ downsample_bitmap(ProcessedBitmap *bm, unsigned int width, unsigned int cell_hei
for (unsigned int y=sr; y < MIN(sr + factor, bm->rows); y++) {
uint8_t *p = bm->buf + (y * bm->stride) + sc * 4;
for (unsigned int x=sc; x < MIN(sc + factor, bm->width); x++, count++) {
b += *(p++); g += *(p++); r += *(p++); a += *(p++);
b += *(p++); g += *(p++); r += *(p++); a += *(p++);
}
}
if (count) {
d[0] = b / count; d[1] = g / count; d[2] = r / count; d[3] = a / count;
}
}
}
}
@@ -450,7 +466,7 @@ place_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size
// We want the glyph to be positioned inside the cell based on the bearingX
// and bearingY values, making sure that it does not overflow the cell.
Region src = { .left = bm->start_x, .bottom = bm->rows, .right = bm->width }, dest = { .bottom = cell_height, .right = cell_width };
Region src = { .left = bm->start_x, .bottom = bm->rows, .right = bm->width + bm->start_x }, dest = { .bottom = cell_height, .right = cell_width };
// Calculate column bounds
float bearing_x = (float)metrics->horiBearingX / 64.f;
@@ -506,7 +522,7 @@ render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *inf
y = (float)positions[i].y_offset / 64.0f;
if ((*was_colored || self->face->glyph->metrics.width > 0) && bm.width > 0) place_bitmap_in_canvas(canvas, &bm, canvas_width, cell_height, x_offset, y, &self->face->glyph->metrics, baseline);
x += (float)positions[i].x_advance / 64.0f;
if (bm.needs_free) free(bm.buf);
if (bm.needs_free) free(bm.buf);
}
// center the glyphs in the canvas
@@ -520,7 +536,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);
@@ -528,8 +545,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 {{{
@@ -560,8 +577,8 @@ PyTypeObject Face_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.Face",
.tp_basicsize = sizeof(Face),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "FreeType Font face",
.tp_methods = methods,
.tp_members = members,
@@ -573,11 +590,11 @@ free_freetype() {
FT_Done_FreeType(library);
}
bool
bool
init_freetype_library(PyObject *m) {
if (PyType_Ready(&Face_Type) < 0) return 0;
if (PyModule_AddObject(m, "Face", (PyObject *)&Face_Type) != 0) return 0;
Py_INCREF(&Face_Type);
if (PyModule_AddObject(m, "Face", (PyObject *)&Face_Type) != 0) return 0;
Py_INCREF(&Face_Type);
FreeType_Exception = PyErr_NewException("fast_data_types.FreeTypeError", NULL, NULL);
if (FreeType_Exception == NULL) return false;
if (PyModule_AddObject(m, "FreeTypeError", FreeType_Exception) != 0) return false;

1622
kitty/gl-wrapper.c generated

File diff suppressed because it is too large Load Diff

View File

@@ -22,24 +22,24 @@ static void
check_for_gl_error(const char *name, void UNUSED *funcptr, int UNUSED len_args, ...) {
#define f(msg) fatal("OpenGL error: %s (calling function: %s)", msg, name); break;
GLenum code = glad_glGetError();
switch(code) {
switch(code) {
case GL_NO_ERROR: break;
case GL_INVALID_ENUM:
f("An enum value is invalid (GL_INVALID_ENUM)");
case GL_INVALID_VALUE:
f("An numeric value is invalid (GL_INVALID_VALUE)");
case GL_INVALID_OPERATION:
f("This operation is invalid (GL_INVALID_OPERATION)");
case GL_INVALID_FRAMEBUFFER_OPERATION:
f("The framebuffer object is not complete (GL_INVALID_FRAMEBUFFER_OPERATION)");
case GL_OUT_OF_MEMORY:
f("There is not enough memory left to execute the command. (GL_OUT_OF_MEMORY)");
case GL_STACK_UNDERFLOW:
f("An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_UNDERFLOW)");
case GL_STACK_OVERFLOW:
f("An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_OVERFLOW)");
default:
fatal("An unknown OpenGL error occurred with code: %d (calling function: %s)", code, name);
case GL_INVALID_ENUM:
f("An enum value is invalid (GL_INVALID_ENUM)");
case GL_INVALID_VALUE:
f("An numeric value is invalid (GL_INVALID_VALUE)");
case GL_INVALID_OPERATION:
f("This operation is invalid (GL_INVALID_OPERATION)");
case GL_INVALID_FRAMEBUFFER_OPERATION:
f("The framebuffer object is not complete (GL_INVALID_FRAMEBUFFER_OPERATION)");
case GL_OUT_OF_MEMORY:
f("There is not enough memory left to execute the command. (GL_OUT_OF_MEMORY)");
case GL_STACK_UNDERFLOW:
f("An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_UNDERFLOW)");
case GL_STACK_OVERFLOW:
f("An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_OVERFLOW)");
default:
fatal("An unknown OpenGL error occurred with code: %d (calling function: %s)", code, name);
break;
}
}
@@ -59,6 +59,9 @@ gl_init() {
ARB_TEST(texture_storage);
#undef ARB_TEST
glad_loaded = true;
if (GLVersion.major < OPENGL_REQUIRED_VERSION_MAJOR || (GLVersion.major == OPENGL_REQUIRED_VERSION_MAJOR && GLVersion.minor < OPENGL_REQUIRED_VERSION_MINOR)) {
fatal("OpenGL version is %d.%d, version >= 3.3 required for kitty", GLVersion.major, GLVersion.minor);
}
}
}
@@ -73,7 +76,7 @@ update_surface_size(int w, int h, GLuint offscreen_texture_id) {
void
free_texture(GLuint *tex_id) {
glDeleteTextures(1, tex_id);
glDeleteTextures(1, tex_id);
*tex_id = 0;
}
@@ -114,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;
@@ -288,7 +291,7 @@ add_buffer_to_vao(ssize_t vao_idx, GLenum usage) {
static void
add_located_attribute_to_vao(ssize_t vao_idx, GLint aloc, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
VAO *vao = vaos + vao_idx;
if (!vao->num_buffers) fatal("You must create a buffer for this attribute first");
if (!vao->num_buffers) fatal("You must create a buffer for this attribute first");
ssize_t buf = vao->buffers[vao->num_buffers - 1];
bind_buffer(buf);
glEnableVertexAttribArray(aloc);
@@ -315,7 +318,7 @@ add_located_attribute_to_vao(ssize_t vao_idx, GLint aloc, GLint size, GLenum dat
static inline void
add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
GLint aloc = attrib_location(p, name);
if (aloc == -1) fatal("No attribute named: %s found in this program", name);
if (aloc == -1) fatal("No attribute named: %s found in this program", name);
add_located_attribute_to_vao(vao_idx, aloc, size, data_type, stride, offset, divisor);
}

21
kitty/glfw-wrapper.c generated
View File

@@ -23,9 +23,6 @@ load_glfw(const char* path) {
*(void **) (&glfwInitHint_impl) = dlsym(handle, "glfwInitHint");
if (glfwInitHint_impl == NULL) fail("Failed to load glfw function glfwInitHint with error: %s", dlerror());
*(void **) (&glfwInitHintString_impl) = dlsym(handle, "glfwInitHintString");
if (glfwInitHintString_impl == NULL) fail("Failed to load glfw function glfwInitHintString with error: %s", dlerror());
*(void **) (&glfwGetVersion_impl) = dlsym(handle, "glfwGetVersion");
if (glfwGetVersion_impl == NULL) fail("Failed to load glfw function glfwGetVersion with error: %s", dlerror());
@@ -56,6 +53,12 @@ load_glfw(const char* path) {
*(void **) (&glfwGetMonitorName_impl) = dlsym(handle, "glfwGetMonitorName");
if (glfwGetMonitorName_impl == NULL) fail("Failed to load glfw function glfwGetMonitorName with error: %s", dlerror());
*(void **) (&glfwSetMonitorUserPointer_impl) = dlsym(handle, "glfwSetMonitorUserPointer");
if (glfwSetMonitorUserPointer_impl == NULL) fail("Failed to load glfw function glfwSetMonitorUserPointer with error: %s", dlerror());
*(void **) (&glfwGetMonitorUserPointer_impl) = dlsym(handle, "glfwGetMonitorUserPointer");
if (glfwGetMonitorUserPointer_impl == NULL) fail("Failed to load glfw function glfwGetMonitorUserPointer with error: %s", dlerror());
*(void **) (&glfwSetMonitorCallback_impl) = dlsym(handle, "glfwSetMonitorCallback");
if (glfwSetMonitorCallback_impl == NULL) fail("Failed to load glfw function glfwSetMonitorCallback with error: %s", dlerror());
@@ -80,6 +83,9 @@ load_glfw(const char* path) {
*(void **) (&glfwWindowHint_impl) = dlsym(handle, "glfwWindowHint");
if (glfwWindowHint_impl == NULL) fail("Failed to load glfw function glfwWindowHint with error: %s", dlerror());
*(void **) (&glfwWindowHintString_impl) = dlsym(handle, "glfwWindowHintString");
if (glfwWindowHintString_impl == NULL) fail("Failed to load glfw function glfwWindowHintString with error: %s", dlerror());
*(void **) (&glfwCreateWindow_impl) = dlsym(handle, "glfwCreateWindow");
if (glfwCreateWindow_impl == NULL) fail("Failed to load glfw function glfwCreateWindow with error: %s", dlerror());
@@ -197,6 +203,9 @@ load_glfw(const char* path) {
*(void **) (&glfwSetFramebufferSizeCallback_impl) = dlsym(handle, "glfwSetFramebufferSizeCallback");
if (glfwSetFramebufferSizeCallback_impl == NULL) fail("Failed to load glfw function glfwSetFramebufferSizeCallback with error: %s", dlerror());
*(void **) (&glfwSetWindowContentScaleCallback_impl) = dlsym(handle, "glfwSetWindowContentScaleCallback");
if (glfwSetWindowContentScaleCallback_impl == NULL) fail("Failed to load glfw function glfwSetWindowContentScaleCallback with error: %s", dlerror());
*(void **) (&glfwPollEvents_impl) = dlsym(handle, "glfwPollEvents");
if (glfwPollEvents_impl == NULL) fail("Failed to load glfw function glfwPollEvents with error: %s", dlerror());
@@ -287,6 +296,12 @@ load_glfw(const char* path) {
*(void **) (&glfwGetJoystickGUID_impl) = dlsym(handle, "glfwGetJoystickGUID");
if (glfwGetJoystickGUID_impl == NULL) fail("Failed to load glfw function glfwGetJoystickGUID with error: %s", dlerror());
*(void **) (&glfwSetJoystickUserPointer_impl) = dlsym(handle, "glfwSetJoystickUserPointer");
if (glfwSetJoystickUserPointer_impl == NULL) fail("Failed to load glfw function glfwSetJoystickUserPointer with error: %s", dlerror());
*(void **) (&glfwGetJoystickUserPointer_impl) = dlsym(handle, "glfwGetJoystickUserPointer");
if (glfwGetJoystickUserPointer_impl == NULL) fail("Failed to load glfw function glfwGetJoystickUserPointer with error: %s", dlerror());
*(void **) (&glfwJoystickIsGamepad_impl) = dlsym(handle, "glfwJoystickIsGamepad");
if (glfwJoystickIsGamepad_impl == NULL) fail("Failed to load glfw function glfwJoystickIsGamepad with error: %s", dlerror());

90
kitty/glfw-wrapper.h generated
View File

@@ -251,17 +251,37 @@
* @{ */
/*! @brief If this bit is set one or more Shift keys were held down.
*
* If this bit is set one or more Shift keys were held down.
*/
#define GLFW_MOD_SHIFT 0x0001
/*! @brief If this bit is set one or more Control keys were held down.
*
* If this bit is set one or more Control keys were held down.
*/
#define GLFW_MOD_CONTROL 0x0002
/*! @brief If this bit is set one or more Alt keys were held down.
*
* If this bit is set one or more Alt keys were held down.
*/
#define GLFW_MOD_ALT 0x0004
/*! @brief If this bit is set one or more Super keys were held down.
*
* If this bit is set one or more Super keys were held down.
*/
#define GLFW_MOD_SUPER 0x0008
/*! @brief If this bit is set the Caps Lock key is enabled.
*
* If this bit is set the Caps Lock key is enabled and the @ref
* GLFW_LOCK_KEY_MODS input mode is set.
*/
#define GLFW_MOD_CAPS_LOCK 0x0010
/*! @brief If this bit is set the Num Lock key is enabled.
*
* If this bit is set the Num Lock key is enabled and the @ref
* GLFW_LOCK_KEY_MODS input mode is set.
*/
#define GLFW_MOD_NUM_LOCK 0x0020
/*! @} */
@@ -552,6 +572,11 @@
* [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib).
*/
#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A
/*! @brief Mouse cursor hover window attribute.
*
* Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).
*/
#define GLFW_HOVERED 0x0002000B
/*! @brief Framebuffer bit depth hint.
*
@@ -702,8 +727,11 @@
#define GLFW_CONTEXT_CREATION_API 0x0002200B
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
#define GLFW_COCOA_FRAME_AUTOSAVE 0x00023002
#define GLFW_COCOA_FRAME_NAME 0x00023002
#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003
#define GLFW_X11_CLASS_NAME 0x00024001
#define GLFW_X11_INSTANCE_NAME 0x00024002
/*! @} */
#define GLFW_NO_API 0
@@ -721,6 +749,7 @@
#define GLFW_CURSOR 0x00033001
#define GLFW_STICKY_KEYS 0x00033002
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
#define GLFW_LOCK_KEY_MODS 0x00033004
#define GLFW_CURSOR_NORMAL 0x00034001
#define GLFW_CURSOR_HIDDEN 0x00034002
@@ -783,9 +812,6 @@
#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001
#define GLFW_COCOA_MENUBAR 0x00051002
#define GLFW_X11_WM_CLASS_NAME 0x00052001
#define GLFW_X11_WM_CLASS_CLASS 0x00052002
/*! @} */
#define GLFW_DONT_CARE -1
@@ -804,7 +830,7 @@
* @sa @ref glfwGetProcAddress
*
* @since Added in version 3.0.
*
* @ingroup context
*/
typedef void (*GLFWglproc)(void);
@@ -1015,6 +1041,24 @@ typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int);
*/
typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);
/*! @brief The function signature for window content scale callbacks.
*
* This is the function signature for window content scale callback
* functions.
*
* @param[in] window The window whose content scale changed.
* @param[in] xscale The new x-axis content scale of the window.
* @param[in] yscale The new y-axis content scale of the window.
*
* @sa @ref window_scale
* @sa @ref glfwSetWindowContentScaleCallback
*
* @since Added in version 3.3.
*
* @ingroup window
*/
typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float);
/*! @brief The function signature for mouse button callbacks.
*
* This is the function signature for mouse button callback functions.
@@ -1172,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
@@ -1189,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
@@ -1359,10 +1405,6 @@ typedef void (*glfwInitHint_func)(int, int);
glfwInitHint_func glfwInitHint_impl;
#define glfwInitHint glfwInitHint_impl
typedef void (*glfwInitHintString_func)(int, const char*);
glfwInitHintString_func glfwInitHintString_impl;
#define glfwInitHintString glfwInitHintString_impl
typedef void (*glfwGetVersion_func)(int*, int*, int*);
glfwGetVersion_func glfwGetVersion_impl;
#define glfwGetVersion glfwGetVersion_impl
@@ -1403,6 +1445,14 @@ typedef const char* (*glfwGetMonitorName_func)(GLFWmonitor*);
glfwGetMonitorName_func glfwGetMonitorName_impl;
#define glfwGetMonitorName glfwGetMonitorName_impl
typedef void (*glfwSetMonitorUserPointer_func)(GLFWmonitor*, void*);
glfwSetMonitorUserPointer_func glfwSetMonitorUserPointer_impl;
#define glfwSetMonitorUserPointer glfwSetMonitorUserPointer_impl
typedef void* (*glfwGetMonitorUserPointer_func)(GLFWmonitor*);
glfwGetMonitorUserPointer_func glfwGetMonitorUserPointer_impl;
#define glfwGetMonitorUserPointer glfwGetMonitorUserPointer_impl
typedef GLFWmonitorfun (*glfwSetMonitorCallback_func)(GLFWmonitorfun);
glfwSetMonitorCallback_func glfwSetMonitorCallback_impl;
#define glfwSetMonitorCallback glfwSetMonitorCallback_impl
@@ -1435,6 +1485,10 @@ typedef void (*glfwWindowHint_func)(int, int);
glfwWindowHint_func glfwWindowHint_impl;
#define glfwWindowHint glfwWindowHint_impl
typedef void (*glfwWindowHintString_func)(int, const char*);
glfwWindowHintString_func glfwWindowHintString_impl;
#define glfwWindowHintString glfwWindowHintString_impl
typedef GLFWwindow* (*glfwCreateWindow_func)(int, int, const char*, GLFWmonitor*, GLFWwindow*);
glfwCreateWindow_func glfwCreateWindow_impl;
#define glfwCreateWindow glfwCreateWindow_impl
@@ -1531,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
@@ -1591,6 +1645,10 @@ typedef GLFWframebuffersizefun (*glfwSetFramebufferSizeCallback_func)(GLFWwindow
glfwSetFramebufferSizeCallback_func glfwSetFramebufferSizeCallback_impl;
#define glfwSetFramebufferSizeCallback glfwSetFramebufferSizeCallback_impl
typedef GLFWwindowcontentscalefun (*glfwSetWindowContentScaleCallback_func)(GLFWwindow*, GLFWwindowcontentscalefun);
glfwSetWindowContentScaleCallback_func glfwSetWindowContentScaleCallback_impl;
#define glfwSetWindowContentScaleCallback glfwSetWindowContentScaleCallback_impl
typedef void (*glfwPollEvents_func)();
glfwPollEvents_func glfwPollEvents_impl;
#define glfwPollEvents glfwPollEvents_impl
@@ -1711,6 +1769,14 @@ typedef const char* (*glfwGetJoystickGUID_func)(int);
glfwGetJoystickGUID_func glfwGetJoystickGUID_impl;
#define glfwGetJoystickGUID glfwGetJoystickGUID_impl
typedef void (*glfwSetJoystickUserPointer_func)(int, void*);
glfwSetJoystickUserPointer_func glfwSetJoystickUserPointer_impl;
#define glfwSetJoystickUserPointer glfwSetJoystickUserPointer_impl
typedef void* (*glfwGetJoystickUserPointer_func)(int);
glfwGetJoystickUserPointer_func glfwGetJoystickUserPointer_impl;
#define glfwGetJoystickUserPointer glfwGetJoystickUserPointer_impl
typedef int (*glfwJoystickIsGamepad_func)(int);
glfwJoystickIsGamepad_func glfwJoystickIsGamepad_impl;
#define glfwJoystickIsGamepad glfwJoystickIsGamepad_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"
@@ -21,20 +22,25 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) {
int w, h;
glfwGetFramebufferSize(window->handle, &window->viewport_width, &window->viewport_height);
glfwGetWindowSize(window->handle, &w, &h);
double xr = window->viewport_x_ratio, yr = window->viewport_y_ratio;
window->viewport_x_ratio = (double)window->viewport_width / (double)w;
window->viewport_y_ratio = (double)window->viewport_height / (double)h;
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, "Kii", window->id, window->viewport_width, window->viewport_height);
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();
}
// callbacks {{{
void
void
update_os_window_references() {
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
@@ -68,25 +74,28 @@ is_window_ready_for_callbacks() {
static inline void
show_mouse_cursor(GLFWwindow *w) {
if (glfwGetInputMode(w, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
if (glfwGetInputMode(w, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
}
static void
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
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;
}
static void
char_mods_callback(GLFWwindow *w, unsigned int codepoint, int mods) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
@@ -94,7 +103,7 @@ char_mods_callback(GLFWwindow *w, unsigned int codepoint, int mods) {
global_state.callback_os_window = NULL;
}
static void
static void
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
@@ -105,7 +114,7 @@ key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) {
global_state.callback_os_window = NULL;
}
static void
static void
mouse_button_callback(GLFWwindow *w, int button, int action, int mods) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
@@ -118,7 +127,7 @@ mouse_button_callback(GLFWwindow *w, int button, int action, int mods) {
global_state.callback_os_window = NULL;
}
static void
static void
cursor_pos_callback(GLFWwindow *w, double x, double y) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
@@ -131,7 +140,7 @@ cursor_pos_callback(GLFWwindow *w, double x, double y) {
global_state.callback_os_window = NULL;
}
static void
static void
scroll_callback(GLFWwindow *w, double xoffset, double yoffset) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
@@ -141,13 +150,40 @@ scroll_callback(GLFWwindow *w, double xoffset, double yoffset) {
global_state.callback_os_window = NULL;
}
static void
static struct {
id_type entries[16];
int next_entry;
} focus_history;
#ifdef __APPLE__
static inline id_type
pop_focus_history() {
int index = --focus_history.next_entry;
if (index < 0) {
focus_history.next_entry = index = 0;
}
id_type result = focus_history.entries[index];
focus_history.entries[index] = 0;
return result;
}
#endif
static inline void
push_focus_history(OSWindow *w) {
focus_history.entries[focus_history.next_entry++] = w->id;
focus_history.next_entry %= (sizeof(focus_history.entries) / sizeof(*(focus_history.entries)));
}
static void
window_focus_callback(GLFWwindow *w, int focused) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->is_focused = focused ? true : false;
if (focused) {
show_mouse_cursor(w);
show_mouse_cursor(w);
focus_in_event();
push_focus_history(global_state.callback_os_window);
}
double now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
@@ -158,7 +194,7 @@ window_focus_callback(GLFWwindow *w, int focused) {
global_state.callback_os_window = NULL;
}
static void
static void
drop_callback(GLFWwindow *w, int count, const char **paths) {
if (!set_callback_window(w)) return;
PyObject *p = PyTuple_New(count);
@@ -201,7 +237,7 @@ set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
static GLFWwindow *current_os_window_ctx = NULL;
void
void
make_os_window_context_current(OSWindow *w) {
if (w->handle != current_os_window_ctx) {
current_os_window_ctx = w->handle;
@@ -246,8 +282,8 @@ current_monitor(GLFWwindow *window) {
}
static inline void
set_dpi_from_window(OSWindow *w) {
void
set_dpi_from_os_window(OSWindow *w) {
GLFWmonitor *monitor = NULL;
if (w) { monitor = current_monitor(w->handle); }
if (monitor == NULL) monitor = glfwGetPrimaryMonitor();
@@ -262,16 +298,17 @@ set_dpi_from_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, visible = 1, swap_interval = 0, x = -1, y = -1;
char *title;
int width, height, x = -1, y = -1;
char *title, *wm_class_class, *wm_class_name;
PyObject *load_programs = NULL;
if (!PyArg_ParseTuple(args, "iis|pOiii", &width, &height, &title, &visible, &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);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_REQUIRED_VERSION_MAJOR);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_REQUIRED_VERSION_MINOR);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
@@ -288,29 +325,33 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
click_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
arrow_cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
if (standard_cursor == NULL || click_cursor == NULL || arrow_cursor == NULL) {
PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL;
PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL;
}
}
#ifndef __APPLE__
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, wm_class_name);
glfwWindowHintString(GLFW_X11_CLASS_NAME, wm_class_class);
#endif
if (global_state.num_os_windows >= MAX_CHILDREN) {
PyErr_SetString(PyExc_ValueError, "Too many windows");
return NULL;
}
glfwWindowHint(GLFW_VISIBLE, visible ? GLFW_TRUE : GLFW_FALSE);
bool want_semi_transparent = (1.0 - OPT(background_opacity) > 0.1) ? true : false;
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
if (glfw_window == NULL) {
fprintf(stderr, "Failed to create a window at size: %dx%d, using a standard size instead.\n", width, height);
log_error("Failed to create a window at size: %dx%d, using a standard size instead.", width, height);
glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
}
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
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
if (is_first_window) {
set_dpi_from_window(NULL);
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();
PyObject *ret = PyObject_CallFunction(load_programs, "i", glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER));
if (ret == NULL) return NULL;
@@ -318,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();
@@ -332,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);
@@ -342,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;
@@ -353,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;
}
}
@@ -364,30 +408,61 @@ static PyObject*
show_window(PyObject UNUSED *self, PyObject *args) {
id_type os_window_id;
int yes = 1;
bool dpi_changed = false;
if (!PyArg_ParseTuple(args, "K|p", &os_window_id, &yes)) return NULL;
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
if (w->id == os_window_id) {
if (yes) glfwShowWindow(w->handle); else glfwHideWindow(w->handle);
if (yes) {
bool first_show = !w->shown_once;
glfwShowWindow(w->handle);
w->shown_once = true;
push_focus_history(w);
if (first_show) {
double before_x = global_state.logical_dpi_x, before_y = global_state.logical_dpi_y;
set_dpi_from_os_window(w);
dpi_changed = before_x != global_state.logical_dpi_x || before_y != global_state.logical_dpi_y;
w->has_pending_resizes = true;
global_state.has_pending_resizes = true;
}
} else glfwHideWindow(w->handle);
break;
}
}
Py_RETURN_NONE;
if (dpi_changed) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
void
destroy_os_window(OSWindow *w) {
if (w->handle) {
glfwDestroyWindow(w->handle);
glfwDestroyWindow(w->handle);
if (current_os_window_ctx == w->handle) current_os_window_ctx = NULL;
}
w->handle = NULL;
#ifdef __APPLE__
// On macOS when closing a window, any other existing windows belonging to the same application do not
// automatically get focus, so we do it manually.
bool change_focus = true;
while (change_focus) {
id_type new_focus_id = pop_focus_history();
if (new_focus_id == 0) break;
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *c = global_state.os_windows + i;
if (c->id != w->id && c->handle && c->shown_once && (c->id == new_focus_id)) {
glfwFocusWindow(c->handle);
change_focus = false;
break;
}
}
}
#endif
}
// Global functions {{{
static void
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);
}
@@ -408,7 +483,7 @@ glfw_init(PyObject UNUSED *self, PyObject *args) {
}
PyObject*
glfw_terminate(PyObject UNUSED *self) {
glfw_terminate(PYNOARG) {
glfwTerminate();
Py_RETURN_NONE;
}
@@ -426,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;
}
@@ -450,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);
@@ -475,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);
@@ -499,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;
@@ -522,14 +597,14 @@ toggle_fullscreen(PyObject UNUSED *self) {
}
}
void
void
ring_audio_bell(OSWindow *w) {
static double last_bell_at = -1;
double now = monotonic();
if (now - last_bell_at <= 0.1) return;
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);
}
}
@@ -555,7 +630,7 @@ hide_mouse(OSWindow *w) {
}
}
void
void
swap_window_buffers(OSWindow *w) {
glfwSwapBuffers(w->handle);
}
@@ -571,7 +646,7 @@ wakeup_main_loop() {
glfwPostEmptyEvent();
}
void
void
mark_os_window_for_close(OSWindow* w, bool yes) {
glfwSetWindowShouldClose(w->handle, yes);
}
@@ -589,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);
@@ -604,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;
}
@@ -626,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;
}
@@ -638,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;
}
@@ -694,14 +769,14 @@ static PyMethodDef module_methods[] = {
METHODB(x11_window_id, METH_O),
METHODB(set_primary_selection, METH_VARARGS),
METHODB(glfw_poll_events, METH_NOARGS),
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
{"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""},
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""},
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
{"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""},
{"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, ""},
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
{"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""},
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""},
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
{"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""},
{"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};
@@ -710,8 +785,6 @@ bool
init_glfw(PyObject *m) {
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
ADDC(GLFW_X11_WM_CLASS_NAME)
ADDC(GLFW_X11_WM_CLASS_CLASS)
ADDC(GLFW_RELEASE);
ADDC(GLFW_PRESS);
ADDC(GLFW_REPEAT);

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;
@@ -81,7 +81,7 @@ static inline void
remove_from_array(void *array, size_t item_size, size_t idx, size_t array_count) {
size_t num_to_right = array_count - 1 - idx;
uint8_t *p = (uint8_t*)array;
if (num_to_right > 0) memmove(p + (idx * item_size), p + ((idx + 1) * item_size), num_to_right * item_size);
if (num_to_right > 0) memmove(p + (idx * item_size), p + ((idx + 1) * item_size), num_to_right * item_size);
memset(p + (item_size * (array_count - 1)), 0, item_size);
}
@@ -130,7 +130,7 @@ trim_predicate(Image *img) {
static int
oldest_last(const void* a, const void *b) {
double ans = ((Image*)(b))->atime - ((Image*)(a))->atime;
return ans < 0 ? -1 : (ans == 0 ? 0 : 1);
return ans < 0 ? -1 : (ans == 0 ? 0 : 1);
}
static inline void
@@ -171,7 +171,7 @@ mmap_img_file(GraphicsManager UNUSED *self, Image *img, int fd, size_t sz, off_t
sz = s.st_size;
}
void *addr = mmap(0, sz, PROT_READ, MAP_SHARED, fd, offset);
if (addr == MAP_FAILED) ABRT(EBADF, "Failed to map image file fd: %d at offset: %zd with size: %zu with error: [%d] %s", fd, offset, sz, errno, strerror(errno));
if (addr == MAP_FAILED) ABRT(EBADF, "Failed to map image file fd: %d at offset: %zd with size: %zu with error: [%d] %s", fd, offset, sz, errno, strerror(errno));
img->load_data.mapped_file = addr;
img->load_data.mapped_file_sz = sz;
return true;
@@ -267,7 +267,7 @@ inflate_png_inner(struct png_jmp_data *d, uint8_t *buf, size_t bufsz) {
if (!info) ABRT(ENOMEM, "Failed to create PNG info structure");
if (setjmp(jb)) goto err;
png_set_read_fn(png, &f, read_png_from_buffer);
png_read_info(png, info);
png_byte color_type, bit_depth;
@@ -312,7 +312,7 @@ inflate_png(GraphicsManager UNUSED *self, Image *img, uint8_t *buf, size_t bufsz
if (d.ok) {
free_load_data(&img->load_data);
img->load_data.buf = d.decompressed;
img->load_data.buf_capacity = d.sz;
img->load_data.buf_capacity = d.sz;
img->load_data.buf_used = d.sz;
img->load_data.data_sz = d.sz;
img->width = d.width; img->height = d.height;
@@ -376,7 +376,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
img->atime = monotonic(); img->used_storage = 0;
img->width = g->data_width; img->height = g->data_height;
switch(fmt) {
case PNG:
case PNG:
if (g->data_sz > MAX_DATA_SZ) ABRT(EINVAL, "PNG data size too large");
img->load_data.is_4byte_aligned = true;
img->load_data.is_opaque = false;
@@ -509,11 +509,11 @@ 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;
snprintf(add_response, 10, "OK");
snprintf(add_response, 10, "OK");
}
snprintf(rbuf, sizeof(rbuf)/sizeof(rbuf[0]) - 1, "Gi=%u;%s", iid, add_response);
return rbuf;
@@ -541,18 +541,18 @@ update_dest_rect(ImageRef *ref, uint32_t num_cols, uint32_t num_rows) {
t = ref->src_width + ref->cell_x_offset;
num_cols = t / global_state.cell_width;
if (t > num_cols * global_state.cell_width) num_cols += 1;
}
}
if (num_rows == 0) {
t = ref->src_height + ref->cell_y_offset;
num_rows = t / global_state.cell_height;
if (t > num_rows * global_state.cell_height) num_rows += 1;
}
}
ref->effective_num_rows = num_rows;
ref->effective_num_cols = num_cols;
}
static void
static void
handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, Image *img) {
has_add_respose = false;
if (img == NULL) img = img_by_client_id(self, g->id);
@@ -584,12 +584,12 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1;
}
static int
cmp_by_zindex_and_image(const void *a_, const void *b_) {
static int
cmp_by_zindex_and_image(const void *a_, const void *b_) {
const ImageRenderData *a = (const ImageRenderData*)a_, *b = (const ImageRenderData*)b_;
int ans = a->z_index - b->z_index;
if (ans == 0) ans = a->image_id - b->image_id;
return ans;
return ans;
}
bool
@@ -656,9 +656,9 @@ filter_refs(GraphicsManager *self, const void* data, bool free_images, bool (*fi
size_t i, j;
if (self->image_count) {
for (i = self->image_count; i-- > 0;) {
for (i = self->image_count; i-- > 0;) {
img = self->images + i;
for (j = img->refcnt; j-- > 0;) {
for (j = img->refcnt; j-- > 0;) {
ref = img->refs + j;
if (filter_func(ref, img, data)) {
remove_from_array(img->refs, sizeof(ImageRef), j, img->refcnt--);
@@ -718,7 +718,7 @@ scroll_filter_margins_func(ImageRef* ref, Image* img, const void* data) {
return false;
}
void
void
grman_scroll_images(GraphicsManager *self, const ScrollData *data) {
filter_refs(self, data, true, data->has_margins ? scroll_filter_margins_func : scroll_filter_func);
}
@@ -728,9 +728,14 @@ clear_filter_func(ImageRef *ref, Image UNUSED *img, const void UNUSED *data) {
return ref->start_row + (int32_t)ref->effective_num_rows > 0;
}
static inline bool
clear_all_filter_func(ImageRef *ref UNUSED, Image UNUSED *img, const void UNUSED *data) {
return true;
}
void
grman_clear(GraphicsManager *self) {
filter_refs(self, NULL, true, clear_filter_func);
grman_clear(GraphicsManager *self, bool all) {
filter_refs(self, NULL, true, all ? clear_all_filter_func : clear_filter_func);
}
static inline bool
@@ -799,18 +804,18 @@ handle_delete_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c
// }}}
void
void
grman_resize(GraphicsManager *self, index_type UNUSED old_lines, index_type UNUSED lines, index_type UNUSED old_columns, index_type UNUSED columns) {
self->layers_dirty = true;
}
void
void
grman_rescale(GraphicsManager *self, unsigned int UNUSED old_cell_width, unsigned int UNUSED old_cell_height) {
ImageRef *ref; Image *img;
self->layers_dirty = true;
for (size_t i = self->image_count; i-- > 0;) {
for (size_t i = self->image_count; i-- > 0;) {
img = self->images + i;
for (size_t j = img->refcnt; j-- > 0;) {
for (size_t j = img->refcnt; j-- > 0;) {
ref = img->refs + j;
ref->cell_x_offset = MIN(ref->cell_x_offset, global_state.cell_width - 1);
ref->cell_y_offset = MIN(ref->cell_y_offset, global_state.cell_height - 1);
@@ -925,7 +930,7 @@ W(update_layers) {
for (size_t i = 0; i < self->count; i++) {
ImageRenderData *r = self->render_data + i;
#define R(offset) Py_BuildValue("{sf sf sf sf}", "left", r->vertices[offset + 8], "top", r->vertices[offset + 1], "right", r->vertices[offset], "bottom", r->vertices[offset + 5])
PyTuple_SET_ITEM(ans, i,
PyTuple_SET_ITEM(ans, i,
Py_BuildValue("{sN sN sI si sI}", "src_rect", R(0), "dest_rect", R(2), "group_count", r->group_count, "z_index", r->z_index, "image_id", r->image_id)
);
#undef R
@@ -950,10 +955,10 @@ PyTypeObject GraphicsManager_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.GraphicsManager",
.tp_basicsize = sizeof(GraphicsManager),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "GraphicsManager",
.tp_new = new,
.tp_new = new,
.tp_methods = methods,
.tp_members = members,
};
@@ -969,7 +974,7 @@ static PyMethodDef module_methods[] = {
bool
init_graphics(PyObject *module) {
if (PyType_Ready(&GraphicsManager_Type) < 0) return false;
if (PyModule_AddObject(module, "GraphicsManager", (PyObject *)&GraphicsManager_Type) != 0) return false;
if (PyModule_AddObject(module, "GraphicsManager", (PyObject *)&GraphicsManager_Type) != 0) return false;
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
Py_INCREF(&GraphicsManager_Type);
return true;

View File

@@ -83,7 +83,7 @@ typedef struct {
} ScrollData;
GraphicsManager* grman_alloc();
void grman_clear(GraphicsManager*);
void grman_clear(GraphicsManager*, bool);
const char* grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty);
bool grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float screen_left, float screen_top, float dx, float dy, unsigned int num_cols, unsigned int num_rows);
void grman_scroll_images(GraphicsManager *self, const ScrollData*);

View File

@@ -2,12 +2,14 @@
#define ALPHA_TYPE
uniform sampler2D image;
uniform float inactive_text_alpha;
in vec2 texcoord;
out vec4 color;
void main() {
color = texture(image, texcoord);
color.a *= inactive_text_alpha;
#ifdef PREMULT
color = vec4(color.rgb * color.a, color.a);
#endif

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