clearscreen-2.0.1/.cargo_vcs_info.json0000644000000001360000000000100133220ustar { "git": { "sha1": "1febc5190854d76b37807a0ed7cbcd94b6cb9224" }, "path_in_vcs": "" }clearscreen-2.0.1/.editorconfig000064400000000000000000000003541046102023000145710ustar 00000000000000root = true [*] indent_style = tab indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yml] indent_style = space indent_size = 2 [*.md] indent_style = space indent_size = 2 clearscreen-2.0.1/.gitignore000064400000000000000000000000231046102023000140750ustar 00000000000000/target Cargo.lock clearscreen-2.0.1/.rustfmt.toml000064400000000000000000000000211046102023000145620ustar 00000000000000hard_tabs = true clearscreen-2.0.1/CHANGELOG.md000064400000000000000000000042321046102023000137240ustar 00000000000000# Changelog ## Next (YYYY-MM-DD) ## v2.0.1 (2023-04-04) ## v2.0.0 (2022-12-28) - Don't use BORS. - Update dependencies. - Update to nix 0.26. - Change MSRV policy to stable-5 supported, and bump MSRV to 1.60.0. - Handle tmux explicitly ([#9](https://github.com/watchexec/clearscreen/pull/9)). - Fall back to hardcoded sequence if terminfo is not available ([#9](https://github.com/watchexec/clearscreen/pull/9)). ## v1.0.10 (2022-06-01) - Use BORS. - Update to nix 0.24, limit features to only those used ([#6](https://github.com/watchexec/clearscreen/pull/6)). ## v1.0.9 (2021-12-02) - Change CI test to test Windows 10 detection with a manifested test executable. - Clarify in documentation the expected behaviour of `is_windows_10()` and what is or not a bug. ## ~~v1.0.8 (2021-12-02)~~ (yanked) - Stop checking powershell's `PackageManagement` capability as a Win10 check ([#5](https://github.com/watchexec/clearscreen/issues/5)). ## v1.0.7 (2021-08-26) - Flush after E3 sequence in `Terminfo` ([#4](https://github.com/watchexec/clearscreen/issues/4)). ## v1.0.6 (2021-07-22) - Omit unsupported UTF8 input flag on non-Linux. ## v1.0.5 (2021-07-22) - Update to nix 0.22. ## v1.0.4 (2021-05-22) - Fix [#1](https://github.com/watchexec/clearscreen/issues/1): need to flush after writing sequences. ## v1.0.3 (2021-05-08) - Drop unused `log` dependency. - Generalise iTerm workaround from 1.0.1 to default behaviour on macOS when the `TERM` starts with `xterm` and the terminfo does not have `E3`. - Hide `WindowsConsoleClear` and `WindowsConsoleBlank` under an undocumented feature as they are buggy/do not work as per my testing on Win10. `WindowsVtClear` and `Cls` are sufficient for clear. ## v1.0.2 (2021-04-29) - Use `VtRis` for Kitty when using its own terminfo. - Use `VtRis` for SyncTERM, Tess, any rxvt, Zellij, and Zutty. - Use `XtermClear` for these when using their own terminfo: - GNOME Terminal, - Konsole, - screen, - Termite, - XFCE4 Terminal. ## v1.0.1 (2021-04-26) - Use `XtermClear` on iTerm on macOS when `TERM` starts with `xterm`, to work around macOS not having the correct terminfo by default. ## v1.0.0 (2021-04-25) Initial release clearscreen-2.0.1/CITATION.cff000064400000000000000000000007161046102023000140100ustar 00000000000000cff-version: 1.2.0 message: | If you use this software, please cite it using these metadata. title: "ClearScreen: a library to clear terminal screens and a research dataset on screen clearing behaviour in terminal emulators" version: "2.0.1" date-released: 2023-04-04 repository-code: https://github.com/watchexec/clearscreen license: Apache-2.0 OR MIT authors: - family-names: Saparelli given-names: Félix orcid: https://orcid.org/0000-0002-2010-630X clearscreen-2.0.1/COPYRIGHT000064400000000000000000000012041046102023000134020ustar 00000000000000Short version for non-lawyers: This project is dual-licensed under Apache 2.0 and MIT terms. Longer version: Copyrights in this project are retained by their contributors. No copyright assignment is required to contribute. Some files include explicit copyright notices and/or license notices. For full authorship information, see the version control history. Except as otherwise noted (below and/or in individual files), the project is licensed under the Apache License, Version 2.0 or or the MIT license or , at your option. clearscreen-2.0.1/Cargo.lock0000644000000174670000000000100113140ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clearscreen" version = "2.0.1" dependencies = [ "nix", "terminfo", "thiserror", "which", "winapi", ] [[package]] name = "dirs" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" dependencies = [ "libc", "redox_users", "winapi", ] [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "libc" version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nix" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", "cfg-if", "libc", "static_assertions", ] [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "phf" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" dependencies = [ "phf_shared", ] [[package]] name = "phf_codegen" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770" dependencies = [ "phf_generator", "phf_shared", ] [[package]] name = "phf_generator" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" dependencies = [ "phf_shared", "rand", ] [[package]] name = "phf_shared" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" dependencies = [ "siphasher", ] [[package]] name = "proc-macro2" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "redox_syscall" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ "bitflags", ] [[package]] name = "redox_users" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", "redox_syscall", ] [[package]] name = "siphasher" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "terminfo" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "666cd3a6681775d22b200409aad3b089c5b99fb11ecdd8a204d9d62f8148498f" dependencies = [ "dirs", "fnv", "nom", "phf", "phf_codegen", ] [[package]] name = "thiserror" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "which" version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", "once_cell", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" clearscreen-2.0.1/Cargo.toml0000644000000027630000000000100113300ustar # 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.60.0" name = "clearscreen" version = "2.0.1" authors = ["Félix Saparelli "] exclude = [ "/bin", "/.github", ] description = "Cross-platform terminal screen clearing" homepage = "https://github.com/watchexec/clearscreen" documentation = "https://github.com/watchexec/clearscreen" readme = "README.md" keywords = [ "clear", "cls", "cli", "terminal", ] license = "Apache-2.0 OR MIT" repository = "https://github.com/watchexec/clearscreen" [dependencies.terminfo] version = "0.8.0" [dependencies.thiserror] version = "1.0.38" [dependencies.which] version = "4.3.0" [features] windows-console = [] [target."cfg(unix)".dependencies.nix] version = "0.26.1" features = [ "fs", "term", ] default-features = false [target."cfg(windows)".dependencies.winapi] version = "0.3.9" features = [ "consoleapi", "errhandlingapi", "handleapi", "impl-default", "lmapibuf", "lmserver", "lmwksta", "processenv", "winbase", "wincon", ] clearscreen-2.0.1/Cargo.toml.orig000064400000000000000000000016441046102023000150060ustar 00000000000000[package] name = "clearscreen" version = "2.0.1" authors = ["Félix Saparelli "] license = "Apache-2.0 OR MIT" description = "Cross-platform terminal screen clearing" keywords = ["clear", "cls", "cli", "terminal"] documentation = "https://github.com/watchexec/clearscreen" homepage = "https://github.com/watchexec/clearscreen" repository = "https://github.com/watchexec/clearscreen" readme = "README.md" edition = "2021" exclude = ["/bin", "/.github"] rust-version = "1.60.0" [features] windows-console = [] [dependencies] terminfo = "0.8.0" thiserror = "1.0.38" which = "4.3.0" [target.'cfg(unix)'.dependencies.nix] version = "0.26.1" default-features = false features = ["fs", "term"] [target.'cfg(windows)'.dependencies.winapi] version = "0.3.9" features = [ "consoleapi", "errhandlingapi", "handleapi", "impl-default", "lmapibuf", "lmserver", "lmwksta", "processenv", "winbase", "wincon", ] clearscreen-2.0.1/LICENSE-APACHE000064400000000000000000000227731046102023000140510ustar 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 clearscreen-2.0.1/LICENSE-MIT000064400000000000000000000017771046102023000135620ustar 00000000000000Permission 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. clearscreen-2.0.1/README.md000064400000000000000000000020371046102023000133730ustar 00000000000000[![Crate release version](https://badgen.net/crates/v/clearscreen)](https://crates.io/crates/clearscreen) [![Crate license: Apache 2.0 or MIT](https://badgen.net/badge/license/Apache%202.0%20or%20MIT)][copyright] [![CI status on main branch](https://github.com/watchexec/clearscreen/actions/workflows/tests.yml/badge.svg)](https://github.com/watchexec/clearscreen/actions/workflows/main.yml) # ClearScreen _Cross-platform terminal screen clearing library._ - **[API documentation][docs]**. - [Dual-licensed][copyright] with Apache 2.0 and MIT. - Minimum Supported Rust Version: 1.60.0. - Only the last five stable versions are supported. - MSRV increases beyond that range at publish time will not incur major version bumps. [copyright]: ./COPYRIGHT [docs]: https://docs.rs/clearscreen Tested with and tweaked for over 80 different terminals, multiplexers, SSH clients. See my research notes in the [TERMINALS.md](./TERMINALS.md) file. ## Quick start ```toml [dependencies] clearscreen = "2.0.1" ``` ```rust clearscreen::clear().unwrap(); ``` clearscreen-2.0.1/TERMINALS.md000064400000000000000000001205441046102023000140000ustar 00000000000000Research on Terminals ===================== All tested with their latest version obtainable of Arch Linux (or macOS 11, Windows 10) as of writing. Version tested is noted where possible, but otherwise compare to the git blame date. To contribute entries: - Insert in the correct category, in lexicographic order - Test with both the terminal’s own terminfo, and with `xterm-256color`. - If the terminal doesn’t have its own terminfo, note that, and note which it is trying to emulate. - And consider filing a bug to tell them to provide their own terminfo! - If a terminal has forks, especially if there’s a lot of them, only document a fork if its behaviour is different. - If the terminal is based on a common library, mention it. - If the terminal is web-based, mention that. - Document the current selection of `::default()`. - Document the behaviour of at least: - `Terminfo` - `TerminfoScreen` - `TerminfoScrollback` - `VtRis` - `XtermClear` - “Normal” behaviour refers to: - `::default()`: screen and scrollback (if at all possible) cleared - `Terminfo`: at least screen cleared, and optionally scrollback - `TerminfoScreen`: only screen cleared - `TerminfoScrollback`: only scrollback cleared - `VtRis`: screen and scrollback cleared, and (at least some modes of) terminal reset - `XtermClear`: screen and scrollback cleared - `Cls`: screen and scrollback cleared - `WindowsVtClear`: screen and scrollback cleared - There is zero tolerance for advertising via this document. How to test: ------------ First link the clscli example program into your PATH, e.g. ``` ln -s $(pwd)/target/debug/examples/clscli ~/.local/share/bin/clscli ``` Open the terminal in its default profile, or as it comes when first installed. Then use `env | grep TERM` to see what the `TERM` and other related variables look like (make note!). Look into `/usr/share/terminfo` for a terminfo that matches the terminal, or wherever it is on your system. If there's a separate but official package for the terminal’s terminfo, use it. First test with the native terminfo: set it either in the terminal’s settings, or use `env TERM=name $SHELL`, then with the `TERM` the terminal first starts with by default, and finally with `xterm-256color` if that’s not been covered yet. 1. First run `clscli auto`. Look quick, the name of the variant selected by default will be printed, and one second later, hopefully, the screen will clear. Document that variant. 2. Then run `clscli Variant` where the variant is: `Terminfo`, `TerminfoScreen`, `TerminfoScrollback`, `VtRis`, `XtermClear`, and the variant discovered in 1, if not one of these. Before each, run `seq 1 100` or something like it to fill the screen and some scrollback. Document the behaviour if it differs from normal, or state “normal.” 3. Optionally (if you want), if `clscli auto` does not exhibit the normal behaviour, open an issue and provide enough details to be able to modify the `::default()` selection to select a different default that works. If you’re really enthusiastic, you can even open a PR with it! 4. To submit your research, either submit a PR to this file (preferred, you can even do it in the GitHub Web UI), or open an issue with your research (I’ll merge it in), or send me an email. Platforms --------- On macOS, the terminfo for `xterm` and variants does not by default include E3, which makes `Terminfo` not clear scrollback and `TerminfoScrollback` return an error (E3 not found), even when the terminal in question actually does support E3. For that reason, default behaviour on macOS is switched to use `XtermClear` if the `TERM` starts with `xterm` and the terminfo doesn’t have E3. If the terminfo database is not available, `::default()` falls back to `XTermClear` instead of supplying a useless `Terminfo`. When testing, it's expected to have a functional terminfo where practical. Emulator libraries ------------------ ### BearLibTerminal ### libamxt ### libt3widget ### libt3window ### libterm ### libtickit ### libtsm ### libvterm ### Qtermwidget ### Rote ### VTE When “VTE-based” is stated and nothing else, assume this: Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. Emulators --------- ### Alacritty - Version 0.7.2 With native `TERM=alacritty`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Aminal - Version Nightly-develop-2020-01-26-4033a8b Native `TERM` is `xterm-256color`. - Default: `Terminfo`. **The better option would be `VtRis`, but there’s no way to tell we’re running in Aminal.** - `Terminfo`: does not clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does not clear scrollback, erases the screen, but leaves cursor position intact, i.e. at the bottom of the screen if we were there. - `VtRis`: clears screen, doesn’t clear scrollback, but does push the existing output up, so that information is not lost. - `XtermClear`: as for `Terminfo`. ### Android Terminal Emulator ### Archipelago - Web-based ### ate - Version 1.0.1 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Blink Shell (iOS) ### Bterm - Version 2.0.0 Native `TERM` is `xterm`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Butterfly - Web-based ### Cathode ### CMD.EXE - Windows 10 Pro, build 19042.630 There's no `TERM` variable and no terminfo database. - Default: `WindowsVtClear`. - `Terminfo`: there's no TERM nor terminfo database. - `TerminfoScreen`: there's no TERM nor terminfo database. - `TerminfoScrollback`: there's no TERM nor terminfo database. - `VtRis`: prints `←c` and does nothing else. - `XtermClear`: prints `←[H←[2J←[3J` and does nothing else. - `Cls`: normal. - `WindowsConsoleClear`: does nothing ***BUG!*** - `WindowsConsoleBlank`: does nothing ***BUG!*** - `WindowsVtClear`: normal. ### ConEmu - Version 210422 There's no `TERM` variable and no terminfo database. - Default: `WindowsVtClear`. - `Terminfo`: there's no TERM nor terminfo database. - `TerminfoScreen`: there's no TERM nor terminfo database. - `TerminfoScrollback`: there's no TERM nor terminfo database. - `VtRis`: normal. - `XtermClear`: normal. - `Cls`: normal. - `WindowsVtClear`: normal. ### ConsoleZ - Version 1.19.0.19104 There's no `TERM` variable and no terminfo database. - Default: `WindowsVtClear`. - `Terminfo`: there's no TERM nor terminfo database. - `TerminfoScreen`: there's no TERM nor terminfo database. - `TerminfoScrollback`: there's no TERM nor terminfo database. - `VtRis`: prints `←c`, does nothing else. - `XtermClear`: prints `←[H←[2J←[3J`, does nothing else. - `Cls`: normal. - `WindowsVtClear`: normal. ### Cool Retro Term - Version 1.1.1 Native `TERM` is `xterm`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: scrollback not cleared. - `XtermClear`: normal. ### Core Terminal - Version 4.2.0 - Doesn’t respect user shell by default. Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: scrollback not cleared. - `XtermClear`: normal. ### Deepin Terminal - Version 5.4.0.6 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: scrollback not cleared. - `XtermClear`: normal. #### Old GTK version - Version 5.0.4.3 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Dinu ### dmenu-term? ### domterm - Web-based? ### dwt - Version 0.6.0 - VTE-based ### eDEX UI - Version 2.2.7 - Doesn’t respect user shell by default. Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Electerm - Version 1.11.16 - Doesn’t respect user shell by default. Native `TERM` is `xterm-256color`. - Default: `Terminfo`. **The better option would be `VtRis`, but there’s no way to tell we’re running in Electerm.** - `Terminfo`: normal, except scrollbar is weird, like it thinks there’s still all the old content, but without showing any scrolling when going up or down. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: as for `Terminfo`. ### Elokab Terminal - Arabic language support! ### eterm ### Evil VTE - VTE-based - Untested yet ### ExtraTerm - Version 0.58.0 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. (Mostly because it’s the least worst and has a chance to get better.) - `Terminfo`: does not clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: behaves like `Terminfo` but also prints `[2m` (badly handled unknown escape). - `XtermClear`: as for `Terminfo`. ### fbpad ### Fingerterm - For Nokia N9 phones? ### Fluent Terminal (Windows) - Version 0.7.5.0 - Xterm.js-based There's no `TERM` variable and no terminfo database. - Default: `WindowsVtClear`. - `Terminfo`: there's no TERM nor terminfo database. - `TerminfoScreen`: there's no TERM nor terminfo database. - `TerminfoScrollback`: there's no TERM nor terminfo database. - `VtRis`: does not clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `XtermClear`: as for `VtRis`. - `Cls`: as for `VtRis`. - `WindowsVtClear`: as for `VtRis`. ### Foot - Version 1.7.2 - Wayland only With `TERM=foot`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### FQTerm - Version 0.9.10.1.1.g55d08df With `TERM=vt102`: - Default: `Terminfo`. - `Terminfo`: doesn’t clear scrollback. - `TerminfoScreen`: normal. - `TerminfoScrollback`: doesn’t support E3. - `VtRis`: does nothing. - `XtermClear`: doesn’t clear scrollback. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: doesn’t clear scrollback. - `TerminfoScreen`: normal. - `TerminfoScrollback`: does nothing. - `VtRis`: does nothing. - `XtermClear`: doesn’t clear scrollback. ### Germinal - Version 26 - VTE-based ### Guake - Version 3.7.0 - VTE-based ### GNOME Terminal - Version 3.40.0 - VTE-based With `TERM=gnome-256color`: - Default: `XTermClear`. - `Terminfo`: behaves like `TerminfoScreen`, doesn’t clear scrollback. - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Goterminal ### Havoc - Wayland only ### Hyper - Web-based ### iTerm2 - Version 3.3.12 Native `TERM` is `xterm-256color`. - Default: `XtermClear`. - `Terminfo`: normal (does not clear scrollback). - `TerminfoScreen`: normal. - `TerminfoScrollback`: does not support E3. - `VtRis`: does not clear scrollback (behaves like `TerminfoScreen`). - `XtermClear`: normal. ### jbxvt ### jfbterm ### JuiceSSH - Version Native `TERM` is `linux`. - Default: `Terminfo`. - `Terminfo`: does not clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: does nothing - `XtermClear`: as for `Terminfo`. ### Kermit - Version 3.4 - VTE-based The `kermit` terminfo also exists, but may not be related, and does not work. ### King’s Cross (kgx) - Version 0.2.1 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Kitty - Version 0.20.1 With native `TERM=xterm-kitty`: - Default: `VtRis`. - `Terminfo`: does not clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=kitty`: as with `xterm-kitty`. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: erases scrollback and screen, but does not clear them (can be scrolled, but all is blank). - `VtRis`: normal. - `XtermClear`: normal. ### KMScon ### Konsole - Version 21.04.0 With native `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `XtermClear`: normal. With `TERM=konsole`: - Default: `XtermClear`. - `Terminfo`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does not support E3. - `VtRis`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `XtermClear`: normal. ### Lilyterm - libvte-based ### Liri Terminal - Version 0.2.0 - Doesn’t respect user shell by default. Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: doesn’t clear scrollback. - `XtermClear`: normal. ### Literm - fingerterm-based? ### lwt - Version 2020-12-02 - VTE-based - Doesn’t respect user shell by default. Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: normal. - `VtRis`: doesn’t clear scrollback. - `XtermClear`: normal. ### LX Terminal - Version 0.4.0 - VTE-based ### MacTerm - Version 5 for macOS =>10.15 in development, I don't have an older mac to test 4.x. ### MacWise - Version 21.6 - In VT100 emulation mode - Does not have a native `TERM`. With `TERM=vt100`: - Default: `Terminfo`. - `Terminfo`: erases the screen without scrolling up, thus losing info, then inserts a screenful of whitespace, then scrolls up. Does not clear scrollback. - `TerminfoScreen`: as for `Terminfo`. - `TerminfoScrollback`: does not support E3. - `VtRis`: does not clear scrollback, does not reset style. - `XtermClear`: scrolls screen up, then fills the screen with whitespace, places the cursor at the bottom right, then prints `3.2$`, then does that once again. (???) With `TERM=xterm-256color`: - Default: `XtermClear`. (`Terminfo` would be better, but impossible to detect.) - `Terminfo`: normal. Does not clear scrollback. - `TerminfoScreen`: normal. - `TerminfoScrollback`: does not support E3. - `VtRis`: does not clear scrollback, does not reset style. - `XtermClear`: as with `TERM=vt100`. ### Mantid - Version 1.0.6 - VTE-based ### MATE Terminal - Version 1.24.1 - VTE-based ### Maui Station - Version 1.2.1 Native `TERM` is `xterm`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: doesn’t clear scrollback. - `XtermClear`: normal. ### Microsoft Terminal / Windows Terminal - Version 1.7.1033.0 There's no `TERM` variable and no terminfo database. - Default: `XtermClear`. - `Terminfo`: there's no TERM nor terminfo database. - `TerminfoScreen`: there's no TERM nor terminfo database. - `TerminfoScrollback`: there's no TERM nor terminfo database. - `VtRis`: normal. - `XtermClear`: normal. - `WindowsVtClear`: normal. - `Cls`: normal. ### Miniterm - Version 1.7.0 - VTE-based ### MinTTY (Windows) - Version 3.1.6 - PuTTY-based? - Via Git-Bash Native `TERM` is `xterm` - Default: `WindowsVtClear`. - `Terminfo`: there's no terminfo database. - `TerminfoScreen`: there's no terminfo database. - `TerminfoScrollback`: there's no terminfo database. - `VtRis`: normal. - `XtermClear`: normal. - `Cls`: does nothing. - `WindowsVtClear`: normal. ### Miro - Version 0.2.0 ### MLTERM - Version 3.9.0 Native `TERM` is `xterm`. - Default: `Terminfo`. (No real good option here.) - `Terminfo`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `XtermClear`: as for `Terminfo`. ### MobaXterm - Version 21.1 build 4628 Home Edition There's no `TERM` variable and no terminfo database. - Default: `WindowsVtClear`. - `Terminfo`: there's no TERM nor terminfo database. - `TerminfoScreen`: there's no TERM nor terminfo database. - `TerminfoScrollback`: there's no TERM nor terminfo database. - `VtRis`: prints `←c`, does nothing else. - `XtermClear`: prints `←[H←[2J←[3J`, does nothing else. - `Cls`: doesn’t clear scrollback. - `WindowsVtClear`: doesn’t clear scrollback. #### With built-in Bash mode Native `TERM` is `xterm`. - Default: `WindowsVtClear`. - `Terminfo`: there's no terminfo database. - `TerminfoScreen`: there's no terminfo database. - `TerminfoScrollback`: there's no terminfo database. - `VtRis`: doesn’t clear scrollback. - `XtermClear`: normal. - `Cls`: doesn’t clear scrollback. - `WindowsVtClear`: normal. ### mrxvt ### mt ### Nautilus Terminal - Version 3.5.0 - VTE-based ### Nemo Terminal - Version 4.8.0 - VTE-based ### Neovim - Version 0.4.4 `TERM` is inherited. With `xterm-256color`: - Default: `Terminfo`. (No real good option here.) - `Terminfo`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `XtermClear`: as for `Terminfo`. ### Orbterm ### Pangoterm - libvterm-based ### Pantheon/Elementary Terminal - Version 5.5.2 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### PowerCmd ### PuTTY - Version 0.74 With native `TERM=xterm`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: does not clear scrollback. - `XtermClear`: normal. With `TERM=putty`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: does nothing. - `VtRis`: does not clear scrollback. - `XtermClear`: normal. ### QML Konsole - Version 0.1.r2.g81e74ad Native `TERM` is `xterm`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: does nothing. - `VtRis`: does not clear scrollback. - `XtermClear`: normal. ### Qt DOM term ### Qterminal - Version 0.16.1 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### rcfvt - Version r66.d390d61 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### ROXTerm - Version 3.10.1 - VTE-based ### Runes ### Sakura - Version 3.8.1 - VTE-based ### sdvt ### Snowflake ### st - Version 0.8.4 With `TERM=st-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: also clears scrollback. - `TerminfoScrollback`: does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: also clears scrollback. - `TerminfoScrollback`: does nothing. - `VtRis`: normal. - `XtermClear`: normal. ### sterm - Version 0.1.2 - VTE-based Native `TERM` is `xterm-256color`. There’s no scrollback at all, so it’s impossible to know how things are really handled, but 🤷. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### stgl ### StupidTerm - Version 1.r24.gf824e41 - VTE-based ### Syncterm - Version 1.1 Native `TERM` is `syncterm`. - Default: `VtRis`. - `Terminfo`: no terminfo found. - `TerminfoScreen`: no terminfo found. - `TerminfoScrollback`: no terminfo found. - `VtRis`: normal. - `XtermClear`: does not clear scrollback. ### Taterm - Version 12 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Terminal.app (GNUstep) ### Terminal.app (macOS) - Version 2.10 (433) Native `TERM` is `xterm-256color`. - Default: `XtermClear`. - `Terminfo`: normal (does not clear scrollback). - `TerminfoScreen`: normal. - `TerminfoScrollback`: does not support E3. - `VtRis`: erases the screen without scrolling up (not abnormal) and does not clear scrollback. - `XtermClear`: normal. ### Terminaleco ### Terminalpp ### Terminate - Version 0.5 - VTE-based - _Requires_ a TERM to be set, doesn’t manage to get set up properly without. - There’s no scrollback at all, so it’s impossible to know how things are really handled, but 🤷. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Terminator - Version 2.1.1 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Terminol ### Terminology - Version 1.9.0 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Terminus ### Termistor - Wayland only ### Termit - Version 3.1.r4.g29bbd1b - VTE-based ### Termite - Version 15 - VTE-based With native `TERM=xterm-termite`: - Default: `XTermClear`. - `Terminfo`: normal (doesn’t clear scrollback). - `TerminfoScreen`: normal. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=termite`: - Default: `XTermClear`. - `Terminfo`: normal (doesn’t clear scrollback). - `TerminfoScreen`: normal. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Termius - Version 7.9.0 Native `TERM` is `xterm`. - Default: `Terminfo`. **The better option would be `VtRis`, but there’s no way to tell we’re running in Termius.** - `Terminfo`: normal, except scrollbar is weird, like it thinks there’s still all the old content, but without showing any scrolling when going up or down. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal, except scrollbar is even weirder, like it thinks there’s still all the old content, but without _allowing the screen to be scrolled at all._ Once the screen fills up again, the scrollbar resets. - `XtermClear`: as for `Terminfo`. ### Termy - Version 0.3.0 - By nature, the prompt remains at the top, and every command clears the screen. - However, running a shell inside the terminal makes it behave as usually expected, so that's how this is tested. Native `TERM` is `xterm-256color`. - Default: `XtermClear`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. (Doesn’t clear scrollback.) - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does not support E3. - `VtRis`: normal. - `XtermClear`: normal. ### Terra ### Tess - Version 1.2r65.12944dd - Doesn’t respect user shell by default. Native `TERM` is `xterm-color`. - Default: `VtRis`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. (Doesn’t clear scrollback.) - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does not support E3. - `VtRis`: normal. - `XtermClear`: normal, except scrollbar is weird, like it thinks there’s still all the old content, but without showing any scrolling when going up or down. ### The Terminal ### TreeTerm ### Tilda - Version 1.5.4 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Tilix - Version 1.9.4 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Tinyterm - VTE-based - Untested yet ### Topinambour - VTE-based - Untested yet ### Tortosa - VTE-based - Untested yet ### Ume - Version r67.242a9f5 - VTE-based ### urxvt - Version 9.22 With native `TERM=rxvt-unicode-265color`: - Default: `VtRis`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. (Doesn’t clear scrollback.) - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: normal. - `XtermClear`: as for `Terminfo`. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. (Doesn’t clear scrollback.) - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: normal. - `XtermClear`: as for `Terminfo`. ### uterm - libtsm-based ### uuterm - Version 80 - There’s no scrollback at all, so it’s impossible to know how things are really handled, but 🤷. With native `TERM=uuterm`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: very broken, but clearing works as normal. ### Viter - Version r166.c8ca21a Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### vt100-parser ### Wayst - Version r223.e72ca78 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. **The better option would be `VtRis`, but there’s no way to tell we’re running in Wayst.** - `Terminfo`: normal, doesn’t clear scrollback. - `TerminfoScreen`: normal. - `TerminfoScrollback`: clears the screen, keeping the cursor position the same, but doesn’t clear scrollback! - `VtRis`: normal. - `XtermClear`: doesn’t clear scrollback. ### Wezterm - Version 20210405.110924.a5bb5be8 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### WindTerm - Version 2.1.0 (Win10 version) There's no `TERM` variable and no terminfo database. - Default: `WindowsVtClear`. - `Terminfo`: there's no TERM nor terminfo database. - `TerminfoScreen`: there's no TERM nor terminfo database. - `TerminfoScrollback`: there's no TERM nor terminfo database. - `VtRis`: does not clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `XtermClear`: as for `VtRis`. - `Cls`: as for `VtRis`. - `WindowsVtClear`: as for `VtRis`. ### Wlterm - libtsm-based ### wlgxterm ### XFCE4 Terminal - Version 0.8.10 - VTE-based With `TERM=xfce`: - Default: `XTermClear`. - `Terminfo`: behaves like `TerminfoScreen`, doesn’t clear scrollback. - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### xiate - Version 20.07 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Xterm - Version 367 Native `TERM` is `xterm`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: normal, and dings the terminal bell. - `XtermClear`: normal. ### Yaft ### Yaftx - Version 0.2.9 - There’s no scrollback at all, so it’s impossible to know how things are really handled, but 🤷. With native `TERM=yaft-265color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: normal. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: does nothing. - `VtRis`: normal. - `XtermClear`: normal. ### Yakuake - Version 21.04.0 - Konsole-based Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: normal. - `VtRis`: doesn’t clear scrollback, appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `XtermClear`: normal. ### z/Scope - Web-based? ### ZOC - Version 8 (8023) Native `TERM` is `xterm-256color`. - Default: `XtermClear`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: does not clear scrollback. - `XtermClear`: normal. ### Zterm ### Zutty - Version 0.8 Native `TERM` is `xterm-256color`. - Default: `VtRis`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. Doesn’t clear scrollback. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: normal. - `XtermClear`: as for `Terminfo`. Serial terminal emulators? ------------------------- ### Bootterm ### Coolterm ### Cutecom ### dterm ### Easyterm ### HTerm ### iserterm ### Microcom ### Minicom ### Moserial ### Picocom ### ssterm ### tio Multiplexers ------------ ### 3mux - Version 1.1.0 Native `TERM` is `xterm-256color`. - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: **erases the screen like `TerminfoScreen`** and clears scrollback. - `VtRis`: does nothing. - `XtermClear`: normal. ### Byobu - Uses Tmux underneath ### Dvtm - Version 0.15 With native `TERM=dvtm-265color`: - Default: `Terminfo`. (The least worse option.) - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. Doesn’t clear scrollback. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: does nothing. - `XtermClear`: as for `Terminfo`. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. Doesn’t clear scrollback. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: does nothing. - `XtermClear`: as for `Terminfo`. ### Eternal Terminal ### Mosh - Version 1.3.2 - `TERM` is inherited. - There’s no scrollback at all, so it’s impossible to know how things are really handled, but 🤷. Tested here with `xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: does nothing. - `VtRis`: normal. - `XtermClear`: normal. ### mtm - Version r394.b14e99c With native `TERM=screen-265color-bce`: - Default: `XtermClear`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. Doesn’t clear scrollback. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: as for `Terminfo`. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: clears scrollback **and screen**, but leaves the cursor position. - `VtRis`: as for `TerminfoScreen`. - `XtermClear`: normal. ### Screen - Version 4.08.00 With `TERM=screen`: - Default: `XtermClear`. - `Terminfo`: normal (does not clear scrollback). - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: terminfo does not support E3. - `VtRis`: adds a screenful of space to the scrollback before clearing, does not clear scrollback. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: **clears scrollback**, even though `TerminfoScrollback` below doesn’t work. - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: doesn’t do anything. - `VtRis`: adds a screenful of space to the scrollback before clearing, does not clear scrollback. - `XtermClear`: normal. ### Tab-rs - Version 0.5.7 - Scrollback is inherited from the terminal, not managed internally, so depends on what you have. - `TERM` is inherited too, so as long as it passes the escapes out, it will work as the terminal. Tested with `xterm-256color` in Alacritty: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: normal. - `XtermClear`: normal. ### Tmux - Version 3.2 With `TERM=tmux-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: normal. - `TerminfoScrollback`: normal. - `VtRis`: does not clear scrollback. - `XtermClear`: normal. With `TERM=xterm-256color`: - Default: `Terminfo`. - `Terminfo`: normal. - `TerminfoScreen`: adds a screenful of space to the scrollback before clearing. - `TerminfoScrollback`: normal. - `VtRis`: does not clear scrollback. - `XtermClear`: normal. ### Zellij - Version 0.5.1 - `TERM` is inherited. Tested with `xterm-256color` in Alacritty: - Default: `VtRis`. - `Terminfo`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. Doesn’t clear scrollback. - `TerminfoScreen`: appears to clear the screen, but really erases the screen without scrolling the existing output up, thus losing a screenful of information. - `TerminfoScrollback`: does nothing. - `VtRis`: normal. - `XtermClear`: as for `Terminfo`. Recorders --------- ### Asciinema ### Asciinema Rust ### GoTTY ### Hasciinema? ### ipbt ### Shell in a box ### Shellshare ### Showterm ### T-Rec ### Term to SVG ### Terminalizer ### Termrec ### tmate.io ### ts-player ### tty-share ### TTYcast ### ttyd ### upterm ### webtty clearscreen-2.0.1/examples/clscli.rs000064400000000000000000000032771046102023000155600ustar 00000000000000use clearscreen::ClearScreen; use std::{env, thread::sleep, time::Duration}; use thiserror::Error; fn main() -> Result<(), Error> { if let Some(variant) = env::args().nth(1) { let cs = match variant.as_str() { "auto" => ClearScreen::default(), "Terminfo" => ClearScreen::Terminfo, "TerminfoScreen" => ClearScreen::TerminfoScreen, "TerminfoScrollback" => ClearScreen::TerminfoScrollback, "TerminfoReset" => ClearScreen::TerminfoReset, "XtermClear" => ClearScreen::XtermClear, "XtermReset" => ClearScreen::XtermReset, "TputClear" => ClearScreen::TputClear, "TputReset" => ClearScreen::TputReset, "Cls" => ClearScreen::Cls, "WindowsVt" => ClearScreen::WindowsVt, "WindowsVtClear" => ClearScreen::WindowsVtClear, #[cfg(feature = "windows-console")] "WindowsConsoleClear" => ClearScreen::WindowsConsoleClear, #[cfg(feature = "windows-console")] "WindowsConsoleBlank" => ClearScreen::WindowsConsoleBlank, "WindowsCooked" => ClearScreen::WindowsCooked, "VtRis" => ClearScreen::VtRis, "VtLeaveAlt" => ClearScreen::VtLeaveAlt, "VtCooked" => ClearScreen::VtCooked, "VtWellDone" => ClearScreen::VtWellDone, _ => return Err(Error::UnknownVariant(variant)), }; println!("variant = {:?}, sleeping 1 second", cs); sleep(Duration::from_secs(1)); cs.clear()?; Ok(()) } else { println!("Usage: cargo run --example cli -- \nWhere is one of the ClearScreen enum variants, same casing, or 'auto'.\nI recommend piping into `hexdump -C` to see what’s happening."); Ok(()) } } #[derive(Debug, Error)] enum Error { #[error("unknown variant: {0}")] UnknownVariant(String), #[error(transparent)] ClearScreen(#[from] clearscreen::Error), } clearscreen-2.0.1/logo.png000064400000000000000000003604601046102023000135710ustar 00000000000000PNG  IHDR+ pHYs(( ivtEXtSoftwarewww.inkscape.org< IDATxw|+ʊ C $@B_ʗ:ZW֟UmժmCb[KK)*Rd%HXd%sy^ys?z}=s?>----txx ^/@ P(x ^/@ P(x ^/@ P(x ^/@ P(x ^/@ P(x ^/@ P(x ^/@ P(x ^/@ P(x ^/@ P(x ^/@ P(x ^/@ԩSZjk: o: ڲeRRRE/5\c:^tȑ#GqFmٲEϿeee99 &ǎӆ _mz݁ OKKKSaaH ע(99Y[nիuQӑ,77WC 1%'OjӦM322LGP`.Jiiik333)tכעmݺ~jll4!(pziƍϴ}v5440&8Pssvܩ 6hƍJMMUMMXFenK7B---ڷo6lؠ 6hͪ0eicN:t6nܨ 6hӦM:yH.+++C(h| 6t$.Ҍ3, _36lѣGMGr098x<ڼy},yǖ9NcjjhhP@@BBBLS(^Zwڥfӱ@Gͦk6lؠs״iӴpB]5.S__m۶ٗt,.5mQG.ȑ#Xhܸq pJGײek2(-YD&LpB:AxMMMڹs}ŸZӱ\@[ STTkĉٳ!̙3ozڵ{=}[rP:E86MoڷeUTTbz^zz^zٿJ1BƍS޽"gΜŋ諸iŊ;wp G8x Mt)ӑ j5a_~կ﬍7 :5N^w^ 4Ȣd8-oذAEEE#YO]vٗμ_l 4Wpy{S||`M(JJJg7nܨG.ӧ飾}ڿ߿s۷-wg~~w*,,T-Nu(.T6mO]_>?w^z *99C_d.S` KRrr ]d~\___Ods1ͺ;tv;;TXów:m۶~jlld`*44>0`/\W߾}կ_?Zr\????:r䈶nڮ׶68){n_^ڴi*++ 0@T޽/=Σ3j*(''ͯq`*:6M{ҿe˖V`],9Kg۷+11Qmz w]:X&;;[סC.]/Whhk߿uf:.VX[oUmt-hŊNH@P,a8Kxb?߻:!'5%S=3[%W>}.{WGBBBt=\w( KOpumpoWGF?|||.Qѣu 7\9 h~S\@;̘1C_z(Ɨ: v=z: vcݺu3hC*88c: vUxxx(Fg s u`@ؿ /w*͵;耜w@  V @;l6 v***RCCg ;hC t!w}ϮX)99YJKKUWW'I դIt뭷k_tb8-|ZZZZLpyyy>|w}z뭷eld;vM:t|M͞=)~_K_PWW;v(%%E)))ںu:4Vnn̙zJ/I˿_r|PZZT;w޲[ZZK/_=e\ @VgeefYz Yxt:Xh&WsݺuJIIQjj֯_NQ---~1< ehϿs]vm8./--*f>|Xk׮eS@@m_>WVVގUoD^{5ܹϿ[?~-Zcǎٯ߲en:|ZZZZLpU֭Ӎ7ި޽{r QeeQ\_|ԷG``_h?_\Qee͛uI۷8fj1f@p{NV2ØI&p_---z/bɓMG8HѢE|rQ PiiMGXh޼y4Ÿ &0m۶M111L+11tPT[[gyFӦMSII8.#!!t:k֬~䘎R|||tqtx\͝;W7|3 9r$`l<ޑ#GꫯMqY\x]vg?VXfq\x6 Qjkkf-YD}( p{ڸq/_2A|f PQQc\ +[:qi͚5ZnN}\֟?x> l6ݫO>DWVzzl6kςu Q.IJNN͛RӱG~\F^^߯TeffIԻwoIRPPv*Kԭ[7W={,KMMeT^^.IRccfNQEE*++UQQю#vC=tY:.P8Pii(//ӧui:uJ'NiXEVvZK.p޽{W0ɓ'URRc*..ֱcǔG}TǎXz{Ä.Vzѣ:z6mڤO?T555oÆ 6m):urss={k5 Fk׮ʕ+fu]tQiiu(RWNNJ簾jҤI444hƍZrVZn\\mtS(r嵚؟;?+jڴi1cf͚;19ͦ=ڻw+Hp6 F477XyyySAA󕟟\\rݻkԩ1cf̘1cǧ=Ϸf{*((?Cs5 ,JyyyI}~~ |O'N(nl_Bwu/_.8qB}5 :F:~=۷+++>?s挥Sdd}ŸU o{=}禣:uJ*((PQQ[?v#<<>OJJj}sNEGGp 444XǎSaaTPP`H3+Og̘x".<@MMURRǏH'N?v1㦣reiڴi>}f̘#Gx, EԩS:y򤊋u ĉ*))iXUUmҭ[7M<>ᏌXW̙3:yN:ӧOԩS:q}]&X͘1CӧOW||tb:(6jllTYYٗJKK/ϟ:uSs?~MiӦiԩ 1 (D6MEEEjhhPn6֭NkjjT__oھK}ccTUU__^^UVVZ*++S}}6w555*++Suu+x3~)SةpQp ͦyizz衆&."<<~?))I3 @P)|vO%YH OO|ZZZZLg[l.\h:hРAi4x`ӑXm6M6p-TRR4m4 >t$@WLL?n: qkJLLTBB&L ӱ8{!jkk5|&.̙JLLTDD~ QxӧOkʕڼy=j^.]]*((~GS^秞={_ݻw^ʽޫLgq+))ISNԩSuUW@6MjjjRee=go{UUUjllTEEUVV&ͦ2577~޳s'+R&Mfo K\yy{9۪u-|}}%BEE<,lС:u}?l0ӑZ`*++Ԥrl6ߡW׫t۳g͛\QDÇWbb}?tPӑQݺuSSS(-]Tr((޽{եKEEEiҤIJHHФIj:)''t ;___Omf:  7TQQǻԛ11p@M8QJHHPttvj:%%%iͦcҵkWرCcƌ1 ŋ3zRtt&NM8QW\qXңGNsMGBfJKKt S^i8qF.[n#\PJJ6oެSnͬ\}zѣG+**.___hz.OaQXX&L>m:K U\\h۪Uhh*++MG?\G6CL``⋦QF)))I&MR\\L|.=k@; ?~\CuX-,,L3gTbbO+t$<ڭުoc\ԧ~YfnM}zwLp;VIIIJJJɓշo_ӱUUU UMM(4n8ڵK>>>[7#ޓf32qqq5w\ 4tի]v/I>(VyiժUctoGQdd8@cРA:zLGk::G5Sf͚={?d(--ڵkMǸL(Xbbxv~M^tR}3fա@'<㪯?~&p={ꮻr (ܜcWXF;wj… :azw/L{?8 ӻ[=zp l6ȑ#= ~~Μ9裏`b'Oرcu 22R}\'|{ݹu3 C>}ZC QuuF*KzΝ;uUW9 謭[kUMMM;^uՁ^Q6lдi26xVx>}8pgM/`ڳgfϞɿ5n8&` CQHS̙3.jݺu:uJKK;8…>8x'2.x# rWk޼y/&_]>>m]p%K覛nRyyy'F9c&C ѭꐱQxG}ԁvm;vslz}OMMMd#azģK IDATD:`g/Ν;т rJGngݻbX{1a 9t&OɿԖ?X͙3GF}9.e˖)::ZK``x1Px =N?n5w\vq|ZpLi hcaU__Ci|wtw:xꮻ҉'LG}5j(qXu}9ӧOwTTT8MaanV͝;m't72a;} jo߾:yÏبbPEEE*((PPPJ]y4hFqjjj[og}VtڦM4uT1#qcU֧O-ZHoÏuW;GiiUTTlegg9//OAAA?~o}[߿Seffva:%&N=CofgС稭U~~ TPP|k4j(bcc!__dw3<>H6t<裦#Gpaaa׾˗;8{v܃fSIIɗ&ܖ˅BCCX+::Zݻwwpm'O /~[ Xjذa[LFxkV^^򔟟o?k]c*22>ᏋӰa7SYY~_^/?MذaDii问?眜uv؁گOLLTTTvj,Zd~m0g-8+))ѫTUU9:$???_~Yzݻ+::Z3Dz7j*b^VPxŋ;er~iUWW+77u2<w*""¾?..NFÎ xbٳtڵ>1+PxÇwuʱrssrյZѳgOM8Ѿ?>>^}qOP[[5khٲeOd:-\A$쩧jf\ua)5(233l2}G*))1%̝;WcנP?SKRjj*ǿ4?wgBBBj?`ӱ_h,q\#ُ)>>{x뭷8Yb;%Kt]w;>p)&_YYi:bS~LL KÎ9(gQ||"""tW>}Z1116!22RƎܬ‹NMGL@@V#GpA~t гgO >\aaa5jFQF)""(PAA8,YYY:shٳMǀ:}E竡tD k5gW~vmZ|n+88X5jFp=Zaaaa@l6rss[M߯ԭ.dȠG)77gsrrT^^n:]veٯ~N>qƩt#G*""¾R`ԨQեKPԤG'gtLppX+4 P\\+_;w+0ڍ}Yw@\\/_6hnnVaaEw9٥g'3fLG%|I˦cf͚O?t h7 ())UW]QRϞ=/8466@yyySnnrss;vB ՏĉY:?^2駟j֬Yc@Pt/'|t ^zI3f0!땟j婨Hͦcz]j„ &jkkUXXbXǎSqqy9Ҳchԩl&f͛+WBJMM5c̟?_ ,ЬYb:N'~=}W_mj 0 *--Uqqco m6K_ڲxAAA*//[`;@``MoYgMGftM\ge]jX]vech˖-JNN͛եK͘1CIIIJJJ-9Nee;"PEEE3:~%W=Sz饗,Heee -gPSUUwn:W=zfϞoY5Is7kkȑRBBGfJIIQjj6nܨKf9rj9R PϞ=խ[7fRUUUɓ'[MNqFM<ٲ1.]|;3gmFN֭^o߾>| AiȐ!ᏌtK;\XUUҔt,dM4ɲ17ߴl<8F޽u)-B{1K'7o~; Ptt}Ÿ^z+TTThZ~RSSFӱ\fwܡ]vYvŋ|r6tqgtUWĉ +((ȒX2' Vdd}QF SRR-L;'OSh"ƃ|}}qƙBAK,=1suժU3ge=zW,)11>ٟ8qMMM:|}¿i&盎m6EGG[2fSttvexu?,ZHnҢsjsjQ:ٳ-g8?44T111Ull&N={ ***uVmݺUھ}jjjLXƍΝ;o}bG#s1’񚚚LKsZ!beddsz-o'|bx=;H@@RRRPtҺut;zɶz/ix?OȲ\p/6Ml5߿X^G:x `x5\+s'@^y=c?8 y>J[Ϟ=_o߾ѣG֒\[MԻwoӱQMMve짦̙3c,Xe˖Y6wכ$O\wu[@}}㝰QO[J,~'l9s!!!?~|[ 6t,p m߾>߱cMBhÆ JJJd"1BՖ~'ڳgBCCzp4 ʅpk_o%cUWWkذa:y%㹟s~OCBBaP8@rrMfwOLLTrre|Zfe]ʅne>յlևsh-W_?nXܓ?owq(G?~8(gؼyLbX7n-Btad?99Y999c}k_7KƪQXX?nxy{-Z^(Q'OVzztл̘1Cׯd,ͦeddX2^5qV_V] JJJf_οsNc (!!x =Ö~'<<\;wTppC&P8Pvv"##UQQa:ٲe&OlXoC [ݏϥnWv?##[2&MRjj%ci*,,d<@m۶MCP8Xg&뮻Nk׮dZiF92ϒ,k9G[bnK/i-|McP8 ?t XKO~G|nEEEi„ J{OlPä#F(++˒⸮NÆ SII I7x֬Y*> FQ={V^mX'OUQQMէOKSRRk.lyTKj…ӟTO=%cy+Rwǣp;v(!!A5+Wss&O/ܒ18_yyva𧦦̙3c4|p8p@LVeeɼ;n@*(^г>k:ۚ:u6md:k.+==]iii6 h_-d{L?-[=zMpfM>][l1-^Zg6Q_FFjkkM:eȐ!:|t  yDmܸђ(رc7nSiڷoZwVFF222yf噎8;C;Ӓ%cy^zi2d(4XB_Mp+Vn5Q_FFoYLxȑ#Ҙ1cG裏tmNE`wܡt 5b)66VkLG NjuU^^n:`~;}{߳ds%cy{WM0ZQQQ:t(+22RQQQJHHPRRg:jjjÇ[]I<Æ Ç-<99YSL =zvء Q( T||-y8p(M8ђ%8Q߹~6eȰd,OյkWע0쥗^O?m:t]111W\\ԧOӱtPuu2224t,m7Nvd{ϲ=Ւ%Kt]wPE7Lǰxk3 @477+++K۷o_~577xuk84hN>mA*Ç몫2ᦧ\t׸q㔘ȵ8w)cW^ ҢE܂TfӲeLGcX`PCC맊 Qڤ{]:Ҟ={d䘎x;w***hl<~~~ڹsƏo: A`кutuיqQn֗Hݕ]v1A\mݦ>Ȓ̙իW[2'2e6ol:A`}ݧ7cHO#nO2EC 1 @+֭[USSc:Kӑ#G4lذN駟믷 _1 :TFxM}t,n$ @999+:=֣>^{͂Tnf (L}.]/=?`%%%iʔ):u""" 5< IDAT55Pzzӵ}vcY^x=ӝ'++KG [r͛g:8577kݺuZtVZ:s_~&Olߥ3ζ_;vt,誫ґ#G,lt RyA)++K!!!P8tR-[LEEE>}MÇN RN8afrӱxM6iԩw y~K&x4 kժUZl>3uM]9&>|] jҥԩS8p vڥ1cƘA ._zƌoQ]w,ࢎ9b?ݻwt,h%((HEEEիWǺvZ Ry믿^|Kj͚5=zfΜٳgk8OEEg?'Omdu]>ТE,H֬Ynt @RRR4l0x㍊hQ{UZZÇYmk֭\l\F#GԾ}`: X駟jÆ ݻϟQF࿊;򧤤h֭1 ,~K>̟?_k~!{OAT(DXqąZ^:V[[GպZVQ[X@B p( [@MՊ !s]%Isss);vO?eBĊ:ꑜ,5l0j |IIIֱ!DV\-[4SNaҤIbHB!bC*..ƅ PYY}R|>޽hDEE!::)))BY[[#--ɻ eeebJ&h[@BQ@K.!665jO_\\:!4իW˙8q"ĐH1#--z& m^z '8(2ܼyݻwY"fO,ƍ۱sNQ!D,n@ [йsgZ)x"""TWWE!2e˖lgRn T@ @<Y"p55aÆ!88X ǢE߳A!MFF[^ zK!ɘ;w.r~,X@ RSSѪU+Q!I^|>m> '266Fvv6TUUTNVVZnM `ݻu Bi V'ŋ2dHqvvFBB) dddؘuBi4/^ ..N8g?&&٬cB0rHh :tWfBF@jkk׮'&&B F!DVVVx x<^ILLR)KKK7y!Bȱ׮'$$u,B!Mnݺ5 ЦMdff)8u&L:!4 M DNxo^ C^^XBMxqA1R?#uBd D n×BC !D899!>>9sǏC"Ţ4mۖuBi0 wbbbX$%%|DbZh---:444}ttt2%%%顼UUUx|>Q[[RTVVBDXYY5BQq#[_}`@H3 (--eHա%lT@ǃ>@YYHՋ/|<}O<p=ttt ---hhhjjjԄ.Ԡ---A__uĵk7o糎E jr9+VŐHƢ{cBHP!RRVVaC?&&=bKC[[6uuu'}}}hii ݮNy9nܸ ;v #"5(**jrŋ)Teʕزe  @Ν;a111HNNj]GGzzzޮ{LWWWxcum~:~2CƥKѤ2ahh*1R|>|:!4uDÇۿy&-h5C_m >ͱ$***ݻgCD}mr9 כ\*lllЪU+ECˑldff";;;;;1!Ddt9~mEX%ׇAZUUK 7ng}{b׮](((`|Xqwwop6z޽{nH|DFFFXXbbbdjW @; ..ӧOYj---&&&022_SSSֈ[!YUVV⫯Y!Dx<rssajjڤr"##ѧO׾}{xyyapuuH'oVVN:CѣGܹscBȨUWW͛6| ::7od&LMMann.J߲eK055VRRbYC!DVĤ95 "pZ rKRRY PQ~]Hu]Bjþ1oll, %B/22 @䂑=mۆٳgO>X<̜9رc>}:" r ¡111HMMKZZZ6*=!#22CsuuEXXXz%՛va֭XvmZ(pɒ%صk".ORSShPZ9&&&033 455%j!ֻwoaذa @d[Yj*o'OFuu5HD!Dd4Ȕ2"22F~~>vۘBXN"ӎ93fQTUUEPVV2.\q㘮S cBHȄR\|YYYӃ! ^khтuLB *++YG!޽+QDGG#)) iii~c XXXCpttD>}п53g`ç̂())YO :@!D}4}}}<{Z3%%%8q?F71h L8&Lhb۶mʕ+U8=}-H!B! θ{.(l8wT,((/8/_l}}}̚5 +Vhжa̘1?Ś4!('԰yf1iOOOq<:o{gT~va֭"/O?R F]AȆ*z0rH̝;V ݻ'sp! '{7 x<,X:!"ٳ'ڶm+z#22O˗1U***8rNбcG4:`dd$rx„ h"@!k8s 222`jj*:jjjпDEE\cb߿W$$$O>ABB[QVV?[޼ypexkkkۣcǎh߾=бcG|Ba:!"]i$iӦh7nĆ {-~W4Jxyy!44hkk#11|;z)5 CE~ЧO5 BiBד'O2ƊȀ***yΟ?]]]ԛ>Sppxw5qDTo1qD=ڵkPfBE !6mڠe˖cR////6`Ϟ=m_l033;qԩ=[hooo\|ϨOQ@!ΎuBޠM6I@ ={@___-[WWW8::ؤIC>} H!:!Wذ@Ν]JD;bСbW\yyoFll{(кuF$YGB+h ܴj _u{2d뭮,~9SL! iRNB*B! uDYY'NȐ|9=8#99PVV9:t6mHKKÕ+WgS>}:|>oߎ-Z\~}|>n߾8ܿGii)@]]FFF077GѹsgzzzMBč:!W4tsB$i۶m߿Tꪭ}9o}ŋزe ~<}T^J? \zǏǟϟ7JJJprrѣ1a„naH!@S!W\~uBg}&DZ}z1zꅭ[J"II 6l؀6mǏop!..k׮E!H uB!z*`Μ9ػwT u\\~ C޽"hАH?lllqF$q !Cп$%%lBiqBHs-gB$iɒ%عs'xb-/)) Æ CNNNc֬Y>|8z˗/k׮HOOZlڴ ߥ#!4B1c;:Q@ď?9N:I&1.<)))СXKJJ€PRRcظq#/^ 55-#229::"44 B:!icbaa͛7㣏>|t of^ H\t޽E ]6p1,\ӳgO\rMBޅ:!e(###,]~)Xy͍70p@|Q}#F… oܯ(8:::g}O>CZ'!@!V'Ѿ}{ܹ?ڵk]\]]zj10c 5m5knݺk>|!!!bB^E#!'Oɓ'Y rHGGcƌi0hРf1}8äI: SNXsqqAll+++b>gÇ}7oޔBlB! 8XXX 77u"'Zn OOO 6 C ɹ5556mN:4G6mVZ;ws>fkk [ %h@B! /%%IгgOxxx`ذappp`TUUq xL28::"00b+ŋo}ϭE||<=z,TWWCMM m۶LLL[V?uuB$:!(+W@d:u=z0\|IӧO޽{6NBB[ʼn'طo;VQFaҥ߿[xg>VoBb)Bرcq91H3c``888666I\ZZzU%;v쀗D8p ]Ǖ퍮]aaa@ ;;;9.rrrpIlٲ;,x'J߾}{|ᇘ8q"z)kh!DRBBرc:SSS W~W_CCu||r7y3gć~dddd ''UUUxttt-[m۶ԩĺp+*sss1!rBQ8'OdE.LxL8j/[ɫǏc ~ŋk.X`oowﲎAS4Bٰa5hժ[Uytm̛7[p4`044DQQ(rwwg"h!I&wGv턫׭|ߩS'?M...w^'2zjlٲuz%$$[ncBuBQpssEΝ; NNNGabb:"Aϓ?C(((`5Æ C`` 9FBBrr2<<<v?tݺu6;v v؁>A???(++K(>}&L`CHMM ppp`"ǨB;wORQĠA:w 0`E~555 &|8ݻp~"2>I&5ogg@o߻wGYG,Y;wd !s]3>dE1x`5 F)HDDqF\zUqEK0_YG:vލٳgBQ 4B8g}rqBYYƌ#W_Iuu5q֭[#_t^={J8p[bڵ`ؿ?XG!(@!D.`֬Y8<(bѵkWL6 'ND˖-Y!"*((@hh(O 4+))L={ SLpnc̙c={ĺu0|pQ! J.;bbbO?? 8ȕJhժLoE_?yyy4:Fٳgݝusn޼Dݻ.8)q1H#++K_'N`**,,e˜1c0}t89 IDAT91A!N())A.]޽;ϟ///0NGHq QXXgϞ v]c_KK ְ3aaaeW^^?U۷o bԩgGee%Ґ )) IIIHIIAmmL0N`w۷o.\('No&Iddd)))M.6mC||<߿,磸 ###l:t#z:"`سgx<8:: GrssyDTTT ??m}?6mIx8;;Lʯwr Ν+ 999aҥ} ѣGHKKӧOR6󡡡!pqqAeeHwLCrr2<==٤r԰zj\-ZS:BaG:`aa]ׯեUVV";;999(..~iP }gggуY+..ʕ+qa+VՕuVPP|!''ENNRe:u&L F"XJ8dDEE Ϟ=kR9...i>B\#G?nsUTTСC3:vHCXUVV jggg#33555b^zND9s .D~~>(2dlڴ֧i$@aC>//O8)//~-Pqի"##Z9sp!x8{,Ǝ+Tqơe`ժU/"eB\u|8r333CϞ=ѳgO ݻw'8Ѻ~8GAA򐗗פ$ ;;;aC ]vi/Dfa޼y|2(ҧOlڴ  bY{|)󑟟/b7g۷T:{,E>ٲe$Hr0n8TWW7 _~bLF!͇\u 4W^X<ܹ3:uGGGtC,'jkk Wn5_>n...pqqA=L}"JKK7`׮]"]nNڷo;v(_!''DNN߬,<}"Mݓ'Æ C``y u&\}k׮AUUUOWWW>}bLF!͋\uG@MMܹЮ];kg>RC@ @aa!Q\\F㾮_oQQ說^zG?'F ѣXv-rssYi0]K.^x4 G!==?ɎiǨQ$V~mm- BRRڵk'Lryx{{7?gٳG&;?!!겵z*7333{C];mhٲ%$VJJJ055ӧOe`Z+ WWWt JJJc"1׮]òe}Omx<Lm۶e˖Uqq1ݻwҠ=[d ݡ%׬Y#r|}}i&YF!y_||}}{[077GV000 ejj|%%%x%`ii|ą xb3.puuE~Я_?mۖu$B޽{Xf Qȑ#YGi DGG#::7oDrr2rrrXǒ;&M‰'ľO?Yf|.^(s 7񯬬~ ӧOs2Bi 44cmWhoс&Ԡ UUU@EEzzz«qJJJUPUUU+++?-[b˖-:u*+ؾ};mۆrf9|AsrrBLL \'+n€Dx<?)S9!N3(5֯_UTT0Ç933&&nӧ$==[d ):w^z17p̿gtvtڕg9???]v q pM7_!0%w# Cm귄-+++l޼ӦMcشi#BuC1p@K8%!ݻoZq~~~֭(ɓ'qQKuzwww̟?礥GݻwUϴi?75dee?ntsBd\v@UU.]Jo2fصkt4ǃh"\t^m4FFF %&&b׮]2 2~~~g ?u"&JJJС,--b"??Ijjj"55Uf,))+ܹ2 nDšv … ouJJJ~ SSSYΞ=]v@  00wFHH8b3{lݻQ%زe ;ЈOc1DRYY OOO\~ei7!@:}۶mlBd!6l؀yQ=!܋/p1ܹICtUUUݻgf5EEEXn>L QWWGZZ,,,XGy/@ &ٳ.CMM 7n@=ĘBdإHCC6l۷1x`qxbt BꑚKK,ƿ.U89rvvvOl̙2K6v?!B!F8/X|9 X! ;vdV]] ٓuBJrr2?cǎ!//uh۶-nܸVZ"CtQ.Q$l#''ꬣUBB &GѣbJE!:^#,Xw066F޽ѷo_ <u,Bd^QQΞ="!!u@XXڵk:kRRR`oo:fÇYxtM*)))044S2B/+lll̟?_]]]aРA8p OTTT… _ҥKͮY_%͍u*((g}vj?{ GVVrrrGYY?zGC__zzzׇ1Zj+++XXXM6PSSc!F#/o>z)e***ի tޝ"F ɓ'qܹ&ezzz 3(ɑ-H󖞞m۲R 4qqqM.kr^$>dHlG+eeem:t@ǎ'''CUUU"uB:#11sELL (rpwwYG"D_~Xɺ-ZҥKhII X 2ҲYj._Zh7oV DPP"""PXX:ѹsg +Zl:!@|>{_~WISS}􁻻;ݛ7BdGTT#55ufȑ#1c$*/B@@SR_|[,ݻ_u8"F%B9h,,Y(2^^^pww+-#Dp \p___1DbllgϞAdo>1^h"ݻW,eÇKyV\\'O㈎fI/// >#BlVZDž h"<~ufnnnpwwc%DBq9sxHȑ#eJu&e5+W[KlaΝ8vX2JJJ޽;FiӦ5-V Qd4˱qFܹS!WͮO}uWi{>B$(7%s£:ƌ1 [z5k17nĆ V^qZ.!![lsfieee 23gȑ#ѢE ֑QX4455m6L:sEDDHLt2dMMM֑[=… p9ܸqCa>86N>-SY MQ\\:Wغu4>u$vqL:u;s Ə:a~!/vڅO?Teb-Seٳ4+nnn/IF ocH /// :%$D^^^4 *++իW`$w:uhԻwoDEEAdXXX\]]Z@ %Kĺ_b/WjjjO?aӦMfGX[[c1c444X!D.Q$2dFA۶"aXXXG[jjj8uL7`ر@СCR3p /A XnRSSYǑIX`6oތ7b̙PVVfB#رc1c b``www1GHȥ\`ddd06oތ5kְdEEEByy9(DF)++#!!+77'Nx?88WFbb"(rYG!DnPěS֭#F`С2r.!$//aaa¹=bI!ur>7w\:uZQ >|8qv܉/B Xz!VZuLtttDvЪU+XYYM6Ѐ&ТE TWW˗DII rssdddɓ'HIIAYYS/%%%L2۷o)8,OUVcg=zcǎ HȕgϞ!""W\իWqmj73֭W_}:-\cȸ~̙3b['%%sΕJ1by|W8x jjjhݺ5 ޽{ ;v<͛7L_aǎ6m($Zhhh02oooL<r"oGDD <<cСnݺ%+Aqq1:uD+&344_Yf5ᘓFuuwޅrڵ ۶mË/^? Ν;K޲2ܸqW\ŋq-.C `mm: !2:Ĩu̔j'D|>߿/lǬc İaXǐ@xyyѨ"mڴ… 1i$XZZ|CRHÇc֬YRDZn:deeIn077ǔ)SGA'550}/ĦMd-QF?!S^^׮Eipqx<VZ[C䈒w.Rnnn###TUUGRR.^\377Gzz:եVŋb &W;u/'N~Icp13'N>`YAb4i$:uJ"ew>>>0aڶm+:QFTTHɉ-Z֭[С(1bBT|XzTꊏʕ+"^իWcȑ2h@ kp={UUURϠGu"K@qYgiiq}[(/_")) ב:˗ccHMYYXQ(---dddPbڵ8}ԇi}~8pyyyRػw/^7!:?wWIѣ OOf=싐n~||O)<|zzzHUaa!<<t"dȮa)>>111Lٱc|駬c&** {ns`nn֭[]v޽;еkw6|>.]{666!ŋ{nUSSG믿FFFXlѣ 3q8z(VZ%uygD*ҜQ@#pe˖a׮]"Aaڴi7n '_ňGDD~NNXDliii}8"--MleZXXfBzމ'0g&C 'N`M*Ǐ͛ck lٲ-hYVRRua~i~ IDATm۶8<:w,i8 ܌38=,--/{؄0W\\̅swM988wD:vW֬Y#׬y{{soݻ\.]|蠣իW7X^^۷fzݻwѯC%&&rB[[;wLSȑ#ҢE ۛ |>Ȅ0q_57fʊH:011***XUTTD_*tRիWsVt#00{EEE͛9SSS555[r555M};{;p-񟋒k./f@D/_<<ϙI`cc]~9]2yAGc+V7͘1dˋF6Bjj*׷o_֮] b >o<֯_?,^cƌ tH9uuu<~fffӌ3$yeeelݺ˗/Zݻ_~%mId6BCCR݃?XYYa3f (2cX~=%Zܹso>Z(x\ :nާc崐[?ifΜ~u:qL"|||?B]]srr_ѣRYtqhѢ&M~A[[HNNF\\ݻ:[x1֭[---qBRRN۷oK_~m wx1<<< 0|̜9zzz">DBBDF[%%%ёuʂTtvvFdd$TUU_:::۷ocŊj6B䍪*f͚k‚uSQQs%Z!uEJJ <<<c;v,YG#I{e?""EEEc"!CŋcVؑ,-/_kbŊXbX~=BCCY _666Ƚ~~D @MxG\\1d$''?9ϟ?Gpp06mڄ#Ge˖#aPȜy汎 RСCoܧ2⫯Bii)틐\~zNBd2Ə[nƿ̟?ׯ_GV$VGDD2AHs@#ڵk … >lE\\Dֱ333dff19۳g/^,:uttPRRVW\6􍌌gaѢE+^ oqJ53y;sΡs\j<|>%%%DvvAff&ܹBƯF6`̙Xd YQX8q"^*:  .@CCCbuu011!($-- ׯ_ǵkt֑˗cz-ZAOO-Zt DOJJ ^=l2̛7YgϞ=8z(IUUYYY055mrY999snݺx"??_ )哣#̙Z9spQϿuAUBduV$^NNKc֑w޾Eyyy@OOfffhݺ5бcG M"ŋׇݼy3֬Y .ľ}޸_GGfҥKѺuk@yy9N<~ L| Ν+q9:tYYYԄtuuannKKKm۶g8í[pe\|W^EMM laܸq裏ЫW/q[|Wذa[===555O3!JKK?͞=kӦ :իWRRR]7g.,,[[[Kկ_z\~SUU&OEDD(O>tuu.(򡢢\"rץKl;wM4۲e w 浟snn.1<)Sp.\ફNرc~/F!:iFݻwܹsbiiF&O\]]ɓWƴi+~-Ծ}{lڴ)5j>|8=9޽g]lխ[!Js)$''$8nݺ?DÆ C|>kע˗3;͚5K|022M8} XFZB-[D-[DZb\jj*Z*<{ u rRm9)B(G (}װm6 '''O?tU{au-:]]|z?N<<֭[JD"GAXXiܸ1ÇpBصk|z(K~o@dd$) |i={ E144DiiiZӱldTN+,,D !>|+>ei&,u"[n;dtQt t$ԩ-T^?~u!Pqq1G(-- bTF ₜ#200/˗?N8I"|||PXXt猋/ǫ ڳgԩd|t{Nsssdaalll FvڨN:\[~H$B ޽{޽{ݻѣGۣZj!GGGT^=UJ"PZjzIIIk׮dff.]ڵkl Ec~Pٳg2BE/իܹs{444DЇ='::ٳ'ܹ̀L8t=Zo޾}aʔ)`eeгgOXjܽ{D"}~ 7t>}:H,\P<&&&j{L׎"Çp! Z~1cMÇ ~Ҷ{LI,)))P^=Ny;;;HJJ""6tB̧OܜR**}Ѯ]X]sc͚5hӦ 駤Vi݆;v쀏?VCXjt5uT65jԀ3gVٌ>}`iɩ\RȘOқܸ8c\F:u ~!HRRR޽{ 6&L^z[R|V-`nn. 2q0fq ;޽{8jڴ)|tZ 3SLO**}@ )۷gӦM4h6T48 x Sa+ż<'+񾧪/شi~Vӧ?#F^SV9[O>@i 2.W^r0gnQV˗ -[ȜuP^=Xx1uI0`$tBHBBBE/Сk2??_#O[jˬYǏezڿ?1OЧOׯ_W𖘘۷o pqq!{\eVV ,Xiiix P}͚5  O<ҩ&mm&]P͛0uTnBlȝvN>Mo5̣GZj􋐐Q(J uz=z #JxbVK4}JlܸX;^Z,tss)Sٳge~68&M&M Aqq1H$QֲMMMaĉ{i;DGGCӦMY-`ij$YYYATT޴}n`Сpԩr3DD"\t ƌɮ L4h ʒ{@@@24haaa4WuXXX`<;F:< tR =z&M=z􀢢"le:u**}ԝ;wX]-҈OUf>}X;C</ڵ+K //שּׁ,8s ̙3:t=M6l'mW"mۆ%611Q|r3gΰfmm cƌK.P\\ NC˷SNiylܸQ#[[[7o^ w\x #)mP8}4̜95kVĩSS\\ nnnD~PQlll@,65]xbmill,&`Æ ϵ2 hݺ5̜9N<)ݓ[ tR֭FgM޹s'8::Ž;Ňo߾ussgϞ@@y۶m*g ͛7w5`ԩp֭r`ЧOڵkNqJ aȐ!p}#v>|φu*BhtWŋaܹжm[k= S+VhUeСCY]EEE`jjNNNJ}h{~[RRReה|>8qBa{x&N^^^Pu\7o㥱krqqs£GʝLضmڃo߾) >>>JgPo6+Γ֭={h@P!0>}tg***ڳgkڵkjmmO~~>:u\o8֔kZFFF333aٲe~NJ|ϟ/]ʖdU19rJ߿ZTR98}4boXt)z9OHH{NyrvvV\FͨL/// ?ƍ/_thREȀÇØ1cɉ񇳢0j(Lxd}###<&LSrss'~Ց? &M,ӧիW* F㒻;ܼySMXMuV(]fAPPtK>@.]8km–-[M pi0`绁 ͛73^^^p%8EuJJJח~6w 5tBEA۶mYlccpS߿5I$SrZ=DUv{/_&~UUj2?ɍiӤuplL|>̟?B!~7]ǃ-[@ @޽. 9"KS1b!aڵXgdxxxرcQNÇvt`۶mP|]vjell ׯڵksk׆ [" ܹsЫW/[ho߾E}||hy$''=>ЧOVgΜ)S@ 8Bnڴi?pㅊݻ˗kܳ*;wL]+ڠ'Lx)4k K<޽{W[j<DEEСCP;:[~47À4&<YϞ=#Gr,V/_A Y/r5NuHFȄPk LęL'??_#O/*Wz?&Yq,y"4u#OҤqߣ *ZOFNv6o\|Q|>,X :lccc7oX'Y&\rE>ۭ%K@ZZ۷oaԩ* 3 !''q',, {_311ǏBh\8v=Z}qqjXcY`FSQU&9G"9%;v,[^xA[կ__ bfϞͺ~y[([ 8͛`ggV9VVVr 맥pBhݺF&NXnΛ7o`СgmL0̃ތ3Xokk aaar련ƈ#3OOO )Say^y211Qimh43UeY][n%{ܹJ͜9xWn]jԨQj7322K"@PPhݺtׯ՞ _|ҝpxر#$%%@ie˖i@ r 2oݺ}9bϞ=N-^upUem6n֬\&L ݫvYf }Oqq1)R:uS|MN׮][*U`҄i>Ė`ڴi0qDuxzzV-ׯg=A`` &III`cc|3( k`޽0h "լYS1bbbDEϚ1ck499֜]vUoҥۘɽ98~8q8dii )))j#6Oc;v(ܜhڴ)6Ȁ-ZUN\[ma… 9.PlmmرcsqF;::½{KVVV5qe… ؿGj׮ YYYCPPĦ"6OG$'񘨨Yguի1B?tq!VpM4IzyQFQD)ݽ`!5Kʯ*жm[?²=zDlƜ4Fvv6k׎q666ҧ8֮AAqq1ٳ@co޼ֹ~9;w6n3f̀>};4(ĉr=EFFb0frB!oy?~8(:@X,{AHHΝ;"33GTTTvZV+W~ IDATwM#BˣA*c=233/_ݧ޾}V|{{{ <Ν;M-nKBVѳgO(Qj޼9v+7e:66ׯf͚IKHHFAݺuuf͂ 6@dd$aÆd{OgX]ڒ\oʕ }FGGX~ٲeKpʊOjժU譌"s t6͛7w$ B|\ҥKDqS(]Ρ-˓SʓҥK"w@^^(:@Q,XzVBQQ###DEѣT$ 8B0i$^!OA@(o[Yxx8:T*ǏPV-bm"jq3@ogGZn-s[/^@Νgkk+vӧOиqc}Q7o'uVZvT<|{iӦRIƈD"8z(xyy^Ngqq1=dˬL?B׮]#[ݽ{WO}d!NQiѢ{匎ΰ2e gt"s_"={z27n@jjVx<عs>"H`СݣGr9(Yr%jZAPXq= Ԋ5ݺuۊ+DEj۶-kUkҤBׯ_'[^ZϬ,:u갚vsNumV-iC}A"X,={2:l,784dI`ҤI29@r:ud``caa!`jԨҕʈH$M4;B :@Brr2 q)@O<Ojj*XZZ@E:wkUn---%[?Dž.\v+))Q1""B:H&D_bxpUصkWm/sw+++鬗W^#(O@tWgux<9s&%›7oJ*X{͚5âT2+>|3fh<ԩS1j(_TT͛dT}!vy&q D"L3f ׯs7nd\(,,͛k@׮]8F36m B$ k׎SK,Q؎b"""ɩqO>e&eff^x666>dpuTam*Um)P80o< 222y{.=w*mΎ{a ?o߾Mܟ~9BWR2 88X:ʊX8::BZZ2:vÆ p}Nxu֬PKr=}0^?|@:}oڴip}8v.___ HGV*W^IoU&qD{{rrr_~)7%55f͚ЩS'钅ݻ#H$8pF<ԭ[ m6[~}wEǃKjvĿ䩨]{f}EO^\q͛7{V֮KII jՊX˗/௿R8@ϟ?smghذ!1={&}=##m&݆4..xT1cȌ1''4hrg U6$ x{{cmŋR "\t Y}H8 rw|guj[&%G:v ͛72Y7.egg999jH466V'{̌Xu$ @ӦMU>;:339YF _z! @f'''H ϟcNHÇgu)$'' 1JJJ ,,L/,Y'))iTTTX]ZnM<Eٳ\ڸ!WbpqqQN(,,T.kwI,U]b W4Klx|AAASΟ?~: ޽{Z 2yzz f̘!oQQǥLMM۷jG6?l_+HߟXHo'OqPf͚ڴiS^ &&&[9;;HUTC1r P-Z֦<nܸA:,C(Z˗~J?짬 W^%eNEU2 t"On޼Iܟ,o^gmqCcǎUO~Ec<N?bm(6Ν۷k̫N|_ySFvXսm6zzYkת|LF@$H$vue ̧ۖؒEEEf8VV 6ͺġCyHDCEٶm?0J;w% ]rEf,/^$M=-H$.O`}^xr}G%F^^^h}޽@fF-X Æ #j2{!$֭[JwMLL趀NpuHMMV^BBҦwV% xyy;&3(IQ"N:)m]DŽbgUo˖-aQҥKU> D"bШ_>/7SH$*7PWuID}3JJJ_S˗/1tH=P*%=z eMEׂ8pxxΞ=Kܛ(zT:HfQL4ikԩC<urJ1?~7EJJJ[HK^BDE~tk]viϟ?f P(X M4ѸoKKK6''^$ԧO]z2&M۷o_WJaa!`k?u&)YR())A3g$mB{PHH2V\RRR02_/..ְf<~X?eժUqpp@C aU/#GO>#GN:Smܸ!Д)ST>(""|4o<%77gϞU%B5Ҹ/\DFFVxshԨQ?FmڴA'CSPl?]r[yJ E\t=UeЪUX])))`ffF<u5n8q޽7EZlLBTo]ϗ>f£G*$HK.)88/ڬ~nW ~ J#00A%۷GF/**ҰfyF*[P֭[8ꊥaÆ\ﵶFK.e]'S^zΝ;8q=|={ٳgsP޽{˜ӤIt9daaA;$ ?~< ^xhӦM؞HeʕhҤIH")K,AXJLLDvRCzB!|SQx<ܺuյ1͋/&M|"͛7{Ӵό1Ι*)~xuh׮tuԁ]ˆ#`ٰn:8p\xܹO<$TI߿ׯ_\3_FTTh2c?~RQQQo 0zhFeʽ^jUE<ȑ#D-)T'--M徧)u rss ]PRR 6^7n$EJˉ'QQ|>#G:v(3YfLr8ԬY?Mjժø?o ˽{.dggðaÔz>T/5'NxF_ݻwCBBVf:߱cN&6lxV\Ij׮dddhj\ABPF ((( EGJM6mQQdB˖-ǁKmڴɓ{S+V=G>>>i[Çr|DFDD@*UzS[xq߫qGgW3t [[Yt8&Tj,YB*ŋ޽{G^@ykIIIr֨Q# :֭[8#=z̿UV H?bZn-(Nkժ0`#l˓&&4iZv-W3e[i3f͚U!믿ϓ'OP~2a)kʕrP?(Jpi2fcVet `rD͛WxF/[_UÎ]vQF1:۷^z  hѢEhժU23xT~}>5iD"F~РA3T\\j֬233U:%%%5jUH$B%%%(??dnnBJ*R(44YYY!dnn,-- @UTA#deeϟs݋^Ν;ׯpI[СCb ,J% E xN&W<|x%o A{S&M=W׮]#7nV3fL:u˗/CZdh۶-B9RmT6՜RsʌҥK~z?~F):==ܠ*x",\QF)m8oHB&&&2g<R2´i 55Ui;RTׯؖ888@AA(:`IIIIQիCPP>|rssI[0СCĿtAZb})Xɓe?䞷kGRjMɑ2Mlv A)}oLL 6_̽租~bTn~roFǃ7o*;|pK7nG]AAAJgP(B$Aff&dff»wאjOYyݰap(:`ɷ#dbb>>>)))RTk׮Ŀ]Ge};wx\i޼y2c M:uJy;qqeիWnݺj=ݻ{7o IDAT%n>SN|_]E(S͕U=XXX_\?^!ivzFEEE 0bر#8;;B oV.}ZjAQQ(:`Att4)0w\y&D"!PdS_XTT]5k5uTpkʌՕ7UsNMmd*777}Yذ}v1;;;CLLҺgϞ @$A͚53g(,}ϟ?uڬCʌйsgæMSNk.u#7ov^nJ:@$==jԨ",!QA%¥ bׯO<m6oaz3DJ6m{޾~Z)J+Ӆ VVVj:r IL"=0w\FuAaa!4j9pqBݺu/ÇOOOU̿i؍z({ݺu5D{H$hΝm;ԩJBGڵ4ၶm&ĉ!yvJLLD_~U7oMҥK׫_ kԶm[o2M>mܸQ#iXzREO!<xzzStfJJc[[?+9;;ˌ7Uh">*Zok.17NiLSiCo߾e 88.^={812 |Wl޽yGQb14mKH$ ./_nfժU56Hُe&ԪUx<\I\TUY&vy޽Gm-15EVVɍE, V2GDdddpA(3#GfhhgϞU 8ݿt8-0$'',kn  +Z@**Ec˗/cR̸?Lܛ9ssxuEݻwr]h  1xL׊?LMMjxB"@-۰aHCRC*1ŋk**rvvrp)y[{7U5ydPwq`"]KwrU],ïV-HJJb|3YBJJ /_.7XZ*ƥƌ#3޽{jժٳg!5ptt$A<<<`ӦMJƂ S**RRD 'N 9=117U5c 簸X+ j͛\055ùs_(nCCC jՊѱ@e&/YDn;CՉ5ddd$fƍĽ~ҝ%;bi3e @ ڷoOڂJ<}M<999ٳgҖt &MAg&O>a+K000@M67]!LMMhȍk.$ IP*U^z!SSSt}]P(D'OD1 @$C! Tۃ,\2֬Y3tuTvm}VxKn$119vرcREj0dV^\]]ѠAz-.122BwF Eۣ:`+O4heMBf͚p| EFF2GǎC %G"駟ct?#=V~ɖ4{l[6hݸqr;wD?oFlnܸ&MDt uؑu9_~U'BԠ_~Am0F,#G͛#tmҖt-Z9s搶Ah~!>Ejj* E%4`ff=p;l߾>|'eݻK}/_FlْѱGO>}:!G߱OmϏ,Ё08 yt -_ \t iǏ}id*"B333ԵkWefffb-OLt@yڵ l߿I$l9r!o>$HFsO}( %x"iy z 2B!|2B{f 4/Ur ի˫7;;;իq_\$ݹ[&Mu\.\Hg|իWc) oݿ]r͘1hŘ]C Ϟ=C͛xhZ5%BMN3ﺴu5}Z8q'FEE!ZGAuڕQ=PnnnЧ-erCAAA(fʕ(&& 6lrqqa]Ε+WН;w08<@H^SN+-[ NfffH8c۶m @AAֲ###?TTeddzhܸ1pz ƽqF>U˗/ǔ>Q&&&r!B˖-9i'''(,,sss߸qƍGidۣGҒxĥ 1x|>qoci$''CTTYƎ ۷kkkW&,2}tig̘Aˏ _+ 6$nM0Aik֬!SU}Ui<_~%S[R!»w8~ ݛSNhu֐+@ 8ԯ_?1Ջ7Y6l7PTTH ۷oIC! ">~.]7/`ggfN>uz!Ⱆ_\\ [&GESsz1rH֭[ƽb >UQ*UT>jZn!͛9mPشick֬ bB!=]tBme}|>xB|QQQĽӾ}8>HϟO: avlݺN PV-,666;!r}jsJJ TV.?5RP(<<jfϞA|guIϻ1>~Μ9pQ[=мysتJ*2 ԙ) 80!!!X(""t(=?T&mW#\Rf|>^o PQʒuqP(!C˗/*;w\~UСCU>;v$W[eoo%%%^D8FԩS0m46jJ?!ظqLLL*UVׯׯ_ʊ7y\XT6mHB!PϘ9sJt-[Ls)K$:wL Exxxp^@ @{EF.7nQ齺!9tۤ ./j{؄,,,7fg1}ttҥ ׫WO/^ߴiH$^@G&H9'ND999m`Zj_~a]Ν;wݻw18"t@ϰ@3gT777t!d``!W=O߿ƏN@:j֬\ Ekc4aԇCCC4c / 9t߫GeeϞ=-0BSϟG!Թsgdff3g K]b1 D>|qƩ5СMzJzξeҤI[?  3gD&&&ٴi7]D;Z +L"\ժUѩSPժU5 gϞEbX{=ۇ^{{{t1dll\ ES4jHcu|e4}tՉaÆڵk~]P^=Kg((33 HKKCO“roߎ,-- 9Æ *zMjl޼Ǔ,/>18t@177GG ѣGQÆ "UzߤIPrr2ֺ۴i°Ih M T#yݺuhܹW]LLLPHHcJJJ1&(9r AnܸBKcjܸ1nkjs5nݺ תU -X#|DGG 2e 7!H%f͚z&oQQڱc&G])cǎE 4(چ Pn9<*OgAAAAJg 0e„ hܸqXˤP4X|9:xR:u*rvvft.066FuUL߮laqqqDСcE"A![ϟ/Fyڴi~^!} ׮] JqqqA `]Ζ-[t⻉:|r$ %6miƍ7dΜ`͛Q.]KpE͚5ހ<eff{UV!???ZNckk͛8M>]U777$T~)nXDچRn߾bccBk׎Q+#::!TX(..FǏpr’&NHȑj: gf]ƻwPdd$7&С <b1i+Ei|>\ptpuu%-*~ %''?m۷èQ[e>>hرWS0.P{w\ @<˗u5̙3Vx}ѢE#<|8|5BRk#IjԨAڂB,--ј1cPll,z>}F,,,?:Lx9zir2ʖ3.@TZuԾ}{k}iFFڲe iӧ6ޱsNVe\z-^Ghڵ˥PpRzuر#ruuEvvvyzz=z &͛7ׯ$$$4PB! Vl!gϢv_[nHQ.\kF %JjVVV055Żw?~Av ӧQn]2rTXQvabbG2`[áC֭%/cbbB9"bggǼ87n...\>}"yT-5kVׯСCFGR92d73TΝUl IDAT;̵bŊ;iӦ`%"ǎ# , #`mmڴ&]ta~=zBHZ #ҪU+k~ ͛7̌6333ҪU+w|Mٙu*mۊ>73f` ٶml_ "wܑdaaa@̿nS$Çe[6lu]|xxxh4dŊ#FZ9)V'͛ Z۷_jݺu#7n$/^eNJJ y1ٿ?1bXb ylZj߿{B4~ٲeB&M|-#IKKp[hL+Yz5y kJ]faaA[ 9rD)YAw81j1j(拍E~IժU +++9}Ç%W^a̘1(UT}WlٲŊæMпIt[az3f ?y x ?$DGG#((&L;mBO1tP>|زe z;;;4h4/^m۶ܹsqM$&&ѣGXjz ''Ƥ`֭i̎W8Ν <{,]xyyɮիWѷo_(PZ GGG>i2 777Q>>|#GPRQ$#F$rܼys9+"%[-ZHr]׬Yٚ/N6nܨۗ$%%Iv-Y=TդsԩS}6;v>Сt-YKFCHPPQ=z1ٸq#8p qqq!%Bft҅B/W!sJZflԨQރk׮6RȞ={$ܹ#h{&` D]^GBx@rMT^]Y1x`,YDԩ%d| vss 9s&ƍxƍn:8;;GXsETT[A-nݺJ*c-Z2b`}pJ4h>>ӧiyhР~:y޿OƏwƍ ƍ_dՂ3?NG&NHڵkG:;;VZQFkג+WUBfdx]||J h|e3k,h֬Y%-b|NݹsdܱcQq9M:Wu}ky%SL! כ/&A?ȅIڴiS뒼?dɒ%$11Qo-$00׏+VLk`}dFg'NBܹ'N$b k8}?$sjՊd{$%%;wN:KK ~s1TիG/_N^z%x;Kv8Ih-O<˶mxl֫W/'O'B ɓTל7o&;vڵk0rE/]D%ӫ#!őիWq?LBBBHJJs*'gf75mFɓo׮!s1_KV6f̘ ñcǨӳgl3g^~M Fk.>5jfgNNNd„ uqR*Bj|if hٲ%/oƆJظq#pKڝ>}:5eg* K!CcccɩSȎ;H@@ٸq#9x ts3)"uhh(@ѐ_~E ,, 0˗ˊ-JH.\ ʁ֔R/!{ H K]Vic>|xƸ82p@hYX1ɓ'ӓٳ(shp RDnݺ^GxcT()][sqq;xwn]iiiL2פ/^߿ !%%߿ 0,Y2KVVVI&d…ɓ'O ,Gt8p@y^:9|s%%%En)n}˖-<$???GkڊC]bEtEСiiidÆ kaL0A:u0?Z311!?#zȿA&MDO333Irӧp̿\2777{APP=]ܸeeÆ v mںwN^~M_6m*on2}GҸqcQHTTT4Ή5ZMvc׮]z(HII%5kLbɛ7/ur˙6rHj?|=Bёŷ|L6-˽ZÆ ɥK?55̜9SaYfAZ[nM=z-[3hZ&]t!Νui||i7*Jmt "ʧgE3={FlmmhוH옝&MHQ%'mVYv‹0xcL4!֡C*Í@bbb>[EV#GT^:$lZM۷ojΌ }fffd66VtHvG'''_B߿@xn߾ <{,1|j,]Tg.X>ر#ñN:AGA۶mz1a899aĈe|EJдiSQ>V^MI 9S8]޽T*iz4mΜ9D|܌͛':<<5Ha^^^Ʌ @ʔ)C?~,m;oJ*YV֭sD۴i(+W|_Zb!d >}:df:ߏoge F7}|:[.,,,HHH^tZZٰaqppl*\vM/=Jb޽΢V ><8ѻwoBXKʨQ0{ldspr>KyDҥKST"xxxɓ'|*hݺ5N< '''cΟ?իc֭t$e\HHH%>PMiTJk|"E0cƌ ?|}Ҧ/o͛73bڵk뢣Ѿ}{S6 ZBtӮ];Q5fJj8Lap$66.]yt^*STdŒu$7/3ʎXݺuEM3_ֶm&j-_?|5|mjj*1c111a~lCQPy&! ߰aCB!׮]cDWCfZ?2eJ 7oޤ:G~9Be[Z&ziSnkkkG8!))4k֌ԦRȒ%K$?G! `^ne;w|1/io*bcc| 4/COtL_'ҽ{wk7;<(;ڴi|ځ!CxGGGB!߿g,11Q{2hР,}['k֬AGZZ+VfsTǐ?7E[ѭ!<((~>| !`Ȑ!7oxzz̙3([prVVVƽ|ׯF9-?~JJC#sbРA:_s4l7o2on@mbH˗/kXYYƆ4dAcΜ9Y~oo:߷=zȾuʕ+:wT*[l8q"|}},zNCټyszJc{cڵpXE0{lX `ȑ3f/_ΝC˖-%sț7q(Q&&&; 5aaa]6.^He܂\ 2!h[ B#gAf9---~z9vvv0`@|2uّ'Nd8޸qcѾG WW,_3aL2E\BIII̙3/j x1<|"9;vqX`ٳ%_{p=Zy89 <ÇF4 ͬZm^x:}aÆx)r.\ׯ%p’a(b֛PdI$!ϔڵkcҤI8yp|4Dhhhcb3ʔ)[,NB D&ߟ")ΝC^׋XNfΜKh0k,By8Ə34fHO ZiiiukfԩSiΜIMMšC$GE fcǎE֭zo׽z O_;XŊEp-[o'O&ɘ8q"k3x`Q׭[;<aƃСCLr˖-C~*aQXώ$JJ#6xC?ErPbFǏŋI ĩjlذ%Jt ^}v)իWuXr6}Ef2L데" W\a- ڷobŊ #Y>8&~[Ns>G;u$y@FpZj%<l^9 ĔT!_|4h޾}+]մ|C֧PT,T6mSSS{}p ;v7nTdɒX`A;** |>ƶ~~~|[Gv>}ݻ(}111Spaa֬YsC&''犧~JY`׮]سgdsǓ'O$IRBR7nݺزez>|@XXees%K.bΝUV>j5֭[i211ފ .!![l#<CCzpM4jպukeJMMŜ9s$T,)"xlrr,EGNAmW\+T`gg, jæƍԈ'Mb֭F``  5*N{1Vї.]͛Y0׮]KI GV#ӦM#` &'NIII!&M"Ν;}[^9?~\T*III8s QחӬ\r߫oUu~k7o$Rn]>>}D!֖z5*]޽EkT-_&vvvק(Q$$$Hr.J*|5K¢ɦM￳atĠYfظqsi4L8GYٛѷ@jj*fΜ)e!&@IEԒ ???E"4޽+SR%I A[L]T@,C2d>C#22K,a- vU`]c8<Ç4}QJ"11>>>4i,yxx ,, ^^^FAѢEQR%xxxC׮]VZ %K;v;wh-(SM\ m֭ש?~\9vȪ|vhHq͋PC)>.Mm_cp / @bb"EE Ʉ۷oGrr2k)F !'OǏrJ=ɳXz5f'''ԨQ#ݪV GGG&&&"22ǃp}ܸq#tVB0c +0N,--Ee(b'LU˻wBA nݺEߢE:lʕ0`\]]YKыAału׭[FDtI*G]꿥JBHHf͚%_,G1vXܸqaaa9r$䶧PLQi0͛W^aqts-IӉ/.Yg| |.]`̘1Xtuuř3g0{lQOf8_V;wo߾TCKAn|| 'OݻwՈ;w`T|qG ZL#@ʗ/M4qrrCVOxZN4#FM,///QSTÑ}zpruEXB0{lx{{Çibb~ aaahذ,sdjԨӧOcʕܫرc sQT"[[[*~&Ol4?jsRԩ#CQǏyRD($%7ziii}(5VpŠ /ZRl4m5j+ OQ G x# UVņ :zklg͚5CXXV\"E>?+T*Ju,_<=67Rre?>|bl2j8"u!@SSSŴv޴QRjB |)%22R9lllp1|wEWbڵed5NNhb\r5kĺud~~~}6.1P@ٳ .T\$''ce0EǑDQx/.x} j8By[n-}ɩTZjqqqrl"`+Ə82<ٳpdӬ%Z>|>}sΈ}B a޼ysz#kBԨQ.]BYKŦMd/"47o.x)*7E%ܹsGRJ PLhCh4X|9&M$ۜ4xQtשVjժ%x|@@E5_EXݻwՕM3p%lْ)cV8vVXG1dL2k֬ pB+xxxKQdORlYEFVsssҴiSpB+~9 ~_._\. vnuւK}BȌ3U!pŋWׯ_go֖ aĄ9|}}+}' Ç^G*Ni#fswwf~ Cjj*ƍZF 0*J]vqHKK%Bb T\AAALmϟǁРAZtajj b۶mȗ/,sj35Kş?k֬Arr29 ~RwmQV$B V7l PRSSI簴Df$#;|.\+\]]%)Ubg%9 m۶عsQq 2B 3?~;vPV pdxUAzÇhРV\)<]tpl޽L;@(^8B.]ڠ)wwwI7iD2BPRQFa̘1edSBNNNEDDٳqYƃRCxyyuָ}6k9>?ԩ:=EZ a߾}Y&.]$< h֭k I?y$Wah@܎\{{{|JR@Iгܮ7o 66 ̘1=z`-#S|DYfɓ'0`|uV\fff)));v,:t it!Cqx:tH򹕎WL7333(WBE1|RTZ11beeJM*UQ9 ޽{%d@RAݺuYKɔ)S ))Li۶"qYǩ>7< %X`ʖ-UV!55tѳgO8q=¬YPD,ǔ/_gΜn(QQQh޼9f͚%N:e84.99YOK,iоͰ0~ZBE1$%%tモ}v\\fdbb" J$J 1޽[MRrUo޼y6KhW)+bl,;;;tQ؃戇8#ύ7ZӧѰaCx{{+G@ЧO%ײyfEH *3gzF7vvvn:X2Rd=wݺum0L:edѣGشik鉊+ ˷8'ښ~c!)) K.Eɒ%ѿ\X~=6l0`eeZje8ӽ~ n:zB$z0ȹ @x(?vtUTI!;vT6W^1/&&&زeK%1w\  Ah1K.ƍp 8rs `ժU(S FiG޽ѻwoOUkԨߧOpFA"EeyDNz.\z>_BBBuEn4X[[LkZh!|\T [y ߑy&YȔ>}@Y x#)Ҵ3;w.J*1cȺUN޽:u(&M\ӧiO !hp>'Y糷m/;#Ymٲsv]93X@ƍos粖) aEmGRX >|ٳg zBDDkIػw/j׮ׯ[c.\`D946%%7n>"""r}AHcA 6bŊW@xMBD d+"k)'NDժUYcǎ):0+3G2pi28 ')) X"|||pM֒aÆcǎYfcJQ!={Lol( )Bw}'y7XZZ"))ITv7󚙙gϞϫ c a¤bɒ%%dJ:uPF Ac6vG2]wޱ1RRRaC e- >} ,Z XXX%/2P T*F)xRvd!Us`l-ZTmڀܳgϐ&؏CVHҷo_fsݻwYK0*U`ҤIe|͛]7ڵ ߿< K#BѸqcԬY6lP|CF-Vd RRRrԶ Ci߾FqqqL9![xx`UTK.@e ~IʕQNfk~go%11edJ=?~cǎ(dp$8b|2|||I&1 BfϞ6m(::_T ?~djQݺu+'R`A[[ʕ++B.Zڵk'ҥK>|(Oɒ%ʗ/dn-Bfݻwj9B)k),_\TVXYYGXcɦiG2x@-^|ɓ'}U^ׯ_u3f J˗G߾}Q~,_+d)ڵkg{βX~GǏЄU%JrʒfseoooE7~+WYH8vk2h ABCCr3<Np%>>k֬;jժUVÇx">.]Xz5N<ӧg%Jd8ƍ<622h ?f1Ͱ(.E,1ZJUz%ͦɵkXKɓaooZF:J>WXQP`bur<ϑK.(V~ٞ2__>ӿz o`:_[p Xe;_ni^ޯs玄J8a**kŭ[)^8Z\8p k0 ȟ??ΝZF:vbC_n;Y~|x# Νc-Kx/^ WWWaҥ>ѭ[7 :yQB+Wc&&&O:1)3f@R K (+jժZ0.XթS+V #=z$؏v?,MʊRhQу0{hܸ1k>`׮]edJ׮]accc𸈈={VE$b;.^֭Ͽqܰm6 *Sn]3«+6oM6<ɓR %Oضmڴi''' >ϟ+ uQWM:UJjՠBq<`\NnԨu Ҩ# J% .]}QW 5j`zȑ#߿uV$&&J H~< ,@ڵQtiL0A= 0`zꥸv[ njM:wڵk ݻwST$-E[  Rq_ A!AytSF=uTf7$99edJRмysǽy@G<o9]ܿӧO+\]]1uTAXr%Yciiiq*U<+BN4Gׯ))C >{`22tB `PZJ*C uʕ+޽{سgk Y2p@A6$4i҄,G׮]1gI2rύ7e˖ѣqYf͚tk:~:)&$$d8BN~GbȃqQP!T*ԫW? ,{{{0&;;fΜ 3grDqĉ7vڡx Ftt8IhѢXH|źup=<۶mȑ#q!?^1^8?uE֒2%/ IDAT:[[[)S&q]J#Ô)SDHII޽{))SSST\YWJ. kkk{NTm{d>)W|||p&sӤx₋ݻw|2khЯ_?%''+aN8`jjq1v>|8vލW^!<<7z ^R0m4_>WEs-Zd[pLW@޼yiR2'O'7$$ĨJTTɠEe-uԡ:ԨQsz}NC:u*dɼ_vUZB81h 4h@yмysL<Gś7opY̛7;vD"EӳgO9rDs8r蘾53tU~ecB WWW 2D;vPP#ޘ=\"%PBfmN[_\"Za?dS(:t(sJtԩk a-!Kmt_."ΗG24 v܉eRkgg .ŋ:t4i"fqyTZb:tvJFP`JuvvMiT*_UƇ VP[,N'^aÆ2 ƏϤSAhhh>o3gΰ-BnܸN#1^" +W駟ڵkݻweҭ[7q>;lذO^si||||9rZ ;w 0M355%iiiTo4lPzCHZZܜ$%%B)RA*U}_ɵzԠq틵gϞ> RbE@RRRXKGRSSɪUHR711!nnn_%v"/_d9--̜9h4ܸ.\$''gNt^=";w$I&:DZ^TfmmM^xAs`РAc|s(7vVX1*6 |}}E~Bݻ'O͚5 !`$7dKݻC@eaPׯSN!44wEBBPlYN:)NR0fTZ]t1kdܸq?~aÆ鬚'''_hQ8;;T&OLJuHG+p-T; 4(lTTXz5UHTlݺTNikx3 7n@Vͯ{oиg[#;v,H/id߾}:m۶SSSի,_ooo=z`>3SB7odCmƌ3us߾{*7 v)z=TYB!hMϏo+ȉL2E700dTYZZ8s, JJ.Zl˗/]v:}׮]HNNƦM_ŋZ,N阛c͚5 i GN%55g4F5{y*~t055E ٳx)V\֭[爧ՙ1fTVM9eOJ|}} θR 0>>;w@ ]8ڵ+ߏB 8.]e"EXb7lؐX0}tTT?c h44h}!D"E)YG }>KbPxq$$$ ,,L"EtHNNkkkxzzbر8x ޼y'N`ʔ)]66mls߿'%J_zlnCpssCZ EJ#w|q8"&.jQ6͚5ƍ|cǎ=u g52R ʔ)#NBWV\ 8u5r +++Ƽ{N"5(Wk X[[ $~ԩJW~XRZȑ#aܾ}o޼#G0c l2WgUP͓mחo߾e6 01xj8<t'lܸ1۴Ӏ{…֕%&P͛h4|^pAX4S3>H#%J1{lhܸ1\\\`nn͛?Q:ׯsFˌ3qF?qqqDpp0̙~ʕcD nݺ6߂ "|RҩS'f$yRӽ{w(P1  V/5 5Wۙ?>lmm|Mdd$BCC:vQ\|Y~-Zuӧ fϞҥKSyq@H*jbbJ8Raoo󳖑)ڶmm~u(x111ś7o8$$$ !!^^^Çҡ===ӳ 8vZܿ_-by!oߎݻK>XZZUVLӪw#ݻ7-Zdиcĉʕ+f͚m4s9::@^xA}dF} TϟxE-@k&'O&ʽ^CCC9lܸ-dʕzj봶&qqqie#%% `ʕ M6U{gϞ-[/`+  {Ja.]*LcǪ]c{ʕhҤ Eݺuw^He͛s*\`r9|||7_~>ѣ|уiJ! ** k֬AVPbE̙3O>UWRRM&Jaii͛ G50eA 777ٽ{7B ,`L&c{/vڥ7objcbbCD8uEO` mwx@7СCۄp<{L0_+@&|999 ÇqF 0W<<js!$&& B F!/,X9` իWW+b׮]_9333]3 sttt< \=ի}B*~ʕ+*L$%%͛7ӧOkBTL:oֹ![nł } o]vEnTMOcpQڵKS*b׮]333'##'ƌ#" .{{?YYYBڻ~R?Lվ===̉D"!#ǎ~=j*TPzݨ?C} 7!_xQq9sB'?nnnBÉ=R R Y&iҤIU^*UP?ȦM;BC\\\c剳3%͛͛$88WZȑ#+_7FFF$ ˗@hݺ5m: 0K?-\P׻wo^Ծ 5jsd\8tAU8q"޽{8l?Wv8::2M )_<'_!{ Al%O>qvv|Nի'JW1)S oST)O|׳gO%::l`0 !33SbExzzyȊ+އ*H}vۗW-1p@̟?_E‚% !ܹ`q K>}`ddH~Zc?666h߾}2r]">>^Rbn:8p@~>| ޏLw޸whiI2{  F!h}PM6)M3{l+ 9 jg``@iӆW=ҥ ݈Jr%333^x2B-G~066HŦMK2 Ν;JBpBJ&5@ӦMqq^c?N iUu޽{u.i6 F!$ ӧzÇ 1իWpqqBP8333?~\GвeK9r&&&Q\JIǎyyg+D:ݻw 矜| 2ć4‚ h(iӦ ?iРFugϞō7ЫW/tZh۷ok/mR)&N1RQ,333Y&1c}ajgmms.= oH*( i[4le˖ۑ%2dp%)_<ڵkL`˾}fСCt]V>VZ*H$h޼9S_&a…ر#޼y[V]]]aff1;vH F!C,Xʕ+y}Ţ,]tFKq-,, ޴ԬYgΜъomР |`ll̋/Pd2?Gؽ{7'?ΐJ8s >|2:( e(e߾}Tq\BoRҕlhРFm۶!66׮]'UVhѢ,Y^&%JPwuU d0`ꙃժUdee֢h:lllӧO5\( 2{l*T@^|cD"PF :%JP3v5^W9"իG!$55XYYqu]BQ4Zdd }6}=|pqq!C !dӦMʕ+$--߸82rHSCA9=>|m: 0g?I$rܹ<יB ڵ]OZ8ZnA {{{)+V~duUT>&fyTRGڵh#{nN~j׮M!$--XZZR??|X=9\0`000 ϟ?=ZI\\6m133wiLǎպfj֬I m:` &(/prrx/;.\rʩu-QzuzіP  ՟ iϟ?ٳgy CCC <gN. Ejj*gmSI[wqͩ\.TT y^ONNٳ)(˗1bˁj wE׮]Uj_ti?ר?@͚550ŋm6hJ=`ooW^q pJ$@fϏKҖT/mۢAغuJ !vI;w Fa5`llÇ޽{ CNЦM? 'ED;w*-37{l}R,YOʕ+ |TL;wX>iԨm ̻O@011Q+deeqNħ&thٲ%*W9s/yZSp]2r499Ǐ8qҥKcذa|Z>! J1Dbb@{ mg ~kd֬Y&7n$JtYJ:m6Ο3g(G_Jr/4qqqC~xnjC}\̾ݻ~ !$((H1ڒ,P(HժU9ڴi!͛7S?7B9ߚ0qDѻwo 111d˖-ёs7Cϟ?c˖-e,` ˗'>>>$))I-dz4IJ*$%%EѶsRrܤ77Fڵ Zi?s5h@Μ9ؘ}o;v\wM1̚5BHDD'?$!!BHV!Hk̘1C+ӧOWHxx8%Τlٲ[Νi7&M؛6mJ[N F!x{{S"lР  ٜQ`YfT*%JɸqSfDDD'''{^KXؘ u[bhh(R)y!{VcBIզR|\eգ.?э dɤcǎV9ر#FLL JjΝ;e ,`Ç|uܙ_7y qssH8ԦNV\d3Fb}"""AQ(zϞ=&MZj.] ~1`A WSLlٲ999iѢ4i#Z1FKKKW^EBBmQre۷oMY|nI{ _W !vvvppp+++L>Ϟ=C``(Yḛ̏o>yoƌJ=222ЫW/jիؾ}{K*MRP!TRfûhܾ}wʨ]6:vl|}g$''!OY~=p%\]bĉoԪUK8zR ? ܲebʕ~'ȏ:u.V0`ժUJɟ:uJj֦W^z[x>|=GGG "HпCN`kk˻߽{3?LD)Ydum6i5=~+)Ν->>>R$ &M1* 0;o֭KvEɓA@ZiwAi>{{{41g WpŋdŊy"OC֭ cdvU(UʭTӬY3N̙C!… ϋ6dޯu~:9j5jԠ:|&|Ջpy'%%X[[<D|9~ C8y$ 8rH+UVZ-+cƌԿ)Q8B-qh[FbŊ!22ׯ_؏T*Ÿq7oKNpK4Ц111hHo+/9)Fr{BTߕ`19#Gu! 7oѣaQbEXXX앨Fb;C" y۰aN8AAr;k֬-nݚ}Zi05`{•r:u7o8eW͛}HOOǺu85kq ܹs'ݻw3*}WZJF|||؏mn/ooo^:Vp@3t|H$ _FFF4i?}Ç Hwac``t:pqK/^˗/SPEBH[R>}] D\\\`ll,<=Z~R|y\rmڴ_pϝ;'oiذ!z쉜AMKKK\x7O uCQ^zT} [Mfbb"_mb񰴴T*E`08uԡ-A'Ynҧ$.\)(Kɒ%Ç R˜̙RI,,^8ʕɓjB֭GJ(p_*5k?̻_BΟ?ϻ;w.$ كW^i*ފ+ܺu ۣdɒ?~-7*Tf͚ [W@sW ¥K$߰Qa~cƌǏK<u\zUvǏ%~y777Q |)Iƈ#^_Z*\N&LŋݻICaӖ+į ~ӧP,`^X(QjUl۶-1Bq}8viBf̘~hLsySwX[[cȑWDoooP DR) &g [-Z &&Fc?>}:`E"q:>}JB=ք x_į ~J*5HKKP FPccc8pŊ󞯯/(CVp=7k.\vM=+Wy}>|80zhXYY []fΜ";wV+:~2dr9,Yȑ#QL<%RիWd2oժ}Gff&m RZ5T\wE%|*IIIؿjt`08իWm:ʕ+Ѵi<_~=#J1oNgf̘r۷o-X`6 &ѣG/"Ӊe" @icǎYlttُ7ݻ+"88xxxoʕ+/}? ,aWdd ~ؼy3bcc5cii,Xl^#4жm[*L&-w(Z{V++ `yϟOhڒGg2`bii)HR+[RM"9sL&e+Zj?iӦq߮];q'۶m#FFFԯM]+Wv~ &ݻBIII!J'r}"Jm6ccc*5̝;W=GC-[>d  uԉ~1P2f^Tg̘sss#YZZ/7Qpss}>N/BA8 yA_hիW' BsSfMAtK$r-B! .dɒ$))BHϞ=]3gvGZZ133>vOOO.zm$IAj}&==]kҖ wܡ+W\xr9y&"khذ!?--ԩS i5j QQQ|\^s"Hݻw+S.]j| yC(^"M4~-]Ts صL!ǏXb|B|2+6o<ѣ{xxP,Z9*^8PWyjժ߷| __zƍG# fffÇJ} tY$&&2bgR|y‚ܿ_i2mVmy#;;,Z-VѤR)y`㯿D!!2}tN~'A!uωXΝn bNeBլYp{bjj\;wd$ fffTmԄ}r{QUVnݺy^?}^7n;ƍׯ_ ?-- }ϟg``gϢq*UǧDQ122… eҖt?`r=i$T^-Z8q\“BT_W}5kWQ.ZT) 6L۶mP@;`0GGGbŊ@AƦnݺ)]"ޓh+W 2b!擪͛:d2ӧJ~', ǏI޽_l{ 4wvvvτB W:uL&#rԯ_5{O~0b*LV_ߟp'###G[2U n)֭[:t Gr%J`yJB0rH{guBhh(fϞM[<|P\D>}=9r[. $D)Y$BCC rKKK899 Gtt4>-[\r  ƒxRXtPVT N߯([q"dɒUjժGGG`+rhG  v{nѣdѢE$%JPIS@@R6lG'wt&55Ԯ]ݛ7cE%q"7~}k5J9djU7nLr9dg6m!deeJ*Q?hނ^CAlll뵣iӆ9ZV(s5.7IQ`0:u_J @оLLL0xBS>GSiTT.]Fі15J'p11999J@xxxkA:0ԩׯcݺu-G+Pg%&<\MH$X~=R)v܉;whׯlܸ/_IeтVSSSu+W?eʔŏ.ӼystQ?~ľ}VЎ@0 99YQ?Y_^&<ԟm 6ԋ=p˖->HN ?|@֭>44TYׯI~#VreKO9rWC%|)-Nme6i$B!޽#χZz FJ*EmB3c ^ɓ9sFSQ-  #ʕ+ %neeou1GWz`i͛7'>}w ,,LW֫WW.OOOҶm[gI{ !e˖~hŋ+V<\;'_%J3bBȈdee ~-GΝ=NAԱТE R F....}yӧTǷj*CbbbBGֽ{wNaѣGXbGP~*$%%)ShUFhܹ3s;j(4{yyBr022k۶mB^J >ٳgĉ}]j;v2?III5O>R F.ǏK[*9sl#qqq;}Ŋ}-24k֌=>{z&!!T^|gBȯ7O%]tEwժUsOp}ۤI"ˉ\.'M4~N"""D!I͚5СC-$!!!Ɔ0 BAիIRA5Z$ #=zA+]4N<+VȈ7RLtז,Y /_.4ӧ= SSSR8!쌧OҖ/pqqaÐL[VRN>|׮]CΝiFƍ>}zBˋ-[Gٳg5#HaHRرnE_QַT*={F_샯RD"9sTjP(ai!# CpJ#NNNZ<888Wc:uLx1133/ի̤0A}>ձ+˗/Ӟ6ٳzDdӦM6U%gÇ'|I [|yNFAbdɒύ؊+D%;;TREq8긅Bu#ah2TVM=15mX`[/"sssuVCȗLRD (O"I˖-/`Ϟ=S344$IKK=zB 'N ]tѹdĉԂ `kkK>|@!dɜ|YYYVqss~~ɾVTΝ;Ewi[n߾yn<<};ԫWDB*T@wN֭[Gi^6l( ؼ|R/kKR2|pCS\ ?~TiСYr%s "##ԩS^ 6$ݻw xL湁 ///NchhHn߾MjLޞ楖L&# 4unnn;111״n,:vH[h`/aj:uD9.vTHH$G봧;٤CΎ 0l۶ywmdǎuւnkժE,Xۛ6oެVZEr#5yr5R|YXXкpeQuTPA'PDTT9Ի9ᛅ F[w̝;+VϟΝ;%ƍD)[,FR-[ ;;[XAaQd3V^M['~wҖ 7oƂ h D___Yr'''$''SՑ֭QNX[[M6AG7c044q۷oe4(۫n*Z/JR4hЀG5ܹsall\h8߿_E`QdYr%/_ 8--1e2DZ-}ٳg(%55W\ ԭ[ŋG-0zhZ ǏGLL =!D嶫VB5 ̚5SFB.]V׮]!J_BժUS|c(bŊ=zJm:̒2"ɻwa24~صk<=±c_?bnnt4-f͚K.іɸv]eʔAʕQRvvvfmm sssXXX(MVP(d"11_F\\bccѣG>}:Ǝ+֐E-]tr9ցo)W\+X@8d2m JYv-AIHH}Tf>>z`[ F#!!A|b``}jժpӧOx}jժ1cmǏcܸqe,ϟ׊-6n܈#**֡ӧsn=`+C[M=z{ʕ+#4\f̝;WO/^?#"aQXn4Yt)hÇѣG="HuVx:}s'iii|2m߿?, 2SЭaÆW ڂ¡J"3TRqFM666Sjeʔ Tj+:(Rddd`ӦMehD^0g28st 񴥈΄ d#!-EE/ ʸ_R%l߾pBܿS?AAA*=fUCW@Re9|0^z%x?B_ 8MTiѣGS r Fb޽:yYlY|ҿׯ_sΈ-Et*Uooo2+VCh N:E[BO ŋ#""sSԭ[W,͔Ph ۷u5%''˖-!8@S(]4&MTh;\??? 0"..J@ɒ%iKǏѵkW|ёH$ضmhKӖ7<~/^!'Y^^^hٲ%޿\-@=z􀛛Y@8}W6l؀jժ ڇ?bbbC(={q]1k,#xe aQd8{,}қ@: 0 ɓ'{.m*cff;vl_BL[ 5ʗ/;vPߧPQַ=o>` }UVELL ._] _ϟ?_0'OԙsttFۆ'ƴi`ggWh;۽B7]2 S &%R)PðČ3 m[HXX`=!!!%?iӦі17niˠƍ/Ж ~~~e \Bo[[[h߿G9;i߾}nY5777DEELٹ.ݻʕu͛7>m۶:BW|H[JHRlܸQggݝ L<cƌ->|ȑ#P(hK)R\xZ9998p ^~2e`044͛stPbE8cooC v]yyyիW拫W}ȑ#s-݋/p  0 ԩS%+5kF[F?~G!jo^+<}M;R4+̜9S8pqubDQ_ $jբ-7o 6;==惴4XZZ"0qDTTv˗/\.^@khfV]:?YqbŊ֊}Ͼ:K߸}6AAAX~='+WD۶mgg϶6QX1Z>vX7NZ[ƍj KKK1bbb%K.::Z[5^MEL۸>wtRaiiGAXhnݺS L^.ܻwMT޽B߽Oj׮M߬_^)SL&KƎ+2ƍnѢE:  ـ֭۷pttDRRm)000@PP5jD[ RSS1ddggӖR3@||<8jժH$,[ aaayhr/G$)`TTϻ_9sFM6E-RTe˖.&&FgW[hUUV\쌌 899!66[{-111ey r8;;˗(Q;kkk>}/VN Y&m `ooؘwV8%%׮]Sӧ =z@v mtR\ CoiK(f͚{eBquR5+7;{Җ^bl3gΞ=񆆆8x W'O`VeE}\VZa֭>>V@lٲ0`]] C/ƭ[h(˗ܞΙ3g"44 8+V-'i1';;['|իWsvZt?F޽o[@;Wm ꊙ3g355W\ W3"lQy_ mX`%nBFFmҡCtԉ زe ֬YC[Uڴi={@*ՎI&e0Am.\9?~<&MLAuD ,/Vm GGGi[|u*.]&LP 0 ݢu[M C;~0 EіiˠJ SSSR!!!:P*HLL iO~s{xx('Zhs4A*"((_ŊywSa f͚sss1 VZ*\-[SX`%>-@жm[2T&::,{Up/^Ǐ1yd2J~:8::"!!Ac*UBpp0( MlU*O±cx)۲eKÑ#GTn[LL8Q@5 UYh l q ^r= ӓIIIA~L[ 5ʖ-pӖĉ2JHOOǓ'Ox󗓓p`eeǏW\QkiqQk E)| V>|&&&hS]u˖-cO%Kb޼y[p!DP`0z!QQQeKFСC2TѣG#::jX[[ɓ\2m)СCe0 ͛p8СC?ӦM!1d-tѦ}49r$֭[VĖ-[bJ#S...:W0pB}ׯI;y֤I`llL[FŅSq]!!!ZU# @} zܽ{S"K. Bc//+N8.]v-:'OFhhJIXK.(]5@ɓqa!d*իxylmms? C"`͚5gTi 0 C[Jp-SL5.addٓ̙3i`HFF=zѱ>2335]vT*Ŋ+T FFFn*0 RfM\r*T!cƌ͛Q\9!~G``J֭[Kp4o,Mbbr`IIID*Ze=5*J}h } 2rHL=۹s966/_Su% BC$ $%%57oP}m۪} ._L\]]IjH H˖-T^PÇEљB+Vy֕ B^|I <ݻw [`0bŊiӦeA&&&RwƀhK˗g2jn"888Y\9xǘ1cxY{ڋZqpp-A+iݺ5vڅO"667Ol !C A@@@ke˖E ͡QbErfϞ]`4҂ ^J[w*UJ'~͘1qqqevR򐝝 &=::322ЧO _'OD c 8<޽S= .|oh3 8pwغuk!00666a'*UT`[ӧR`0zѣQF 2r>>u?KKKU}ڵkS5 ;wЖ!*FFF8p@}i2yddddЖ@aO.\۷sc͚5yX]v" 6oAAe5e2Yn~_o{Wz !!!ߢE Ο{}֭[CC|'`̙Zp% šk׮022BDDiذ!bccѻwoNk;LKSSSoі!*8x hKɗіH$aɒ%/YSN󺩩)z}q@\J(Ν;ӖUo2 ժU9KDEE!==]e?XJ??|UTѣGb"Ճ֯_oBETV 0 <TR%J6mHrrr?FlR &>}i&zjꥮ4rq^ III\rwѣsk.NZutaѫW/ᄄs6f(Oꌏ'J.]4y3%!!xUR%4`[ 6mŋ]|:tǏ=_~'OC?^i M\2&O3gӧ􄭭-/&33~~~e1ѫW/R dx m ZPP|Ǐ/t4[޾}V{ -A밳O*Ux6l@jjjqTVMRxqX6/^mDR# 7n F###U%ȓ'O޽{ɀHժUE---IZH.]'ٷoy#7R%Jtt4122>_1TJ222r#G!'C%r\idɒj]lj^WtD&5EYf2iLHH y,S _] iժUC?L[*!` ΝC߾}oXi .ĬY o``5jFdHMMENN% IDAT $ y[1MlڴQ077GhhN쩝1coZ* gϞ LfV{ݻU.ػ{ovJ"C 5%B0vY%,F1(H1dQ5f-/Yo1Znw|<9s^s||>زe۫W###)g?ճV.e…_MieeT| ;.K `ŊhԨQ_z3gbJN5z֮]:X[nn.~Ge>ʗ/KKKXYYRJY?qDݓ5Ԣ!$''A.?]";;[<==P%)߿/ }򎠒W^~/>ѣGU~& ";''Ruõkהx@Qi1L4 e~*zExzzm۶?P}ϼ#()ۇ6mR*X~w "'իWGjj*?]t)HմiS$&&1R$aRJV$@͍w =Ν;88ѣW1cjԨQ<3FyA;BTVvv6部(9t<<<舸8`ڴic9acc .m۶x5l@.M4zQb+WCe4,,,PR%9?gpؿ?~zQLCƍ+_xA !*'==:uݻwRX,FZZ|RQV-|ppp@ժUQbEXYY}T"iyڷo cmmTԩSwEGGy5zz8<<==e;;;#%%E.]G2A ճgOBy4(‘#GsNDDD`ڴitkX}*r۷'*9= ![jj*+_aÆ 033[D4j!!!Ů9r$󕘊!%%%I&t(LLLd9r(ijgxG+Lٹsg1֭Ços%SQFVe֭[m' e[J=ţ6)n@5IzrJ@k%zr…/Tj (X,ƴiХKz߿C|2FO޽{;\lRI+SDDcc)4iǎ5N< Fwuu2mXǎeܼySm5o&ǘ1cxP 6ԯ__դ˗Odd$=zL[@Q t 3gTOʕd>PUо}{߿iř3g;;RSSannC}25d6k @嘲xBP^eiח8bR jAR7n,$M֭PL+-5B? ; _4>T{n"iӦL7SSSݻ:u‡.)))J+Vj_I_@i̯dhY{h`DDGGں;wDrrRP!DcXx1q]q| xq,_\iOT:X#11Q+gΜ}x eԳg†[gϞΖnݺ!))Ԣ j7nH:*CNh2̰z=zRBB=yڵCppJL=ghhh>}͚5 umݻ7vܩ݊NJOՌm===iҁb\cJ5܃$\Wmʔ)#Ypױcg{.fϞ@Q$4l-W\?S+U$8p 6oެɓ__~H$BLL dlԨQXn]sC+^ pԩ;1Jff:99iܔD=-\666Ů?§kB 88]t˗/yǁ5oߎ$ԨQwx)f̘Q互QEÆ úu xGYdd$ ~G,YB3ge1yT? U%]q@"""xGP;O-\dee1 ]xmlll_Җ &|ח9EOOmݺ1X~~> <@bcccL%w2==2LsvY_UԢE 7x' YYY޾{АݼyS!VG*߿vaԨQ⚥ZjHHH@jj*ԩ5 /˗/ׯ۷o/rUK{ x 033ѧOdeeK.\]]]lܸcǎE^^-Z$ǴQF*뀣ԭ[7^JҾPbEԪUKia2E#қ!2پ};ww+WLJk2331woڴ<<<""";\ũ,xUT#GЦM~2lll{z聤$9&)S@_.vFH"ӧOCmI @51h777L8ʕ+~\j HѣGСz7op˗/#&&&&&\w)r6m(3ĦOq}QDq 4hG-p)˫\2> ///}:t@RR+cmm#GJGݻR/ :C-eddH= OTK$,, >1RfׯGz~9lmm}ޞkU ~y||Wխ[ժUSF,͟?_Tڵk _ Ǐ-.]777\vMnLhܸ1=zwww9rT%n@eIt;3 ),6kLI/]]]l޼E=zLj Hӧҥ orajj9sի֭&"" X羾ʈ%9s $$w ŊɗȤC8t,--qQ4XӦMq ʕ+pssåK 4xR CʕyP[=j?CCC9 !UV->99֭R*V^ GGGݻ[Hܸq4Կ\r+V(rݧOp…>|8' f_}:B>}$>͛7_Skp+;;YXXpyqpp`of}]\\d*̌1rrrX~$7%%)lfkk[r̙˹wks6lf{TO>"{ԯ_eggK]>@c 6mB:uj*0ƔA(bvbbb$J%&&b۶mo"۫\2֭[H$xE6m~i?ѪU+ߴiS>}Zlmm۷Сl"R[YUvPR%yER;3gAl%%$(H$¦M`mm]/bԩ@nMtE)7;wSV|Xr!Z*++ ӧO =%C&MpA۷ 6A10h zwj/2uרQ-rUѽ{p!1@ @XX㡯u֡cǎ2߻wo'ODSWIu@E8U$?6pqqaxΝ+>M6{hwww̚5ubR}hXYʕ=g L,>jI!]FUl7oޔCjյ:u*ڼ]v1+((`'O<@O^x4;ʖ-[xޞeSboo_͛7~O(sDj1У:o|̙;6!r!YΝ;w\X@9j+--z,\c8puUU^;+𗟟ϪUƽKj dݻw<}}}a?:H婋[nNQe2a%]K~\;:!rQ}/B4ׯ+V@ǷĉCCC_Sx{Fnn\ .F]vڡ]vcp ܸq<+acc;;;8;;E+_8p>C+j ;v@Ŋq=t/^<+++ڵ M6ŧO0x`$$$ȜY&F ~.??%eeex*3fC8q١nݺ JDUP۶mC˖-|3<<-Z@&M$+P1UV\)<^,,,Xdd${SJFE 0zJHܟ@jPسJ򜜜ݻwc@ʳԚ5k8ޡ`E<%[|֚" -8q"؄(닽W.4 !*551b^|c#22oFDDB6'N^.c sE֭cMrrrw^1j*aϥ.k׮HKKC5pE4nǏ[fu*TPS.]*q_m0rHjՊw #^~JVb)!ʖ:v co޼}4ʒ%Krʕ+XVVY-ݻSHmZYjj*c<6f,""p0{2^d Tj999˫~Wի+Jhw1]]2]:uMB֭[;lٲRˠh/_bȑprrBrrRmffH<|QQQ(_Rك`+;;s΅5&L@ﲗю;xG_m͛7ر#.;wDdd$`ڵ+2330>/mʕ++*Xr%LLLx8B^^^߿tuu"O0gϞ-%4TBӧO,::)96m}4_Ō>ٲcK,a>Ԫ*77UPHmX>|cիv2Wv333Y^?&&*3x=zQij]FYq ޱ Qs1CC"lll˗/ݗQS,!!iAXYYH@wܑy 3y/fffgϞlȑ#%q6ܯ/"EEE}E_ֆOoo[nz)Xj[UfW^[߯bE,rqQYfw};2!J믿;M+r?j D 0gggٱeǏOF`uVؘ9991777֮];֫W/*СC_ M^,--١CcgĘ|svڈEnnibbÇʕ+HLLI>ͅ~7QʬJ*8rz; 7)))#hjժȑ#9r$1~x 0?~mڴЄE$HVPPcѣZ27n\8xwX^S~ ǦLR6O>~e]lmm۷oxfӃԨjeeŲy&Dlݺؙrbbbh Bxغu+֭ ???ܻwO300@@@.]cǎ"H$EC߾}{nQD("""v킱18ܥDGGO<ѣG#-- 5jѨQ#رC)cv;v,.]T6VVVjꃮ.n 333Q4^|||; ~ٳg.$$b1۹s'WR\ԪU͝;k-+++}ܟhuPƌ`./ժU~]yiժ{ cr΂all6o\x}֮] ^޽{vUo2WWQ;E,Sҙ$_rʱϟL )w0BMJJ sqqQ===RRRh?Zh mY ۷o>}*%==uQE `c̙32Ogoo.^c,;;s9ej,##}~Y:vHC$--L׆#db 6Bĉqvǐ!CPR%̓tQ$00 .Ը˲cnnxt tR ''G2UVw܁Ξ=+rcee; 2Ů[ȇ i*\%Yn"<<\qa+VׯX95`'Nip!C__]tѶm[^ ooo<|w/_+V@>}xGQIǎA4o7oFՑl۶M @RRXT\wD,vN__;v쀅(ZݻwؼyN "[lANpA@œ}̀C~p)yEVM~45kքLz(ˀ0b1X,Ʋe$~]yLLLVZQ!rL>;v(333t>>>ꪃwaڵpppEx{{cT޾}333êUлwo;o+BisK.PԫWVC$''֭[mS*8!Bd(lڴI=H6m=z􀑑& ]w ""Dj(.~'@ ɓ߿L&\]]eӧO0aVX!J عs' yG*qqqo;j(zXYYQ!Һy&f̘-[ȭ/мys{\%3iiiH]vXt)u,#j(lWWWb̝;SNE~~T য়~.ѷo_\xQOTy&$$$VZhK<9LDh>j ܹ3gbƍR7}}}mݻwG.]h>5vt]-FZ*.\X͙ؕ3gxGPI}ʕ+affgϞO---~ztƎ,yEV*mPcbb ǩ 3{l_:u**TDh>Sr Q!<… b 1244D۶m.]LN) /[n?>~;Jttt0fL>&&&㨥GZjc,^8p~~~xezyy_~A*U2M={սnܸcbcht899A,mڵqe)!!zRbXrL#[XXSNAv/ǔ1i$~ <==pBԫWwvTJfͰqFe:r!** cǎ@ ӧѯ_? SUZ]>`ddR=8ڴiŋ󎡕̙#QC*5RǏcΜ9XfTOE"\\\Ю];x{{Yf4y56壼899ah߾=(ƍ#`̙ŵk+ظq#6l/^~rL·=܉D"4nG۷CWWwslٲEm틎;*8!ڃObܹR=Y&ڵkvڡM6KKKC߾}U}jժaԩ:t(/G7oZjaÆ hڴ)cX|9BCC~F `ܸq3gq}LEQh\]]U⺚`׮]E+M>]q̰pB%$"D{P!իW?>$kllMsܹ3lmmC\\J?DHHƎ q47 :066Ƌ/$˳ƺuЮ];͛1j(}V^UՕwBlڴ NNNhcƍm;{lTRE.@˗/1o<,[ѴiS4k hܸ1=Y"_e(%BCC1zhZ|Xl@C 'O.{Xz5,,,{Xj"*4DEEsμch0@PP]h222Eݻw_с7o-Zq*=1Qӧ޽;W0~x3* CCCڵúu`mm?"44+Vz?cccpQRO*Uٳg\s96~wx{{).^%"DPu޿  &&߿/ZjpuuEfдiS4nʕ㘔,Xɓ'#//w/Ԯ]aaa8p $nҪʿ1rH={ kפ. ׯGZDGG@U+٣6kLc{Xp?! B Dk| K,A\\*U^zA_>4h@<xG)$ ÇG iSW HǾnj3HׯOsrSre/<쐘HSrn:?u!C(!!ډָ}6||| @;Qq۷oG`` 222xG!C ((jGk=xw H7obРA8ye6nׯ#cXd ¤5@4nܘwW!,,, +++###CիWǚ5kE DkhD޽Ø1caQ|oG ___^x;B5lW\*꺺1yd:t(:$䪭I&#WWWBNc``ݻwA" ˗/KFOO۷o Q0j wa <@dҥ кukYȗJttt3f@OOO>E@@L8::bݺupqqW͈#yV%333Ԯ]ׯ_WqB!6n܈͛+8diiiXvmEGG@(5B^NNL rM E/4ׯw}رcE"1A___. FN -B=z R|u;vRݨΟ?Aŋ\___F\2iRP(Dpp0fϞ x#F].vXn[lѣUf, lllPR%1TN˖-CCCBbbbp…iڴ)֬YC3$@J9999s&>% ___f4k׮-'&&"00P1B!F9s/_DPPv!jCaesUXD26mZX[[cǎ42!JD s)իJ;fjСCt^^^UڱzwB 6 .޾}0FY&V^6m'O+Zfgg5j޽{r-ׯP(klO>)QjU%&#P!Dk|SLŋQPP9::[n֭7nL\AAZ`Wn]]]_Fϥ*###Q\9~ƍƍ[hp8Iխ[v*`ƌ8{lc4h@! `1!!D ;P(+wnݺ^a"Gӡ/^~k'''υ$%%!00?Wl oaƍ8p\ʪR N< Gw4k֬D"l%'#޿'bժUPD{g*U///xzzbŊr?Q +Bի_~)m0f2ɓ1i$5j~WynnnT/A۶mR0`bXlU !k7n\EG˖-Ѿ}{xyy~r+|$I&={ $&&J] ֮]z6l؀qiWWSURuAzze`Ϟ=hذi;@ ŋ1|p%"5B4Ν;w0j(߿_沄B!իxzzu044CJnԥ3֬YF$ʕCxx8&O ]]]<}AAA2M-<==yGPyR7D"lܸrNErb_-Xt)FT1!#//˗/ɓ%u9vvv􄧧'ڴiC Ç^:222¬Y0fD"<~#G޽{.VcXz5&Nw1f233ëWCZJsNٳ [~~~ HEٳh޼9Z' tRrHF/DFPPTOj׮֭[M6hӦ *WDݩr///X5jc +V@xx * ::sp!9'\mڴʿZn PX\͛G[Yźuп!ELI&a-Zݝ"Q+b…#_~8ve-xbL6M^5ڈ94hsIϔ)STDR1 4wjvooo!šBZNJ+0mڴf _>ZjwwwlJLJ4@  GLL ,--h̚5ȧpŲe 9sÇ/q.oR¶m`ff!!D>}#** yyypuuڴiMwDb1tttT64i+W#00ݓ<̞=AAADx)Ǝ;v19;;cƍ2 .\@yP+ǖ-[\w!55{#͛h֬^~]@ @xx8fΜI48j d,_D'bjjٳgcȑDx9&L͛7K]f=ob+WĤInj?H'OI&ӧO#,,L~: h֭ׯW;99ðWFF5k7n~fffxt҅c2B222  K?Q+Wϕr,bWƤIJ$5kD\\:t|2F4yVѣG#..Ǐ>°a{RaÆq=>!RPPaÆŋ8q6m;!DB!DBիWz_ Z iiihԨ?AUVRU# IDAT0qD\v (((EPN׬YE:u*޽D_\2:w5!򐗗 8;;㯿?!zB!PC"** (((ҥK1e}Vވ=]ǎ+WHU*ƍabbպXx1T_:t(tuuy D&yyyHJJ̙3QR%q! "`B!j"77߸qc,Y)ڟ ={V׬Y .D.]Ghh(v!Uyয়~¤I<;;...=n޼5krAb1BLrB!DBzzzppp(u; ?DӦM7͛7ohhH\|]tO0w\899itaaaE ^>g?T'D;+BHԯ_/_.r.ƌiӦ Xr%~G~Z+| 11&L{B5aÆ"+%*@ B*>B! 4hP:tK`ȑ#pqqA``T غu+Uto=zʿ!vڅ+~.##>?yFVZA!H!2_GRRO>hݺ5Ο?_򭬬zj>}Z»w0a4h)))1Tիmh9r$?~DEB @!SSSSL:cǎ>~hDGGӧOe.W__ƍɓajj X5k`ʔ)x 5~x߿u hڵ+BH,BH7&M`۶m8q"1!P!0~x_e 1b16n܈)SHՐԩǏܼϞ=CÆ UjPD]]]={NNNB!21!h7obҤI_˼H$СC1}tTR''NTT5Wl_,W*0~xBB@tt4bbbS===`ԯ_l߾]Q՞)= ̙31m4%* \###Q!Q!ŋcΜ9R(4k cDDD`ݺu(((w\dm۶m9m۪ۻw/{1!W!h ǝ;wʼݺu ϒѷo_dff3Xzu/_b*WѣU !hj NUK0o<1aÆJLU:###,Xw B!DB!tunnne[XX ** 7nÿ,,,W#$$m͛={()ϟ5jA!@!D#=矑_}1~xԴǏij$lݺBa/hyyyJLV;")) wB!DBF#O?eWOOFddd~r M/Z}}by=5j۷o+1Y*VK.rʼB!rGBbk׮=TCPPܹ+W3h;GGGڵ?\VZEB!!/`r:::Ǎ7tRTZU Æ z_Mamm~ ˗/qcӦMJJ%C{cB! CBQ[8q"ʴP(DϞ=1k,%KVVځ055ѣGѠAy&5j>()dj>+q7 \fjH,E6BF[+%KsT%+KRuӹ+1*#W0[ P7I?߯W#9'9s}>k%y&H+3g(!!A=zkPLL=u/}ڸ^wאhÆ 53f 7|h(n˚9sb YVFDDO>ս{w{饗c*-[L5oNHe%KgϞF(.DE>,n޼iް0۷O;wThhSJ?Oskܸq5;tNӦMSBB1p pYZ|RSSuE_4x`ڑ#GԻwo1cj*yxxTT{ֿ/'%̀g ˩ʕ+խ[7M<ٮo߾WAAӇIի~^gׯrssk%iƌ.7kNׯg W\Ɲ;wa͙3G'Nko=RGڷo}QC38ҏc*00Ƶ۷oWttK]ѴiS۷O?Qp* Kصk^y}v ֬YOu.l{Go>cԻ8p} ѿo'$6oެaÆsE Rdd]ݵzj;vL.5Kb1JիW3f/I/fk0Ǐ+66Vaaaڿ~ӟjŊ***Ҙ1c\n}׿1Ujjnݻw+''3c M2[Nui͝;WVT=cn'ԣGUTT΢?٦¥T={ӧ6cƌʕ+]0-pK.i̙޽VZe?`竰PÇw_v':vh<{l|I-_`z\p˗/+##CoJKKm7x`(秏?X}i}aa ۷o;8m M6(*ŲX, by ݻG/I;w1jmѢE6VUIII.3GDD>`q^kɒ%JOO׵kl{Lss'tJ(v9r֭[gM4Ɂl#hjܸQpzQRRlY,]zզ=ܹs:xzڴiGu6zuK.98Y"##i&5i(nIii3g4{zzjȑ:v옶lIׯ|;ne˖| c:hԨ6nh^k,ӵn:j+˵vZ͟?_NqFٳgk׮NHh6lPll! ё#Ge/BAAAZL>OL濯ܹƎ[߬Y3 :}{=ы/hȹg͚e/Iӆ (--Unn,Ν;W6mhʔ)JII1u3TDDsvYmsMuA/_v`2[iiizW.(@lpK%%%믿 .Ը>((HӦMSbbw5kjSASOn/I֭sߩS'^Zsy0ڵkTff^Zp⋊*֠ATZZ4nXΝ_WJJHHPVV5ks`V\rEoMx K۷&[lш#tmc˳kiFҥK]h .^ jɒ%*..vmV4yd%%%m۶NJ[=Zp^xc„ vٶmCq魷R-Px?h…~zk|A%''k„ jҤ6uI-^ޏݡC}w;wVvvjΞ=7xC999*++v5}t 6L][/e˖z=nLLLgϞz9f̘ٳgQFrLP3nҥKu{L6|K0\rE[l=Cu>"Zzuk޼ƍ_~Y۷wbB8Jqq"##UXXXcʕ+kݻ5dȐZSNZ +((b֭[U]giӦq۷+<<\Gӱh/I'O5kYf)%%E: 4o<ڵuaaaJNNֈ#tp-[jǎ4h>Z'<:???=ZӧOW^F Ν;SN?p]zզu>>>7n̙;\1(TTTh͚5Z`s]6miӦpbB:h2dHKm #ZzgN:(@7nPNN222t{ SRRFQѰkN{Udd=j۫UV:o?R\\ԥKZ8ڵkVff.]TiڴiݻjϞ=/Ç۴.̷m{QFW_Uppp ҥKZd-Zk׮Um۶)ooymZh;w*66V۶mvm]k?~<MyG*!!A]vUvv???;Vߴw^0nM4͛5a„jG?9LpY,mݺUwڵSbbynڀhhkUiԨSWBjj۶mZ`= Srrz)C̛7O۷ԩSUYY J\}@ݺuK+VPFFN8ϛ6m~ZIIIѣ a6'NT5zh}w+$07 bz7_nݺi4iRj#**J|x $ ty-ZH_|}}5bM:UOyy֮] }~iԩ9r$SۡIׯ_˕go{m`Bn(ڙ3g\yHHLx5o@`J}-Z_$???=JLLԐ!CapJP0 ժ[7{JLLرcղeKCu֭[t?~\|(4X.]R^^t9IRn4~xM0A[68!<wܹctONŋ?Q7n~@\)((Pff6nܨ۷om?pfZm6-X@~(b*33SgΜQϞ=5a$ \pAK.UVV*++5zh;V 0:Ҹ[8r.\kת_~zgF p \bÊױcdt,Pp9Zv.\֭[+11Q7n7d_\׵|rmٲEht,A _hŊ5j(^8|:p6m#G* H@EV:/[n 6:` LϟWiiz! 0 WYYr5n(iQ`FG P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P`&@ P IDAT`)mIENDB`clearscreen-2.0.1/logo.svg000064400000000000000000000607471046102023000136110ustar 00000000000000 image/svg+xml clearscreen-2.0.1/src/lib.rs000064400000000000000000001202151046102023000140160ustar 00000000000000//! Cross-platform terminal screen clearing. //! //! This library provides a set of ways to clear a screen, plus a “best effort” convenience function //! to do the right thing most of the time. //! //! Unlike many cross-platform libraries, this one exposes every available choice all the time, and //! only the convenience function varies based on compilation target or environmental factors. //! //! 90% of the time, you’ll want to use the convenience short-hand: //! //! ```no_run //! clearscreen::clear().expect("failed to clear screen"); //! ``` //! //! For anything else, refer to the [`ClearScreen`] enum. //! //! If you are supporting Windows in any capacity, the [`is_windows_10()`] documentation is //! **required reading**. #![doc(html_favicon_url = "https://watchexec.github.io/logo:clearscreen.svg")] #![doc(html_logo_url = "https://watchexec.github.io/logo:clearscreen.svg")] #![warn(missing_docs)] use std::{ borrow::Cow, env, io::{self, Write}, process::{Command, ExitStatus}, }; use terminfo::{ capability::{self, Expansion}, expand::{Context, Parameter}, Capability, Database, Value, }; use thiserror::Error; use which::which; /// Ways to clear the screen. /// /// There isn’t a single way to clear the (terminal/console) screen. Not only are there several /// techniques to achieve the outcome, there are differences in the way terminal emulators intepret /// some of these techniques, as well as platform particularities. /// /// In addition, there are other conditions a screen can be in that might be beneficial to reset, /// such as when a TUI application crashes and leaves the terminal in a less than useful state. /// /// Finally, a terminal may have scrollback, and this can be kept as-is or cleared as well. /// /// Your application may need one particular clearing method, or it might offer several options to /// the user, such as “hard” and “soft” clearing. This library makes no assumption and no judgement /// on what is considered hard, soft, or something else: that is your responsibility to determine in /// your context. /// /// For most cases, you should use [`ClearScreen::default()`] to select the most appropriate method. /// /// In any event, once a way is selected, call [`clear()`][ClearScreen::clear()] to apply it. /// /// # Example /// /// ```no_run /// # use clearscreen::ClearScreen; /// ClearScreen::default().clear().expect("failed to clear the screen"); /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum ClearScreen { /// Does both [`TerminfoScreen`][ClearScreen::TerminfoScreen] and /// [`TerminfoScrollback`][ClearScreen::TerminfoScrollback], in this order, but skips the /// scrollback reset if the capability isn’t available. /// /// This is essentially what the [`clear`] command on unix does. /// [`clear`]: https://invisible-island.net/ncurses/man/clear.1.html Terminfo, /// Looks up the `clear` capability in the terminfo (from the TERM env var), and applies it. /// /// A non-hashed terminfo database is required (this is a [terminfo crate] limitation), such as /// the one provided with ncurses. /// /// [terminfo crate]: https://lib.rs/crates/terminfo TerminfoScreen, /// Looks up the `E3` (Erase Scrollback) capability in the terminfo (from the TERM env var), and applies it. /// /// The same terminfo limitation applies as for [`TerminfoScreen`][ClearScreen::TerminfoScreen]. TerminfoScrollback, /// Performs a terminfo-driven terminal reset sequence. /// /// This prints whichever are available of the **rs1**, **rs2**, **rs3**, and **rf** sequences. /// If none of these are available, it prints whichever are available of the **is1**, **is2**, /// **is3**, and **if** sequences. If none are available, an error is returned. /// /// This generally issues at least an `ESC c` sequence, which resets all terminal state to /// default values, and then may issue more sequences to reset other things or enforce a /// particular kind of state. See [`XtermReset`][ClearScreen::XtermReset] for a description of /// what XTerm does, as an example. /// /// Note that this is _not_ analogous to what `tput reset` does: to emulate that, issuing first /// one of VtCooked/VtWellDone/WindowsCooked followed by this variant will come close. /// /// The same terminfo limitation applies as for [`TerminfoScreen`][ClearScreen::TerminfoScreen]. TerminfoReset, /// Prints clear screen and scrollback sequence as if TERM=xterm. /// /// This does not look up the correct sequence in the terminfo database, but rather prints: /// /// - `CSI H` (Cursor Position 0,0), which sets the cursor position to 0,0. /// - `CSI 2J` (Erase Screen), which erases the whole screen. /// - `CSI 3J` (Erase Scrollback), which erases the scrollback (xterm extension). XtermClear, /// Prints the terminal reset sequence as if TERM=xterm. /// /// This does not look up the correct sequence in the terminfo database, but rather prints: /// /// - `ESC c` (Reset to Initial State), which nominally resets all terminal state to initial /// values, but see the documentation for [`VtRis`][ClearScreen::VtRis]. /// - `CSI !p` (Soft Terminal Reset), which nominally does the same thing as RIS, but without /// disconnecting the terminal data lines… which matters when you’re living in 1970. /// - `CSI ?3l` (Reset to 80 Columns), which resets the terminal width to 80 columns, or more /// accurately, resets the option that selects 132 column mode, to its default value of no. /// I don’t know, man. /// - `CSI ?4l` (Reset to Jump Scrolling), which sets the scrolling mode to jump. This is naught /// to do with what we think of as “scrolling,” but rather it’s about the speed at which the /// terminal will add lines to the screen. Jump mode means “give it to me as fast as it comes” /// and Smooth mode means to do some buffering and output lines “at a moderate, smooth rate.” /// - `CSI 4l` (Reset to Replace Mode), which sets the cursor writing mode to Replace, i.e. /// overwriting characters at cursor position, instead of Insert, which pushes characters /// under the cursor to the right. /// - `ESC >` (Set Key Pad to Normal), which sets the keyboard’s numeric keypad to send “what’s /// printed on the keys” i.e. numbers and the arithmetic symbols. /// - `CSI ?69l` (Reset Left and Right Margins to the page), which sets the horizontal margins /// to coincide with the page’s margins: nowadays, no margins. XtermReset, /// Calls the command `tput clear`. /// /// That command most likely does what [`Terminfo`][ClearScreen::Terminfo] does internally, but /// may work better in some cases, such as when the terminfo database on the system is hashed or /// in a non-standard location that the terminfo crate does not find. /// /// However, it relies on the `tput` command being available, and on being able to run commands. TputClear, /// Calls the command `tput reset`. /// /// See the documentation above on [`TputClear`][ClearScreen::TputClear] for more details, save /// that the equivalent is [`TerminfoReset`][ClearScreen::TerminfoReset]. TputReset, /// Calls the command `cls`. /// /// This is the Windows command to clear the screen. It has the same caveats as /// [`TputClear`][ClearScreen::TputClear] does, but its internal mechanism is not known. Prefer /// [`WindowsClear`][ClearScreen::WindowsClear] instead to avoid relying on an external command. /// /// This will always attempt to run the command, regardless of compile target, which may have /// unintended effects if the `cls` executable does something different on the platform. Cls, /// Sets the Windows Console to support VT escapes. /// /// This sets the `ENABLE_VIRTUAL_TERMINAL_PROCESSING` bit in the console mode, which enables /// support for the terminal escape sequences every other terminal uses. This is supported since /// Windows 10, from the Threshold 2 Update in November 2015. /// /// Does nothing on non-Windows targets. WindowsVt, /// Sets the Windows Console to support VT escapes and prints the clear sequence. /// /// This runs [`WindowsVt`][ClearScreen::WindowsVt] and [`XtermClear`][ClearScreen::XtermClear], /// in this order. This is described here: /// https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-1 as the /// recommended clearing method for all new development, although we also reset the cursor /// position. /// /// While `WindowsVt` will do nothing on non-Windows targets, `XtermClear` will still run. WindowsVtClear, /// Uses Windows Console function to scroll the screen buffer and fill it with white space. /// /// - Scrolls up one screenful /// - Fills the buffer with whitespace and attributes set to default. /// - Flushes the input buffer /// - Sets the cursor position to 0,0 /// /// This is described here: https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-2 /// as the equivalent to CMD.EXE's `cls` command. /// /// Does nothing on non-Windows targets. #[cfg(feature = "windows-console")] WindowsConsoleClear, /// Uses Windows Console function to blank the screen state. /// /// - Fills the screen buffer with ` ` (space) characters /// - Resets cell attributes over the entire buffer /// - Flushes the input buffer /// - Sets the cursor position to 0,0 /// /// This is described here: https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-3 /// /// Does nothing on non-Windows targets. #[cfg(feature = "windows-console")] WindowsConsoleBlank, /// Uses Windows Console function to disable raw mode. /// /// Does nothing on non-Windows targets. WindowsCooked, /// Prints the RIS VT100 escape code: Reset to Initial State. /// /// This is the `ESC c` or `1b 63` escape, which by spec is defined to reset the terminal state /// to all initial values, which may be a range of things, for example as described in the VT510 /// manual: https://vt100.net/docs/vt510-rm/RIS /// /// However, the exact behaviour is highly dependent on the terminal emulator, and some modern /// terminal emulators do not always clear scrollback, for example Tmux and GNOME VTE. VtRis, /// Prints the CSI sequence to leave the Alternate Screen mode. /// /// If the screen is in alternate screen mode, like how vim or a pager or another such rich TUI /// application would do, this sequence will clear the alternate screen buffer, then revert the /// terminal to normal mode, and restore the position of the cursor to what it was before /// Alternate Screen mode was entered, assuming the proper sequence was used. /// /// It will not clear the normal mode buffer. /// /// This is useful when recovering from a TUI application which crashed without resetting state. VtLeaveAlt, /// Sets the terminal to cooked mode. /// /// This attempts to switch the terminal to “cooked” mode, which can be thought of as the /// opposite of “raw” mode, where the terminal does not respond to line discipline (which makes /// carriage return, line feed, and general typing display out to screen, and translates Ctrl-C /// to sending the SIGINT signal, etc) but instead passes all input to the controlling program /// and only displays what it outputs explicitly. /// /// There’s also an intermediate “cbreak” or “rare” mode which behaves like “cooked” but sends /// each character one at a time immediately rather buffering and sending lines. /// /// TUI applications such as editors and pagers often set raw mode to gain precise control of /// the terminal state. If such a program crashes, it may not reset the terminal mode back to /// the mode it found it in, which can leave the terminal behaving oddly or rendering it /// completely unusable. /// /// In truth, these terminal modes are a set of configuration bits that are given to the /// `termios(3)` libc API, and control a variety of terminal modes. “Cooked” mode sets: /// /// - Input BRKINT set: on BREAK, flush i/o queues and send a SIGINT to any running process. /// - Input ICRNL set: translate Carriage Returns to New Lines on input. /// - Input IGNPAR set: ignore framing and parity errors. /// - Input ISTRIP set: strip off eigth bit. /// - Input IXON set: enable XON/XOFF flow control on output. /// - Output OPOST set: enable output processing. /// - Local ICANON set: enable canonical mode (see below). /// - Local ISIG set: when Ctrl-C, Ctrl-Q, etc are received, send the appropriate signal. /// /// Canonical mode is really the core of “cooked” mode and enables: /// /// - line buffering, so input is only sent to the underlying program when a line delimiter /// character is entered (usually a newline); /// - line editing, so ERASE (backspace) and KILL (remove entire line) control characters edit /// the line before it is sent to the program; /// - a maximum line length of 4096 characters (bytes). /// /// When canonical mode is unset (when the bit is cleared), all input processing is disabled. /// /// Due to how the underlying [`tcsetattr`] function is defined in POSIX, this may complete /// without error if _any part_ of the configuration is applied, not just when all of it is set. /// /// Note that you generally want [`VtWellDone`][ClearScreen::VtWellDone] instead. /// /// Does nothing on non-Unix targets. /// /// [`tcsetattr`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html VtCooked, /// Sets the terminal to “well done” mode. /// /// This is similar to [`VtCooked`][ClearScreen::VtCooked], but with a different, broader, mode /// configuration which approximates a terminal’s initial state, such as is expected by a shell, /// and clears many bits that should probably never be set (like the translation/mapping modes). /// /// “Well done” mode is an invention of this library, inspired by several other sources such as /// Golang’s goterm, the termios(3) and tput(1) manual pages, but not identical to any. /// /// Notably most implementations read the terminal configuration bits and only modify that set, /// whereas this library authoritatively writes the entire configuration from scratch. /// /// It is a strict superset of [`VtCooked`][ClearScreen::VtCooked]. /// /// - Input BRKINT set: on BREAK, flush i/o queues and send a SIGINT to any running process. /// - Input ICRNL set: translate Carriage Return to New Line on input. /// - Input IUTF8 set: input is UTF-8 (Linux only, since 2.6.4). /// - Input IGNPAR set: ignore framing and parity errors. /// - Input IMAXBEL set: ring terminal bell when input queue is full (not implemented in Linux). /// - Input ISTRIP set: strip off eigth bit. /// - Input IXON set: enable XON/XOFF flow control on output. /// - Output ONLCR set: do not translate Carriage Return to CR NL. /// - Output OPOST set: enable output processing. /// - Control CREAD set: enable receiver. /// - Local ICANON set: enable canonical mode (see [`VtCooked`][ClearScreen::VtCooked]). /// - Local ISIG set: when Ctrl-C, Ctrl-Q, etc are received, send the appropriate signal. /// /// Does nothing on non-Unix targets. VtWellDone, } impl Default for ClearScreen { /// Detects the environment and makes its best guess as how to clear the screen. /// /// This function’s behaviour (but not its type signature) may change without notice, as better /// techniques appear. However, it will always strive to provide the best method. It will also /// never have side-effects, and finding any such behaviour should be reported as a bug. /// /// If you wish to make your own, the [`is_microsoft_terminal()`] and [`is_windows_10()`] /// functions may be useful. /// /// The [`ClearScreen`] variant selected is always in the “clear” behaviour side of things. If /// you wish to only clear the screen and not the scrollback, or to perform a terminal reset, or /// apply the other available clearing strategies, you’ll need to select what’s best yourself. /// /// See the [TERMINALS.md file in the repo][TERMINALS.md] for research on many terminals as well /// as the current result of this function for each terminal. /// /// [TERMINALS.md]: https://github.com/watchexec/clearscreen/blob/main/TERMINALS.md fn default() -> Self { use env::var; use std::ffi::OsStr; fn varfull(key: impl AsRef) -> bool { var(key).map_or(false, |s| !s.is_empty()) } let term = var("TERM").ok(); let term = term.as_ref(); if cfg!(windows) { return if is_microsoft_terminal() { Self::XtermClear } else if is_windows_10() { Self::WindowsVtClear } else if term.is_some() && varfull("TERMINFO") { Self::Terminfo } else if term.is_some() && which("tput").is_ok() { Self::TputClear } else { Self::Cls }; } if let Some(term) = term { // These VTE-based terminals support CSI 3J but their own terminfos don’t have E3 if (term.starts_with("gnome") && varfull("GNOME_TERMINAL_SCREEN") && varfull("GNOME_TERMINAL_SERVICE")) || term == "xfce" || term.contains("termite") { return Self::XtermClear; } // - SyncTERM does support the XtermClear sequence but does not clear the scrollback, // and does not have a terminfo, so VtRis is the only option. // - rxvt, when using its own terminfos, erases the screen instead of clearing and // doesn’t clear scrollback. It supports and behave properly for the entire XtermClear // sequence, but it also does the right thing with VtRis, and that seems more reliable. // - Other variants of (u)rxvt do the same. // - Kitty does as rxvt does here. // - Tess does support the XtermClear sequence but has a weird scrollbar behaviour, // which does not happen with VtRis. // - Zutty does not support E3, and erases the buffer on clear like rxvt, but does work // properly with VtRis. // - Same behaviour with the multiplexer Zellij. if term == "syncterm" || term.contains("rxvt") || term.contains("kitty") || var("CHROME_DESKTOP").map_or(false, |cd| cd == "tess.desktop") || varfull("ZUTTY_VERSION") || varfull("ZELLIJ") { return Self::VtRis; } // - screen supports CSI 3J only within the XtermClear sequence, without E3 capability. // - Konsole handles CSI 3J correctly only within the XtermClear sequence. // - assume tmux TERMs are only used within tmux, and avoid the requirement for a functioning terminfo then if term.starts_with("screen") || term.starts_with("konsole") || term.starts_with("tmux") { return Self::XtermClear; } // Default xterm* terminfo on macOS does not include E3, but many terminals support it. if cfg!(target_os = "macos") && term.starts_with("xterm") && Database::from_env() .map(|info| info.get::().is_none()) .unwrap_or(true) { return Self::XtermClear; } if !term.is_empty() && Database::from_env().is_ok() { return Self::Terminfo; } } Self::XtermClear } } const ESC: &[u8] = b"\x1b"; const CSI: &[u8] = b"\x1b["; const RIS: &[u8] = b"c"; impl ClearScreen { /// Performs the clearing action, printing to stdout. pub fn clear(self) -> Result<(), Error> { let mut stdout = io::stdout(); self.clear_to(&mut stdout) } /// Performs the clearing action, printing to a given writer. /// /// This allows to capture any escape sequences that might be printed, for example, but note /// that it will not prevent actions taken via system APIs, such as the Windows, VtCooked, and /// VtWellDone variants do. /// /// For normal use, prefer [`clear()`]. pub fn clear_to(self, mut w: &mut impl Write) -> Result<(), Error> { match self { Self::Terminfo => { let info = Database::from_env()?; let mut ctx = Context::default(); if let Some(seq) = info.get::() { seq.expand().with(&mut ctx).to(&mut w)?; w.flush()?; } else { return Err(Error::TerminfoCap("clear")); } if let Some(seq) = info.get::() { seq.expand().with(&mut ctx).to(&mut w)?; w.flush()?; } } Self::TerminfoScreen => { let info = Database::from_env()?; if let Some(seq) = info.get::() { seq.expand().to(&mut w)?; w.flush()?; } else { return Err(Error::TerminfoCap("clear")); } } Self::TerminfoScrollback => { let info = Database::from_env()?; if let Some(seq) = info.get::() { seq.expand().to(&mut w)?; w.flush()?; } else { return Err(Error::TerminfoCap("E3")); } } Self::TerminfoReset => { let info = Database::from_env()?; let mut ctx = Context::default(); let mut reset = false; if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } w.flush()?; if reset { return Ok(()); } if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } if let Some(seq) = info.get::() { reset = true; seq.expand().with(&mut ctx).to(&mut w)?; } w.flush()?; if !reset { return Err(Error::TerminfoCap("reset")); } } Self::XtermClear => { const CURSOR_HOME: &[u8] = b"H"; const ERASE_SCREEN: &[u8] = b"2J"; const ERASE_SCROLLBACK: &[u8] = b"3J"; w.write_all(CSI)?; w.write_all(CURSOR_HOME)?; w.write_all(CSI)?; w.write_all(ERASE_SCREEN)?; w.write_all(CSI)?; w.write_all(ERASE_SCROLLBACK)?; w.flush()?; } Self::XtermReset => { const STR: &[u8] = b"!p"; const RESET_WIDTH_AND_SCROLL: &[u8] = b"?3;4l"; const RESET_REPLACE: &[u8] = b"4l"; const RESET_KEYPAD: &[u8] = b">"; const RESET_MARGINS: &[u8] = b"?69l"; w.write_all(ESC)?; w.write_all(RIS)?; w.write_all(CSI)?; w.write_all(STR)?; w.write_all(CSI)?; w.write_all(RESET_WIDTH_AND_SCROLL)?; w.write_all(CSI)?; w.write_all(RESET_REPLACE)?; w.write_all(ESC)?; w.write_all(RESET_KEYPAD)?; w.write_all(CSI)?; w.write_all(RESET_MARGINS)?; w.flush()?; } Self::TputClear => { let status = Command::new("tput").arg("clear").status()?; if !status.success() { return Err(Error::Command("tput clear", status)); } } Self::TputReset => { let status = Command::new("tput").arg("reset").status()?; if !status.success() { return Err(Error::Command("tput reset", status)); } } Self::Cls => { let status = Command::new("cmd.exe").arg("/C").arg("cls").status()?; if !status.success() { return Err(Error::Command("cls", status)); } } Self::WindowsVt => win::vt()?, Self::WindowsVtClear => { let vtres = win::vt(); Self::XtermClear.clear_to(w)?; vtres?; } #[cfg(feature = "windows-console")] Self::WindowsConsoleClear => win::clear()?, #[cfg(feature = "windows-console")] Self::WindowsConsoleBlank => win::blank()?, Self::WindowsCooked => win::cooked()?, Self::VtRis => { w.write_all(ESC)?; w.write_all(RIS)?; w.flush()?; } Self::VtLeaveAlt => { const LEAVE_ALT: &[u8] = b"?1049l"; w.write_all(CSI)?; w.write_all(LEAVE_ALT)?; w.flush()?; } Self::VtCooked => unix::vt_cooked()?, Self::VtWellDone => unix::vt_well_done()?, } Ok(()) } } /// Shorthand for `ClearScreen::default().clear()`. pub fn clear() -> Result<(), Error> { ClearScreen::default().clear() } /// Detects Microsoft Terminal. /// /// Note that this is only provided to write your own clearscreen logic and _should not_ be relied /// on for other purposes, as it makes no guarantees of reliable detection, and its internal /// behaviour may change without notice. pub fn is_microsoft_terminal() -> bool { env::var("WT_SESSION").is_ok() } /// Detects Windows ≥10. /// /// As mentioned in the [`WindowsVt`][ClearScreen::WindowsVt] documentation, Windows 10 from the /// Threshold 2 Update in November 2015 supports the `ENABLE_VIRTUAL_TERMINAL_PROCESSING` console /// mode bit, which enables VT100/ECMA-48 escape sequence processing in the console. This in turn /// makes clearing the console vastly easier and is the recommended mode of operation by Microsoft. /// /// However, detecting Windows ≥10 is not trivial. To mitigate broken programs that incorrectly /// perform version shimming, Microsoft has deprecated most ways to obtain the version of Windows by /// making the relevant APIs _lie_ unless the calling executable [embeds a manifest that explicitely /// opts-in to support Windows 10][manifesting]. /// /// To be clear, **this is the proper way to go**, and while this function tries, it may return /// false under some Win10s if you don't manifest. If you are writing an application which uses this /// library, or indeed any application targeting Windows at all, you should embed such a manifest /// (and take that opportunity to opt-in to long path support, see e.g. [watchexec#163]). If you are /// writing a library on top of this one, it is your responsibility to communicate this requirement /// to your users. /// /// It is important to remark that it is not possible to manifest twice. In plainer words, /// **libraries _must not_ embed a manifest** as that will make it impossible for applications which /// depend on them to embed their own manifest. /// /// This function tries its best to detect Windows ≥10, and specifically, whether the mentioned mode /// bit can be used. Critically, it leaves trying to set the bit as feature detection as a last /// resort, such that _an error setting the bit_ is not confunded with _the bit not being supported_. /// /// Note that this is only provided to write your own clearscreen logic and _should not_ be relied /// on for other purposes, as it makes no guarantees of reliable detection, and its internal /// behaviour may change without notice. Additionally, this will always return false if the library /// was compiled for a non-Windows target, even if e.g. it’s running under WSL in a Windows 10 host. /// /// TL;DR: /// /// - Runs on Windows ≥10 without manifest and returns `true`: good, expected behaviour. /// - Runs on Windows ≥10 without manifest and returns `false`: **not a bug**, please manifest. /// - Runs on Windows ≥10 with manifest and returns `true`: good, expected behaviour. /// - Runs on Windows ≥10 with manifest and returns `false`: **is a bug**, please report it. /// - Runs on Windows <10 and returns `true`: **is a bug**, please report it. [ex #5] /// - Runs on Windows <10 and returns `false`: good, expected behaviour. /// /// [ex #5]: https://github.com/watchexec/clearscreen/issues/5 /// [manifesting]: https://docs.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 /// [watchexec#163]: https://github.com/watchexec/watchexec/issues/163 pub fn is_windows_10() -> bool { win::is_windows_10() } /// Error type. #[derive(Debug, Error)] pub enum Error { /// Any I/O error. #[error(transparent)] Io(#[from] io::Error), /// A non-success exit status from a command. #[error("{0}: {1}")] Command(&'static str, ExitStatus), /// Any nix (libc) error. #[cfg(unix)] #[error(transparent)] Nix(#[from] nix::Error), /// Any terminfo error. #[error(transparent)] Terminfo(#[from] terminfo::Error), /// A missing terminfo capability. #[error("required terminfo capability not available: {0}")] TerminfoCap(&'static str), /// A null-pointer error. #[error("encountered a null pointer while reading {0}")] NullPtr(&'static str), } #[cfg(unix)] mod unix { use super::Error; use nix::{ libc::STDIN_FILENO, sys::termios::{ tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, SetArg::TCSANOW, Termios, }, unistd::isatty, }; use std::{fs::OpenOptions, os::unix::prelude::AsRawFd}; pub(crate) fn vt_cooked() -> Result<(), Error> { write_termios(|t| { t.input_flags.insert( InputFlags::BRKINT | InputFlags::ICRNL | InputFlags::IGNPAR | InputFlags::ISTRIP | InputFlags::IXON, ); t.output_flags.insert(OutputFlags::OPOST); t.local_flags.insert(LocalFlags::ICANON | LocalFlags::ISIG); }) } pub(crate) fn vt_well_done() -> Result<(), Error> { write_termios(|t| { let mut inserts = InputFlags::BRKINT | InputFlags::ICRNL | InputFlags::IGNPAR | InputFlags::IMAXBEL | InputFlags::ISTRIP | InputFlags::IXON; #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] { inserts |= InputFlags::IUTF8; } t.input_flags.insert(inserts); t.output_flags .insert(OutputFlags::ONLCR | OutputFlags::OPOST); t.control_flags.insert(ControlFlags::CREAD); t.local_flags.insert(LocalFlags::ICANON | LocalFlags::ISIG); }) } fn reset_termios(t: &mut Termios) { t.input_flags.remove(InputFlags::all()); t.output_flags.remove(OutputFlags::all()); t.control_flags.remove(ControlFlags::all()); t.local_flags.remove(LocalFlags::all()); } fn write_termios(f: impl Fn(&mut Termios)) -> Result<(), Error> { if isatty(STDIN_FILENO)? { let mut t = tcgetattr(STDIN_FILENO)?; reset_termios(&mut t); f(&mut t); tcsetattr(STDIN_FILENO, TCSANOW, &t)?; } else { let tty = OpenOptions::new().read(true).write(true).open("/dev/tty")?; let fd = tty.as_raw_fd(); let mut t = tcgetattr(fd)?; reset_termios(&mut t); f(&mut t); tcsetattr(fd, TCSANOW, &t)?; } Ok(()) } } #[cfg(windows)] mod win { use super::Error; use std::{convert::TryFrom, io, mem::size_of, ptr}; use winapi::{ shared::minwindef::{DWORD, FALSE}, um::{ consoleapi::{GetConsoleMode, SetConsoleMode}, handleapi::INVALID_HANDLE_VALUE, lmapibuf::{NetApiBufferAllocate, NetApiBufferFree}, lmserver::{NetServerGetInfo, MAJOR_VERSION_MASK, SERVER_INFO_101, SV_PLATFORM_ID_NT}, lmwksta::{NetWkstaGetInfo, WKSTA_INFO_100}, processenv::GetStdHandle, winbase::{VerifyVersionInfoW, STD_OUTPUT_HANDLE}, wincon::{ ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT, ENABLE_VIRTUAL_TERMINAL_PROCESSING, }, winnt::{ VerSetConditionMask, HANDLE, OSVERSIONINFOEXW, POSVERSIONINFOEXW, ULONGLONG, VER_GREATER_EQUAL, VER_MAJORVERSION, VER_MINORVERSION, VER_SERVICEPACKMAJOR, }, }, }; #[cfg(feature = "windows-console")] use winapi::um::{ wincon::{ FillConsoleOutputAttribute, FillConsoleOutputCharacterW, GetConsoleScreenBufferInfo, ScrollConsoleScreenBufferW, SetConsoleCursorPosition, CONSOLE_SCREEN_BUFFER_INFO, PCONSOLE_SCREEN_BUFFER_INFO, }, wincontypes::{CHAR_INFO_Char, CHAR_INFO, COORD, SMALL_RECT}, winnt::SHORT, }; fn console_handle() -> Result { match unsafe { GetStdHandle(STD_OUTPUT_HANDLE) } { INVALID_HANDLE_VALUE => Err(io::Error::last_os_error().into()), handle => Ok(handle), } } #[cfg(feature = "windows-console")] fn buffer_info(console: HANDLE) -> Result { let csbi: PCONSOLE_SCREEN_BUFFER_INFO = ptr::null_mut(); if unsafe { GetConsoleScreenBufferInfo(console, csbi) } == FALSE { return Err(io::Error::last_os_error().into()); } if csbi.is_null() { Err(Error::NullPtr("GetConsoleScreenBufferInfo")) } else { Ok(unsafe { ptr::read(csbi) }) } } pub(crate) fn vt() -> Result<(), Error> { let stdout = console_handle()?; let mut mode = 0; if unsafe { GetConsoleMode(stdout, &mut mode) } == FALSE { return Err(io::Error::last_os_error().into()); } mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; if unsafe { SetConsoleMode(stdout, mode) } == FALSE { return Err(io::Error::last_os_error().into()); } Ok(()) } // Ref https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-2 #[cfg(feature = "windows-console")] pub(crate) fn clear() -> Result<(), Error> { let console = console_handle()?; let csbi = buffer_info(console)?; // Scroll the rectangle of the entire buffer. let rect = SMALL_RECT { Left: 0, Top: 0, Right: csbi.dwSize.X, Bottom: csbi.dwSize.Y, }; // Scroll it upwards off the top of the buffer with a magnitude of the entire height. let target = COORD { X: 0, Y: (0 - csbi.dwSize.Y) as SHORT, }; // Fill with empty spaces with the buffer’s default text attribute. let mut space = CHAR_INFO_Char::default(); unsafe { *space.AsciiChar_mut() = b' ' as i8 }; let fill = CHAR_INFO { Char: space, Attributes: csbi.wAttributes, }; // Do the scroll. if unsafe { ScrollConsoleScreenBufferW(console, &rect, ptr::null(), target, &fill) } == FALSE { return Err(io::Error::last_os_error().into()); } // Move the cursor to the top left corner too. let mut cursor = csbi.dwCursorPosition; cursor.X = 0; cursor.Y = 0; if unsafe { SetConsoleCursorPosition(console, cursor) } == FALSE { return Err(io::Error::last_os_error().into()); } Ok(()) } // Ref https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-3 #[cfg(feature = "windows-console")] pub(crate) fn blank() -> Result<(), Error> { let console = console_handle()?; // Fill the entire screen with blanks. let csbi = buffer_info(console)?; let buffer_size = csbi.dwSize.X * csbi.dwSize.Y; let home_coord = COORD { X: 0, Y: 0 }; if FALSE == unsafe { FillConsoleOutputCharacterW( console, b' ' as u16, u32::try_from(buffer_size).unwrap_or(0), home_coord, ptr::null_mut(), ) } { return Err(io::Error::last_os_error().into()); } // Set the buffer's attributes accordingly. let csbi = buffer_info(console)?; if FALSE == unsafe { FillConsoleOutputAttribute( console, csbi.wAttributes, u32::try_from(buffer_size).unwrap_or(0), home_coord, ptr::null_mut(), ) } { return Err(io::Error::last_os_error().into()); } // Put the cursor at its home coordinates. if unsafe { SetConsoleCursorPosition(console, home_coord) } == FALSE { return Err(io::Error::last_os_error().into()); } Ok(()) } const ENABLE_COOKED_MODE: DWORD = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; pub(crate) fn cooked() -> Result<(), Error> { let stdout = console_handle()?; let mut mode = 0; if unsafe { GetConsoleMode(stdout, &mut mode) } == FALSE { return Err(io::Error::last_os_error().into()); } mode |= ENABLE_COOKED_MODE; if unsafe { SetConsoleMode(stdout, mode) } == FALSE { return Err(io::Error::last_os_error().into()); } Ok(()) } // I hope someone searches for this one day and gets mad at me for making their life harder. const ABRACADABRA_THRESHOLD: (u8, u8) = (0x0A, 0x00); // proper way, requires manifesting #[inline] fn um_verify_version() -> bool { let condition_mask: ULONGLONG = unsafe { VerSetConditionMask( VerSetConditionMask( VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL, ), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL, ) }; let mut osvi = OSVERSIONINFOEXW { dwMinorVersion: ABRACADABRA_THRESHOLD.1 as _, dwMajorVersion: ABRACADABRA_THRESHOLD.0 as _, wServicePackMajor: 0, ..OSVERSIONINFOEXW::default() }; let ret = unsafe { VerifyVersionInfoW( &mut osvi as POSVERSIONINFOEXW, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, condition_mask, ) }; ret != FALSE } // querying the local netserver management api? #[inline] fn um_netserver() -> Result { unsafe { let mut buf = ptr::null_mut(); match NetApiBufferAllocate( u32::try_from(size_of::()).unwrap(), &mut buf, ) { 0 => {} err => return Err(io::Error::from_raw_os_error(i32::try_from(err).unwrap()).into()), } let ret = match NetServerGetInfo(ptr::null_mut(), 101, buf as _) { 0 => { let info: SERVER_INFO_101 = ptr::read(buf as _); let version = info.sv101_version_major | MAJOR_VERSION_MASK; // IS it using the same magic version number? who the fuck knows. let's hope so. Ok(info.sv101_platform_id == SV_PLATFORM_ID_NT && version > ABRACADABRA_THRESHOLD.0 as _) } err => Err(io::Error::from_raw_os_error(i32::try_from(err).unwrap()).into()), }; // always free, even if the netservergetinfo call fails match NetApiBufferFree(buf) { 0 => {} err => return Err(io::Error::from_raw_os_error(i32::try_from(err).unwrap()).into()), } ret } } // querying the local workstation management api? #[inline] fn um_workstation() -> Result { unsafe { let mut buf = ptr::null_mut(); match NetApiBufferAllocate( u32::try_from(size_of::()).unwrap(), &mut buf, ) { 0 => {} err => return Err(io::Error::from_raw_os_error(i32::try_from(err).unwrap()).into()), } let ret = match NetWkstaGetInfo(ptr::null_mut(), 100, buf as _) { 0 => { let info: WKSTA_INFO_100 = ptr::read(buf as _); // IS it using the same magic version number? who the fuck knows. let's hope so. Ok(info.wki100_platform_id == SV_PLATFORM_ID_NT && info.wki100_ver_major > ABRACADABRA_THRESHOLD.0 as _) } err => Err(io::Error::from_raw_os_error(i32::try_from(err).unwrap()).into()), }; // always free, even if the netservergetinfo call fails match NetApiBufferFree(buf) { 0 => {} err => return Err(io::Error::from_raw_os_error(i32::try_from(err).unwrap()).into()), } ret } } // attempt to set the bit, then undo it fn vt_attempt() -> Result { let stdout = console_handle()?; let mut mode = 0; if unsafe { GetConsoleMode(stdout, &mut mode) } == FALSE { return Err(io::Error::last_os_error().into()); } let mut support = false; let mut newmode = mode; newmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; if unsafe { SetConsoleMode(stdout, newmode) } != FALSE { support = true; } // reset it to original value, whatever we do unsafe { SetConsoleMode(stdout, mode) }; Ok(support) } #[inline] pub(crate) fn is_windows_10() -> bool { if um_verify_version() { return true; } if um_netserver().unwrap_or(false) { return true; } if um_workstation().unwrap_or(false) { return true; } vt_attempt().unwrap_or(false) } } #[cfg(not(unix))] #[allow(clippy::unnecessary_wraps)] mod unix { use super::Error; pub(crate) fn vt_cooked() -> Result<(), Error> { Ok(()) } pub(crate) fn vt_well_done() -> Result<(), Error> { Ok(()) } } #[cfg(not(windows))] #[allow(clippy::unnecessary_wraps)] mod win { use super::Error; pub(crate) fn vt() -> Result<(), Error> { Ok(()) } #[cfg(feature = "windows-console")] pub(crate) fn clear() -> Result<(), Error> { Ok(()) } #[cfg(feature = "windows-console")] pub(crate) fn blank() -> Result<(), Error> { Ok(()) } pub(crate) fn cooked() -> Result<(), Error> { Ok(()) } #[inline] pub(crate) fn is_windows_10() -> bool { false } } #[derive(Eq, PartialEq, Clone, Debug)] struct ResetScrollback<'a>(Cow<'a, [u8]>); impl<'a> Capability<'a> for ResetScrollback<'a> { #[inline] fn name() -> &'static str { "E3" } #[inline] fn from(value: Option<&'a Value>) -> Option { if let Some(&Value::String(ref value)) = value { Some(Self(Cow::Borrowed(value))) } else { None } } #[inline] fn into(self) -> Option { Some(Value::String(match self.0 { Cow::Borrowed(value) => value.into(), Cow::Owned(value) => value, })) } } impl<'a, T: AsRef<&'a [u8]>> From for ResetScrollback<'a> { #[inline] fn from(value: T) -> Self { Self(Cow::Borrowed(value.as_ref())) } } impl<'a> AsRef<[u8]> for ResetScrollback<'a> { #[inline] fn as_ref(&self) -> &[u8] { &self.0 } } impl<'a> ResetScrollback<'a> { #[inline] fn expand(&self) -> Expansion { #[allow(dead_code)] struct ExpansionHere<'a, T: 'a + AsRef<[u8]>> { string: &'a T, params: [Parameter; 9], context: Option<&'a mut Context>, } let here = ExpansionHere { string: self, params: Default::default(), context: None, }; // UNSAFE >:( this is iffy af but also the only way to create an Expansion // such that we can add the E3 capability. unsafe { std::mem::transmute(here) } } } clearscreen-2.0.1/tests/default.rs000064400000000000000000000002461046102023000152500ustar 00000000000000#[test] fn default() { let cs = clearscreen::ClearScreen::default(); dbg!(&cs); cs.clear().unwrap(); } #[test] fn shorthand() { clearscreen::clear().unwrap(); } clearscreen-2.0.1/tests/platformed.rs000064400000000000000000000031701046102023000157600ustar 00000000000000use std::env::var; use clearscreen::ClearScreen; #[test] fn terminfo() { if var("TERM").is_ok() && (cfg!(unix) || var("TERMINFO").is_ok()) { ClearScreen::Terminfo.clear().unwrap(); } } #[test] fn terminfo_screen() { if var("TERM").is_ok() && (cfg!(unix) || var("TERMINFO").is_ok()) { ClearScreen::TerminfoScreen.clear().unwrap(); } } #[test] fn terminfo_scrollback() { if var("TERM").is_ok() && (cfg!(unix) || var("TERMINFO").is_ok()) { ClearScreen::TerminfoScrollback.clear().unwrap(); } } #[test] fn terminfo_reset() { if var("TERM").is_ok() && (cfg!(unix) || var("TERMINFO").is_ok()) { ClearScreen::TerminfoReset.clear().unwrap(); } } #[test] fn xterm_clear() { ClearScreen::XtermClear.clear().unwrap(); } #[test] fn xterm_reset() { ClearScreen::XtermReset.clear().unwrap(); } #[test] fn tput_clear() { if var("TERM").is_ok() && (cfg!(unix) || var("TERMINFO").is_ok()) { ClearScreen::TputClear.clear().unwrap(); } } #[test] fn tput_reset() { if var("TERM").is_ok() && (cfg!(unix) || var("TERMINFO").is_ok()) { ClearScreen::TputReset.clear().unwrap(); } } #[cfg(windows)] #[test] fn windows_cls() { ClearScreen::Cls.clear().unwrap(); } #[test] fn windows_vt() { ClearScreen::WindowsVt.clear().unwrap(); } #[test] fn windows_vt_clear() { ClearScreen::WindowsVtClear.clear().unwrap(); } #[test] fn vt_ris() { ClearScreen::VtRis.clear().unwrap(); } // TODO: test these under Win8? why don't they work // // #[test] // fn windows_console_clear() { // ClearScreen::WindowsConsoleClear.clear().unwrap(); // } // // #[test] // fn windows_console_blank() { // ClearScreen::WindowsConsoleBlank.clear().unwrap(); // }