vte-0.15.0/.builds/linux.yml000064400000000000000000000016201046102023000137260ustar 00000000000000image: archlinux sources: - https://github.com/alacritty/vte tasks: - rustup: | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable --profile minimal -c clippy - stable: | cd vte $HOME/.cargo/bin/cargo +stable test $HOME/.cargo/bin/cargo +stable test --features=ansi $HOME/.cargo/bin/cargo +stable test --features=ansi --no-default-features - clippy: | cd vte $HOME/.cargo/bin/cargo +stable clippy $HOME/.cargo/bin/cargo +stable clippy --features=ansi - rustfmt: | $HOME/.cargo/bin/rustup toolchain install nightly -c rustfmt cd vte $HOME/.cargo/bin/cargo +nightly fmt -- --check - msrv: | cd vte msrv=$(cat Cargo.toml | grep "rust-version" | sed 's/.*"\(.*\)".*/\1/') $HOME/.cargo/bin/rustup toolchain install --profile minimal $msrv rm Cargo.lock $HOME/.cargo/bin/cargo +$msrv test vte-0.15.0/.cargo_vcs_info.json0000644000000001360000000000100117150ustar { "git": { "sha1": "3b3da71c34cc1256c7e20981cf03f8eb95e08ffc" }, "path_in_vcs": "" }vte-0.15.0/.gitignore000064400000000000000000000000221046102023000124670ustar 00000000000000target Cargo.lock vte-0.15.0/CHANGELOG.md000064400000000000000000000050651046102023000123240ustar 00000000000000CHANGELOG ========= ## 0.15.0 - Support `CSI ? 5 W` to reset tabs stops to every 8th column - Replaced `no_std` with a new `std` feature - Changed default features to include `std` ## 0.14.1 - Crash when partial advance buffer stopped inside some grapheme boundaries ## 0.14.0 - `Parser::advance` now takes byte slices, instead of individual bytes - `Parser::advance_until_terminated` allows premature termination, by checking for `Perform::terminated` after each dispatch ## 0.13.1 - Add SCP control support - Improve SGR performance ## 0.13.0 - Reexport `cursor_icon` crate in `ansi` - Split-out private modes from `Mode` into `PrivateMode` - Add `unset_private_mode` and `set_private_mode` - Add `report_mode` and `report_private_mode` to handle DECRPM/DECRQM ## 0.12.0 - Add support for OSC 22 - Add support for kitty keyboard protocol - Add support for XTerm's modifyOtherKeys protocol ## 0.11.1 - Minimum rust version has been bumped to 1.62.1 - Support for ANSI terminal stream parsing under the `ansi` feature. - Addition of the `serde` feature which derives `Serialize` and `Deserialize` for the types provided in the `ansi` module. ## 0.11.0 - Minimum rust version has been bumped to 1.56.0 - Fixed infinite loop in `Params` iterator when 32nd parameter is a subparameter ## 0.10.1 - Fixed invalid intermediates when transitioning from DCS to ESC ## 0.10.0 - Changed the type of CSI parameters from i64 to u16 - All methods of the `Perform` trait are now optional ## 0.9.0 - Added CSI subparameter support; required changes can be seen in Alacritty: https://github.com/alacritty/alacritty/commit/576252294d09c1f52ec73bde03652349bdf5a529#diff-49ac9e6f6e6a855312bfcd393201f18ca53e6148c4a22a3a4949f1f9d1d137a8 ## 0.8.0 - Remove C1 ST support in OSCs, fixing OSCs with ST in the payload ## 0.7.1 - Out of bounds when parsing a DCS with more than 16 parameters ## 0.7.0 - Fix params reset between escapes - Removed unused parameter from `esc_dispatch` ## 0.6.0 - Fix build failure on Rust 1.36.0 - Add `bool_terminated` parameter to osc dispatch ## 0.5.0 - Support for dynamically sized escape buffers without feature `no_std` - Improved UTF8 parser performance - Migrate to Rust 2018 ## 0.4.0 - Fix handling of DCS escapes ## 0.3.3 - Fix off-by-one error in CSI parsing when params list was at max length (previously caused a panic). - Support no_std ## 0.2.0 - Removes `osc_start`, `osc_put`, and `osc_end` - Adds `osc_dispatch` which simply receives a list of parameters - Removes `byte: u8` parameter from `hook` and `unhook` because it's always zero. vte-0.15.0/Cargo.lock0000644000000050240000000000100076710ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "cursor-icon" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740bb192a8e2d1350119916954f4409ee7f62f149b536911eeb78ba5a20526bf" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "proc-macro2" version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e02e55d62894af2a08aca894c6577281f76769ba47c94d5756bec8ac6e7373" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "vte" version = "0.15.0" dependencies = [ "arrayvec", "bitflags", "cursor-icon", "log", "memchr", "serde", ] vte-0.15.0/Cargo.toml0000644000000033430000000000100077160ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.62.1" name = "vte" version = "0.15.0" authors = [ "Joe Wilm ", "Christian Duerr ", ] build = false exclude = ["/.travis.yml"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Parser for implementing terminal emulators" documentation = "https://docs.rs/vte/" readme = "README.md" keywords = [ "ansi", "vte", "parser", "terminal", ] categories = [ "parsing", "no-std", ] license = "Apache-2.0 OR MIT" repository = "https://github.com/alacritty/vte" [features] ansi = [ "log", "cursor-icon", "bitflags", ] default = ["std"] serde = ["dep:serde"] std = ["memchr/std"] [lib] name = "vte" path = "src/lib.rs" [[example]] name = "parselog" path = "examples/parselog.rs" [dependencies.arrayvec] version = "0.7.2" default-features = false [dependencies.bitflags] version = "2.3.3" optional = true default-features = false [dependencies.cursor-icon] version = "1.0.0" optional = true default-features = false [dependencies.log] version = "0.4.17" optional = true [dependencies.memchr] version = "2.7.4" default-features = false [dependencies.serde] version = "1.0.160" features = ["derive"] optional = true vte-0.15.0/Cargo.toml.orig000064400000000000000000000017461046102023000134040ustar 00000000000000[package] authors = ["Joe Wilm ", "Christian Duerr "] description = "Parser for implementing terminal emulators" repository = "https://github.com/alacritty/vte" documentation = "https://docs.rs/vte/" keywords = ["ansi", "vte", "parser", "terminal"] categories = ["parsing", "no-std"] exclude = ["/.travis.yml"] readme = "README.md" license = "Apache-2.0 OR MIT" version = "0.15.0" name = "vte" edition = "2021" rust-version = "1.62.1" [features] ansi = ["log", "cursor-icon", "bitflags"] default = ["std"] std = ["memchr/std"] serde = ["dep:serde"] [dependencies] arrayvec = { version = "0.7.2", default-features = false } bitflags = { version = "2.3.3", default-features = false, optional = true } cursor-icon = { version = "1.0.0", default-features = false, optional = true } log = { version = "0.4.17", optional = true } memchr = { version = "2.7.4", default-features = false } serde = { version = "1.0.160", features = ["derive"], optional = true } vte-0.15.0/LICENSE-APACHE000064400000000000000000000227731046102023000124440ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS vte-0.15.0/LICENSE-MIT000064400000000000000000000020341046102023000121400ustar 00000000000000Copyright (c) 2016 Joe Wilm Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. vte-0.15.0/README.md000064400000000000000000000015131046102023000117640ustar 00000000000000vte === [![Build Status](https://travis-ci.org/alacritty/vte.svg?branch=master)](https://travis-ci.org/alacritty/vte) [![Crates.io Version](https://img.shields.io/crates/v/vte.svg)](https://crates.io/crates/vte/) Parser for implementing virtual terminal emulators in Rust. The parser is implemented according to [Paul Williams' ANSI parser state machine]. The state machine doesn't assign meaning to the parsed data and is thus not itself sufficient for writing a terminal emulator. Instead, it is expected that an implementation of the `Perform` trait which does something useful with the parsed data. The `Parser` handles the book keeping, and the `Perform` gets to simply handle actions. See the [docs] for more info. [Paul Williams' ANSI parser state machine]: https://vt100.net/emu/dec_ansi_parser [docs]: https://docs.rs/crate/vte/ vte-0.15.0/doc/modifyOtherKeys-example.txt000064400000000000000000004341051046102023000166000ustar 00000000000000Output of localectl status: System Locale: LANG=en_US.UTF-8 VC Keymap: us X11 Layout: us Symbols from keysymdef.h: 2061 keysyms are defined (longest 30) 1966 keycodes are defined 1624 keycodes are equated to Unicode Summary from xkbcomp: 256 keyNames 24 keyTypes 367 symCache 256 symMap Key types: Type Used Levels Name 0 169 1 ONE_LEVEL 1 30 2 TWO_LEVEL 2 26 2 ALPHABETIC 3 12 2 KEYPAD 6 1 2 PC_CONTROL_LEVEL2 7 1 2 PC_ALT_LEVEL2 8 16 5 CTRL_ALT 16 1 4 FOUR_LEVEL Key map: Type Level Name Code Symbol 0 1/1 ESC 0xff1b XK_Escape 1 1/2 AE01 0x0031 XK_1 2/2 0x0021 XK_exclam 1 1/2 AE02 0x0032 XK_2 2/2 0x0040 XK_at 1 1/2 AE03 0x0033 XK_3 2/2 0x0023 XK_numbersign 1 1/2 AE04 0x0034 XK_4 2/2 0x0024 XK_dollar 1 1/2 AE05 0x0035 XK_5 2/2 0x0025 XK_percent 1 1/2 AE06 0x0036 XK_6 2/2 0x005e XK_asciicircum 1 1/2 AE07 0x0037 XK_7 2/2 0x0026 XK_ampersand 1 1/2 AE08 0x0038 XK_8 2/2 0x002a XK_asterisk 1 1/2 AE09 0x0039 XK_9 2/2 0x0028 XK_parenleft 1 1/2 AE10 0x0030 XK_0 2/2 0x0029 XK_parenright 1 1/2 AE11 0x002d XK_minus 2/2 0x005f XK_underscore 1 1/2 AE12 0x003d XK_equal 2/2 0x002b XK_plus 1 1/2 BKSP 0xff08 XK_BackSpace 2/2 0xff08 XK_BackSpace 1 1/2 TAB 0xff09 XK_Tab 2/2 0xfe20 XK_ISO_Left_Tab 2 1/2 AD01 0x0071 XK_q 2/2 0x0051 XK_Q 2 1/2 AD02 0x0077 XK_w 2/2 0x0057 XK_W 2 1/2 AD03 0x0065 XK_e 2/2 0x0045 XK_E 2 1/2 AD04 0x0072 XK_r 2/2 0x0052 XK_R 2 1/2 AD05 0x0074 XK_t 2/2 0x0054 XK_T 2 1/2 AD06 0x0079 XK_y 2/2 0x0059 XK_Y 2 1/2 AD07 0x0075 XK_u 2/2 0x0055 XK_U 2 1/2 AD08 0x0069 XK_i 2/2 0x0049 XK_I 2 1/2 AD09 0x006f XK_o 2/2 0x004f XK_O 2 1/2 AD10 0x0070 XK_p 2/2 0x0050 XK_P 1 1/2 AD11 0x005b XK_bracketleft 2/2 0x007b XK_braceleft 1 1/2 AD12 0x005d XK_bracketright 2/2 0x007d XK_braceright 0 1/1 RTRN 0xff0d XK_Return 0 1/1 LCTL 0xffe3 XK_Control_L 2 1/2 AC01 0x0061 XK_a 2/2 0x0041 XK_A 2 1/2 AC02 0x0073 XK_s 2/2 0x0053 XK_S 2 1/2 AC03 0x0064 XK_d 2/2 0x0044 XK_D 2 1/2 AC04 0x0066 XK_f 2/2 0x0046 XK_F 2 1/2 AC05 0x0067 XK_g 2/2 0x0047 XK_G 2 1/2 AC06 0x0068 XK_h 2/2 0x0048 XK_H 2 1/2 AC07 0x006a XK_j 2/2 0x004a XK_J 2 1/2 AC08 0x006b XK_k 2/2 0x004b XK_K 2 1/2 AC09 0x006c XK_l 2/2 0x004c XK_L 1 1/2 AC10 0x003b XK_semicolon 2/2 0x003a XK_colon 1 1/2 AC11 0x0027 XK_apostrophe 2/2 0x0022 XK_quotedbl 1 1/2 TLDE 0x0060 XK_grave 2/2 0x007e XK_asciitilde 0 1/1 LFSH 0xffe1 XK_Shift_L 1 1/2 BKSL 0x005c XK_backslash 2/2 0x007c XK_bar 2 1/2 AB01 0x007a XK_z 2/2 0x005a XK_Z 2 1/2 AB02 0x0078 XK_x 2/2 0x0058 XK_X 2 1/2 AB03 0x0063 XK_c 2/2 0x0043 XK_C 2 1/2 AB04 0x0076 XK_v 2/2 0x0056 XK_V 2 1/2 AB05 0x0062 XK_b 2/2 0x0042 XK_B 2 1/2 AB06 0x006e XK_n 2/2 0x004e XK_N 2 1/2 AB07 0x006d XK_m 2/2 0x004d XK_M 1 1/2 AB08 0x002c XK_comma 2/2 0x003c XK_less 1 1/2 AB09 0x002e XK_period 2/2 0x003e XK_greater 1 1/2 AB10 0x002f XK_slash 2/2 0x003f XK_question 0 1/1 RTSH 0xffe2 XK_Shift_R 8 1/5 KPMU 0xffaa XK_KP_Multiply 2/5 0xffaa XK_KP_Multiply 3/5 0xffaa XK_KP_Multiply 4/5 0xffaa XK_KP_Multiply 5/5 XK_XF86ClearGrab 1 1/2 LALT 0xffe9 XK_Alt_L 2/2 0xffe7 XK_Meta_L 0 1/1 SPCE 0x0020 XK_space 0 1/1 CAPS 0xffe5 XK_Caps_Lock 8 1/5 FK01 0xffbe XK_F1 2/5 0xffbe XK_F1 3/5 0xffbe XK_F1 4/5 0xffbe XK_F1 5/5 XK_XF86Switch_VT_1 8 1/5 FK02 0xffbf XK_F2 2/5 0xffbf XK_F2 3/5 0xffbf XK_F2 4/5 0xffbf XK_F2 5/5 XK_XF86Switch_VT_2 8 1/5 FK03 0xffc0 XK_F3 2/5 0xffc0 XK_F3 3/5 0xffc0 XK_F3 4/5 0xffc0 XK_F3 5/5 XK_XF86Switch_VT_3 8 1/5 FK04 0xffc1 XK_F4 2/5 0xffc1 XK_F4 3/5 0xffc1 XK_F4 4/5 0xffc1 XK_F4 5/5 XK_XF86Switch_VT_4 8 1/5 FK05 0xffc2 XK_F5 2/5 0xffc2 XK_F5 3/5 0xffc2 XK_F5 4/5 0xffc2 XK_F5 5/5 XK_XF86Switch_VT_5 8 1/5 FK06 0xffc3 XK_F6 2/5 0xffc3 XK_F6 3/5 0xffc3 XK_F6 4/5 0xffc3 XK_F6 5/5 XK_XF86Switch_VT_6 8 1/5 FK07 0xffc4 XK_F7 2/5 0xffc4 XK_F7 3/5 0xffc4 XK_F7 4/5 0xffc4 XK_F7 5/5 XK_XF86Switch_VT_7 8 1/5 FK08 0xffc5 XK_F8 2/5 0xffc5 XK_F8 3/5 0xffc5 XK_F8 4/5 0xffc5 XK_F8 5/5 XK_XF86Switch_VT_8 8 1/5 FK09 0xffc6 XK_F9 2/5 0xffc6 XK_F9 3/5 0xffc6 XK_F9 4/5 0xffc6 XK_F9 5/5 XK_XF86Switch_VT_9 8 1/5 FK10 0xffc7 XK_F10 2/5 0xffc7 XK_F10 3/5 0xffc7 XK_F10 4/5 0xffc7 XK_F10 5/5 XK_XF86Switch_VT_10 0 1/1 NMLK 0xff7f XK_Num_Lock 0 1/1 SCLK 0xff14 XK_Scroll_Lock 3 1/2 KP7 0xff95 XK_KP_Home 2/2 0xffb7 XK_KP_7 3 1/2 KP8 0xff97 XK_KP_Up 2/2 0xffb8 XK_KP_8 3 1/2 KP9 0xff9a XK_KP_Prior 2/2 0xffb9 XK_KP_9 8 1/5 KPSU 0xffad XK_KP_Subtract 2/5 0xffad XK_KP_Subtract 3/5 0xffad XK_KP_Subtract 4/5 0xffad XK_KP_Subtract 5/5 XK_XF86Prev_VMode 3 1/2 KP4 0xff96 XK_KP_Left 2/2 0xffb4 XK_KP_4 3 1/2 KP5 0xff9d XK_KP_Begin 2/2 0xffb5 XK_KP_5 3 1/2 KP6 0xff98 XK_KP_Right 2/2 0xffb6 XK_KP_6 8 1/5 KPAD 0xffab XK_KP_Add 2/5 0xffab XK_KP_Add 3/5 0xffab XK_KP_Add 4/5 0xffab XK_KP_Add 5/5 XK_XF86Next_VMode 3 1/2 KP1 0xff9c XK_KP_End 2/2 0xffb1 XK_KP_1 3 1/2 KP2 0xff99 XK_KP_Down 2/2 0xffb2 XK_KP_2 3 1/2 KP3 0xff9b XK_KP_Next 2/2 0xffb3 XK_KP_3 3 1/2 KP0 0xff9e XK_KP_Insert 2/2 0xffb0 XK_KP_0 3 1/2 KPDL 0xff9f XK_KP_Delete 2/2 0xffae XK_KP_Decimal 0 1/1 LVL3 0xfe03 XK_ISO_Level3_Shift 16 1/4 LSGT 0x003c XK_less 2/4 0x003e XK_greater 3/4 0x007c XK_bar 4/4 0x00a6 XK_brokenbar 8 1/5 FK11 0xffc8 XK_F11 2/5 0xffc8 XK_F11 3/5 0xffc8 XK_F11 4/5 0xffc8 XK_F11 5/5 XK_XF86Switch_VT_11 8 1/5 FK12 0xffc9 XK_F12 2/5 0xffc9 XK_F12 3/5 0xffc9 XK_F12 4/5 0xffc9 XK_F12 5/5 XK_XF86Switch_VT_12 0 1/1 KATA 0xff26 XK_Katakana 0 1/1 HIRA 0xff25 XK_Hiragana 0 1/1 HENK 0xff23 XK_Henkan_Mode 0 1/1 HKTG 0xff27 XK_Hiragana_Katakana 0 1/1 MUHE 0xff22 XK_Muhenkan 0 1/1 KPEN 0xff8d XK_KP_Enter 0 1/1 RCTL 0xffe4 XK_Control_R 8 1/5 KPDV 0xffaf XK_KP_Divide 2/5 0xffaf XK_KP_Divide 3/5 0xffaf XK_KP_Divide 4/5 0xffaf XK_KP_Divide 5/5 XK_XF86Ungrab 7 1/2 PRSC 0xff61 XK_Print 2/2 0xff15 XK_Sys_Req 0 1/1 RALT 0xfe03 XK_ISO_Level3_Shift 0 1/1 LNFD 0xff0a XK_Linefeed 0 1/1 HOME 0xff50 XK_Home 0 1/1 UP 0xff52 XK_Up 0 1/1 PGUP 0xff55 XK_Prior 0 1/1 LEFT 0xff51 XK_Left 0 1/1 RGHT 0xff53 XK_Right 0 1/1 END 0xff57 XK_End 0 1/1 DOWN 0xff54 XK_Down 0 1/1 PGDN 0xff56 XK_Next 0 1/1 INS 0xff63 XK_Insert 0 1/1 DELE 0xffff XK_Delete 0 1/1 KPEQ 0xffbd XK_KP_Equal 0 1/1 I126 0x00b1 XK_plusminus 6 1/2 PAUS 0xff13 XK_Pause 2/2 0xff6b XK_Break 3 1/2 I129 0xffae XK_KP_Decimal 2/2 0xffae XK_KP_Decimal 0 1/1 HNGL 0xff31 XK_Hangul 0 1/1 HJCV 0xff34 XK_Hangul_Hanja 0 1/1 LWIN 0xffeb XK_Super_L 0 1/1 RWIN 0xffec XK_Super_R 0 1/1 COMP 0xff67 XK_Menu 0 1/1 STOP 0xff69 XK_Cancel 0 1/1 AGAI 0xff66 XK_Redo 0 1/1 UNDO 0xff65 XK_Undo 0 1/1 FIND 0xff68 XK_Find 0 1/1 HELP 0xff6a XK_Help 0 1/1 I187 0x0028 XK_parenleft 0 1/1 I188 0x0029 XK_parenright 0 1/1 I190 0xff66 XK_Redo 0 1/1 LVL5 0xfe11 XK_ISO_Level5_Shift 0 1/1 I218 0xff61 XK_Print 0 1/1 I231 0xff69 XK_Cancel Other modifiable keycodes: Code Symbol Actual Mode 0 Mode 1 Mode 2 0x0020 XK_space 32 -(skip)- --**--** -******* 0x0021 XK_exclam 33 -(skip)- --****** --****** 0x0022 XK_quotedbl 34 -(skip)- --****** --****** 0x0023 XK_numbersign 35 -(skip)- --****** --****** 0x0024 XK_dollar 36 -(skip)- --****** --****** 0x0025 XK_percent 37 -(skip)- --****** --****** 0x0026 XK_ampersand 38 -(skip)- --****** --****** 0x0027 XK_apostrophe 39 -(skip)- --****** --****** 0x0028 XK_parenleft 40 -(skip)- --****** --****** 0x0029 XK_parenright 41 -(skip)- --****** --****** 0x002a XK_asterisk 42 -(skip)- --****** --****** 0x002b XK_plus 43 -(skip)- --****** --****** 0x002c XK_comma 44 -(skip)- --****** --****** 0x002d XK_minus 45 -(skip)- --****** --****** 0x002e XK_period 46 -(skip)- --****** --****** 0x002f XK_slash 47 -(skip)- --**-*** --****** 0x0030 XK_0 48 -(skip)- --****** --****** 0x0031 XK_1 49 -(skip)- --****** --****** 0x0032 XK_2 50 -(skip)- --**--** --****** 0x0033 XK_3 51 -(skip)- --**-*** --****** 0x0034 XK_4 52 -(skip)- --**-*** --****** 0x0035 XK_5 53 -(skip)- --**-*** --****** 0x0036 XK_6 54 -(skip)- --**--** --****** 0x0037 XK_7 55 -(skip)- --**-*** --****** 0x0038 XK_8 56 -(skip)- --**-*** --****** 0x0039 XK_9 57 -(skip)- --****** --****** 0x003a XK_colon 58 -(skip)- --****** --****** 0x003b XK_semicolon 59 -(skip)- --****** --****** 0x003c XK_less 60 -(skip)- --****** --****** 0x003d XK_equal 61 -(skip)- --****** --****** 0x003e XK_greater 62 -(skip)- --****** --****** 0x003f XK_question 63 -(skip)- --****** --****** 0x0040 XK_at 64 -(skip)- --**--** -******* 0x0041 XK_A 65 -(skip)- --**--** -******* 0x0042 XK_B 66 -(skip)- --**--** -******* 0x0043 XK_C 67 -(skip)- --**--** -******* 0x0044 XK_D 68 -(skip)- --**--** -******* 0x0045 XK_E 69 -(skip)- --**--** -******* 0x0046 XK_F 70 -(skip)- --**--** -******* 0x0047 XK_G 71 -(skip)- --**--** -******* 0x0048 XK_H 72 -(skip)- --**--** -******* 0x0049 XK_I 73 -(skip)- --**--** -******* 0x004a XK_J 74 -(skip)- --**--** -******* 0x004b XK_K 75 -(skip)- --**--** -******* 0x004c XK_L 76 -(skip)- --**--** -******* 0x004d XK_M 77 -(skip)- --**--** -******* 0x004e XK_N 78 -(skip)- --**--** -******* 0x004f XK_O 79 -(skip)- --**--** -******* 0x0050 XK_P 80 -(skip)- --**--** -******* 0x0051 XK_Q 81 -(skip)- --**--** -******* 0x0052 XK_R 82 -(skip)- --**--** -******* 0x0053 XK_S 83 -(skip)- --**--** -******* 0x0054 XK_T 84 -(skip)- --**--** -******* 0x0055 XK_U 85 -(skip)- --**--** -******* 0x0056 XK_V 86 -(skip)- --**--** -******* 0x0057 XK_W 87 -(skip)- --**--** -******* 0x0058 XK_X 88 -(skip)- --**--** -******* 0x0059 XK_Y 89 -(skip)- --**--** -******* 0x005a XK_Z 90 -(skip)- --**--** -******* 0x005b XK_bracketleft 91 -(skip)- --**--** -******* 0x005c XK_backslash 92 -(skip)- --**--** -******* 0x005d XK_bracketright 93 -(skip)- --**--** -******* 0x005e XK_asciicircum 94 -(skip)- --**--** -******* 0x005f XK_underscore 95 -(skip)- --**--** -******* 0x0060 XK_grave 96 -(skip)- --**--** -******* 0x0061 XK_a 97 -(skip)- --**--** -******* 0x0062 XK_b 98 -(skip)- --**--** -******* 0x0063 XK_c 99 -(skip)- --**--** -******* 0x0064 XK_d 100 -(skip)- --**--** -******* 0x0065 XK_e 101 -(skip)- --**--** -******* 0x0066 XK_f 102 -(skip)- --**--** -******* 0x0067 XK_g 103 -(skip)- --**--** -******* 0x0068 XK_h 104 -(skip)- --**--** -******* 0x0069 XK_i 105 -(skip)- --**--** -******* 0x006a XK_j 106 -(skip)- --**--** -******* 0x006b XK_k 107 -(skip)- --**--** -******* 0x006c XK_l 108 -(skip)- --**--** -******* 0x006d XK_m 109 -(skip)- --**--** -******* 0x006e XK_n 110 -(skip)- --**--** -******* 0x006f XK_o 111 -(skip)- --**--** -******* 0x0070 XK_p 112 -(skip)- --**--** -******* 0x0071 XK_q 113 -(skip)- --**--** -******* 0x0072 XK_r 114 -(skip)- --**--** -******* 0x0073 XK_s 115 -(skip)- --**--** -******* 0x0074 XK_t 116 -(skip)- --**--** -******* 0x0075 XK_u 117 -(skip)- --**--** -******* 0x0076 XK_v 118 -(skip)- --**--** -******* 0x0077 XK_w 119 -(skip)- --**--** -******* 0x0078 XK_x 120 -(skip)- --**--** -******* 0x0079 XK_y 121 -(skip)- --**--** -******* 0x007a XK_z 122 -(skip)- --**--** -******* 0x007b XK_braceleft 123 -(skip)- --**--** -******* 0x007c XK_bar 124 -(skip)- --**--** -******* 0x007d XK_braceright 125 -(skip)- --**--** -******* 0x007e XK_asciitilde 126 -(skip)- --**--** -******* 0x00a6 XK_brokenbar 166 -(skip)- --****** --****** 0x00b1 XK_plusminus 177 -(skip)- --****** --****** 0xfe03 XK_ISO_Level3_Shift 65027 -(skip)- -(skip)- -(skip)- 0xfe11 XK_ISO_Level5_Shift 65041 -(skip)- -(skip)- -(skip)- 0xfe20 XK_ISO_Left_Tab 65056 -(skip)- -(skip)- -(skip)- 0xff08 XK_BackSpace 65288 -(skip)- -(skip)- -***-*** 0xff09 XK_Tab 65289 -(skip)- -******* -******* 0xff0a XK_Linefeed 65290 -(skip)- -******* --****** 0xff0d XK_Return 65293 -(skip)- -******* -******* 0xff13 XK_Pause 65299 -(skip)- -******* --****** 0xff14 XK_Scroll_Lock 65300 -(skip)- -******* --****** 0xff15 XK_Sys_Req 65301 -(skip)- -******* --****** 0xff1b XK_Escape 65307 -(skip)- --**--** -******* 0xff22 XK_Muhenkan 65314 -(skip)- -******* --****** 0xff23 XK_Henkan_Mode 65315 -(skip)- -******* --****** 0xff25 XK_Hiragana 65317 -(skip)- -******* --****** 0xff26 XK_Katakana 65318 -(skip)- -******* --****** 0xff27 XK_Hiragana_Katakana 65319 -(skip)- -******* --****** 0xff31 XK_Hangul 65329 -(skip)- -******* --****** 0xff34 XK_Hangul_Hanja 65332 -(skip)- -******* --****** 0xff50 XK_Home 65360 -(skip)- -(skip)- -(skip)- 0xff51 XK_Left 65361 -(skip)- -(skip)- -(skip)- 0xff52 XK_Up 65362 -(skip)- -(skip)- -(skip)- 0xff53 XK_Right 65363 -(skip)- -(skip)- -(skip)- 0xff54 XK_Down 65364 -(skip)- -(skip)- -(skip)- 0xff55 XK_Prior 65365 -(skip)- -(skip)- -(skip)- 0xff56 XK_Next 65366 -(skip)- -(skip)- -(skip)- 0xff57 XK_End 65367 -(skip)- -(skip)- -(skip)- 0xff61 XK_Print 65377 -(skip)- -(skip)- -(skip)- 0xff63 XK_Insert 65379 -(skip)- -(skip)- -(skip)- 0xff65 XK_Undo 65381 -(skip)- -(skip)- -(skip)- 0xff66 XK_Redo 65382 -(skip)- -(skip)- -(skip)- 0xff67 XK_Menu 65383 -(skip)- -(skip)- -(skip)- 0xff68 XK_Find 65384 -(skip)- -(skip)- -(skip)- 0xff69 XK_Cancel 65385 -(skip)- -(skip)- -(skip)- 0xff6a XK_Help 65386 -(skip)- -(skip)- -(skip)- 0xff6b XK_Break 65387 -(skip)- -(skip)- -(skip)- 0xff7f XK_Num_Lock 65407 -(skip)- -(skip)- -(skip)- 0xff8d XK_KP_Enter 65421 -(skip)- -(skip)- -(skip)- 0xff95 XK_KP_Home 65429 -(skip)- -(skip)- -(skip)- 0xff96 XK_KP_Left 65430 -(skip)- -(skip)- -(skip)- 0xff97 XK_KP_Up 65431 -(skip)- -(skip)- -(skip)- 0xff98 XK_KP_Right 65432 -(skip)- -(skip)- -(skip)- 0xff99 XK_KP_Down 65433 -(skip)- -(skip)- -(skip)- 0xff9a XK_KP_Prior 65434 -(skip)- -(skip)- -(skip)- 0xff9b XK_KP_Next 65435 -(skip)- -(skip)- -(skip)- 0xff9c XK_KP_End 65436 -(skip)- -(skip)- -(skip)- 0xff9d XK_KP_Begin 65437 -(skip)- -(skip)- -(skip)- 0xff9e XK_KP_Insert 65438 -(skip)- -(skip)- -(skip)- 0xff9f XK_KP_Delete 65439 -(skip)- -(skip)- -(skip)- 0xffaa XK_KP_Multiply 65450 -(skip)- -(skip)- -(skip)- 0xffab XK_KP_Add 65451 -(skip)- -(skip)- -(skip)- 0xffad XK_KP_Subtract 65453 -(skip)- -(skip)- -(skip)- 0xffae XK_KP_Decimal 65454 -(skip)- -(skip)- -(skip)- 0xffaf XK_KP_Divide 65455 -(skip)- -(skip)- -(skip)- 0xffb0 XK_KP_0 65456 -(skip)- -(skip)- -(skip)- 0xffb1 XK_KP_1 65457 -(skip)- -(skip)- -(skip)- 0xffb2 XK_KP_2 65458 -(skip)- -(skip)- -(skip)- 0xffb3 XK_KP_3 65459 -(skip)- -(skip)- -(skip)- 0xffb4 XK_KP_4 65460 -(skip)- -(skip)- -(skip)- 0xffb5 XK_KP_5 65461 -(skip)- -(skip)- -(skip)- 0xffb6 XK_KP_6 65462 -(skip)- -(skip)- -(skip)- 0xffb7 XK_KP_7 65463 -(skip)- -(skip)- -(skip)- 0xffb8 XK_KP_8 65464 -(skip)- -(skip)- -(skip)- 0xffb9 XK_KP_9 65465 -(skip)- -(skip)- -(skip)- 0xffbd XK_KP_Equal 65469 -(skip)- -(skip)- -(skip)- 0xffbe XK_F1 65470 -(skip)- -(skip)- -(skip)- 0xffbf XK_F2 65471 -(skip)- -(skip)- -(skip)- 0xffc0 XK_F3 65472 -(skip)- -(skip)- -(skip)- 0xffc1 XK_F4 65473 -(skip)- -(skip)- -(skip)- 0xffc2 XK_F5 65474 -(skip)- -(skip)- -(skip)- 0xffc3 XK_F6 65475 -(skip)- -(skip)- -(skip)- 0xffc4 XK_F7 65476 -(skip)- -(skip)- -(skip)- 0xffc5 XK_F8 65477 -(skip)- -(skip)- -(skip)- 0xffc6 XK_F9 65478 -(skip)- -(skip)- -(skip)- 0xffc7 XK_F10 65479 -(skip)- -(skip)- -(skip)- 0xffc8 XK_F11 65480 -(skip)- -(skip)- -(skip)- 0xffc9 XK_F12 65481 -(skip)- -(skip)- -(skip)- 0xffe1 XK_Shift_L 65505 -(skip)- -(skip)- -(skip)- 0xffe2 XK_Shift_R 65506 -(skip)- -(skip)- -(skip)- 0xffe3 XK_Control_L 65507 -(skip)- -(skip)- -(skip)- 0xffe4 XK_Control_R 65508 -(skip)- -(skip)- -(skip)- 0xffe5 XK_Caps_Lock 65509 -(skip)- -(skip)- -(skip)- 0xffe7 XK_Meta_L 65511 -(skip)- -(skip)- -(skip)- 0xffe9 XK_Alt_L 65513 -(skip)- -(skip)- -(skip)- 0xffeb XK_Super_L 65515 -(skip)- -(skip)- -(skip)- 0xffec XK_Super_R 65516 -(skip)- -(skip)- -(skip)- 0xffff XK_Delete 65535 -(skip)- -(skip)- -(skip)- Modify-param to/from state: PARAM 0 -> 0 -> 0 () PARAM 1 -> 0 -> 0 () PARAM 2 -> 1 -> 2 (Shift) PARAM 3 -> 8 -> 3 (Alt) PARAM 4 -> 9 -> 4 (Shift+Alt) PARAM 5 -> 4 -> 5 (Ctrl) PARAM 6 -> 5 -> 6 (Shift+Ctrl) PARAM 7 -> 12 -> 7 (Alt+Ctrl) PARAM 8 -> 13 -> 8 (Shift+Alt+Ctrl) State to/from modify-param: STATE 0 -> 0 -> 0 () STATE 1 -> 2 -> 1 (Shift) STATE 2 -> 0 -> 0 (Lock) STATE 3 -> 2 -> 1 (Shift+Lock) STATE 4 -> 5 -> 4 (Ctrl) STATE 5 -> 6 -> 5 (Shift+Ctrl) STATE 6 -> 5 -> 4 (Lock+Ctrl) STATE 7 -> 6 -> 5 (Shift+Lock+Ctrl) STATE 8 -> 3 -> 8 (Alt) STATE 9 -> 4 -> 9 (Shift+Alt) STATE 10 -> 3 -> 8 (Lock+Alt) STATE 11 -> 4 -> 9 (Shift+Lock+Alt) STATE 12 -> 7 -> 12 (Ctrl+Alt) STATE 13 -> 8 -> 13 (Shift+Ctrl+Alt) STATE 14 -> 7 -> 12 (Lock+Ctrl+Alt) STATE 15 -> 8 -> 13 (Shift+Lock+Ctrl+Alt) Other modified-key escapes: Code Symbol Actual Mode 0 Mode 1 Mode 2 0x0020 XK_space 32 -(skip)- --**--** -******* ---- \s \s \s s--- \s \s \E[32;2u -a-- \s \E[32;3u \E[32;3u sa-- \s \E[32;4u \E[32;4u --c- ^@ ^@ \E[32;5u s-c- ^@ ^@ \E[32;6u -ac- ^@ \E[32;7u \E[32;7u sac- ^@ \E[32;8u \E[32;8u 0x0021 XK_exclam 33 -(skip)- --****** --****** ---- ! ! ! s--- ! ! ! -a-- ! \E[33;3u \E[33;3u sa-- ! \E[33;4u \E[33;4u --c- ! \E[33;5u \E[33;5u s-c- ! \E[33;6u \E[33;6u -ac- ! \E[33;7u \E[33;7u sac- ! \E[33;8u \E[33;8u 0x0022 XK_quotedbl 34 -(skip)- --****** --****** ---- " " " s--- " " " -a-- " \E[34;3u \E[34;3u sa-- " \E[34;4u \E[34;4u --c- " \E[34;5u \E[34;5u s-c- " \E[34;6u \E[34;6u -ac- " \E[34;7u \E[34;7u sac- " \E[34;8u \E[34;8u 0x0023 XK_numbersign 35 -(skip)- --****** --****** ---- # # # s--- # # # -a-- # \E[35;3u \E[35;3u sa-- # \E[35;4u \E[35;4u --c- # \E[35;5u \E[35;5u s-c- # \E[35;6u \E[35;6u -ac- # \E[35;7u \E[35;7u sac- # \E[35;8u \E[35;8u 0x0024 XK_dollar 36 -(skip)- --****** --****** ---- $ $ $ s--- $ $ $ -a-- $ \E[36;3u \E[36;3u sa-- $ \E[36;4u \E[36;4u --c- $ \E[36;5u \E[36;5u s-c- $ \E[36;6u \E[36;6u -ac- $ \E[36;7u \E[36;7u sac- $ \E[36;8u \E[36;8u 0x0025 XK_percent 37 -(skip)- --****** --****** ---- % % % s--- % % % -a-- % \E[37;3u \E[37;3u sa-- % \E[37;4u \E[37;4u --c- % \E[37;5u \E[37;5u s-c- % \E[37;6u \E[37;6u -ac- % \E[37;7u \E[37;7u sac- % \E[37;8u \E[37;8u 0x0026 XK_ampersand 38 -(skip)- --****** --****** ---- & & & s--- & & & -a-- & \E[38;3u \E[38;3u sa-- & \E[38;4u \E[38;4u --c- & \E[38;5u \E[38;5u s-c- & \E[38;6u \E[38;6u -ac- & \E[38;7u \E[38;7u sac- & \E[38;8u \E[38;8u 0x0027 XK_apostrophe 39 -(skip)- --****** --****** ---- ' ' ' s--- " " " -a-- ' \E[39;3u \E[39;3u sa-- " \E[34;4u \E[34;4u --c- ' \E[39;5u \E[39;5u s-c- " \E[34;6u \E[34;6u -ac- ' \E[39;7u \E[39;7u sac- " \E[34;8u \E[34;8u 0x0028 XK_parenleft 40 -(skip)- --****** --****** ---- ( ( ( s--- ( ( ( -a-- ( \E[40;3u \E[40;3u sa-- ( \E[40;4u \E[40;4u --c- ( \E[40;5u \E[40;5u s-c- ( \E[40;6u \E[40;6u -ac- ( \E[40;7u \E[40;7u sac- ( \E[40;8u \E[40;8u 0x0029 XK_parenright 41 -(skip)- --****** --****** ---- ) ) ) s--- ) ) ) -a-- ) \E[41;3u \E[41;3u sa-- ) \E[41;4u \E[41;4u --c- ) \E[41;5u \E[41;5u s-c- ) \E[41;6u \E[41;6u -ac- ) \E[41;7u \E[41;7u sac- ) \E[41;8u \E[41;8u 0x002a XK_asterisk 42 -(skip)- --****** --****** ---- * * * s--- * * * -a-- * \E[42;3u \E[42;3u sa-- * \E[42;4u \E[42;4u --c- * \E[42;5u \E[42;5u s-c- * \E[42;6u \E[42;6u -ac- * \E[42;7u \E[42;7u sac- * \E[42;8u \E[42;8u 0x002b XK_plus 43 -(skip)- --****** --****** ---- + + + s--- + + + -a-- + \E[43;3u \E[43;3u sa-- + \E[43;4u \E[43;4u --c- + \E[43;5u \E[43;5u s-c- + \E[43;6u \E[43;6u -ac- + \E[43;7u \E[43;7u sac- + \E[43;8u \E[43;8u 0x002c XK_comma 44 -(skip)- --****** --****** ---- , , , s--- < < < -a-- , \E[44;3u \E[44;3u sa-- < \E[60;4u \E[60;4u --c- , \E[44;5u \E[44;5u s-c- < \E[60;6u \E[60;6u -ac- , \E[44;7u \E[44;7u sac- < \E[60;8u \E[60;8u 0x002d XK_minus 45 -(skip)- --****** --****** ---- - - - s--- _ _ _ -a-- - \E[45;3u \E[45;3u sa-- _ \E[95;4u \E[95;4u --c- - \E[45;5u \E[45;5u s-c- ^_ \E[95;6u \E[95;6u -ac- - \E[45;7u \E[45;7u sac- ^_ \E[95;8u \E[95;8u 0x002e XK_period 46 -(skip)- --****** --****** ---- . . . s--- > > > -a-- . \E[46;3u \E[46;3u sa-- > \E[62;4u \E[62;4u --c- . \E[46;5u \E[46;5u s-c- > \E[62;6u \E[62;6u -ac- . \E[46;7u \E[46;7u sac- > \E[62;8u \E[62;8u 0x002f XK_slash 47 -(skip)- --**-*** --****** ---- / / / s--- ? ? ? -a-- / \E[47;3u \E[47;3u sa-- ? \E[63;4u \E[63;4u --c- ^_ ^_ \E[47;5u s-c- ^? \E[63;6u \E[63;6u -ac- ^_ \E[47;7u \E[47;7u sac- ^? \E[63;8u \E[63;8u 0x0030 XK_0 48 -(skip)- --****** --****** ---- 0 0 0 s--- ) ) ) -a-- 0 \E[48;3u \E[48;3u sa-- ) \E[41;4u \E[41;4u --c- 0 \E[48;5u \E[48;5u s-c- ) \E[41;6u \E[41;6u -ac- 0 \E[48;7u \E[48;7u sac- ) \E[41;8u \E[41;8u 0x0031 XK_1 49 -(skip)- --****** --****** ---- 1 1 1 s--- ! ! ! -a-- 1 \E[49;3u \E[49;3u sa-- ! \E[33;4u \E[33;4u --c- 1 \E[49;5u \E[49;5u s-c- ! \E[33;6u \E[33;6u -ac- 1 \E[49;7u \E[49;7u sac- ! \E[33;8u \E[33;8u 0x0032 XK_2 50 -(skip)- --**--** --****** ---- 2 2 2 s--- @ @ @ -a-- 2 \E[50;3u \E[50;3u sa-- @ \E[64;4u \E[64;4u --c- ^@ ^@ \E[50;5u s-c- ^@ ^@ \E[64;6u -ac- ^@ \E[50;7u \E[50;7u sac- ^@ \E[64;8u \E[64;8u 0x0033 XK_3 51 -(skip)- --**-*** --****** ---- 3 3 3 s--- # # # -a-- 3 \E[51;3u \E[51;3u sa-- # \E[35;4u \E[35;4u --c- \E \E \E[51;5u s-c- # \E[35;6u \E[35;6u -ac- \E \E[51;7u \E[51;7u sac- # \E[35;8u \E[35;8u 0x0034 XK_4 52 -(skip)- --**-*** --****** ---- 4 4 4 s--- $ $ $ -a-- 4 \E[52;3u \E[52;3u sa-- $ \E[36;4u \E[36;4u --c- ^\ ^\ \E[52;5u s-c- $ \E[36;6u \E[36;6u -ac- ^\ \E[52;7u \E[52;7u sac- $ \E[36;8u \E[36;8u 0x0035 XK_5 53 -(skip)- --**-*** --****** ---- 5 5 5 s--- % % % -a-- 5 \E[53;3u \E[53;3u sa-- % \E[37;4u \E[37;4u --c- ^] ^] \E[53;5u s-c- % \E[37;6u \E[37;6u -ac- ^] \E[53;7u \E[53;7u sac- % \E[37;8u \E[37;8u 0x0036 XK_6 54 -(skip)- --**--** --****** ---- 6 6 6 s--- \^ \^ \^ -a-- 6 \E[54;3u \E[54;3u sa-- \^ \E[94;4u \E[94;4u --c- ^^ ^^ \E[54;5u s-c- ^^ ^^ \E[94;6u -ac- ^^ \E[54;7u \E[54;7u sac- ^^ \E[94;8u \E[94;8u 0x0037 XK_7 55 -(skip)- --**-*** --****** ---- 7 7 7 s--- & & & -a-- 7 \E[55;3u \E[55;3u sa-- & \E[38;4u \E[38;4u --c- ^_ ^_ \E[55;5u s-c- & \E[38;6u \E[38;6u -ac- ^_ \E[55;7u \E[55;7u sac- & \E[38;8u \E[38;8u 0x0038 XK_8 56 -(skip)- --**-*** --****** ---- 8 8 8 s--- * * * -a-- 8 \E[56;3u \E[56;3u sa-- * \E[42;4u \E[42;4u --c- ^? ^? \E[56;5u s-c- * \E[42;6u \E[42;6u -ac- ^? \E[56;7u \E[56;7u sac- * \E[42;8u \E[42;8u 0x0039 XK_9 57 -(skip)- --****** --****** ---- 9 9 9 s--- ( ( ( -a-- 9 \E[57;3u \E[57;3u sa-- ( \E[40;4u \E[40;4u --c- 9 \E[57;5u \E[57;5u s-c- ( \E[40;6u \E[40;6u -ac- 9 \E[57;7u \E[57;7u sac- ( \E[40;8u \E[40;8u 0x003a XK_colon 58 -(skip)- --****** --****** ---- : : : s--- : : : -a-- : \E[58;3u \E[58;3u sa-- : \E[58;4u \E[58;4u --c- : \E[58;5u \E[58;5u s-c- : \E[58;6u \E[58;6u -ac- : \E[58;7u \E[58;7u sac- : \E[58;8u \E[58;8u 0x003b XK_semicolon 59 -(skip)- --****** --****** ---- ; ; ; s--- : : : -a-- ; \E[59;3u \E[59;3u sa-- : \E[58;4u \E[58;4u --c- ; \E[59;5u \E[59;5u s-c- : \E[58;6u \E[58;6u -ac- ; \E[59;7u \E[59;7u sac- : \E[58;8u \E[58;8u 0x003c XK_less 60 -(skip)- --****** --****** ---- < < < s--- < < < -a-- < \E[60;3u \E[60;3u sa-- < \E[60;4u \E[60;4u --c- < \E[60;5u \E[60;5u s-c- < \E[60;6u \E[60;6u -ac- < \E[60;7u \E[60;7u sac- < \E[60;8u \E[60;8u 0x003d XK_equal 61 -(skip)- --****** --****** ---- = = = s--- + + + -a-- = \E[61;3u \E[61;3u sa-- + \E[43;4u \E[43;4u --c- = \E[61;5u \E[61;5u s-c- + \E[43;6u \E[43;6u -ac- = \E[61;7u \E[61;7u sac- + \E[43;8u \E[43;8u 0x003e XK_greater 62 -(skip)- --****** --****** ---- > > > s--- > > > -a-- > \E[62;3u \E[62;3u sa-- > \E[62;4u \E[62;4u --c- > \E[62;5u \E[62;5u s-c- > \E[62;6u \E[62;6u -ac- > \E[62;7u \E[62;7u sac- > \E[62;8u \E[62;8u 0x003f XK_question 63 -(skip)- --****** --****** ---- ? ? ? s--- ? ? ? -a-- ? \E[63;3u \E[63;3u sa-- ? \E[63;4u \E[63;4u --c- ^? \E[63;5u \E[63;5u s-c- ^? \E[63;6u \E[63;6u -ac- ^? \E[63;7u \E[63;7u sac- ^? \E[63;8u \E[63;8u 0x0040 XK_at 64 -(skip)- --**--** -******* ---- @ @ @ s--- @ @ \E[64;2u -a-- @ \E[64;3u \E[64;3u sa-- @ \E[64;4u \E[64;4u --c- ^@ ^@ \E[64;5u s-c- ^@ ^@ \E[64;6u -ac- ^@ \E[64;7u \E[64;7u sac- ^@ \E[64;8u \E[64;8u 0x0041 XK_A 65 -(skip)- --**--** -******* ---- A A A s--- A A \E[65;2u -a-- A \E[65;3u \E[65;3u sa-- A \E[65;4u \E[65;4u --c- ^A ^A \E[65;5u s-c- ^A ^A \E[65;6u -ac- ^A \E[65;7u \E[65;7u sac- ^A \E[65;8u \E[65;8u 0x0042 XK_B 66 -(skip)- --**--** -******* ---- B B B s--- B B \E[66;2u -a-- B \E[66;3u \E[66;3u sa-- B \E[66;4u \E[66;4u --c- ^B ^B \E[66;5u s-c- ^B ^B \E[66;6u -ac- ^B \E[66;7u \E[66;7u sac- ^B \E[66;8u \E[66;8u 0x0043 XK_C 67 -(skip)- --**--** -******* ---- C C C s--- C C \E[67;2u -a-- C \E[67;3u \E[67;3u sa-- C \E[67;4u \E[67;4u --c- ^C ^C \E[67;5u s-c- ^C ^C \E[67;6u -ac- ^C \E[67;7u \E[67;7u sac- ^C \E[67;8u \E[67;8u 0x0044 XK_D 68 -(skip)- --**--** -******* ---- D D D s--- D D \E[68;2u -a-- D \E[68;3u \E[68;3u sa-- D \E[68;4u \E[68;4u --c- ^D ^D \E[68;5u s-c- ^D ^D \E[68;6u -ac- ^D \E[68;7u \E[68;7u sac- ^D \E[68;8u \E[68;8u 0x0045 XK_E 69 -(skip)- --**--** -******* ---- E E E s--- E E \E[69;2u -a-- E \E[69;3u \E[69;3u sa-- E \E[69;4u \E[69;4u --c- ^E ^E \E[69;5u s-c- ^E ^E \E[69;6u -ac- ^E \E[69;7u \E[69;7u sac- ^E \E[69;8u \E[69;8u 0x0046 XK_F 70 -(skip)- --**--** -******* ---- F F F s--- F F \E[70;2u -a-- F \E[70;3u \E[70;3u sa-- F \E[70;4u \E[70;4u --c- ^F ^F \E[70;5u s-c- ^F ^F \E[70;6u -ac- ^F \E[70;7u \E[70;7u sac- ^F \E[70;8u \E[70;8u 0x0047 XK_G 71 -(skip)- --**--** -******* ---- G G G s--- G G \E[71;2u -a-- G \E[71;3u \E[71;3u sa-- G \E[71;4u \E[71;4u --c- ^G ^G \E[71;5u s-c- ^G ^G \E[71;6u -ac- ^G \E[71;7u \E[71;7u sac- ^G \E[71;8u \E[71;8u 0x0048 XK_H 72 -(skip)- --**--** -******* ---- H H H s--- H H \E[72;2u -a-- H \E[72;3u \E[72;3u sa-- H \E[72;4u \E[72;4u --c- \b \b \E[72;5u s-c- \b \b \E[72;6u -ac- \b \E[72;7u \E[72;7u sac- \b \E[72;8u \E[72;8u 0x0049 XK_I 73 -(skip)- --**--** -******* ---- I I I s--- I I \E[73;2u -a-- I \E[73;3u \E[73;3u sa-- I \E[73;4u \E[73;4u --c- \t \t \E[73;5u s-c- \t \t \E[73;6u -ac- \t \E[73;7u \E[73;7u sac- \t \E[73;8u \E[73;8u 0x004a XK_J 74 -(skip)- --**--** -******* ---- J J J s--- J J \E[74;2u -a-- J \E[74;3u \E[74;3u sa-- J \E[74;4u \E[74;4u --c- \n \n \E[74;5u s-c- \n \n \E[74;6u -ac- \n \E[74;7u \E[74;7u sac- \n \E[74;8u \E[74;8u 0x004b XK_K 75 -(skip)- --**--** -******* ---- K K K s--- K K \E[75;2u -a-- K \E[75;3u \E[75;3u sa-- K \E[75;4u \E[75;4u --c- ^K ^K \E[75;5u s-c- ^K ^K \E[75;6u -ac- ^K \E[75;7u \E[75;7u sac- ^K \E[75;8u \E[75;8u 0x004c XK_L 76 -(skip)- --**--** -******* ---- L L L s--- L L \E[76;2u -a-- L \E[76;3u \E[76;3u sa-- L \E[76;4u \E[76;4u --c- \f \f \E[76;5u s-c- \f \f \E[76;6u -ac- \f \E[76;7u \E[76;7u sac- \f \E[76;8u \E[76;8u 0x004d XK_M 77 -(skip)- --**--** -******* ---- M M M s--- M M \E[77;2u -a-- M \E[77;3u \E[77;3u sa-- M \E[77;4u \E[77;4u --c- \r \r \E[77;5u s-c- \r \r \E[77;6u -ac- \r \E[77;7u \E[77;7u sac- \r \E[77;8u \E[77;8u 0x004e XK_N 78 -(skip)- --**--** -******* ---- N N N s--- N N \E[78;2u -a-- N \E[78;3u \E[78;3u sa-- N \E[78;4u \E[78;4u --c- ^N ^N \E[78;5u s-c- ^N ^N \E[78;6u -ac- ^N \E[78;7u \E[78;7u sac- ^N \E[78;8u \E[78;8u 0x004f XK_O 79 -(skip)- --**--** -******* ---- O O O s--- O O \E[79;2u -a-- O \E[79;3u \E[79;3u sa-- O \E[79;4u \E[79;4u --c- ^O ^O \E[79;5u s-c- ^O ^O \E[79;6u -ac- ^O \E[79;7u \E[79;7u sac- ^O \E[79;8u \E[79;8u 0x0050 XK_P 80 -(skip)- --**--** -******* ---- P P P s--- P P \E[80;2u -a-- P \E[80;3u \E[80;3u sa-- P \E[80;4u \E[80;4u --c- ^P ^P \E[80;5u s-c- ^P ^P \E[80;6u -ac- ^P \E[80;7u \E[80;7u sac- ^P \E[80;8u \E[80;8u 0x0051 XK_Q 81 -(skip)- --**--** -******* ---- Q Q Q s--- Q Q \E[81;2u -a-- Q \E[81;3u \E[81;3u sa-- Q \E[81;4u \E[81;4u --c- ^Q ^Q \E[81;5u s-c- ^Q ^Q \E[81;6u -ac- ^Q \E[81;7u \E[81;7u sac- ^Q \E[81;8u \E[81;8u 0x0052 XK_R 82 -(skip)- --**--** -******* ---- R R R s--- R R \E[82;2u -a-- R \E[82;3u \E[82;3u sa-- R \E[82;4u \E[82;4u --c- ^R ^R \E[82;5u s-c- ^R ^R \E[82;6u -ac- ^R \E[82;7u \E[82;7u sac- ^R \E[82;8u \E[82;8u 0x0053 XK_S 83 -(skip)- --**--** -******* ---- S S S s--- S S \E[83;2u -a-- S \E[83;3u \E[83;3u sa-- S \E[83;4u \E[83;4u --c- ^S ^S \E[83;5u s-c- ^S ^S \E[83;6u -ac- ^S \E[83;7u \E[83;7u sac- ^S \E[83;8u \E[83;8u 0x0054 XK_T 84 -(skip)- --**--** -******* ---- T T T s--- T T \E[84;2u -a-- T \E[84;3u \E[84;3u sa-- T \E[84;4u \E[84;4u --c- ^T ^T \E[84;5u s-c- ^T ^T \E[84;6u -ac- ^T \E[84;7u \E[84;7u sac- ^T \E[84;8u \E[84;8u 0x0055 XK_U 85 -(skip)- --**--** -******* ---- U U U s--- U U \E[85;2u -a-- U \E[85;3u \E[85;3u sa-- U \E[85;4u \E[85;4u --c- ^U ^U \E[85;5u s-c- ^U ^U \E[85;6u -ac- ^U \E[85;7u \E[85;7u sac- ^U \E[85;8u \E[85;8u 0x0056 XK_V 86 -(skip)- --**--** -******* ---- V V V s--- V V \E[86;2u -a-- V \E[86;3u \E[86;3u sa-- V \E[86;4u \E[86;4u --c- ^V ^V \E[86;5u s-c- ^V ^V \E[86;6u -ac- ^V \E[86;7u \E[86;7u sac- ^V \E[86;8u \E[86;8u 0x0057 XK_W 87 -(skip)- --**--** -******* ---- W W W s--- W W \E[87;2u -a-- W \E[87;3u \E[87;3u sa-- W \E[87;4u \E[87;4u --c- ^W ^W \E[87;5u s-c- ^W ^W \E[87;6u -ac- ^W \E[87;7u \E[87;7u sac- ^W \E[87;8u \E[87;8u 0x0058 XK_X 88 -(skip)- --**--** -******* ---- X X X s--- X X \E[88;2u -a-- X \E[88;3u \E[88;3u sa-- X \E[88;4u \E[88;4u --c- ^X ^X \E[88;5u s-c- ^X ^X \E[88;6u -ac- ^X \E[88;7u \E[88;7u sac- ^X \E[88;8u \E[88;8u 0x0059 XK_Y 89 -(skip)- --**--** -******* ---- Y Y Y s--- Y Y \E[89;2u -a-- Y \E[89;3u \E[89;3u sa-- Y \E[89;4u \E[89;4u --c- ^Y ^Y \E[89;5u s-c- ^Y ^Y \E[89;6u -ac- ^Y \E[89;7u \E[89;7u sac- ^Y \E[89;8u \E[89;8u 0x005a XK_Z 90 -(skip)- --**--** -******* ---- Z Z Z s--- Z Z \E[90;2u -a-- Z \E[90;3u \E[90;3u sa-- Z \E[90;4u \E[90;4u --c- ^Z ^Z \E[90;5u s-c- ^Z ^Z \E[90;6u -ac- ^Z \E[90;7u \E[90;7u sac- ^Z \E[90;8u \E[90;8u 0x005b XK_bracketleft 91 -(skip)- --**--** -******* ---- [ [ [ s--- { { \E[123;2u -a-- [ \E[91;3u \E[91;3u sa-- { \E[123;4u \E[123;4u --c- \E \E \E[91;5u s-c- \E \E \E[123;6u -ac- \E \E[91;7u \E[91;7u sac- \E \E[123;8u \E[123;8u 0x005c XK_backslash 92 -(skip)- --**--** -******* ---- \\ \\ \\ s--- | | \E[124;2u -a-- \\ \E[92;3u \E[92;3u sa-- | \E[124;4u \E[124;4u --c- ^\ ^\ \E[92;5u s-c- ^\ ^\ \E[124;6u -ac- ^\ \E[92;7u \E[92;7u sac- ^\ \E[124;8u \E[124;8u 0x005d XK_bracketright 93 -(skip)- --**--** -******* ---- ] ] ] s--- } } \E[125;2u -a-- ] \E[93;3u \E[93;3u sa-- } \E[125;4u \E[125;4u --c- ^] ^] \E[93;5u s-c- ^] ^] \E[125;6u -ac- ^] \E[93;7u \E[93;7u sac- ^] \E[125;8u \E[125;8u 0x005e XK_asciicircum 94 -(skip)- --**--** -******* ---- \^ \^ \^ s--- \^ \^ \E[94;2u -a-- \^ \E[94;3u \E[94;3u sa-- \^ \E[94;4u \E[94;4u --c- ^^ ^^ \E[94;5u s-c- ^^ ^^ \E[94;6u -ac- ^^ \E[94;7u \E[94;7u sac- ^^ \E[94;8u \E[94;8u 0x005f XK_underscore 95 -(skip)- --**--** -******* ---- _ _ _ s--- _ _ \E[95;2u -a-- _ \E[95;3u \E[95;3u sa-- _ \E[95;4u \E[95;4u --c- ^_ ^_ \E[95;5u s-c- ^_ ^_ \E[95;6u -ac- ^_ \E[95;7u \E[95;7u sac- ^_ \E[95;8u \E[95;8u 0x0060 XK_grave 96 -(skip)- --**--** -******* ---- ` ` ` s--- ~ ~ \E[126;2u -a-- ` \E[96;3u \E[96;3u sa-- ~ \E[126;4u \E[126;4u --c- ^@ ^@ \E[96;5u s-c- ^^ ^^ \E[126;6u -ac- ^@ \E[96;7u \E[96;7u sac- ^^ \E[126;8u \E[126;8u 0x0061 XK_a 97 -(skip)- --**--** -******* ---- a a a s--- A A \E[65;2u -a-- a \E[97;3u \E[97;3u sa-- A \E[65;4u \E[65;4u --c- ^A ^A \E[97;5u s-c- ^A ^A \E[65;6u -ac- ^A \E[97;7u \E[97;7u sac- ^A \E[65;8u \E[65;8u 0x0062 XK_b 98 -(skip)- --**--** -******* ---- b b b s--- B B \E[66;2u -a-- b \E[98;3u \E[98;3u sa-- B \E[66;4u \E[66;4u --c- ^B ^B \E[98;5u s-c- ^B ^B \E[66;6u -ac- ^B \E[98;7u \E[98;7u sac- ^B \E[66;8u \E[66;8u 0x0063 XK_c 99 -(skip)- --**--** -******* ---- c c c s--- C C \E[67;2u -a-- c \E[99;3u \E[99;3u sa-- C \E[67;4u \E[67;4u --c- ^C ^C \E[99;5u s-c- ^C ^C \E[67;6u -ac- ^C \E[99;7u \E[99;7u sac- ^C \E[67;8u \E[67;8u 0x0064 XK_d 100 -(skip)- --**--** -******* ---- d d d s--- D D \E[68;2u -a-- d \E[100;3u \E[100;3u sa-- D \E[68;4u \E[68;4u --c- ^D ^D \E[100;5u s-c- ^D ^D \E[68;6u -ac- ^D \E[100;7u \E[100;7u sac- ^D \E[68;8u \E[68;8u 0x0065 XK_e 101 -(skip)- --**--** -******* ---- e e e s--- E E \E[69;2u -a-- e \E[101;3u \E[101;3u sa-- E \E[69;4u \E[69;4u --c- ^E ^E \E[101;5u s-c- ^E ^E \E[69;6u -ac- ^E \E[101;7u \E[101;7u sac- ^E \E[69;8u \E[69;8u 0x0066 XK_f 102 -(skip)- --**--** -******* ---- f f f s--- F F \E[70;2u -a-- f \E[102;3u \E[102;3u sa-- F \E[70;4u \E[70;4u --c- ^F ^F \E[102;5u s-c- ^F ^F \E[70;6u -ac- ^F \E[102;7u \E[102;7u sac- ^F \E[70;8u \E[70;8u 0x0067 XK_g 103 -(skip)- --**--** -******* ---- g g g s--- G G \E[71;2u -a-- g \E[103;3u \E[103;3u sa-- G \E[71;4u \E[71;4u --c- ^G ^G \E[103;5u s-c- ^G ^G \E[71;6u -ac- ^G \E[103;7u \E[103;7u sac- ^G \E[71;8u \E[71;8u 0x0068 XK_h 104 -(skip)- --**--** -******* ---- h h h s--- H H \E[72;2u -a-- h \E[104;3u \E[104;3u sa-- H \E[72;4u \E[72;4u --c- \b \b \E[104;5u s-c- \b \b \E[72;6u -ac- \b \E[104;7u \E[104;7u sac- \b \E[72;8u \E[72;8u 0x0069 XK_i 105 -(skip)- --**--** -******* ---- i i i s--- I I \E[73;2u -a-- i \E[105;3u \E[105;3u sa-- I \E[73;4u \E[73;4u --c- \t \t \E[105;5u s-c- \t \t \E[73;6u -ac- \t \E[105;7u \E[105;7u sac- \t \E[73;8u \E[73;8u 0x006a XK_j 106 -(skip)- --**--** -******* ---- j j j s--- J J \E[74;2u -a-- j \E[106;3u \E[106;3u sa-- J \E[74;4u \E[74;4u --c- \n \n \E[106;5u s-c- \n \n \E[74;6u -ac- \n \E[106;7u \E[106;7u sac- \n \E[74;8u \E[74;8u 0x006b XK_k 107 -(skip)- --**--** -******* ---- k k k s--- K K \E[75;2u -a-- k \E[107;3u \E[107;3u sa-- K \E[75;4u \E[75;4u --c- ^K ^K \E[107;5u s-c- ^K ^K \E[75;6u -ac- ^K \E[107;7u \E[107;7u sac- ^K \E[75;8u \E[75;8u 0x006c XK_l 108 -(skip)- --**--** -******* ---- l l l s--- L L \E[76;2u -a-- l \E[108;3u \E[108;3u sa-- L \E[76;4u \E[76;4u --c- \f \f \E[108;5u s-c- \f \f \E[76;6u -ac- \f \E[108;7u \E[108;7u sac- \f \E[76;8u \E[76;8u 0x006d XK_m 109 -(skip)- --**--** -******* ---- m m m s--- M M \E[77;2u -a-- m \E[109;3u \E[109;3u sa-- M \E[77;4u \E[77;4u --c- \r \r \E[109;5u s-c- \r \r \E[77;6u -ac- \r \E[109;7u \E[109;7u sac- \r \E[77;8u \E[77;8u 0x006e XK_n 110 -(skip)- --**--** -******* ---- n n n s--- N N \E[78;2u -a-- n \E[110;3u \E[110;3u sa-- N \E[78;4u \E[78;4u --c- ^N ^N \E[110;5u s-c- ^N ^N \E[78;6u -ac- ^N \E[110;7u \E[110;7u sac- ^N \E[78;8u \E[78;8u 0x006f XK_o 111 -(skip)- --**--** -******* ---- o o o s--- O O \E[79;2u -a-- o \E[111;3u \E[111;3u sa-- O \E[79;4u \E[79;4u --c- ^O ^O \E[111;5u s-c- ^O ^O \E[79;6u -ac- ^O \E[111;7u \E[111;7u sac- ^O \E[79;8u \E[79;8u 0x0070 XK_p 112 -(skip)- --**--** -******* ---- p p p s--- P P \E[80;2u -a-- p \E[112;3u \E[112;3u sa-- P \E[80;4u \E[80;4u --c- ^P ^P \E[112;5u s-c- ^P ^P \E[80;6u -ac- ^P \E[112;7u \E[112;7u sac- ^P \E[80;8u \E[80;8u 0x0071 XK_q 113 -(skip)- --**--** -******* ---- q q q s--- Q Q \E[81;2u -a-- q \E[113;3u \E[113;3u sa-- Q \E[81;4u \E[81;4u --c- ^Q ^Q \E[113;5u s-c- ^Q ^Q \E[81;6u -ac- ^Q \E[113;7u \E[113;7u sac- ^Q \E[81;8u \E[81;8u 0x0072 XK_r 114 -(skip)- --**--** -******* ---- r r r s--- R R \E[82;2u -a-- r \E[114;3u \E[114;3u sa-- R \E[82;4u \E[82;4u --c- ^R ^R \E[114;5u s-c- ^R ^R \E[82;6u -ac- ^R \E[114;7u \E[114;7u sac- ^R \E[82;8u \E[82;8u 0x0073 XK_s 115 -(skip)- --**--** -******* ---- s s s s--- S S \E[83;2u -a-- s \E[115;3u \E[115;3u sa-- S \E[83;4u \E[83;4u --c- ^S ^S \E[115;5u s-c- ^S ^S \E[83;6u -ac- ^S \E[115;7u \E[115;7u sac- ^S \E[83;8u \E[83;8u 0x0074 XK_t 116 -(skip)- --**--** -******* ---- t t t s--- T T \E[84;2u -a-- t \E[116;3u \E[116;3u sa-- T \E[84;4u \E[84;4u --c- ^T ^T \E[116;5u s-c- ^T ^T \E[84;6u -ac- ^T \E[116;7u \E[116;7u sac- ^T \E[84;8u \E[84;8u 0x0075 XK_u 117 -(skip)- --**--** -******* ---- u u u s--- U U \E[85;2u -a-- u \E[117;3u \E[117;3u sa-- U \E[85;4u \E[85;4u --c- ^U ^U \E[117;5u s-c- ^U ^U \E[85;6u -ac- ^U \E[117;7u \E[117;7u sac- ^U \E[85;8u \E[85;8u 0x0076 XK_v 118 -(skip)- --**--** -******* ---- v v v s--- V V \E[86;2u -a-- v \E[118;3u \E[118;3u sa-- V \E[86;4u \E[86;4u --c- ^V ^V \E[118;5u s-c- ^V ^V \E[86;6u -ac- ^V \E[118;7u \E[118;7u sac- ^V \E[86;8u \E[86;8u 0x0077 XK_w 119 -(skip)- --**--** -******* ---- w w w s--- W W \E[87;2u -a-- w \E[119;3u \E[119;3u sa-- W \E[87;4u \E[87;4u --c- ^W ^W \E[119;5u s-c- ^W ^W \E[87;6u -ac- ^W \E[119;7u \E[119;7u sac- ^W \E[87;8u \E[87;8u 0x0078 XK_x 120 -(skip)- --**--** -******* ---- x x x s--- X X \E[88;2u -a-- x \E[120;3u \E[120;3u sa-- X \E[88;4u \E[88;4u --c- ^X ^X \E[120;5u s-c- ^X ^X \E[88;6u -ac- ^X \E[120;7u \E[120;7u sac- ^X \E[88;8u \E[88;8u 0x0079 XK_y 121 -(skip)- --**--** -******* ---- y y y s--- Y Y \E[89;2u -a-- y \E[121;3u \E[121;3u sa-- Y \E[89;4u \E[89;4u --c- ^Y ^Y \E[121;5u s-c- ^Y ^Y \E[89;6u -ac- ^Y \E[121;7u \E[121;7u sac- ^Y \E[89;8u \E[89;8u 0x007a XK_z 122 -(skip)- --**--** -******* ---- z z z s--- Z Z \E[90;2u -a-- z \E[122;3u \E[122;3u sa-- Z \E[90;4u \E[90;4u --c- ^Z ^Z \E[122;5u s-c- ^Z ^Z \E[90;6u -ac- ^Z \E[122;7u \E[122;7u sac- ^Z \E[90;8u \E[90;8u 0x007b XK_braceleft 123 -(skip)- --**--** -******* ---- { { { s--- { { \E[123;2u -a-- { \E[123;3u \E[123;3u sa-- { \E[123;4u \E[123;4u --c- \E \E \E[123;5u s-c- \E \E \E[123;6u -ac- \E \E[123;7u \E[123;7u sac- \E \E[123;8u \E[123;8u 0x007c XK_bar 124 -(skip)- --**--** -******* ---- | | | s--- | | \E[124;2u -a-- | \E[124;3u \E[124;3u sa-- | \E[124;4u \E[124;4u --c- ^\ ^\ \E[124;5u s-c- ^\ ^\ \E[124;6u -ac- ^\ \E[124;7u \E[124;7u sac- ^\ \E[124;8u \E[124;8u 0x007d XK_braceright 125 -(skip)- --**--** -******* ---- } } } s--- } } \E[125;2u -a-- } \E[125;3u \E[125;3u sa-- } \E[125;4u \E[125;4u --c- ^] ^] \E[125;5u s-c- ^] ^] \E[125;6u -ac- ^] \E[125;7u \E[125;7u sac- ^] \E[125;8u \E[125;8u 0x007e XK_asciitilde 126 -(skip)- --**--** -******* ---- ~ ~ ~ s--- ~ ~ \E[126;2u -a-- ~ \E[126;3u \E[126;3u sa-- ~ \E[126;4u \E[126;4u --c- ^^ ^^ \E[126;5u s-c- ^^ ^^ \E[126;6u -ac- ^^ \E[126;7u \E[126;7u sac- ^^ \E[126;8u \E[126;8u 0x00a6 XK_brokenbar 166 -(skip)- --****** --****** ---- ¦ ¦ ¦ s--- ¦ ¦ ¦ -a-- ¦ \E[166;3u \E[166;3u sa-- ¦ \E[166;4u \E[166;4u --c- ¦ \E[166;5u \E[166;5u s-c- ¦ \E[166;6u \E[166;6u -ac- ¦ \E[166;7u \E[166;7u sac- ¦ \E[166;8u \E[166;8u 0x00b1 XK_plusminus 177 -(skip)- --****** --****** ---- ± ± ± s--- ± ± ± -a-- ± \E[177;3u \E[177;3u sa-- ± \E[177;4u \E[177;4u --c- ± \E[177;5u \E[177;5u s-c- ± \E[177;6u \E[177;6u -ac- ± \E[177;7u \E[177;7u sac- ± \E[177;8u \E[177;8u 0xfe03 XK_ISO_Level3_Shift 65027 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xfe11 XK_ISO_Level5_Shift 65041 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xfe20 XK_ISO_Left_Tab 65056 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key 0xff08 XK_BackSpace 65288 -(skip)- -(skip)- -***-*** ---- \b \b \b s--- \b \b \E[8;2u -a-- \b \b \E[8;3u sa-- \b \b \E[8;4u --c- \b \b \b s-c- \b \b \E[8;6u -ac- \b \b \E[8;7u sac- \b \b \E[8;8u 0xff09 XK_Tab 65289 -(skip)- -******* -******* ---- \t \t \t s--- \t \E[9;2u \E[9;2u -a-- \t \E[9;3u \E[9;3u sa-- \t \E[9;4u \E[9;4u --c- \t \E[9;5u \E[9;5u s-c- \t \E[9;6u \E[9;6u -ac- \t \E[9;7u \E[9;7u sac- \t \E[9;8u \E[9;8u 0xff0a XK_Linefeed 65290 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff0d XK_Return 65293 -(skip)- -******* -******* ---- \r \r \r s--- \r \E[13;2u \E[13;2u -a-- \r \E[13;3u \E[13;3u sa-- \r \E[13;4u \E[13;4u --c- \r \E[13;5u \E[13;5u s-c- \r \E[13;6u \E[13;6u -ac- \r \E[13;7u \E[13;7u sac- \r \E[13;8u \E[13;8u 0xff13 XK_Pause 65299 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff14 XK_Scroll_Lock 65300 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff15 XK_Sys_Req 65301 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff1b XK_Escape 65307 -(skip)- --**--** -******* ---- \E \E \E s--- \E \E \E[27;2u -a-- \E \E[27;3u \E[27;3u sa-- \E \E[27;4u \E[27;4u --c- \E \E \E[27;5u s-c- \E \E \E[27;6u -ac- \E \E[27;7u \E[27;7u sac- \E \E[27;8u \E[27;8u 0xff22 XK_Muhenkan 65314 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff23 XK_Henkan_Mode 65315 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff25 XK_Hiragana 65317 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff26 XK_Katakana 65318 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff27 XK_Hiragana_Katakana 65319 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff31 XK_Hangul 65329 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff34 XK_Hangul_Hanja 65332 -(skip)- -******* --****** ---- -ignore- -ignore- -ignore- s--- -ignore- -ignore- -ignore- -a-- -ignore- -ignore- -ignore- sa-- -ignore- -ignore- -ignore- --c- -ignore- -ignore- -ignore- s-c- -ignore- -ignore- -ignore- -ac- -ignore- -ignore- -ignore- sac- -ignore- -ignore- -ignore- 0xff50 XK_Home 65360 -(skip)- -(skip)- -(skip)- ---- 1:cursor 1:cursor 1:cursor s--- 2:cursor 2:cursor 2:cursor -a-- 3:cursor 3:cursor 3:cursor sa-- 4:cursor 4:cursor 4:cursor --c- 5:cursor 5:cursor 5:cursor s-c- 6:cursor 6:cursor 6:cursor -ac- 7:cursor 7:cursor 7:cursor sac- 8:cursor 8:cursor 8:cursor 0xff51 XK_Left 65361 -(skip)- -(skip)- -(skip)- ---- 1:cursor 1:cursor 1:cursor s--- 2:cursor 2:cursor 2:cursor -a-- 3:cursor 3:cursor 3:cursor sa-- 4:cursor 4:cursor 4:cursor --c- 5:cursor 5:cursor 5:cursor s-c- 6:cursor 6:cursor 6:cursor -ac- 7:cursor 7:cursor 7:cursor sac- 8:cursor 8:cursor 8:cursor 0xff52 XK_Up 65362 -(skip)- -(skip)- -(skip)- ---- 1:cursor 1:cursor 1:cursor s--- 2:cursor 2:cursor 2:cursor -a-- 3:cursor 3:cursor 3:cursor sa-- 4:cursor 4:cursor 4:cursor --c- 5:cursor 5:cursor 5:cursor s-c- 6:cursor 6:cursor 6:cursor -ac- 7:cursor 7:cursor 7:cursor sac- 8:cursor 8:cursor 8:cursor 0xff53 XK_Right 65363 -(skip)- -(skip)- -(skip)- ---- 1:cursor 1:cursor 1:cursor s--- 2:cursor 2:cursor 2:cursor -a-- 3:cursor 3:cursor 3:cursor sa-- 4:cursor 4:cursor 4:cursor --c- 5:cursor 5:cursor 5:cursor s-c- 6:cursor 6:cursor 6:cursor -ac- 7:cursor 7:cursor 7:cursor sac- 8:cursor 8:cursor 8:cursor 0xff54 XK_Down 65364 -(skip)- -(skip)- -(skip)- ---- 1:cursor 1:cursor 1:cursor s--- 2:cursor 2:cursor 2:cursor -a-- 3:cursor 3:cursor 3:cursor sa-- 4:cursor 4:cursor 4:cursor --c- 5:cursor 5:cursor 5:cursor s-c- 6:cursor 6:cursor 6:cursor -ac- 7:cursor 7:cursor 7:cursor sac- 8:cursor 8:cursor 8:cursor 0xff55 XK_Prior 65365 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key 0xff56 XK_Next 65366 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key 0xff57 XK_End 65367 -(skip)- -(skip)- -(skip)- ---- 1:cursor 1:cursor 1:cursor s--- 2:cursor 2:cursor 2:cursor -a-- 3:cursor 3:cursor 3:cursor sa-- 4:cursor 4:cursor 4:cursor --c- 5:cursor 5:cursor 5:cursor s-c- 6:cursor 6:cursor 6:cursor -ac- 7:cursor 7:cursor 7:cursor sac- 8:cursor 8:cursor 8:cursor 0xff61 XK_Print 65377 -(skip)- -(skip)- -(skip)- ---- 1:misc-key 1:misc-key 1:misc-key s--- 2:misc-key 2:misc-key 2:misc-key -a-- 3:misc-key 3:misc-key 3:misc-key sa-- 4:misc-key 4:misc-key 4:misc-key --c- 5:misc-key 5:misc-key 5:misc-key s-c- 6:misc-key 6:misc-key 6:misc-key -ac- 7:misc-key 7:misc-key 7:misc-key sac- 8:misc-key 8:misc-key 8:misc-key 0xff63 XK_Insert 65379 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key 0xff65 XK_Undo 65381 -(skip)- -(skip)- -(skip)- ---- 1:misc-key 1:misc-key 1:misc-key s--- 2:misc-key 2:misc-key 2:misc-key -a-- 3:misc-key 3:misc-key 3:misc-key sa-- 4:misc-key 4:misc-key 4:misc-key --c- 5:misc-key 5:misc-key 5:misc-key s-c- 6:misc-key 6:misc-key 6:misc-key -ac- 7:misc-key 7:misc-key 7:misc-key sac- 8:misc-key 8:misc-key 8:misc-key 0xff66 XK_Redo 65382 -(skip)- -(skip)- -(skip)- ---- 1:misc-key 1:misc-key 1:misc-key s--- 2:misc-key 2:misc-key 2:misc-key -a-- 3:misc-key 3:misc-key 3:misc-key sa-- 4:misc-key 4:misc-key 4:misc-key --c- 5:misc-key 5:misc-key 5:misc-key s-c- 6:misc-key 6:misc-key 6:misc-key -ac- 7:misc-key 7:misc-key 7:misc-key sac- 8:misc-key 8:misc-key 8:misc-key 0xff67 XK_Menu 65383 -(skip)- -(skip)- -(skip)- ---- 1:misc-key 1:misc-key 1:misc-key s--- 2:misc-key 2:misc-key 2:misc-key -a-- 3:misc-key 3:misc-key 3:misc-key sa-- 4:misc-key 4:misc-key 4:misc-key --c- 5:misc-key 5:misc-key 5:misc-key s-c- 6:misc-key 6:misc-key 6:misc-key -ac- 7:misc-key 7:misc-key 7:misc-key sac- 8:misc-key 8:misc-key 8:misc-key 0xff68 XK_Find 65384 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key 0xff69 XK_Cancel 65385 -(skip)- -(skip)- -(skip)- ---- 1:misc-key 1:misc-key 1:misc-key s--- 2:misc-key 2:misc-key 2:misc-key -a-- 3:misc-key 3:misc-key 3:misc-key sa-- 4:misc-key 4:misc-key 4:misc-key --c- 5:misc-key 5:misc-key 5:misc-key s-c- 6:misc-key 6:misc-key 6:misc-key -ac- 7:misc-key 7:misc-key 7:misc-key sac- 8:misc-key 8:misc-key 8:misc-key 0xff6a XK_Help 65386 -(skip)- -(skip)- -(skip)- ---- 1:misc-key 1:misc-key 1:misc-key s--- 2:misc-key 2:misc-key 2:misc-key -a-- 3:misc-key 3:misc-key 3:misc-key sa-- 4:misc-key 4:misc-key 4:misc-key --c- 5:misc-key 5:misc-key 5:misc-key s-c- 6:misc-key 6:misc-key 6:misc-key -ac- 7:misc-key 7:misc-key 7:misc-key sac- 8:misc-key 8:misc-key 8:misc-key 0xff6b XK_Break 65387 -(skip)- -(skip)- -(skip)- ---- 1:misc-key 1:misc-key 1:misc-key s--- 2:misc-key 2:misc-key 2:misc-key -a-- 3:misc-key 3:misc-key 3:misc-key sa-- 4:misc-key 4:misc-key 4:misc-key --c- 5:misc-key 5:misc-key 5:misc-key s-c- 6:misc-key 6:misc-key 6:misc-key -ac- 7:misc-key 7:misc-key 7:misc-key sac- 8:misc-key 8:misc-key 8:misc-key 0xff7f XK_Num_Lock 65407 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xff8d XK_KP_Enter 65421 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff95 XK_KP_Home 65429 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff96 XK_KP_Left 65430 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff97 XK_KP_Up 65431 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff98 XK_KP_Right 65432 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff99 XK_KP_Down 65433 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff9a XK_KP_Prior 65434 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff9b XK_KP_Next 65435 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff9c XK_KP_End 65436 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff9d XK_KP_Begin 65437 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xff9e XK_KP_Insert 65438 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key 0xff9f XK_KP_Delete 65439 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key 0xffaa XK_KP_Multiply 65450 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffab XK_KP_Add 65451 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffad XK_KP_Subtract 65453 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffae XK_KP_Decimal 65454 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffaf XK_KP_Divide 65455 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb0 XK_KP_0 65456 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb1 XK_KP_1 65457 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb2 XK_KP_2 65458 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb3 XK_KP_3 65459 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb4 XK_KP_4 65460 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb5 XK_KP_5 65461 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb6 XK_KP_6 65462 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb7 XK_KP_7 65463 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb8 XK_KP_8 65464 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffb9 XK_KP_9 65465 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffbd XK_KP_Equal 65469 -(skip)- -(skip)- -(skip)- ---- 1:keypad 1:keypad 1:keypad s--- 2:keypad 2:keypad 2:keypad -a-- 3:keypad 3:keypad 3:keypad sa-- 4:keypad 4:keypad 4:keypad --c- 5:keypad 5:keypad 5:keypad s-c- 6:keypad 6:keypad 6:keypad -ac- 7:keypad 7:keypad 7:keypad sac- 8:keypad 8:keypad 8:keypad 0xffbe XK_F1 65470 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffbf XK_F2 65471 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc0 XK_F3 65472 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc1 XK_F4 65473 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc2 XK_F5 65474 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc3 XK_F6 65475 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc4 XK_F7 65476 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc5 XK_F8 65477 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc6 XK_F9 65478 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc7 XK_F10 65479 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc8 XK_F11 65480 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffc9 XK_F12 65481 -(skip)- -(skip)- -(skip)- ---- 1:func-key 1:func-key 1:func-key s--- 2:func-key 2:func-key 2:func-key -a-- 3:func-key 3:func-key 3:func-key sa-- 4:func-key 4:func-key 4:func-key --c- 5:func-key 5:func-key 5:func-key s-c- 6:func-key 6:func-key 6:func-key -ac- 7:func-key 7:func-key 7:func-key sac- 8:func-key 8:func-key 8:func-key 0xffe1 XK_Shift_L 65505 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffe2 XK_Shift_R 65506 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffe3 XK_Control_L 65507 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffe4 XK_Control_R 65508 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffe5 XK_Caps_Lock 65509 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffe7 XK_Meta_L 65511 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffe9 XK_Alt_L 65513 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffeb XK_Super_L 65515 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffec XK_Super_R 65516 -(skip)- -(skip)- -(skip)- ---- 1:modifier 1:modifier 1:modifier s--- 2:modifier 2:modifier 2:modifier -a-- 3:modifier 3:modifier 3:modifier sa-- 4:modifier 4:modifier 4:modifier --c- 5:modifier 5:modifier 5:modifier s-c- 6:modifier 6:modifier 6:modifier -ac- 7:modifier 7:modifier 7:modifier sac- 8:modifier 8:modifier 8:modifier 0xffff XK_Delete 65535 -(skip)- -(skip)- -(skip)- ---- 1:edit-key 1:edit-key 1:edit-key s--- 2:edit-key 2:edit-key 2:edit-key -a-- 3:edit-key 3:edit-key 3:edit-key sa-- 4:edit-key 4:edit-key 4:edit-key --c- 5:edit-key 5:edit-key 5:edit-key s-c- 6:edit-key 6:edit-key 6:edit-key -ac- 7:edit-key 7:edit-key 7:edit-key sac- 8:edit-key 8:edit-key 8:edit-key vte-0.15.0/examples/parselog.rs000064400000000000000000000035361046102023000145140ustar 00000000000000//! Parse input from stdin and log actions on stdout use std::io::{self, Read}; use vte::{Params, Parser, Perform}; /// A type implementing Perform that just logs actions struct Log; impl Perform for Log { fn print(&mut self, c: char) { println!("[print] {:?}", c); } fn execute(&mut self, byte: u8) { println!("[execute] {:02x}", byte); } fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { println!( "[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}", params, intermediates, ignore, c ); } fn put(&mut self, byte: u8) { println!("[put] {:02x}", byte); } fn unhook(&mut self) { println!("[unhook]"); } fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { println!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated); } fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { println!( "[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}", params, intermediates, ignore, c ); } fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { println!( "[esc_dispatch] intermediates={:?}, ignore={:?}, byte={:02x}", intermediates, ignore, byte ); } } fn main() { let input = io::stdin(); let mut handle = input.lock(); let mut statemachine = Parser::new(); let mut performer = Log; let mut buf = [0; 2048]; loop { match handle.read(&mut buf) { Ok(0) => break, Ok(n) => statemachine.advance(&mut performer, &buf[..n]), Err(err) => { println!("err: {}", err); break; }, } } } vte-0.15.0/rustfmt.toml000064400000000000000000000007541046102023000131140ustar 00000000000000format_code_in_doc_comments = true group_imports = "StdExternalCrate" match_block_trailing_comma = true condense_wildcard_suffixes = true use_field_init_shorthand = true normalize_doc_attributes = true overflow_delimited_expr = true imports_granularity = "Module" format_macro_matchers = true use_small_heuristics = "Max" hex_literal_case = "Upper" normalize_comments = true reorder_impl_items = true use_try_shorthand = true newline_style = "Unix" format_strings = true wrap_comments = true vte-0.15.0/src/ansi.rs000064400000000000000000002434161046102023000126060ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 // // This module was originally part of the `alacritty_terminal` crate, which is // licensed under the Apache License, Version 2.0 and is part of the Alacritty // project (https://github.com/alacritty/alacritty). //! ANSI Terminal Stream Parsing. extern crate alloc; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::convert::TryFrom; use core::fmt::{self, Display, Formatter, Write}; #[cfg(feature = "std")] use core::ops::Mul; use core::ops::{Add, Sub}; use core::str::FromStr; use core::time::Duration; use core::{iter, mem, str}; #[cfg(feature = "std")] use std::time::Instant; use bitflags::bitflags; #[doc(inline)] pub use cursor_icon; use cursor_icon::CursorIcon; use log::debug; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{Params, ParamsIter}; /// Maximum time before a synchronized update is aborted. const SYNC_UPDATE_TIMEOUT: Duration = Duration::from_millis(150); /// Maximum number of bytes read in one synchronized update (2MiB). const SYNC_BUFFER_SIZE: usize = 0x20_0000; /// Number of bytes in the BSU/ESU CSI sequences. const SYNC_ESCAPE_LEN: usize = 8; /// BSU CSI sequence for beginning or extending synchronized updates. const BSU_CSI: [u8; SYNC_ESCAPE_LEN] = *b"\x1b[?2026h"; /// ESU CSI sequence for terminating synchronized updates. const ESU_CSI: [u8; SYNC_ESCAPE_LEN] = *b"\x1b[?2026l"; #[derive(Debug, PartialEq, Eq, Hash)] pub struct Hyperlink { /// Identifier for the given hyperlink. pub id: Option, /// Resource identifier of the hyperlink. pub uri: String, } #[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Rgb { pub r: u8, pub g: u8, pub b: u8, } impl Rgb { /// Implementation of [W3C's luminance algorithm]. /// /// [W3C's luminance algorithm]: https://www.w3.org/TR/WCAG20/#relativeluminancedef #[cfg(feature = "std")] pub fn luminance(self) -> f64 { let channel_luminance = |channel| { let channel = channel as f64 / 255.; if channel <= 0.03928 { channel / 12.92 } else { f64::powf((channel + 0.055) / 1.055, 2.4) } }; let r_luminance = channel_luminance(self.r); let g_luminance = channel_luminance(self.g); let b_luminance = channel_luminance(self.b); 0.2126 * r_luminance + 0.7152 * g_luminance + 0.0722 * b_luminance } /// Implementation of [W3C's contrast algorithm]. /// /// [W3C's contrast algorithm]: https://www.w3.org/TR/WCAG20/#contrast-ratiodef #[cfg(feature = "std")] pub fn contrast(self, other: Rgb) -> f64 { let self_luminance = self.luminance(); let other_luminance = other.luminance(); let (darker, lighter) = if self_luminance > other_luminance { (other_luminance, self_luminance) } else { (self_luminance, other_luminance) }; (lighter + 0.05) / (darker + 0.05) } } // A multiply function for Rgb, as the default dim is just *2/3. #[cfg(feature = "std")] impl Mul for Rgb { type Output = Rgb; fn mul(self, rhs: f32) -> Rgb { let result = Rgb { r: (f32::from(self.r) * rhs).clamp(0.0, 255.0) as u8, g: (f32::from(self.g) * rhs).clamp(0.0, 255.0) as u8, b: (f32::from(self.b) * rhs).clamp(0.0, 255.0) as u8, }; log::trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result); result } } impl Add for Rgb { type Output = Rgb; fn add(self, rhs: Rgb) -> Rgb { Rgb { r: self.r.saturating_add(rhs.r), g: self.g.saturating_add(rhs.g), b: self.b.saturating_add(rhs.b), } } } impl Sub for Rgb { type Output = Rgb; fn sub(self, rhs: Rgb) -> Rgb { Rgb { r: self.r.saturating_sub(rhs.r), g: self.g.saturating_sub(rhs.g), b: self.b.saturating_sub(rhs.b), } } } impl Display for Rgb { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b) } } impl FromStr for Rgb { type Err = (); fn from_str(s: &str) -> Result { let chars = if s.starts_with("0x") && s.len() == 8 { &s[2..] } else if s.starts_with('#') && s.len() == 7 { &s[1..] } else { return Err(()); }; match u32::from_str_radix(chars, 16) { Ok(mut color) => { let b = (color & 0xFF) as u8; color >>= 8; let g = (color & 0xFF) as u8; color >>= 8; let r = color as u8; Ok(Rgb { r, g, b }) }, Err(_) => Err(()), } } } /// Parse colors in XParseColor format. fn xparse_color(color: &[u8]) -> Option { if !color.is_empty() && color[0] == b'#' { parse_legacy_color(&color[1..]) } else if color.len() >= 4 && &color[..4] == b"rgb:" { parse_rgb_color(&color[4..]) } else { None } } /// Parse colors in `rgb:r(rrr)/g(ggg)/b(bbb)` format. fn parse_rgb_color(color: &[u8]) -> Option { let colors = str::from_utf8(color).ok()?.split('/').collect::>(); if colors.len() != 3 { return None; } // Scale values instead of filling with `0`s. let scale = |input: &str| { if input.len() > 4 { None } else { let max = u32::pow(16, input.len() as u32) - 1; let value = u32::from_str_radix(input, 16).ok()?; Some((255 * value / max) as u8) } }; Some(Rgb { r: scale(colors[0])?, g: scale(colors[1])?, b: scale(colors[2])? }) } /// Parse colors in `#r(rrr)g(ggg)b(bbb)` format. fn parse_legacy_color(color: &[u8]) -> Option { let item_len = color.len() / 3; // Truncate/Fill to two byte precision. let color_from_slice = |slice: &[u8]| { let col = usize::from_str_radix(str::from_utf8(slice).ok()?, 16).ok()? << 4; Some((col >> (4 * slice.len().saturating_sub(1))) as u8) }; Some(Rgb { r: color_from_slice(&color[0..item_len])?, g: color_from_slice(&color[item_len..item_len * 2])?, b: color_from_slice(&color[item_len * 2..])?, }) } fn parse_number(input: &[u8]) -> Option { if input.is_empty() { return None; } let mut num: u8 = 0; for c in input { let c = *c as char; let digit = c.to_digit(10)?; num = num.checked_mul(10).and_then(|v| v.checked_add(digit as u8))?; } Some(num) } /// Internal state for VTE processor. #[derive(Debug, Default)] struct ProcessorState { /// Last processed character for repetition. preceding_char: Option, /// State for synchronized terminal updates. sync_state: SyncState, } #[derive(Debug)] struct SyncState { /// Handler for synchronized updates. timeout: T, /// Bytes read during the synchronized update. buffer: Vec, } impl Default for SyncState { fn default() -> Self { Self { buffer: Vec::with_capacity(SYNC_BUFFER_SIZE), timeout: Default::default() } } } /// The processor wraps a `crate::Parser` to ultimately call methods on a /// Handler. #[cfg(feature = "std")] #[derive(Default)] pub struct Processor { state: ProcessorState, parser: crate::Parser, } /// The processor wraps a `crate::Parser` to ultimately call methods on a /// Handler. #[cfg(not(feature = "std"))] #[derive(Default)] pub struct Processor { state: ProcessorState, parser: crate::Parser, } impl Processor { #[inline] pub fn new() -> Self { Self::default() } /// Synchronized update timeout. pub fn sync_timeout(&self) -> &T { &self.state.sync_state.timeout } /// Process a new byte from the PTY. #[inline] pub fn advance(&mut self, handler: &mut H, bytes: &[u8]) where H: Handler, { let mut processed = 0; while processed != bytes.len() { if self.state.sync_state.timeout.pending_timeout() { processed += self.advance_sync(handler, &bytes[processed..]); } else { let mut performer = Performer::new(&mut self.state, handler); processed += self.parser.advance_until_terminated(&mut performer, &bytes[processed..]); } } } /// End a synchronized update. pub fn stop_sync(&mut self, handler: &mut H) where H: Handler, { self.stop_sync_internal(handler, None); } /// End a synchronized update. /// /// The `bsu_offset` parameter should be passed if the sync buffer contains /// a new BSU escape that is not part of the current synchronized /// update. fn stop_sync_internal(&mut self, handler: &mut H, bsu_offset: Option) where H: Handler, { // Process all synchronized bytes. // // NOTE: We do not use `advance_until_terminated` here since BSU sequences are // processed automatically during the synchronized update. let buffer = mem::take(&mut self.state.sync_state.buffer); let offset = bsu_offset.unwrap_or(buffer.len()); let mut performer = Performer::new(&mut self.state, handler); self.parser.advance(&mut performer, &buffer[..offset]); self.state.sync_state.buffer = buffer; match bsu_offset { // Just clear processed bytes if there is a new BSU. // // NOTE: We do not need to re-process for a new ESU since the `advance_sync` // function checks for BSUs in reverse. Some(bsu_offset) => { let new_len = self.state.sync_state.buffer.len() - bsu_offset; self.state.sync_state.buffer.copy_within(bsu_offset.., 0); self.state.sync_state.buffer.truncate(new_len); }, // Report mode and clear state if no new BSU is present. None => { handler.unset_private_mode(NamedPrivateMode::SyncUpdate.into()); self.state.sync_state.timeout.clear_timeout(); self.state.sync_state.buffer.clear(); }, } } /// Number of bytes in the synchronization buffer. #[inline] pub fn sync_bytes_count(&self) -> usize { self.state.sync_state.buffer.len() } /// Process a new byte during a synchronized update. /// /// Returns the number of bytes processed. #[cold] fn advance_sync(&mut self, handler: &mut H, bytes: &[u8]) -> usize where H: Handler, { // Advance sync parser or stop sync if we'd exceed the maximum buffer size. if self.state.sync_state.buffer.len() + bytes.len() >= SYNC_BUFFER_SIZE - 1 { // Terminate the synchronized update. self.stop_sync_internal(handler, None); // Just parse the bytes normally. let mut performer = Performer::new(&mut self.state, handler); self.parser.advance_until_terminated(&mut performer, bytes) } else { self.state.sync_state.buffer.extend(bytes); self.advance_sync_csi(handler, bytes.len()); bytes.len() } } /// Handle BSU/ESU CSI sequences during synchronized update. fn advance_sync_csi(&mut self, handler: &mut H, new_bytes: usize) where H: Handler, { // Get constraints within which a new escape character might be relevant. let buffer_len = self.state.sync_state.buffer.len(); let start_offset = (buffer_len - new_bytes).saturating_sub(SYNC_ESCAPE_LEN - 1); let end_offset = buffer_len.saturating_sub(SYNC_ESCAPE_LEN - 1); let search_buffer = &self.state.sync_state.buffer[start_offset..end_offset]; // Search for termination/extension escapes in the added bytes. // // NOTE: It is technically legal to specify multiple private modes in the same // escape, but we only allow EXACTLY `\e[?2026h`/`\e[?2026l` to keep the parser // more simple. let mut bsu_offset = None; for index in memchr::memchr_iter(0x1B, search_buffer).rev() { let offset = start_offset + index; let escape = &self.state.sync_state.buffer[offset..offset + SYNC_ESCAPE_LEN]; if escape == BSU_CSI { self.state.sync_state.timeout.set_timeout(SYNC_UPDATE_TIMEOUT); bsu_offset = Some(offset); } else if escape == ESU_CSI { self.stop_sync_internal(handler, bsu_offset); break; } } } } /// Helper type that implements `crate::Perform`. /// /// Processor creates a Performer when running advance and passes the Performer /// to `crate::Parser`. struct Performer<'a, H: Handler, T: Timeout> { state: &'a mut ProcessorState, handler: &'a mut H, /// Whether the parser should be prematurely terminated. terminated: bool, } impl<'a, H: Handler + 'a, T: Timeout> Performer<'a, H, T> { /// Create a performer. #[inline] pub fn new<'b>(state: &'b mut ProcessorState, handler: &'b mut H) -> Performer<'b, H, T> { Performer { state, handler, terminated: Default::default() } } } #[cfg(feature = "std")] #[derive(Default)] pub struct StdSyncHandler { timeout: Option, } #[cfg(feature = "std")] impl StdSyncHandler { /// Synchronized update expiration time. #[inline] pub fn sync_timeout(&self) -> Option { self.timeout } } #[cfg(feature = "std")] impl Timeout for StdSyncHandler { #[inline] fn set_timeout(&mut self, duration: Duration) { self.timeout = Some(Instant::now() + duration); } #[inline] fn clear_timeout(&mut self) { self.timeout = None; } #[inline] fn pending_timeout(&self) -> bool { self.timeout.is_some() } } /// Interface for creating timeouts and checking their expiry. /// /// This is internally used by the [`Processor`] to handle synchronized /// updates. pub trait Timeout: Default { /// Sets the timeout for the next synchronized update. /// /// The `duration` parameter specifies the duration of the timeout. Once the /// specified duration has elapsed, the synchronized update rotuine can be /// performed. fn set_timeout(&mut self, duration: Duration); /// Clear the current timeout. fn clear_timeout(&mut self); /// Returns whether a timeout is currently active and has not yet expired. fn pending_timeout(&self) -> bool; } /// Type that handles actions from the parser. /// /// XXX Should probably not provide default impls for everything, but it makes /// writing specific handler impls for tests far easier. pub trait Handler { /// OSC to set window title. fn set_title(&mut self, _: Option) {} /// Set the cursor style. fn set_cursor_style(&mut self, _: Option) {} /// Set the cursor shape. fn set_cursor_shape(&mut self, _shape: CursorShape) {} /// A character to be displayed. fn input(&mut self, _c: char) {} /// Set cursor to position. fn goto(&mut self, _line: i32, _col: usize) {} /// Set cursor to specific row. fn goto_line(&mut self, _line: i32) {} /// Set cursor to specific column. fn goto_col(&mut self, _col: usize) {} /// Insert blank characters in current line starting from cursor. fn insert_blank(&mut self, _: usize) {} /// Move cursor up `rows`. fn move_up(&mut self, _: usize) {} /// Move cursor down `rows`. fn move_down(&mut self, _: usize) {} /// Identify the terminal (should write back to the pty stream). fn identify_terminal(&mut self, _intermediate: Option) {} /// Report device status. fn device_status(&mut self, _: usize) {} /// Move cursor forward `cols`. fn move_forward(&mut self, _col: usize) {} /// Move cursor backward `cols`. fn move_backward(&mut self, _col: usize) {} /// Move cursor down `rows` and set to column 1. fn move_down_and_cr(&mut self, _row: usize) {} /// Move cursor up `rows` and set to column 1. fn move_up_and_cr(&mut self, _row: usize) {} /// Put `count` tabs. fn put_tab(&mut self, _count: u16) {} /// Backspace `count` characters. fn backspace(&mut self) {} /// Carriage return. fn carriage_return(&mut self) {} /// Linefeed. fn linefeed(&mut self) {} /// Ring the bell. /// /// Hopefully this is never implemented. fn bell(&mut self) {} /// Substitute char under cursor. fn substitute(&mut self) {} /// Newline. fn newline(&mut self) {} /// Set current position as a tabstop. fn set_horizontal_tabstop(&mut self) {} /// Scroll up `rows` rows. fn scroll_up(&mut self, _: usize) {} /// Scroll down `rows` rows. fn scroll_down(&mut self, _: usize) {} /// Insert `count` blank lines. fn insert_blank_lines(&mut self, _: usize) {} /// Delete `count` lines. fn delete_lines(&mut self, _: usize) {} /// Erase `count` chars in current line following cursor. /// /// Erase means resetting to the default state (default colors, no content, /// no mode flags). fn erase_chars(&mut self, _: usize) {} /// Delete `count` chars. /// /// Deleting a character is like the delete key on the keyboard - everything /// to the right of the deleted things is shifted left. fn delete_chars(&mut self, _: usize) {} /// Move backward `count` tabs. fn move_backward_tabs(&mut self, _count: u16) {} /// Move forward `count` tabs. fn move_forward_tabs(&mut self, _count: u16) {} /// Save current cursor position. fn save_cursor_position(&mut self) {} /// Restore cursor position. fn restore_cursor_position(&mut self) {} /// Clear current line. fn clear_line(&mut self, _mode: LineClearMode) {} /// Clear screen. fn clear_screen(&mut self, _mode: ClearMode) {} /// Clear tab stops. fn clear_tabs(&mut self, _mode: TabulationClearMode) {} /// Set tab stops at every `interval`. fn set_tabs(&mut self, _interval: u16) {} /// Reset terminal state. fn reset_state(&mut self) {} /// Reverse Index. /// /// Move the active position to the same horizontal position on the /// preceding line. If the active position is at the top margin, a scroll /// down is performed. fn reverse_index(&mut self) {} /// Set a terminal attribute. fn terminal_attribute(&mut self, _attr: Attr) {} /// Set mode. fn set_mode(&mut self, _mode: Mode) {} /// Unset mode. fn unset_mode(&mut self, _mode: Mode) {} /// DECRPM - report mode. fn report_mode(&mut self, _mode: Mode) {} /// Set private mode. fn set_private_mode(&mut self, _mode: PrivateMode) {} /// Unset private mode. fn unset_private_mode(&mut self, _mode: PrivateMode) {} /// DECRPM - report private mode. fn report_private_mode(&mut self, _mode: PrivateMode) {} /// DECSTBM - Set the terminal scrolling region. fn set_scrolling_region(&mut self, _top: usize, _bottom: Option) {} /// DECKPAM - Set keypad to applications mode (ESCape instead of digits). fn set_keypad_application_mode(&mut self) {} /// DECKPNM - Set keypad to numeric mode (digits instead of ESCape seq). fn unset_keypad_application_mode(&mut self) {} /// Set one of the graphic character sets, G0 to G3, as the active charset. /// /// 'Invoke' one of G0 to G3 in the GL area. Also referred to as shift in, /// shift out and locking shift depending on the set being activated. fn set_active_charset(&mut self, _: CharsetIndex) {} /// Assign a graphic character set to G0, G1, G2 or G3. /// /// 'Designate' a graphic character set as one of G0 to G3, so that it can /// later be 'invoked' by `set_active_charset`. fn configure_charset(&mut self, _: CharsetIndex, _: StandardCharset) {} /// Set an indexed color value. fn set_color(&mut self, _: usize, _: Rgb) {} /// Respond to a color query escape sequence. fn dynamic_color_sequence(&mut self, _: String, _: usize, _: &str) {} /// Reset an indexed color to original value. fn reset_color(&mut self, _: usize) {} /// Store data into clipboard. fn clipboard_store(&mut self, _: u8, _: &[u8]) {} /// Load data from clipboard. fn clipboard_load(&mut self, _: u8, _: &str) {} /// Run the decaln routine. fn decaln(&mut self) {} /// Push a title onto the stack. fn push_title(&mut self) {} /// Pop the last title from the stack. fn pop_title(&mut self) {} /// Report text area size in pixels. fn text_area_size_pixels(&mut self) {} /// Report text area size in characters. fn text_area_size_chars(&mut self) {} /// Set hyperlink. fn set_hyperlink(&mut self, _: Option) {} /// Set mouse cursor icon. fn set_mouse_cursor_icon(&mut self, _: CursorIcon) {} /// Report current keyboard mode. fn report_keyboard_mode(&mut self) {} /// Push keyboard mode into the keyboard mode stack. fn push_keyboard_mode(&mut self, _mode: KeyboardModes) {} /// Pop the given amount of keyboard modes from the /// keyboard mode stack. fn pop_keyboard_modes(&mut self, _to_pop: u16) {} /// Set the [`keyboard mode`] using the given [`behavior`]. /// /// [`keyboard mode`]: crate::ansi::KeyboardModes /// [`behavior`]: crate::ansi::KeyboardModesApplyBehavior fn set_keyboard_mode(&mut self, _mode: KeyboardModes, _behavior: KeyboardModesApplyBehavior) {} /// Set XTerm's [`ModifyOtherKeys`] option. fn set_modify_other_keys(&mut self, _mode: ModifyOtherKeys) {} /// Report XTerm's [`ModifyOtherKeys`] state. /// /// The output is of form `CSI > 4 ; mode m`. fn report_modify_other_keys(&mut self) {} // Set SCP control. fn set_scp(&mut self, _char_path: ScpCharPath, _update_mode: ScpUpdateMode) {} } bitflags! { /// A set of [`kitty keyboard protocol'] modes. /// /// [`kitty keyboard protocol']: https://sw.kovidgoyal.net/kitty/keyboard-protocol #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct KeyboardModes : u8 { /// No keyboard protocol mode is set. const NO_MODE = 0b0000_0000; /// Report `Esc`, `alt` + `key`, `ctrl` + `key`, `ctrl` + `alt` + `key`, `shift` /// + `alt` + `key` keys using `CSI u` sequence instead of raw ones. const DISAMBIGUATE_ESC_CODES = 0b0000_0001; /// Report key presses, release, and repetition alongside the escape. Key events /// that result in text are reported as plain UTF-8, unless the /// [`Self::REPORT_ALL_KEYS_AS_ESC`] is enabled. const REPORT_EVENT_TYPES = 0b0000_0010; /// Additionally report shifted key an dbase layout key. const REPORT_ALTERNATE_KEYS = 0b0000_0100; /// Report every key as an escape sequence. const REPORT_ALL_KEYS_AS_ESC = 0b0000_1000; /// Report the text generated by the key event. const REPORT_ASSOCIATED_TEXT = 0b0001_0000; } } /// XTMODKEYS modifyOtherKeys state. /// /// This only applies to keys corresponding to ascii characters. /// /// For the details on how to implement the mode handling correctly, consult /// [`XTerm's implementation`] and the [`output`] of XTerm's provided [`perl /// script`]. Some libraries and implementations also use the [`fixterms`] /// definition of the `CSI u`. /// /// The end escape sequence has a `CSI char; modifiers u` form while the /// original `CSI 27 ; modifier ; char ~`. The clients should prefer the `CSI /// u`, since it has more adoption. /// /// [`XTerm's implementation`]: https://invisible-island.net/xterm/modified-keys.html /// [`perl script`]: https://github.com/ThomasDickey/xterm-snapshots/blob/master/vttests/modify-keys.pl /// [`output`]: https://github.com/alacritty/vte/blob/master/doc/modifyOtherKeys-example.txt /// [`fixterms`]: http://www.leonerd.org.uk/hacks/fixterms/ #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ModifyOtherKeys { /// Reset the state. Reset, /// Enables this feature except for keys with well-known behavior, e.g., /// Tab, Backspace and some special control character cases which are /// built into the X11 library (e.g., Control-Space to make a NUL, or /// Control-3 to make an Escape character). /// /// Escape sequences shouldn't be emitted under the following circumstances: /// - When the key is in range of `[64;127]` and the modifier is either /// Control or Shift /// - When the key combination is a known control combination alias /// /// For more details, consult the [`example`] for the suggested translation. /// /// [`example`]: https://github.com/alacritty/vte/blob/master/doc/modifyOtherKeys-example.txt EnableExceptWellDefined, /// Enables this feature for all keys including the exceptions of /// [`Self::EnableExceptWellDefined`]. XTerm still ignores the special /// cases built into the X11 library. Any shifted (modified) ordinary /// key send an escape sequence. The Alt- and Meta- modifiers cause /// XTerm to send escape sequences. /// /// For more details, consult the [`example`] for the suggested translation. /// /// [`example`]: https://github.com/alacritty/vte/blob/master/doc/modifyOtherKeys-example.txt EnableAll, } /// Describes how the new [`KeyboardModes`] should be applied. #[repr(u8)] #[derive(Default, Clone, Copy, PartialEq, Eq)] pub enum KeyboardModesApplyBehavior { /// Replace the active flags with the new ones. #[default] Replace, /// Merge the given flags with currently active ones. Union, /// Remove the given flags from the active ones. Difference, } /// Terminal cursor configuration. #[derive(Default, Debug, Eq, PartialEq, Copy, Clone, Hash)] pub struct CursorStyle { pub shape: CursorShape, pub blinking: bool, } /// Terminal cursor shape. #[derive(Debug, Default, Eq, PartialEq, Copy, Clone, Hash)] pub enum CursorShape { /// Cursor is a block like `▒`. #[default] Block, /// Cursor is an underscore like `_`. Underline, /// Cursor is a vertical bar `⎸`. Beam, /// Cursor is a box like `☐`. HollowBlock, /// Invisible cursor. Hidden, } /// Wrapper for the ANSI modes. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Mode { /// Known ANSI mode. Named(NamedMode), /// Unidentified publc mode. Unknown(u16), } impl Mode { fn new(mode: u16) -> Self { match mode { 4 => Self::Named(NamedMode::Insert), 20 => Self::Named(NamedMode::LineFeedNewLine), _ => Self::Unknown(mode), } } /// Get the raw value of the mode. pub fn raw(self) -> u16 { match self { Self::Named(named) => named as u16, Self::Unknown(mode) => mode, } } } impl From for Mode { fn from(value: NamedMode) -> Self { Self::Named(value) } } /// ANSI modes. #[repr(u16)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum NamedMode { /// IRM Insert Mode. Insert = 4, LineFeedNewLine = 20, } /// Wrapper for the private DEC modes. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum PrivateMode { /// Known private mode. Named(NamedPrivateMode), /// Unknown private mode. Unknown(u16), } impl PrivateMode { fn new(mode: u16) -> Self { match mode { 1 => Self::Named(NamedPrivateMode::CursorKeys), 3 => Self::Named(NamedPrivateMode::ColumnMode), 6 => Self::Named(NamedPrivateMode::Origin), 7 => Self::Named(NamedPrivateMode::LineWrap), 12 => Self::Named(NamedPrivateMode::BlinkingCursor), 25 => Self::Named(NamedPrivateMode::ShowCursor), 1000 => Self::Named(NamedPrivateMode::ReportMouseClicks), 1002 => Self::Named(NamedPrivateMode::ReportCellMouseMotion), 1003 => Self::Named(NamedPrivateMode::ReportAllMouseMotion), 1004 => Self::Named(NamedPrivateMode::ReportFocusInOut), 1005 => Self::Named(NamedPrivateMode::Utf8Mouse), 1006 => Self::Named(NamedPrivateMode::SgrMouse), 1007 => Self::Named(NamedPrivateMode::AlternateScroll), 1042 => Self::Named(NamedPrivateMode::UrgencyHints), 1049 => Self::Named(NamedPrivateMode::SwapScreenAndSetRestoreCursor), 2004 => Self::Named(NamedPrivateMode::BracketedPaste), 2026 => Self::Named(NamedPrivateMode::SyncUpdate), _ => Self::Unknown(mode), } } /// Get the raw value of the mode. pub fn raw(self) -> u16 { match self { Self::Named(named) => named as u16, Self::Unknown(mode) => mode, } } } impl From for PrivateMode { fn from(value: NamedPrivateMode) -> Self { Self::Named(value) } } /// Private DEC modes. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum NamedPrivateMode { CursorKeys = 1, /// Select 80 or 132 columns per page (DECCOLM). /// /// CSI ? 3 h -> set 132 column font. /// CSI ? 3 l -> reset 80 column font. /// /// Additionally, /// /// * set margins to default positions /// * erases all data in page memory /// * resets DECLRMM to unavailable /// * clears data from the status line (if set to host-writable) ColumnMode = 3, Origin = 6, LineWrap = 7, BlinkingCursor = 12, ShowCursor = 25, ReportMouseClicks = 1000, ReportCellMouseMotion = 1002, ReportAllMouseMotion = 1003, ReportFocusInOut = 1004, Utf8Mouse = 1005, SgrMouse = 1006, AlternateScroll = 1007, UrgencyHints = 1042, SwapScreenAndSetRestoreCursor = 1049, BracketedPaste = 2004, /// The mode is handled automatically by [`Processor`]. SyncUpdate = 2026, } /// Mode for clearing line. /// /// Relative to cursor. #[derive(Debug)] pub enum LineClearMode { /// Clear right of cursor. Right, /// Clear left of cursor. Left, /// Clear entire line. All, } /// Mode for clearing terminal. /// /// Relative to cursor. #[derive(Debug)] pub enum ClearMode { /// Clear below cursor. Below, /// Clear above cursor. Above, /// Clear entire terminal. All, /// Clear 'saved' lines (scrollback). Saved, } /// Mode for clearing tab stops. #[derive(Debug)] pub enum TabulationClearMode { /// Clear stop under cursor. Current, /// Clear all stops. All, } /// Standard colors. /// /// The order here matters since the enum should be castable to a `usize` for /// indexing a color list. #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum NamedColor { /// Black. Black = 0, /// Red. Red, /// Green. Green, /// Yellow. Yellow, /// Blue. Blue, /// Magenta. Magenta, /// Cyan. Cyan, /// White. White, /// Bright black. BrightBlack, /// Bright red. BrightRed, /// Bright green. BrightGreen, /// Bright yellow. BrightYellow, /// Bright blue. BrightBlue, /// Bright magenta. BrightMagenta, /// Bright cyan. BrightCyan, /// Bright white. BrightWhite, /// The foreground color. Foreground = 256, /// The background color. Background, /// Color for the cursor itself. Cursor, /// Dim black. DimBlack, /// Dim red. DimRed, /// Dim green. DimGreen, /// Dim yellow. DimYellow, /// Dim blue. DimBlue, /// Dim magenta. DimMagenta, /// Dim cyan. DimCyan, /// Dim white. DimWhite, /// The bright foreground color. BrightForeground, /// Dim foreground. DimForeground, } impl NamedColor { #[must_use] pub fn to_bright(self) -> Self { match self { NamedColor::Foreground => NamedColor::BrightForeground, NamedColor::Black => NamedColor::BrightBlack, NamedColor::Red => NamedColor::BrightRed, NamedColor::Green => NamedColor::BrightGreen, NamedColor::Yellow => NamedColor::BrightYellow, NamedColor::Blue => NamedColor::BrightBlue, NamedColor::Magenta => NamedColor::BrightMagenta, NamedColor::Cyan => NamedColor::BrightCyan, NamedColor::White => NamedColor::BrightWhite, NamedColor::DimForeground => NamedColor::Foreground, NamedColor::DimBlack => NamedColor::Black, NamedColor::DimRed => NamedColor::Red, NamedColor::DimGreen => NamedColor::Green, NamedColor::DimYellow => NamedColor::Yellow, NamedColor::DimBlue => NamedColor::Blue, NamedColor::DimMagenta => NamedColor::Magenta, NamedColor::DimCyan => NamedColor::Cyan, NamedColor::DimWhite => NamedColor::White, val => val, } } #[must_use] pub fn to_dim(self) -> Self { match self { NamedColor::Black => NamedColor::DimBlack, NamedColor::Red => NamedColor::DimRed, NamedColor::Green => NamedColor::DimGreen, NamedColor::Yellow => NamedColor::DimYellow, NamedColor::Blue => NamedColor::DimBlue, NamedColor::Magenta => NamedColor::DimMagenta, NamedColor::Cyan => NamedColor::DimCyan, NamedColor::White => NamedColor::DimWhite, NamedColor::Foreground => NamedColor::DimForeground, NamedColor::BrightBlack => NamedColor::Black, NamedColor::BrightRed => NamedColor::Red, NamedColor::BrightGreen => NamedColor::Green, NamedColor::BrightYellow => NamedColor::Yellow, NamedColor::BrightBlue => NamedColor::Blue, NamedColor::BrightMagenta => NamedColor::Magenta, NamedColor::BrightCyan => NamedColor::Cyan, NamedColor::BrightWhite => NamedColor::White, NamedColor::BrightForeground => NamedColor::Foreground, val => val, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Color { Named(NamedColor), Spec(Rgb), Indexed(u8), } /// Terminal character attributes. #[derive(Debug, Eq, PartialEq)] pub enum Attr { /// Clear all special abilities. Reset, /// Bold text. Bold, /// Dim or secondary color. Dim, /// Italic text. Italic, /// Underline text. Underline, /// Underlined twice. DoubleUnderline, /// Undercurled text. Undercurl, /// Dotted underlined text. DottedUnderline, /// Dashed underlined text. DashedUnderline, /// Blink cursor slowly. BlinkSlow, /// Blink cursor fast. BlinkFast, /// Invert colors. Reverse, /// Do not display characters. Hidden, /// Strikeout text. Strike, /// Cancel bold. CancelBold, /// Cancel bold and dim. CancelBoldDim, /// Cancel italic. CancelItalic, /// Cancel all underlines. CancelUnderline, /// Cancel blink. CancelBlink, /// Cancel inversion. CancelReverse, /// Cancel text hiding. CancelHidden, /// Cancel strikeout. CancelStrike, /// Set indexed foreground color. Foreground(Color), /// Set indexed background color. Background(Color), /// Underline color. UnderlineColor(Option), } /// Identifiers which can be assigned to a graphic character set. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub enum CharsetIndex { /// Default set, is designated as ASCII at startup. #[default] G0, G1, G2, G3, } /// Standard or common character sets which can be designated as G0-G3. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub enum StandardCharset { #[default] Ascii, SpecialCharacterAndLineDrawing, } impl StandardCharset { /// Switch/Map character to the active charset. Ascii is the common case and /// for that we want to do as little as possible. #[inline] pub fn map(self, c: char) -> char { match self { StandardCharset::Ascii => c, StandardCharset::SpecialCharacterAndLineDrawing => match c { '_' => ' ', '`' => '◆', 'a' => '▒', 'b' => '\u{2409}', // Symbol for horizontal tabulation 'c' => '\u{240c}', // Symbol for form feed 'd' => '\u{240d}', // Symbol for carriage return 'e' => '\u{240a}', // Symbol for line feed 'f' => '°', 'g' => '±', 'h' => '\u{2424}', // Symbol for newline 'i' => '\u{240b}', // Symbol for vertical tabulation 'j' => '┘', 'k' => '┐', 'l' => '┌', 'm' => '└', 'n' => '┼', 'o' => '⎺', 'p' => '⎻', 'q' => '─', 'r' => '⎼', 's' => '⎽', 't' => '├', 'u' => '┤', 'v' => '┴', 'w' => '┬', 'x' => '│', 'y' => '≤', 'z' => '≥', '{' => 'π', '|' => '≠', '}' => '£', '~' => '·', _ => c, }, } } } /// SCP control's first parameter which determines character path. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ScpCharPath { /// SCP's first parameter value of 0. Behavior is implementation defined. Default, /// SCP's first parameter value of 1 which sets character path to /// LEFT-TO-RIGHT. LTR, /// SCP's first parameter value of 2 which sets character path to /// RIGHT-TO-LEFT. RTL, } /// SCP control's second parameter which determines update mode/direction /// between components. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ScpUpdateMode { /// SCP's second parameter value of 0 (the default). Implementation /// dependant update. ImplementationDependant, /// SCP's second parameter value of 1. /// /// Reflect data component changes in the presentation component. DataToPresentation, /// SCP's second parameter value of 2. /// /// Reflect presentation component changes in the data component. PresentationToData, } impl<'a, H, T> crate::Perform for Performer<'a, H, T> where H: Handler + 'a, T: Timeout, { #[inline] fn print(&mut self, c: char) { self.handler.input(c); self.state.preceding_char = Some(c); } #[inline] fn execute(&mut self, byte: u8) { match byte { C0::HT => self.handler.put_tab(1), C0::BS => self.handler.backspace(), C0::CR => self.handler.carriage_return(), C0::LF | C0::VT | C0::FF => self.handler.linefeed(), C0::BEL => self.handler.bell(), C0::SUB => self.handler.substitute(), C0::SI => self.handler.set_active_charset(CharsetIndex::G0), C0::SO => self.handler.set_active_charset(CharsetIndex::G1), _ => debug!("[unhandled] execute byte={:02x}", byte), } } #[inline] fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, action: char) { debug!( "[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}, action: {:?}", params, intermediates, ignore, action ); } #[inline] fn put(&mut self, byte: u8) { debug!("[unhandled put] byte={:?}", byte); } #[inline] fn unhook(&mut self) { debug!("[unhandled unhook]"); } #[inline] fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { let terminator = if bell_terminated { "\x07" } else { "\x1b\\" }; fn unhandled(params: &[&[u8]]) { let mut buf = String::new(); for items in params { buf.push('['); for item in *items { let _ = write!(buf, "{:?}", *item as char); } buf.push_str("],"); } debug!("[unhandled osc_dispatch]: [{}] at line {}", &buf, line!()); } if params.is_empty() || params[0].is_empty() { return; } match params[0] { // Set window title. b"0" | b"2" => { if params.len() >= 2 { let title = params[1..] .iter() .flat_map(|x| str::from_utf8(x)) .collect::>() .join(";") .trim() .to_owned(); self.handler.set_title(Some(title)); return; } unhandled(params); }, // Set color index. b"4" => { if params.len() <= 1 || params.len() % 2 == 0 { unhandled(params); return; } for chunk in params[1..].chunks(2) { let index = match parse_number(chunk[0]) { Some(index) => index, None => { unhandled(params); continue; }, }; if let Some(c) = xparse_color(chunk[1]) { self.handler.set_color(index as usize, c); } else if chunk[1] == b"?" { let prefix = alloc::format!("4;{index}"); self.handler.dynamic_color_sequence(prefix, index as usize, terminator); } else { unhandled(params); } } }, // Hyperlink. b"8" if params.len() > 2 => { let link_params = params[1]; // NOTE: The escape sequence is of form 'OSC 8 ; params ; URI ST', where // URI is URL-encoded. However `;` is a special character and might be // passed as is, thus we need to rebuild the URI. let mut uri = str::from_utf8(params[2]).unwrap_or_default().to_string(); for param in params[3..].iter() { uri.push(';'); uri.push_str(str::from_utf8(param).unwrap_or_default()); } // The OSC 8 escape sequence must be stopped when getting an empty `uri`. if uri.is_empty() { self.handler.set_hyperlink(None); return; } // Link parameters are in format of `key1=value1:key2=value2`. Currently only // key `id` is defined. let id = link_params .split(|&b| b == b':') .find_map(|kv| kv.strip_prefix(b"id=")) .and_then(|kv| str::from_utf8(kv).ok().map(|e| e.to_owned())); self.handler.set_hyperlink(Some(Hyperlink { id, uri })); }, // Get/set Foreground, Background, Cursor colors. b"10" | b"11" | b"12" => { if params.len() >= 2 { if let Some(mut dynamic_code) = parse_number(params[0]) { for param in ¶ms[1..] { // 10 is the first dynamic color, also the foreground. let offset = dynamic_code as usize - 10; let index = NamedColor::Foreground as usize + offset; // End of setting dynamic colors. if index > NamedColor::Cursor as usize { unhandled(params); break; } if let Some(color) = xparse_color(param) { self.handler.set_color(index, color); } else if param == b"?" { self.handler.dynamic_color_sequence( dynamic_code.to_string(), index, terminator, ); } else { unhandled(params); } dynamic_code += 1; } return; } } unhandled(params); }, // Set mouse cursor shape. b"22" if params.len() == 2 => { let shape = String::from_utf8_lossy(params[1]); match CursorIcon::from_str(&shape) { Ok(cursor_icon) => self.handler.set_mouse_cursor_icon(cursor_icon), Err(_) => debug!("[osc 22] unrecognized cursor icon shape: {shape:?}"), } }, // Set cursor style. b"50" => { if params.len() >= 2 && params[1].len() >= 13 && params[1][0..12] == *b"CursorShape=" { let shape = match params[1][12] as char { '0' => CursorShape::Block, '1' => CursorShape::Beam, '2' => CursorShape::Underline, _ => return unhandled(params), }; self.handler.set_cursor_shape(shape); return; } unhandled(params); }, // Set clipboard. b"52" => { if params.len() < 3 { return unhandled(params); } let clipboard = params[1].first().unwrap_or(&b'c'); match params[2] { b"?" => self.handler.clipboard_load(*clipboard, terminator), base64 => self.handler.clipboard_store(*clipboard, base64), } }, // Reset color index. b"104" => { // Reset all color indexes when no parameters are given. if params.len() == 1 || params[1].is_empty() { for i in 0..256 { self.handler.reset_color(i); } return; } // Reset color indexes given as parameters. for param in ¶ms[1..] { match parse_number(param) { Some(index) => self.handler.reset_color(index as usize), None => unhandled(params), } } }, // Reset foreground color. b"110" => self.handler.reset_color(NamedColor::Foreground as usize), // Reset background color. b"111" => self.handler.reset_color(NamedColor::Background as usize), // Reset text cursor color. b"112" => self.handler.reset_color(NamedColor::Cursor as usize), _ => unhandled(params), } } #[allow(clippy::cognitive_complexity)] #[inline] fn csi_dispatch( &mut self, params: &Params, intermediates: &[u8], has_ignored_intermediates: bool, action: char, ) { macro_rules! unhandled { () => {{ debug!( "[Unhandled CSI] action={:?}, params={:?}, intermediates={:?}", action, params, intermediates ); }}; } if has_ignored_intermediates || intermediates.len() > 2 { unhandled!(); return; } let mut params_iter = params.iter(); let handler = &mut self.handler; let mut next_param_or = |default: u16| match params_iter.next() { Some(&[param, ..]) if param != 0 => param, _ => default, }; match (action, intermediates) { ('@', []) => handler.insert_blank(next_param_or(1) as usize), ('A', []) => handler.move_up(next_param_or(1) as usize), ('B', []) | ('e', []) => handler.move_down(next_param_or(1) as usize), ('b', []) => { if let Some(c) = self.state.preceding_char { for _ in 0..next_param_or(1) { handler.input(c); } } else { debug!("tried to repeat with no preceding char"); } }, ('C', []) | ('a', []) => handler.move_forward(next_param_or(1) as usize), ('c', intermediates) if next_param_or(0) == 0 => { handler.identify_terminal(intermediates.first().map(|&i| i as char)) }, ('D', []) => handler.move_backward(next_param_or(1) as usize), ('d', []) => handler.goto_line(next_param_or(1) as i32 - 1), ('E', []) => handler.move_down_and_cr(next_param_or(1) as usize), ('F', []) => handler.move_up_and_cr(next_param_or(1) as usize), ('G', []) | ('`', []) => handler.goto_col(next_param_or(1) as usize - 1), ('W', [b'?']) if next_param_or(0) == 5 => handler.set_tabs(8), ('g', []) => { let mode = match next_param_or(0) { 0 => TabulationClearMode::Current, 3 => TabulationClearMode::All, _ => { unhandled!(); return; }, }; handler.clear_tabs(mode); }, ('H', []) | ('f', []) => { let y = next_param_or(1) as i32; let x = next_param_or(1) as usize; handler.goto(y - 1, x - 1); }, ('h', []) => { for param in params_iter.map(|param| param[0]) { handler.set_mode(Mode::new(param)) } }, ('h', [b'?']) => { for param in params_iter.map(|param| param[0]) { // Handle sync updates opaquely. if param == NamedPrivateMode::SyncUpdate as u16 { self.state.sync_state.timeout.set_timeout(SYNC_UPDATE_TIMEOUT); self.terminated = true; } handler.set_private_mode(PrivateMode::new(param)) } }, ('I', []) => handler.move_forward_tabs(next_param_or(1)), ('J', []) => { let mode = match next_param_or(0) { 0 => ClearMode::Below, 1 => ClearMode::Above, 2 => ClearMode::All, 3 => ClearMode::Saved, _ => { unhandled!(); return; }, }; handler.clear_screen(mode); }, ('K', []) => { let mode = match next_param_or(0) { 0 => LineClearMode::Right, 1 => LineClearMode::Left, 2 => LineClearMode::All, _ => { unhandled!(); return; }, }; handler.clear_line(mode); }, ('k', [b' ']) => { // SCP control. let char_path = match next_param_or(0) { 0 => ScpCharPath::Default, 1 => ScpCharPath::LTR, 2 => ScpCharPath::RTL, _ => { unhandled!(); return; }, }; let update_mode = match next_param_or(0) { 0 => ScpUpdateMode::ImplementationDependant, 1 => ScpUpdateMode::DataToPresentation, 2 => ScpUpdateMode::PresentationToData, _ => { unhandled!(); return; }, }; handler.set_scp(char_path, update_mode); }, ('L', []) => handler.insert_blank_lines(next_param_or(1) as usize), ('l', []) => { for param in params_iter.map(|param| param[0]) { handler.unset_mode(Mode::new(param)) } }, ('l', [b'?']) => { for param in params_iter.map(|param| param[0]) { handler.unset_private_mode(PrivateMode::new(param)) } }, ('M', []) => handler.delete_lines(next_param_or(1) as usize), ('m', []) => { if params.is_empty() { handler.terminal_attribute(Attr::Reset); } else { attrs_from_sgr_parameters(*handler, &mut params_iter); } }, ('m', [b'>']) => { let mode = match (next_param_or(1) == 4).then(|| next_param_or(0)) { Some(0) => ModifyOtherKeys::Reset, Some(1) => ModifyOtherKeys::EnableExceptWellDefined, Some(2) => ModifyOtherKeys::EnableAll, _ => return unhandled!(), }; handler.set_modify_other_keys(mode); }, ('m', [b'?']) => { if params_iter.next() == Some(&[4]) { handler.report_modify_other_keys(); } else { unhandled!() } }, ('n', []) => handler.device_status(next_param_or(0) as usize), ('P', []) => handler.delete_chars(next_param_or(1) as usize), ('p', [b'$']) => { let mode = next_param_or(0); handler.report_mode(Mode::new(mode)); }, ('p', [b'?', b'$']) => { let mode = next_param_or(0); handler.report_private_mode(PrivateMode::new(mode)); }, ('q', [b' ']) => { // DECSCUSR (CSI Ps SP q) -- Set Cursor Style. let cursor_style_id = next_param_or(0); let shape = match cursor_style_id { 0 => None, 1 | 2 => Some(CursorShape::Block), 3 | 4 => Some(CursorShape::Underline), 5 | 6 => Some(CursorShape::Beam), _ => { unhandled!(); return; }, }; let cursor_style = shape.map(|shape| CursorStyle { shape, blinking: cursor_style_id % 2 == 1 }); handler.set_cursor_style(cursor_style); }, ('r', []) => { let top = next_param_or(1) as usize; let bottom = params_iter.next().map(|param| param[0] as usize).filter(|¶m| param != 0); handler.set_scrolling_region(top, bottom); }, ('S', []) => handler.scroll_up(next_param_or(1) as usize), ('s', []) => handler.save_cursor_position(), ('T', []) => handler.scroll_down(next_param_or(1) as usize), ('t', []) => match next_param_or(1) as usize { 14 => handler.text_area_size_pixels(), 18 => handler.text_area_size_chars(), 22 => handler.push_title(), 23 => handler.pop_title(), _ => unhandled!(), }, ('u', [b'?']) => handler.report_keyboard_mode(), ('u', [b'=']) => { let mode = KeyboardModes::from_bits_truncate(next_param_or(0) as u8); let behavior = match next_param_or(1) { 3 => KeyboardModesApplyBehavior::Difference, 2 => KeyboardModesApplyBehavior::Union, // Default is replace. _ => KeyboardModesApplyBehavior::Replace, }; handler.set_keyboard_mode(mode, behavior); }, ('u', [b'>']) => { let mode = KeyboardModes::from_bits_truncate(next_param_or(0) as u8); handler.push_keyboard_mode(mode); }, ('u', [b'<']) => { // The default is 1. handler.pop_keyboard_modes(next_param_or(1)); }, ('u', []) => handler.restore_cursor_position(), ('X', []) => handler.erase_chars(next_param_or(1) as usize), ('Z', []) => handler.move_backward_tabs(next_param_or(1)), _ => unhandled!(), } } #[inline] fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) { macro_rules! unhandled { () => {{ debug!( "[unhandled] esc_dispatch ints={:?}, byte={:?} ({:02x})", intermediates, byte as char, byte ); }}; } macro_rules! configure_charset { ($charset:path, $intermediates:expr) => {{ let index: CharsetIndex = match $intermediates { [b'('] => CharsetIndex::G0, [b')'] => CharsetIndex::G1, [b'*'] => CharsetIndex::G2, [b'+'] => CharsetIndex::G3, _ => { unhandled!(); return; }, }; self.handler.configure_charset(index, $charset) }}; } match (byte, intermediates) { (b'B', intermediates) => configure_charset!(StandardCharset::Ascii, intermediates), (b'D', []) => self.handler.linefeed(), (b'E', []) => { self.handler.linefeed(); self.handler.carriage_return(); }, (b'H', []) => self.handler.set_horizontal_tabstop(), (b'M', []) => self.handler.reverse_index(), (b'Z', []) => self.handler.identify_terminal(None), (b'c', []) => self.handler.reset_state(), (b'0', intermediates) => { configure_charset!(StandardCharset::SpecialCharacterAndLineDrawing, intermediates) }, (b'7', []) => self.handler.save_cursor_position(), (b'8', [b'#']) => self.handler.decaln(), (b'8', []) => self.handler.restore_cursor_position(), (b'=', []) => self.handler.set_keypad_application_mode(), (b'>', []) => self.handler.unset_keypad_application_mode(), // String terminator, do nothing (parser handles as string terminator). (b'\\', []) => (), _ => unhandled!(), } } #[inline] fn terminated(&self) -> bool { self.terminated } } #[inline] fn attrs_from_sgr_parameters(handler: &mut H, params: &mut ParamsIter<'_>) { while let Some(param) = params.next() { let attr = match param { [0] => Some(Attr::Reset), [1] => Some(Attr::Bold), [2] => Some(Attr::Dim), [3] => Some(Attr::Italic), [4, 0] => Some(Attr::CancelUnderline), [4, 2] => Some(Attr::DoubleUnderline), [4, 3] => Some(Attr::Undercurl), [4, 4] => Some(Attr::DottedUnderline), [4, 5] => Some(Attr::DashedUnderline), [4, ..] => Some(Attr::Underline), [5] => Some(Attr::BlinkSlow), [6] => Some(Attr::BlinkFast), [7] => Some(Attr::Reverse), [8] => Some(Attr::Hidden), [9] => Some(Attr::Strike), [21] => Some(Attr::CancelBold), [22] => Some(Attr::CancelBoldDim), [23] => Some(Attr::CancelItalic), [24] => Some(Attr::CancelUnderline), [25] => Some(Attr::CancelBlink), [27] => Some(Attr::CancelReverse), [28] => Some(Attr::CancelHidden), [29] => Some(Attr::CancelStrike), [30] => Some(Attr::Foreground(Color::Named(NamedColor::Black))), [31] => Some(Attr::Foreground(Color::Named(NamedColor::Red))), [32] => Some(Attr::Foreground(Color::Named(NamedColor::Green))), [33] => Some(Attr::Foreground(Color::Named(NamedColor::Yellow))), [34] => Some(Attr::Foreground(Color::Named(NamedColor::Blue))), [35] => Some(Attr::Foreground(Color::Named(NamedColor::Magenta))), [36] => Some(Attr::Foreground(Color::Named(NamedColor::Cyan))), [37] => Some(Attr::Foreground(Color::Named(NamedColor::White))), [38] => { let mut iter = params.map(|param| param[0]); parse_sgr_color(&mut iter).map(Attr::Foreground) }, [38, params @ ..] => handle_colon_rgb(params).map(Attr::Foreground), [39] => Some(Attr::Foreground(Color::Named(NamedColor::Foreground))), [40] => Some(Attr::Background(Color::Named(NamedColor::Black))), [41] => Some(Attr::Background(Color::Named(NamedColor::Red))), [42] => Some(Attr::Background(Color::Named(NamedColor::Green))), [43] => Some(Attr::Background(Color::Named(NamedColor::Yellow))), [44] => Some(Attr::Background(Color::Named(NamedColor::Blue))), [45] => Some(Attr::Background(Color::Named(NamedColor::Magenta))), [46] => Some(Attr::Background(Color::Named(NamedColor::Cyan))), [47] => Some(Attr::Background(Color::Named(NamedColor::White))), [48] => { let mut iter = params.map(|param| param[0]); parse_sgr_color(&mut iter).map(Attr::Background) }, [48, params @ ..] => handle_colon_rgb(params).map(Attr::Background), [49] => Some(Attr::Background(Color::Named(NamedColor::Background))), [58] => { let mut iter = params.map(|param| param[0]); parse_sgr_color(&mut iter).map(|color| Attr::UnderlineColor(Some(color))) }, [58, params @ ..] => { handle_colon_rgb(params).map(|color| Attr::UnderlineColor(Some(color))) }, [59] => Some(Attr::UnderlineColor(None)), [90] => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlack))), [91] => Some(Attr::Foreground(Color::Named(NamedColor::BrightRed))), [92] => Some(Attr::Foreground(Color::Named(NamedColor::BrightGreen))), [93] => Some(Attr::Foreground(Color::Named(NamedColor::BrightYellow))), [94] => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlue))), [95] => Some(Attr::Foreground(Color::Named(NamedColor::BrightMagenta))), [96] => Some(Attr::Foreground(Color::Named(NamedColor::BrightCyan))), [97] => Some(Attr::Foreground(Color::Named(NamedColor::BrightWhite))), [100] => Some(Attr::Background(Color::Named(NamedColor::BrightBlack))), [101] => Some(Attr::Background(Color::Named(NamedColor::BrightRed))), [102] => Some(Attr::Background(Color::Named(NamedColor::BrightGreen))), [103] => Some(Attr::Background(Color::Named(NamedColor::BrightYellow))), [104] => Some(Attr::Background(Color::Named(NamedColor::BrightBlue))), [105] => Some(Attr::Background(Color::Named(NamedColor::BrightMagenta))), [106] => Some(Attr::Background(Color::Named(NamedColor::BrightCyan))), [107] => Some(Attr::Background(Color::Named(NamedColor::BrightWhite))), _ => None, }; match attr { Some(attr) => handler.terminal_attribute(attr), None => continue, } } } /// Handle colon separated rgb color escape sequence. #[inline] fn handle_colon_rgb(params: &[u16]) -> Option { let rgb_start = if params.len() > 4 { 2 } else { 1 }; let rgb_iter = params[rgb_start..].iter().copied(); let mut iter = iter::once(params[0]).chain(rgb_iter); parse_sgr_color(&mut iter) } /// Parse a color specifier from list of attributes. fn parse_sgr_color(params: &mut dyn Iterator) -> Option { match params.next() { Some(2) => Some(Color::Spec(Rgb { r: u8::try_from(params.next()?).ok()?, g: u8::try_from(params.next()?).ok()?, b: u8::try_from(params.next()?).ok()?, })), Some(5) => Some(Color::Indexed(u8::try_from(params.next()?).ok()?)), _ => None, } } /// C0 set of 7-bit control characters (from ANSI X3.4-1977). #[allow(non_snake_case)] pub mod C0 { /// Null filler, terminal should ignore this character. pub const NUL: u8 = 0x00; /// Start of Header. pub const SOH: u8 = 0x01; /// Start of Text, implied end of header. pub const STX: u8 = 0x02; /// End of Text, causes some terminal to respond with ACK or NAK. pub const ETX: u8 = 0x03; /// End of Transmission. pub const EOT: u8 = 0x04; /// Enquiry, causes terminal to send ANSWER-BACK ID. pub const ENQ: u8 = 0x05; /// Acknowledge, usually sent by terminal in response to ETX. pub const ACK: u8 = 0x06; /// Bell, triggers the bell, buzzer, or beeper on the terminal. pub const BEL: u8 = 0x07; /// Backspace, can be used to define overstruck characters. pub const BS: u8 = 0x08; /// Horizontal Tabulation, move to next predetermined position. pub const HT: u8 = 0x09; /// Linefeed, move to same position on next line (see also NL). pub const LF: u8 = 0x0A; /// Vertical Tabulation, move to next predetermined line. pub const VT: u8 = 0x0B; /// Form Feed, move to next form or page. pub const FF: u8 = 0x0C; /// Carriage Return, move to first character of current line. pub const CR: u8 = 0x0D; /// Shift Out, switch to G1 (other half of character set). pub const SO: u8 = 0x0E; /// Shift In, switch to G0 (normal half of character set). pub const SI: u8 = 0x0F; /// Data Link Escape, interpret next control character specially. pub const DLE: u8 = 0x10; /// (DC1) Terminal is allowed to resume transmitting. pub const XON: u8 = 0x11; /// Device Control 2, causes ASR-33 to activate paper-tape reader. pub const DC2: u8 = 0x12; /// (DC2) Terminal must pause and refrain from transmitting. pub const XOFF: u8 = 0x13; /// Device Control 4, causes ASR-33 to deactivate paper-tape reader. pub const DC4: u8 = 0x14; /// Negative Acknowledge, used sometimes with ETX and ACK. pub const NAK: u8 = 0x15; /// Synchronous Idle, used to maintain timing in Sync communication. pub const SYN: u8 = 0x16; /// End of Transmission block. pub const ETB: u8 = 0x17; /// Cancel (makes VT100 abort current escape sequence if any). pub const CAN: u8 = 0x18; /// End of Medium. pub const EM: u8 = 0x19; /// Substitute (VT100 uses this to display parity errors). pub const SUB: u8 = 0x1A; /// Prefix to an escape sequence. pub const ESC: u8 = 0x1B; /// File Separator. pub const FS: u8 = 0x1C; /// Group Separator. pub const GS: u8 = 0x1D; /// Record Separator (sent by VT132 in block-transfer mode). pub const RS: u8 = 0x1E; /// Unit Separator. pub const US: u8 = 0x1F; /// Delete, should be ignored by terminal. pub const DEL: u8 = 0x7F; } // Tests for parsing escape sequences. // // Byte sequences used in these tests are recording of pty stdout. #[cfg(test)] mod tests { use super::*; #[derive(Default)] pub struct TestSyncHandler { is_sync: usize, } impl Timeout for TestSyncHandler { #[inline] fn set_timeout(&mut self, _: Duration) { self.is_sync += 1; } #[inline] fn clear_timeout(&mut self) { self.is_sync = 0; } #[inline] fn pending_timeout(&self) -> bool { self.is_sync != 0 } } struct MockHandler { index: CharsetIndex, charset: StandardCharset, attr: Option, identity_reported: bool, color: Option, reset_colors: Vec, } impl Handler for MockHandler { fn terminal_attribute(&mut self, attr: Attr) { self.attr = Some(attr); } fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) { self.index = index; self.charset = charset; } fn set_active_charset(&mut self, index: CharsetIndex) { self.index = index; } fn identify_terminal(&mut self, _intermediate: Option) { self.identity_reported = true; } fn reset_state(&mut self) { *self = Self::default(); } fn set_color(&mut self, _: usize, c: Rgb) { self.color = Some(c); } fn reset_color(&mut self, index: usize) { self.reset_colors.push(index) } } impl Default for MockHandler { fn default() -> MockHandler { MockHandler { index: CharsetIndex::G0, charset: StandardCharset::Ascii, attr: None, identity_reported: false, color: None, reset_colors: Vec::new(), } } } #[test] fn parse_control_attribute() { static BYTES: &[u8] = &[0x1B, b'[', b'1', b'm']; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, BYTES); assert_eq!(handler.attr, Some(Attr::Bold)); } #[test] fn parse_terminal_identity_csi() { let bytes: &[u8] = &[0x1B, b'[', b'1', b'c']; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, bytes); assert!(!handler.identity_reported); handler.reset_state(); let bytes: &[u8] = &[0x1B, b'[', b'c']; parser.advance(&mut handler, bytes); assert!(handler.identity_reported); handler.reset_state(); let bytes: &[u8] = &[0x1B, b'[', b'0', b'c']; parser.advance(&mut handler, bytes); assert!(handler.identity_reported); } #[test] fn parse_terminal_identity_esc() { let bytes: &[u8] = &[0x1B, b'Z']; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, bytes); assert!(handler.identity_reported); handler.reset_state(); let bytes: &[u8] = &[0x1B, b'#', b'Z']; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, bytes); assert!(!handler.identity_reported); handler.reset_state(); } #[test] fn parse_truecolor_attr() { static BYTES: &[u8] = &[ 0x1B, b'[', b'3', b'8', b';', b'2', b';', b'1', b'2', b'8', b';', b'6', b'6', b';', b'2', b'5', b'5', b'm', ]; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, BYTES); let spec = Rgb { r: 128, g: 66, b: 255 }; assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec)))); } /// No exactly a test; useful for debugging. #[test] fn parse_zsh_startup() { static BYTES: &[u8] = &[ 0x1B, b'[', b'1', b'm', 0x1B, b'[', b'7', b'm', b'%', 0x1B, b'[', b'2', b'7', b'm', 0x1B, b'[', b'1', b'm', 0x1B, b'[', b'0', b'm', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b'\r', b' ', b'\r', b'\r', 0x1B, b'[', b'0', b'm', 0x1B, b'[', b'2', b'7', b'm', 0x1B, b'[', b'2', b'4', b'm', 0x1B, b'[', b'J', b'j', b'w', b'i', b'l', b'm', b'@', b'j', b'w', b'i', b'l', b'm', b'-', b'd', b'e', b's', b'k', b' ', 0x1B, b'[', b'0', b'1', b';', b'3', b'2', b'm', 0xE2, 0x9E, 0x9C, b' ', 0x1B, b'[', b'0', b'1', b';', b'3', b'2', b'm', b' ', 0x1B, b'[', b'3', b'6', b'm', b'~', b'/', b'c', b'o', b'd', b'e', ]; let mut handler = MockHandler::default(); let mut parser = Processor::::new(); parser.advance(&mut handler, BYTES); } #[test] fn parse_designate_g0_as_line_drawing() { static BYTES: &[u8] = &[0x1B, b'(', b'0']; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, BYTES); assert_eq!(handler.index, CharsetIndex::G0); assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); } #[test] fn parse_designate_g1_as_line_drawing_and_invoke() { static BYTES: &[u8] = &[0x1B, b')', b'0', 0x0E]; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, &BYTES[..3]); assert_eq!(handler.index, CharsetIndex::G1); assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); let mut handler = MockHandler::default(); parser.advance(&mut handler, &[BYTES[3]]); assert_eq!(handler.index, CharsetIndex::G1); } #[test] fn parse_valid_rgb_colors() { assert_eq!(xparse_color(b"rgb:f/e/d"), Some(Rgb { r: 0xFF, g: 0xEE, b: 0xDD })); assert_eq!(xparse_color(b"rgb:11/aa/ff"), Some(Rgb { r: 0x11, g: 0xAA, b: 0xFF })); assert_eq!(xparse_color(b"rgb:f/ed1/cb23"), Some(Rgb { r: 0xFF, g: 0xEC, b: 0xCA })); assert_eq!(xparse_color(b"rgb:ffff/0/0"), Some(Rgb { r: 0xFF, g: 0x0, b: 0x0 })); } #[test] fn parse_valid_legacy_rgb_colors() { assert_eq!(xparse_color(b"#1af"), Some(Rgb { r: 0x10, g: 0xA0, b: 0xF0 })); assert_eq!(xparse_color(b"#11aaff"), Some(Rgb { r: 0x11, g: 0xAA, b: 0xFF })); assert_eq!(xparse_color(b"#110aa0ff0"), Some(Rgb { r: 0x11, g: 0xAA, b: 0xFF })); assert_eq!(xparse_color(b"#1100aa00ff00"), Some(Rgb { r: 0x11, g: 0xAA, b: 0xFF })); } #[test] fn parse_invalid_rgb_colors() { assert_eq!(xparse_color(b"rgb:0//"), None); assert_eq!(xparse_color(b"rgb://///"), None); } #[test] fn parse_invalid_legacy_rgb_colors() { assert_eq!(xparse_color(b"#"), None); assert_eq!(xparse_color(b"#f"), None); } #[test] fn parse_invalid_number() { assert_eq!(parse_number(b"1abc"), None); } #[test] fn parse_valid_number() { assert_eq!(parse_number(b"123"), Some(123)); } #[test] fn parse_number_too_large() { assert_eq!(parse_number(b"321"), None); } #[test] fn parse_osc4_set_color() { let bytes: &[u8] = b"\x1b]4;0;#fff\x1b\\"; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, bytes); assert_eq!(handler.color, Some(Rgb { r: 0xF0, g: 0xF0, b: 0xF0 })); } #[test] fn parse_osc104_reset_color() { let bytes: &[u8] = b"\x1b]104;1;\x1b\\"; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, bytes); assert_eq!(handler.reset_colors, vec![1]); } #[test] fn parse_osc104_reset_all_colors() { let bytes: &[u8] = b"\x1b]104;\x1b\\"; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, bytes); let expected: Vec = (0..256).collect(); assert_eq!(handler.reset_colors, expected); } #[test] fn parse_osc104_reset_all_colors_no_semicolon() { let bytes: &[u8] = b"\x1b]104\x1b\\"; let mut parser = Processor::::new(); let mut handler = MockHandler::default(); parser.advance(&mut handler, bytes); let expected: Vec = (0..256).collect(); assert_eq!(handler.reset_colors, expected); } #[test] fn partial_sync_updates() { let mut parser = Processor::::new(); let mut handler = MockHandler::default(); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.is_none()); // Start synchronized update. parser.advance(&mut handler, b"\x1b[?20"); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.is_none()); parser.advance(&mut handler, b"26h"); assert_eq!(parser.state.sync_state.timeout.is_sync, 1); assert!(handler.attr.is_none()); // Dispatch some data. parser.advance(&mut handler, b"random \x1b[31m stuff"); assert_eq!(parser.state.sync_state.timeout.is_sync, 1); assert!(handler.attr.is_none()); // Extend synchronized update. parser.advance(&mut handler, b"\x1b[?20"); assert_eq!(parser.state.sync_state.timeout.is_sync, 1); assert!(handler.attr.is_none()); parser.advance(&mut handler, b"26h"); assert_eq!(parser.state.sync_state.timeout.is_sync, 2); assert!(handler.attr.is_none()); // Terminate synchronized update. parser.advance(&mut handler, b"\x1b[?20"); assert_eq!(parser.state.sync_state.timeout.is_sync, 2); assert!(handler.attr.is_none()); parser.advance(&mut handler, b"26l"); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.is_some()); } #[test] fn sync_bursts_buffer() { let mut parser = Processor::::new(); let mut handler = MockHandler::default(); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.is_none()); // Repeat test twice to ensure internal state is reset properly. for _ in 0..2 { // Start synchronized update. parser.advance(&mut handler, b"\x1b[?2026h"); assert_eq!(parser.state.sync_state.timeout.is_sync, 1); assert!(handler.attr.is_none()); // Ensure sync works. parser.advance(&mut handler, b"\x1b[31m"); assert_eq!(parser.state.sync_state.timeout.is_sync, 1); assert!(handler.attr.is_none()); // Exceed sync buffer dimensions. parser.advance(&mut handler, "a".repeat(SYNC_BUFFER_SIZE).as_bytes()); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.take().is_some()); // Ensure new events are dispatched directly. parser.advance(&mut handler, b"\x1b[31m"); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.take().is_some()); } } #[test] fn mixed_sync_escape() { let mut parser = Processor::::new(); let mut handler = MockHandler::default(); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.is_none()); // Start synchronized update with immediate SGR. parser.advance(&mut handler, b"\x1b[?2026h\x1b[31m"); assert_eq!(parser.state.sync_state.timeout.is_sync, 1); assert!(handler.attr.is_none()); // Terminate synchronized update and check for SGR. parser.advance(&mut handler, b"\x1b[?2026l"); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.is_some()); } #[test] fn sync_bsu_with_esu() { let mut parser = Processor::::new(); let mut handler = MockHandler::default(); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert!(handler.attr.is_none()); // Start synchronized update with immediate SGR. parser.advance(&mut handler, b"\x1b[?2026h\x1b[1m"); assert_eq!(parser.state.sync_state.timeout.is_sync, 1); assert!(handler.attr.is_none()); // Terminate synchronized update, but immediately start a new one. parser.advance(&mut handler, b"\x1b[?2026l\x1b[?2026h\x1b[4m"); assert_eq!(parser.state.sync_state.timeout.is_sync, 2); assert_eq!(handler.attr.take(), Some(Attr::Bold)); // Terminate again, expecting one buffered SGR. parser.advance(&mut handler, b"\x1b[?2026l"); assert_eq!(parser.state.sync_state.timeout.is_sync, 0); assert_eq!(handler.attr.take(), Some(Attr::Underline)); } #[test] #[cfg(feature = "std")] fn contrast() { let rgb1 = Rgb { r: 0xFF, g: 0xFF, b: 0xFF }; let rgb2 = Rgb { r: 0x00, g: 0x00, b: 0x00 }; assert!((rgb1.contrast(rgb2) - 21.).abs() < f64::EPSILON); let rgb1 = Rgb { r: 0xFF, g: 0xFF, b: 0xFF }; assert!((rgb1.contrast(rgb1) - 1.).abs() < f64::EPSILON); let rgb1 = Rgb { r: 0xFF, g: 0x00, b: 0xFF }; let rgb2 = Rgb { r: 0x00, g: 0xFF, b: 0x00 }; assert!((rgb1.contrast(rgb2) - 2.285_543_608_124_253_3).abs() < f64::EPSILON); let rgb1 = Rgb { r: 0x12, g: 0x34, b: 0x56 }; let rgb2 = Rgb { r: 0xFE, g: 0xDC, b: 0xBA }; assert!((rgb1.contrast(rgb2) - 9.786_558_997_257_74).abs() < f64::EPSILON); } } vte-0.15.0/src/lib.rs000064400000000000000000001506001046102023000124120ustar 00000000000000//! Parser for implementing virtual terminal emulators //! //! [`Parser`] is implemented according to [Paul Williams' ANSI parser state //! machine]. The state machine doesn't assign meaning to the parsed data and is //! thus not itself sufficient for writing a terminal emulator. Instead, it is //! expected that an implementation of [`Perform`] is provided which does //! something useful with the parsed data. The [`Parser`] handles the book //! keeping, and the [`Perform`] gets to simply handle actions. //! //! # Examples //! //! For an example of using the [`Parser`] please see the examples folder. The //! example included there simply logs all the actions [`Perform`] does. One //! quick way to see it in action is to pipe `printf` into it //! //! ```sh //! printf '\x1b[31mExample' | cargo run --example parselog //! ``` //! //! # Differences from original state machine description //! //! * UTF-8 Support for Input //! * OSC Strings can be terminated by 0x07 //! * Only supports 7-bit codes //! //! [`Parser`]: struct.Parser.html //! [`Perform`]: trait.Perform.html //! [Paul Williams' ANSI parser state machine]: https://vt100.net/emu/dec_ansi_parser #![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)] #![cfg_attr(not(feature = "std"), no_std)] use core::mem::MaybeUninit; use core::str; #[cfg(not(feature = "std"))] use arrayvec::ArrayVec; mod params; #[cfg(feature = "ansi")] pub mod ansi; pub use params::{Params, ParamsIter}; const MAX_INTERMEDIATES: usize = 2; const MAX_OSC_PARAMS: usize = 16; const MAX_OSC_RAW: usize = 1024; /// Parser for raw _VTE_ protocol which delegates actions to a [`Perform`] /// /// [`Perform`]: trait.Perform.html /// /// Generic over the value for the size of the raw Operating System Command /// buffer. Only used when the `std` feature is not enabled. #[derive(Default)] pub struct Parser { state: State, intermediates: [u8; MAX_INTERMEDIATES], intermediate_idx: usize, params: Params, param: u16, #[cfg(not(feature = "std"))] osc_raw: ArrayVec, #[cfg(feature = "std")] osc_raw: Vec, osc_params: [(usize, usize); MAX_OSC_PARAMS], osc_num_params: usize, ignoring: bool, partial_utf8: [u8; 4], partial_utf8_len: usize, } impl Parser { /// Create a new Parser pub fn new() -> Parser { Default::default() } } impl Parser { /// Create a new Parser with a custom size for the Operating System Command /// buffer. /// /// Call with a const-generic param on `Parser`, like: /// /// ```rust /// let mut p = vte::Parser::<64>::new_with_size(); /// ``` #[cfg(not(feature = "std"))] pub fn new_with_size() -> Parser { Default::default() } #[inline] fn params(&self) -> &Params { &self.params } #[inline] fn intermediates(&self) -> &[u8] { &self.intermediates[..self.intermediate_idx] } /// Advance the parser state. /// /// Requires a [`Perform`] implementation to handle the triggered actions. /// /// [`Perform`]: trait.Perform.html #[inline] pub fn advance(&mut self, performer: &mut P, bytes: &[u8]) { let mut i = 0; // Handle partial codepoints from previous calls to `advance`. if self.partial_utf8_len != 0 { i += self.advance_partial_utf8(performer, bytes); } while i != bytes.len() { match self.state { State::Ground => i += self.advance_ground(performer, &bytes[i..]), _ => { // Inlining it results in worse codegen. let byte = bytes[i]; self.change_state(performer, byte); i += 1; }, } } } /// Partially advance the parser state. /// /// This is equivalent to [`Self::advance`], but stops when /// [`Perform::terminated`] is true after reading a byte. /// /// Returns the number of bytes read before termination. /// /// See [`Perform::advance`] for more details. #[inline] #[must_use = "Returned value should be used to processs the remaining bytes"] pub fn advance_until_terminated( &mut self, performer: &mut P, bytes: &[u8], ) -> usize { let mut i = 0; // Handle partial codepoints from previous calls to `advance`. if self.partial_utf8_len != 0 { i += self.advance_partial_utf8(performer, bytes); } while i != bytes.len() && !performer.terminated() { match self.state { State::Ground => i += self.advance_ground(performer, &bytes[i..]), _ => { // Inlining it results in worse codegen. let byte = bytes[i]; self.change_state(performer, byte); i += 1; }, } } i } #[inline(always)] fn change_state(&mut self, performer: &mut P, byte: u8) { match self.state { State::CsiEntry => self.advance_csi_entry(performer, byte), State::CsiIgnore => self.advance_csi_ignore(performer, byte), State::CsiIntermediate => self.advance_csi_intermediate(performer, byte), State::CsiParam => self.advance_csi_param(performer, byte), State::DcsEntry => self.advance_dcs_entry(performer, byte), State::DcsIgnore => self.anywhere(performer, byte), State::DcsIntermediate => self.advance_dcs_intermediate(performer, byte), State::DcsParam => self.advance_dcs_param(performer, byte), State::DcsPassthrough => self.advance_dcs_passthrough(performer, byte), State::Escape => self.advance_esc(performer, byte), State::EscapeIntermediate => self.advance_esc_intermediate(performer, byte), State::OscString => self.advance_osc_string(performer, byte), State::SosPmApcString => self.anywhere(performer, byte), State::Ground => unreachable!(), } } #[inline(always)] fn advance_csi_entry(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), 0x20..=0x2F => { self.action_collect(byte); self.state = State::CsiIntermediate }, 0x30..=0x39 => { self.action_paramnext(byte); self.state = State::CsiParam }, 0x3A => { self.action_subparam(); self.state = State::CsiParam }, 0x3B => { self.action_param(); self.state = State::CsiParam }, 0x3C..=0x3F => { self.action_collect(byte); self.state = State::CsiParam }, 0x40..=0x7E => self.action_csi_dispatch(performer, byte), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_csi_ignore(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), 0x20..=0x3F => (), 0x40..=0x7E => self.state = State::Ground, 0x7F => (), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_csi_intermediate(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), 0x20..=0x2F => self.action_collect(byte), 0x30..=0x3F => self.state = State::CsiIgnore, 0x40..=0x7E => self.action_csi_dispatch(performer, byte), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_csi_param(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), 0x20..=0x2F => { self.action_collect(byte); self.state = State::CsiIntermediate }, 0x30..=0x39 => self.action_paramnext(byte), 0x3A => self.action_subparam(), 0x3B => self.action_param(), 0x3C..=0x3F => self.state = State::CsiIgnore, 0x40..=0x7E => self.action_csi_dispatch(performer, byte), 0x7F => (), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_dcs_entry(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => (), 0x20..=0x2F => { self.action_collect(byte); self.state = State::DcsIntermediate }, 0x30..=0x39 => { self.action_paramnext(byte); self.state = State::DcsParam }, 0x3A => { self.action_subparam(); self.state = State::DcsParam }, 0x3B => { self.action_param(); self.state = State::DcsParam }, 0x3C..=0x3F => { self.action_collect(byte); self.state = State::DcsParam }, 0x40..=0x7E => self.action_hook(performer, byte), 0x7F => (), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_dcs_intermediate(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => (), 0x20..=0x2F => self.action_collect(byte), 0x30..=0x3F => self.state = State::DcsIgnore, 0x40..=0x7E => self.action_hook(performer, byte), 0x7F => (), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_dcs_param(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => (), 0x20..=0x2F => { self.action_collect(byte); self.state = State::DcsIntermediate }, 0x30..=0x39 => self.action_paramnext(byte), 0x3A => self.action_subparam(), 0x3B => self.action_param(), 0x3C..=0x3F => self.state = State::DcsIgnore, 0x40..=0x7E => self.action_hook(performer, byte), 0x7F => (), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_dcs_passthrough(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x7E => performer.put(byte), 0x18 | 0x1A => { performer.unhook(); performer.execute(byte); self.state = State::Ground }, 0x1B => { performer.unhook(); self.reset_params(); self.state = State::Escape }, 0x7F => (), 0x9C => { performer.unhook(); self.state = State::Ground }, _ => (), } } #[inline(always)] fn advance_esc(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), 0x20..=0x2F => { self.action_collect(byte); self.state = State::EscapeIntermediate }, 0x30..=0x4F => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); self.state = State::Ground }, 0x50 => { self.reset_params(); self.state = State::DcsEntry }, 0x51..=0x57 => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); self.state = State::Ground }, 0x58 => self.state = State::SosPmApcString, 0x59..=0x5A => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); self.state = State::Ground }, 0x5B => { self.reset_params(); self.state = State::CsiEntry }, 0x5C => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); self.state = State::Ground }, 0x5D => { self.osc_raw.clear(); self.osc_num_params = 0; self.state = State::OscString }, 0x5E..=0x5F => self.state = State::SosPmApcString, 0x60..=0x7E => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); self.state = State::Ground }, // Anywhere. 0x18 | 0x1A => { performer.execute(byte); self.state = State::Ground }, 0x1B => (), _ => (), } } #[inline(always)] fn advance_esc_intermediate(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), 0x20..=0x2F => self.action_collect(byte), 0x30..=0x7E => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); self.state = State::Ground }, 0x7F => (), _ => self.anywhere(performer, byte), } } #[inline(always)] fn advance_osc_string(&mut self, performer: &mut P, byte: u8) { match byte { 0x00..=0x06 | 0x08..=0x17 | 0x19 | 0x1C..=0x1F => (), 0x07 => { self.osc_end(performer, byte); self.state = State::Ground }, 0x18 | 0x1A => { self.osc_end(performer, byte); performer.execute(byte); self.state = State::Ground }, 0x1B => { self.osc_end(performer, byte); self.reset_params(); self.state = State::Escape }, 0x3B => { #[cfg(not(feature = "std"))] { if self.osc_raw.is_full() { return; } } self.action_osc_put_param() }, _ => self.action_osc_put(byte), } } #[inline(always)] fn anywhere(&mut self, performer: &mut P, byte: u8) { match byte { 0x18 | 0x1A => { performer.execute(byte); self.state = State::Ground }, 0x1B => { self.reset_params(); self.state = State::Escape }, _ => (), } } #[inline] fn action_csi_dispatch(&mut self, performer: &mut P, byte: u8) { if self.params.is_full() { self.ignoring = true; } else { self.params.push(self.param); } performer.csi_dispatch(self.params(), self.intermediates(), self.ignoring, byte as char); self.state = State::Ground } #[inline] fn action_hook(&mut self, performer: &mut P, byte: u8) { if self.params.is_full() { self.ignoring = true; } else { self.params.push(self.param); } performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char); self.state = State::DcsPassthrough; } #[inline] fn action_collect(&mut self, byte: u8) { if self.intermediate_idx == MAX_INTERMEDIATES { self.ignoring = true; } else { self.intermediates[self.intermediate_idx] = byte; self.intermediate_idx += 1; } } /// Advance to the next subparameter. #[inline] fn action_subparam(&mut self) { if self.params.is_full() { self.ignoring = true; } else { self.params.extend(self.param); self.param = 0; } } /// Advance to the next parameter. #[inline] fn action_param(&mut self) { if self.params.is_full() { self.ignoring = true; } else { self.params.push(self.param); self.param = 0; } } /// Advance inside the parameter without terminating it. #[inline] fn action_paramnext(&mut self, byte: u8) { if self.params.is_full() { self.ignoring = true; } else { // Continue collecting bytes into param. self.param = self.param.saturating_mul(10); self.param = self.param.saturating_add((byte - b'0') as u16); } } /// Add OSC param separator. #[inline] fn action_osc_put_param(&mut self) { let idx = self.osc_raw.len(); let param_idx = self.osc_num_params; match param_idx { // First param is special - 0 to current byte index. 0 => self.osc_params[param_idx] = (0, idx), // Only process up to MAX_OSC_PARAMS. MAX_OSC_PARAMS => return, // All other params depend on previous indexing. _ => { let prev = self.osc_params[param_idx - 1]; let begin = prev.1; self.osc_params[param_idx] = (begin, idx); }, } self.osc_num_params += 1; } #[inline(always)] fn action_osc_put(&mut self, byte: u8) { #[cfg(not(feature = "std"))] { if self.osc_raw.is_full() { return; } } self.osc_raw.push(byte); } fn osc_end(&mut self, performer: &mut P, byte: u8) { self.action_osc_put_param(); self.osc_dispatch(performer, byte); self.osc_raw.clear(); self.osc_num_params = 0; } /// Reset escape sequence parameters and intermediates. #[inline] fn reset_params(&mut self) { self.intermediate_idx = 0; self.ignoring = false; self.param = 0; self.params.clear(); } /// Separate method for osc_dispatch that borrows self as read-only /// /// The aliasing is needed here for multiple slices into self.osc_raw #[inline] fn osc_dispatch(&self, performer: &mut P, byte: u8) { let mut slices: [MaybeUninit<&[u8]>; MAX_OSC_PARAMS] = unsafe { MaybeUninit::uninit().assume_init() }; for (i, slice) in slices.iter_mut().enumerate().take(self.osc_num_params) { let indices = self.osc_params[i]; *slice = MaybeUninit::new(&self.osc_raw[indices.0..indices.1]); } unsafe { let num_params = self.osc_num_params; let params = &slices[..num_params] as *const [MaybeUninit<&[u8]>] as *const [&[u8]]; performer.osc_dispatch(&*params, byte == 0x07); } } /// Advance the parser state from ground. /// /// The ground state is handled separately since it can only be left using /// the escape character (`\x1b`). This allows more efficient parsing by /// using SIMD search with [`memchr`]. #[inline] fn advance_ground(&mut self, performer: &mut P, bytes: &[u8]) -> usize { // Find the next escape character. let num_bytes = bytes.len(); let plain_chars = memchr::memchr(0x1B, bytes).unwrap_or(num_bytes); // If the next character is ESC, just process it and short-circuit. if plain_chars == 0 { self.state = State::Escape; self.reset_params(); return 1; } match str::from_utf8(&bytes[..plain_chars]) { Ok(parsed) => { Self::ground_dispatch(performer, parsed); let mut processed = plain_chars; // If there's another character, it must be escape so process it directly. if processed < num_bytes { self.state = State::Escape; self.reset_params(); processed += 1; } processed }, // Handle invalid and partial utf8. Err(err) => { // Dispatch all the valid bytes. let valid_bytes = err.valid_up_to(); let parsed = unsafe { str::from_utf8_unchecked(&bytes[..valid_bytes]) }; Self::ground_dispatch(performer, parsed); match err.error_len() { Some(len) => { // Execute C1 escapes or emit replacement character. if len == 1 && bytes[valid_bytes] <= 0x9F { performer.execute(bytes[valid_bytes]); } else { performer.print('�'); } // Restart processing after the invalid bytes. // // While we could theoretically try to just re-parse // `bytes[valid_bytes + len..plain_chars]`, it's easier // to just skip it and invalid utf8 is pretty rare anyway. valid_bytes + len }, None => { if plain_chars < num_bytes { // Process bytes cut off by escape. performer.print('�'); self.state = State::Escape; self.reset_params(); plain_chars + 1 } else { // Process bytes cut off by the buffer end. let extra_bytes = num_bytes - valid_bytes; let partial_len = self.partial_utf8_len + extra_bytes; self.partial_utf8[self.partial_utf8_len..partial_len] .copy_from_slice(&bytes[valid_bytes..valid_bytes + extra_bytes]); self.partial_utf8_len = partial_len; num_bytes } }, } }, } } /// Advance the parser while processing a partial utf8 codepoint. #[inline] fn advance_partial_utf8(&mut self, performer: &mut P, bytes: &[u8]) -> usize { // Try to copy up to 3 more characters, to ensure the codepoint is complete. let old_bytes = self.partial_utf8_len; let to_copy = bytes.len().min(self.partial_utf8.len() - old_bytes); self.partial_utf8[old_bytes..old_bytes + to_copy].copy_from_slice(&bytes[..to_copy]); self.partial_utf8_len += to_copy; // Parse the unicode character. match str::from_utf8(&self.partial_utf8[..self.partial_utf8_len]) { // If the entire buffer is valid, use the first character and continue parsing. Ok(parsed) => { let c = unsafe { parsed.chars().next().unwrap_unchecked() }; performer.print(c); self.partial_utf8_len = 0; c.len_utf8() - old_bytes }, Err(err) => { let valid_bytes = err.valid_up_to(); // If we have any valid bytes, that means we partially copied another // utf8 character into `partial_utf8`. Since we only care about the // first character, we just ignore the rest. if valid_bytes > 0 { let c = unsafe { let parsed = str::from_utf8_unchecked(&self.partial_utf8[..valid_bytes]); parsed.chars().next().unwrap_unchecked() }; performer.print(c); self.partial_utf8_len = 0; return valid_bytes - old_bytes; } match err.error_len() { // If the partial character was also invalid, emit the replacement // character. Some(invalid_len) => { performer.print('�'); self.partial_utf8_len = 0; invalid_len - old_bytes }, // If the character still isn't complete, wait for more data. None => to_copy, } }, } } /// Handle ground dispatch of print/execute for all characters in a string. #[inline] fn ground_dispatch(performer: &mut P, text: &str) { for c in text.chars() { match c { '\x00'..='\x1f' | '\u{80}'..='\u{9f}' => performer.execute(c as u8), _ => performer.print(c), } } } } #[derive(PartialEq, Eq, Debug, Default, Copy, Clone)] enum State { CsiEntry, CsiIgnore, CsiIntermediate, CsiParam, DcsEntry, DcsIgnore, DcsIntermediate, DcsParam, DcsPassthrough, Escape, EscapeIntermediate, OscString, SosPmApcString, #[default] Ground, } /// Performs actions requested by the Parser /// /// Actions in this case mean, for example, handling a CSI escape sequence /// describing cursor movement, or simply printing characters to the screen. /// /// The methods on this type correspond to actions described in /// . I've done my best to describe them in /// a useful way in my own words for completeness, but the site should be /// referenced if something isn't clear. If the site disappears at some point in /// the future, consider checking archive.org. pub trait Perform { /// Draw a character to the screen and update states. fn print(&mut self, _c: char) {} /// Execute a C0 or C1 control function. fn execute(&mut self, _byte: u8) {} /// Invoked when a final character arrives in first part of device control /// string. /// /// The control function should be determined from the private marker, final /// character, and execute with a parameter list. A handler should be /// selected for remaining characters in the string; the handler /// function should subsequently be called by `put` for every character in /// the control string. /// /// The `ignore` flag indicates that more than two intermediates arrived and /// subsequent characters were ignored. fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _action: char) {} /// Pass bytes as part of a device control string to the handle chosen in /// `hook`. C0 controls will also be passed to the handler. fn put(&mut self, _byte: u8) {} /// Called when a device control string is terminated. /// /// The previously selected handler should be notified that the DCS has /// terminated. fn unhook(&mut self) {} /// Dispatch an operating system command. fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {} /// A final character has arrived for a CSI sequence /// /// The `ignore` flag indicates that either more than two intermediates /// arrived or the number of parameters exceeded the maximum supported /// length, and subsequent characters were ignored. fn csi_dispatch( &mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _action: char, ) { } /// The final character of an escape sequence has arrived. /// /// The `ignore` flag indicates that more than two intermediates arrived and /// subsequent characters were ignored. fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {} /// Whether the parser should terminate prematurely. /// /// This can be used in conjunction with /// [`Parser::advance_until_terminated`] to terminate the parser after /// receiving certain escape sequences like synchronized updates. /// /// This is checked after every parsed byte, so no expensive computation /// should take place in this function. #[inline(always)] fn terminated(&self) -> bool { false } } #[cfg(all(test, not(feature = "std")))] #[macro_use] extern crate std; #[cfg(test)] mod tests { use std::vec::Vec; use super::*; const OSC_BYTES: &[u8] = &[ 0x1B, 0x5D, // Begin OSC b'2', b';', b'j', b'w', b'i', b'l', b'm', b'@', b'j', b'w', b'i', b'l', b'm', b'-', b'd', b'e', b's', b'k', b':', b' ', b'~', b'/', b'c', b'o', b'd', b'e', b'/', b'a', b'l', b'a', b'c', b'r', b'i', b't', b't', b'y', 0x07, // End OSC ]; #[derive(Default)] struct Dispatcher { dispatched: Vec, } #[derive(Debug, PartialEq, Eq)] enum Sequence { Osc(Vec>, bool), Csi(Vec>, Vec, bool, char), Esc(Vec, bool, u8), DcsHook(Vec>, Vec, bool, char), DcsPut(u8), Print(char), Execute(u8), DcsUnhook, } impl Perform for Dispatcher { fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { let params = params.iter().map(|p| p.to_vec()).collect(); self.dispatched.push(Sequence::Osc(params, bell_terminated)); } fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { let params = params.iter().map(|subparam| subparam.to_vec()).collect(); let intermediates = intermediates.to_vec(); self.dispatched.push(Sequence::Csi(params, intermediates, ignore, c)); } fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { let intermediates = intermediates.to_vec(); self.dispatched.push(Sequence::Esc(intermediates, ignore, byte)); } fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { let params = params.iter().map(|subparam| subparam.to_vec()).collect(); let intermediates = intermediates.to_vec(); self.dispatched.push(Sequence::DcsHook(params, intermediates, ignore, c)); } fn put(&mut self, byte: u8) { self.dispatched.push(Sequence::DcsPut(byte)); } fn unhook(&mut self) { self.dispatched.push(Sequence::DcsUnhook); } fn print(&mut self, c: char) { self.dispatched.push(Sequence::Print(c)); } fn execute(&mut self, byte: u8) { self.dispatched.push(Sequence::Execute(byte)); } } #[test] fn parse_osc() { let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, OSC_BYTES); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Osc(params, _) => { assert_eq!(params.len(), 2); assert_eq!(params[0], &OSC_BYTES[2..3]); assert_eq!(params[1], &OSC_BYTES[4..(OSC_BYTES.len() - 1)]); }, _ => panic!("expected osc sequence"), } } #[test] fn parse_empty_osc() { let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &[0x1B, 0x5D, 0x07]); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Osc(..) => (), _ => panic!("expected osc sequence"), } } #[test] fn parse_osc_max_params() { let params = ";".repeat(params::MAX_PARAMS + 1); let input = format!("\x1b]{}\x1b", ¶ms[..]).into_bytes(); let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &input); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Osc(params, _) => { assert_eq!(params.len(), MAX_OSC_PARAMS); assert!(params.iter().all(Vec::is_empty)); }, _ => panic!("expected osc sequence"), } } #[test] fn osc_bell_terminated() { const INPUT: &[u8] = b"\x1b]11;ff/00/ff\x07"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Osc(_, true) => (), _ => panic!("expected osc with bell terminator"), } } #[test] fn osc_c0_st_terminated() { const INPUT: &[u8] = b"\x1b]11;ff/00/ff\x1b\\"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 2); match &dispatcher.dispatched[0] { Sequence::Osc(_, false) => (), _ => panic!("expected osc with ST terminator"), } } #[test] fn parse_osc_with_utf8_arguments() { const INPUT: &[u8] = &[ 0x0D, 0x1B, 0x5D, 0x32, 0x3B, 0x65, 0x63, 0x68, 0x6F, 0x20, 0x27, 0xC2, 0xAF, 0x5C, 0x5F, 0x28, 0xE3, 0x83, 0x84, 0x29, 0x5F, 0x2F, 0xC2, 0xAF, 0x27, 0x20, 0x26, 0x26, 0x20, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x20, 0x31, 0x07, ]; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched[0], Sequence::Execute(b'\r')); let osc_data = INPUT[5..(INPUT.len() - 1)].into(); assert_eq!(dispatcher.dispatched[1], Sequence::Osc(vec![vec![b'2'], osc_data], true)); assert_eq!(dispatcher.dispatched.len(), 2); } #[test] fn osc_containing_string_terminator() { const INPUT: &[u8] = b"\x1b]2;\xe6\x9c\xab\x1b\\"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 2); match &dispatcher.dispatched[0] { Sequence::Osc(params, _) => { assert_eq!(params[1], &INPUT[4..(INPUT.len() - 2)]); }, _ => panic!("expected osc sequence"), } } #[test] fn exceed_max_buffer_size() { const NUM_BYTES: usize = MAX_OSC_RAW + 100; const INPUT_START: &[u8] = b"\x1b]52;s"; const INPUT_END: &[u8] = b"\x07"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); // Create valid OSC escape parser.advance(&mut dispatcher, INPUT_START); // Exceed max buffer size parser.advance(&mut dispatcher, &[b'a'; NUM_BYTES]); // Terminate escape for dispatch parser.advance(&mut dispatcher, INPUT_END); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Osc(params, _) => { assert_eq!(params.len(), 2); assert_eq!(params[0], b"52"); #[cfg(feature = "std")] assert_eq!(params[1].len(), NUM_BYTES + INPUT_END.len()); #[cfg(not(feature = "std"))] assert_eq!(params[1].len(), MAX_OSC_RAW - params[0].len()); }, _ => panic!("expected osc sequence"), } } #[test] fn parse_csi_max_params() { // This will build a list of repeating '1;'s // The length is MAX_PARAMS - 1 because the last semicolon is interpreted // as an implicit zero, making the total number of parameters MAX_PARAMS let params = "1;".repeat(params::MAX_PARAMS - 1); let input = format!("\x1b[{}p", ¶ms[..]).into_bytes(); let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &input); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, _, ignore, _) => { assert_eq!(params.len(), params::MAX_PARAMS); assert!(!ignore); }, _ => panic!("expected csi sequence"), } } #[test] fn parse_csi_params_ignore_long_params() { // This will build a list of repeating '1;'s // The length is MAX_PARAMS because the last semicolon is interpreted // as an implicit zero, making the total number of parameters MAX_PARAMS + 1 let params = "1;".repeat(params::MAX_PARAMS); let input = format!("\x1b[{}p", ¶ms[..]).into_bytes(); let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &input); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, _, ignore, _) => { assert_eq!(params.len(), params::MAX_PARAMS); assert!(ignore); }, _ => panic!("expected csi sequence"), } } #[test] fn parse_csi_params_trailing_semicolon() { let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, b"\x1b[4;m"); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, ..) => assert_eq!(params, &[[4], [0]]), _ => panic!("expected csi sequence"), } } #[test] fn parse_csi_params_leading_semicolon() { // Create dispatcher and check state let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, b"\x1b[;4m"); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, ..) => assert_eq!(params, &[[0], [4]]), _ => panic!("expected csi sequence"), } } #[test] fn parse_long_csi_param() { // The important part is the parameter, which is (i64::MAX + 1) const INPUT: &[u8] = b"\x1b[9223372036854775808m"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, ..) => assert_eq!(params, &[[u16::MAX]]), _ => panic!("expected csi sequence"), } } #[test] fn csi_reset() { const INPUT: &[u8] = b"\x1b[3;1\x1b[?1049h"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, intermediates, ignore, _) => { assert_eq!(intermediates, b"?"); assert_eq!(params, &[[1049]]); assert!(!ignore); }, _ => panic!("expected csi sequence"), } } #[test] fn csi_subparameters() { const INPUT: &[u8] = b"\x1b[38:2:255:0:255;1m"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, intermediates, ignore, _) => { assert_eq!(params, &[vec![38, 2, 255, 0, 255], vec![1]]); assert_eq!(intermediates, &[]); assert!(!ignore); }, _ => panic!("expected csi sequence"), } } #[test] fn parse_dcs_max_params() { let params = "1;".repeat(params::MAX_PARAMS + 1); let input = format!("\x1bP{}p", ¶ms[..]).into_bytes(); let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &input); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::DcsHook(params, _, ignore, _) => { assert_eq!(params.len(), params::MAX_PARAMS); assert!(params.iter().all(|param| param == &[1])); assert!(ignore); }, _ => panic!("expected dcs sequence"), } } #[test] fn dcs_reset() { const INPUT: &[u8] = b"\x1b[3;1\x1bP1$tx\x9c"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 3); match &dispatcher.dispatched[0] { Sequence::DcsHook(params, intermediates, ignore, _) => { assert_eq!(intermediates, b"$"); assert_eq!(params, &[[1]]); assert!(!ignore); }, _ => panic!("expected dcs sequence"), } assert_eq!(dispatcher.dispatched[1], Sequence::DcsPut(b'x')); assert_eq!(dispatcher.dispatched[2], Sequence::DcsUnhook); } #[test] fn parse_dcs() { const INPUT: &[u8] = b"\x1bP0;1|17/ab\x9c"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 7); match &dispatcher.dispatched[0] { Sequence::DcsHook(params, _, _, c) => { assert_eq!(params, &[[0], [1]]); assert_eq!(c, &'|'); }, _ => panic!("expected dcs sequence"), } for (i, byte) in b"17/ab".iter().enumerate() { assert_eq!(dispatcher.dispatched[1 + i], Sequence::DcsPut(*byte)); } assert_eq!(dispatcher.dispatched[6], Sequence::DcsUnhook); } #[test] fn intermediate_reset_on_dcs_exit() { const INPUT: &[u8] = b"\x1bP=1sZZZ\x1b+\x5c"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 6); match &dispatcher.dispatched[5] { Sequence::Esc(intermediates, ..) => assert_eq!(intermediates, b"+"), _ => panic!("expected esc sequence"), } } #[test] fn esc_reset() { const INPUT: &[u8] = b"\x1b[3;1\x1b(A"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Esc(intermediates, ignore, byte) => { assert_eq!(intermediates, b"("); assert_eq!(*byte, b'A'); assert!(!ignore); }, _ => panic!("expected esc sequence"), } } #[test] fn esc_reset_intermediates() { const INPUT: &[u8] = b"\x1b[?2004l\x1b#8"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 2); assert_eq!(dispatcher.dispatched[0], Sequence::Csi(vec![vec![2004]], vec![63], false, 'l')); assert_eq!(dispatcher.dispatched[1], Sequence::Esc(vec![35], false, 56)); } #[test] fn params_buffer_filled_with_subparam() { const INPUT: &[u8] = b"\x1b[::::::::::::::::::::::::::::::::x\x1b"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, intermediates, ignore, c) => { assert_eq!(intermediates, &[]); assert_eq!(params, &[[0; 32]]); assert_eq!(c, &'x'); assert!(ignore); }, _ => panic!("expected csi sequence"), } } #[cfg(not(feature = "std"))] #[test] fn build_with_fixed_size() { const INPUT: &[u8] = b"\x1b[3;1\x1b[?1049h"; let mut dispatcher = Dispatcher::default(); let mut parser: Parser<30> = Parser::new_with_size(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Csi(params, intermediates, ignore, _) => { assert_eq!(intermediates, b"?"); assert_eq!(params, &[[1049]]); assert!(!ignore); }, _ => panic!("expected csi sequence"), } } #[cfg(not(feature = "std"))] #[test] fn exceed_fixed_osc_buffer_size() { const OSC_BUFFER_SIZE: usize = 32; const NUM_BYTES: usize = OSC_BUFFER_SIZE + 100; const INPUT_START: &[u8] = b"\x1b]52;"; const INPUT_END: &[u8] = b"\x07"; let mut dispatcher = Dispatcher::default(); let mut parser: Parser = Parser::new_with_size(); // Create valid OSC escape parser.advance(&mut dispatcher, INPUT_START); // Exceed max buffer size parser.advance(&mut dispatcher, &[b'a'; NUM_BYTES]); // Terminate escape for dispatch parser.advance(&mut dispatcher, INPUT_END); assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { Sequence::Osc(params, _) => { assert_eq!(params.len(), 2); assert_eq!(params[0], b"52"); assert_eq!(params[1].len(), OSC_BUFFER_SIZE - params[0].len()); for item in params[1].iter() { assert_eq!(*item, b'a'); } }, _ => panic!("expected osc sequence"), } } #[cfg(not(feature = "std"))] #[test] fn fixed_size_osc_containing_string_terminator() { const INPUT_START: &[u8] = b"\x1b]2;"; const INPUT_MIDDLE: &[u8] = b"s\xe6\x9c\xab"; const INPUT_END: &[u8] = b"\x1b\\"; let mut dispatcher = Dispatcher::default(); let mut parser: Parser<5> = Parser::new_with_size(); parser.advance(&mut dispatcher, INPUT_START); parser.advance(&mut dispatcher, INPUT_MIDDLE); parser.advance(&mut dispatcher, INPUT_END); assert_eq!(dispatcher.dispatched.len(), 2); match &dispatcher.dispatched[0] { Sequence::Osc(params, false) => { assert_eq!(params[0], b"2"); assert_eq!(params[1], INPUT_MIDDLE); }, _ => panic!("expected osc sequence"), } } #[test] fn unicode() { const INPUT: &[u8] = b"\xF0\x9F\x8E\x89_\xF0\x9F\xA6\x80\xF0\x9F\xA6\x80_\xF0\x9F\x8E\x89"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 6); assert_eq!(dispatcher.dispatched[0], Sequence::Print('🎉')); assert_eq!(dispatcher.dispatched[1], Sequence::Print('_')); assert_eq!(dispatcher.dispatched[2], Sequence::Print('🦀')); assert_eq!(dispatcher.dispatched[3], Sequence::Print('🦀')); assert_eq!(dispatcher.dispatched[4], Sequence::Print('_')); assert_eq!(dispatcher.dispatched[5], Sequence::Print('🎉')); } #[test] fn invalid_utf8() { const INPUT: &[u8] = b"a\xEF\xBCb"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 3); assert_eq!(dispatcher.dispatched[0], Sequence::Print('a')); assert_eq!(dispatcher.dispatched[1], Sequence::Print('�')); assert_eq!(dispatcher.dispatched[2], Sequence::Print('b')); } #[test] fn partial_utf8() { const INPUT: &[u8] = b"\xF0\x9F\x9A\x80"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &INPUT[..1]); parser.advance(&mut dispatcher, &INPUT[1..2]); parser.advance(&mut dispatcher, &INPUT[2..3]); parser.advance(&mut dispatcher, &INPUT[3..]); assert_eq!(dispatcher.dispatched.len(), 1); assert_eq!(dispatcher.dispatched[0], Sequence::Print('🚀')); } #[test] fn partial_utf8_separating_utf8() { // This is different from the `partial_utf8` test since it has a multi-byte UTF8 // character after the partial UTF8 state, causing a partial byte to be present // in the `partial_utf8` buffer after the 2-byte codepoint. // "ĸ🎉" const INPUT: &[u8] = b"\xC4\xB8\xF0\x9F\x8E\x89"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &INPUT[..1]); parser.advance(&mut dispatcher, &INPUT[1..]); assert_eq!(dispatcher.dispatched.len(), 2); assert_eq!(dispatcher.dispatched[0], Sequence::Print('ĸ')); assert_eq!(dispatcher.dispatched[1], Sequence::Print('🎉')); } #[test] fn partial_invalid_utf8() { const INPUT: &[u8] = b"a\xEF\xBCb"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &INPUT[..1]); parser.advance(&mut dispatcher, &INPUT[1..2]); parser.advance(&mut dispatcher, &INPUT[2..3]); parser.advance(&mut dispatcher, &INPUT[3..]); assert_eq!(dispatcher.dispatched.len(), 3); assert_eq!(dispatcher.dispatched[0], Sequence::Print('a')); assert_eq!(dispatcher.dispatched[1], Sequence::Print('�')); assert_eq!(dispatcher.dispatched[2], Sequence::Print('b')); } #[test] fn partial_invalid_utf8_split() { const INPUT: &[u8] = b"\xE4\xBF\x99\xB5"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, &INPUT[..2]); parser.advance(&mut dispatcher, &INPUT[2..]); assert_eq!(dispatcher.dispatched[0], Sequence::Print('俙')); assert_eq!(dispatcher.dispatched[1], Sequence::Print('�')); } #[test] fn partial_utf8_into_esc() { const INPUT: &[u8] = b"\xD8\x1b012"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 4); assert_eq!(dispatcher.dispatched[0], Sequence::Print('�')); assert_eq!(dispatcher.dispatched[1], Sequence::Esc(Vec::new(), false, b'0')); assert_eq!(dispatcher.dispatched[2], Sequence::Print('1')); assert_eq!(dispatcher.dispatched[3], Sequence::Print('2')); } #[test] fn c1s() { const INPUT: &[u8] = b"\x00\x1f\x80\x90\x98\x9b\x9c\x9d\x9e\x9fa"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 11); assert_eq!(dispatcher.dispatched[0], Sequence::Execute(0)); assert_eq!(dispatcher.dispatched[1], Sequence::Execute(31)); assert_eq!(dispatcher.dispatched[2], Sequence::Execute(128)); assert_eq!(dispatcher.dispatched[3], Sequence::Execute(144)); assert_eq!(dispatcher.dispatched[4], Sequence::Execute(152)); assert_eq!(dispatcher.dispatched[5], Sequence::Execute(155)); assert_eq!(dispatcher.dispatched[6], Sequence::Execute(156)); assert_eq!(dispatcher.dispatched[7], Sequence::Execute(157)); assert_eq!(dispatcher.dispatched[8], Sequence::Execute(158)); assert_eq!(dispatcher.dispatched[9], Sequence::Execute(159)); assert_eq!(dispatcher.dispatched[10], Sequence::Print('a')); } #[test] fn execute_anywhere() { const INPUT: &[u8] = b"\x18\x1a"; let mut dispatcher = Dispatcher::default(); let mut parser = Parser::new(); parser.advance(&mut dispatcher, INPUT); assert_eq!(dispatcher.dispatched.len(), 2); assert_eq!(dispatcher.dispatched[0], Sequence::Execute(0x18)); assert_eq!(dispatcher.dispatched[1], Sequence::Execute(0x1A)); } } vte-0.15.0/src/params.rs000064400000000000000000000071261046102023000131330ustar 00000000000000//! Fixed size parameters list with optional subparameters. use core::fmt::{self, Debug, Formatter}; pub(crate) const MAX_PARAMS: usize = 32; #[derive(Default)] pub struct Params { /// Number of subparameters for each parameter. /// /// For each entry in the `params` slice, this stores the length of the /// param as number of subparams at the same index as the param in the /// `params` slice. /// /// At the subparam positions the length will always be `0`. subparams: [u8; MAX_PARAMS], /// All parameters and subparameters. params: [u16; MAX_PARAMS], /// Number of suparameters in the current parameter. current_subparams: u8, /// Total number of parameters and subparameters. len: usize, } impl Params { /// Returns the number of parameters. #[inline] pub fn len(&self) -> usize { self.len } /// Returns `true` if there are no parameters present. #[inline] pub fn is_empty(&self) -> bool { self.len == 0 } /// Returns an iterator over all parameters and subparameters. #[inline] pub fn iter(&self) -> ParamsIter<'_> { ParamsIter::new(self) } /// Returns `true` if there is no more space for additional parameters. #[inline] pub(crate) fn is_full(&self) -> bool { self.len == MAX_PARAMS } /// Clear all parameters. #[inline] pub(crate) fn clear(&mut self) { self.current_subparams = 0; self.len = 0; } /// Add an additional parameter. #[inline] pub(crate) fn push(&mut self, item: u16) { self.subparams[self.len - self.current_subparams as usize] = self.current_subparams + 1; self.params[self.len] = item; self.current_subparams = 0; self.len += 1; } /// Add an additional subparameter to the current parameter. #[inline] pub(crate) fn extend(&mut self, item: u16) { self.subparams[self.len - self.current_subparams as usize] = self.current_subparams + 1; self.params[self.len] = item; self.current_subparams += 1; self.len += 1; } } impl<'a> IntoIterator for &'a Params { type IntoIter = ParamsIter<'a>; type Item = &'a [u16]; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// Immutable subparameter iterator. pub struct ParamsIter<'a> { params: &'a Params, index: usize, } impl<'a> ParamsIter<'a> { fn new(params: &'a Params) -> Self { Self { params, index: 0 } } } impl<'a> Iterator for ParamsIter<'a> { type Item = &'a [u16]; fn next(&mut self) -> Option { if self.index >= self.params.len() { return None; } // Get all subparameters for the current parameter. let num_subparams = self.params.subparams[self.index]; let param = &self.params.params[self.index..self.index + num_subparams as usize]; // Jump to the next parameter. self.index += num_subparams as usize; Some(param) } fn size_hint(&self) -> (usize, Option) { let remaining = self.params.len() - self.index; (remaining, Some(remaining)) } } impl Debug for Params { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "[")?; for (i, param) in self.iter().enumerate() { if i != 0 { write!(f, ";")?; } for (i, subparam) in param.iter().enumerate() { if i != 0 { write!(f, ":")?; } subparam.fmt(f)?; } } write!(f, "]") } } vte-0.15.0/tests/demo.vte000064400000000000000000000327611046102023000133240ustar 00000000000000Test RED ON GREEN ]52;c;Y2xpcGJvYXJkIHRlc3Q=];;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;some random text;  what   P0;1|17/ab ]2;echo '¯\_(ツ)_/¯' && sleep 1 REEEEEED Test RED ON GREEN ]52;c;Y2xpcGJvYXJkIHRlc3Q=];;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;some random text;  what   P0;1|17/ab ]2;echo '¯\_(ツ)_/¯' && sleep 1 REEEEEEEEEEEEED [6 q [0 q   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 Hello, World Oops Z 汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字 -[ \eAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFFFFFFGGGGGGG SA8SyD4+ĜPVLx yij)]9VmbЮ+~=K.tI zep+dۨX120;#r=:Bʾ>voxY̊曮Bk.l&=^"hl 9˖(ɇzXRV50x>%1]/L6ޔ݄ /:ɀ$%|H6'^oդqE(pop(4qwRfpl5<#y^N伸l HQ&VPYƃ" `TuhM %=D̹]iTYn`w{b{ד`^ WhCT ŀ6|>4|huﴯU-:aу_'rUu8p35 sjm79GCG8 ܘ ޮM!8 ]w!tTrRh ks>XجIy'\\Z0:lwkI4Jg`Mb$p;gj ՙ`!vb$1~'V+W=(sfB g !EI#߉kۘ7"BV|ؽ=]K试7)H{Q a2B7YI|"w]mwNJb ],p gԍ@rB$3zSDKqZEr"BgM+9EQ䑽'&'mν||"4ガB4kY$?8\G,BNӢl^{3ʻ1\=6vn%q"A6A aw0ۂFA .<(+ʙ pAsQERĤ2tZܣ&CϾ\}#5"5Hp-X'B4Tm.nG-;f}ʕ ɸJhW #Z/)xx+J*##p{ r^ƮxJEM>vF?D O}wEqoYtAZ X{JN JcZ!= ߎ񇻨b8 D/!IK` >B(HL؃ FEO: +LA/ u:|ۆ5& FMΏsa ֎Å,\8m/yaݟ}3(dןf͵␗uJ\9Q ^QIo8fuڀG~YeA>b@amKk=VyNR芫x܊8Qf^&c}k+{%@b)tj jkPoŎ.2?t\})C>`D I',Wّ6?At6WdGi*~H଺V }p3dw<Ҝo=DT=F Mwحnԃ54wbdw(IJRNv fD֪Vљ4ѧ]c"/Eg,~m afE@;V]S<-L"raE4"Joo@te"0c>FV 70z RMQ;t開C [P5ȏ[nx}O#I d!lW ]oC#Mux*ۘ8_)p6B3L mìO1 Iw@qR)Ue@V2VzDzӃ>~r$ȸπW7דJ;jd"~J;^G91(<]ayh-PSg(6BD 3O7laU%& :>+ )L؞xeS6o}/ =5EM7'*j/wp'ED"E; xm`},ovJi=D$ҼNqA?њ h:?'_-+>2[A!č~p+F˚Z}Ir:Q`CIT{+6`\gEeY7{j:!~>ybնAMH#We[]Rt;%Na1Ā 4UcOnh0hVVE\D:J`nO HzX45,>pd!^'hOw.K @ ׁ;o"˼"bj)!EJ7D댩cjVn(3@QLn:rR Ma_~Қ7?BH{Q'k s<3o[x瘘Fo׊rl偞ջ p`3uY %?eJ?`ܛ=QW+ۘ*k<[ř;s0#tgH<43HJg'dc8_SW5LBϚ1Zo?| ИDW9_lx_/Hyb0;ϫO,eͦ8gG]8;j拱g,uH'rU[YPͣEp.9WMs)řmfΧ͍Va>D"h