From ac7e8c50691140010d6fb0916d149bf49aa6e67a Mon Sep 17 00:00:00 2001 From: "R. Eric Wheeler" Date: Mon, 1 Mar 2021 19:10:51 +0000 Subject: [PATCH] 1.x --- .gitlab-ci.yml | 4 +- README.md | 3 + composer.lock | 144 +++++++------- functions.php | 9 - src/Console/Getch.php | 55 +++++- src/Console/Resources/Makefile | 2 +- src/Console/Resources/getch.c | 299 ++++++++++++++++++++++++++--- src/Console/Resources/keycodes.txt | 16 ++ src/Console/Resources/libgetch.so | Bin 16440 -> 17536 bytes tests/Getch/GetchTest.php | 35 +++- tests/Ungetch/UngetchTest.php | 4 +- tests/test.h | 2 + 12 files changed, 453 insertions(+), 120 deletions(-) create mode 100644 src/Console/Resources/keycodes.txt diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c842cc3..981c08c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ before_script: # If Xdebug was installed you can generate a coverage report and see code coverage metrics. test:7.4: only: - - master + - 1.x tags: - default image: php:7.4 @@ -29,7 +29,7 @@ test:7.4: - vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never test:8.0: only: - - master + - 1.x tags: - default image: php:8.0 diff --git a/README.md b/README.md index b4cb16d..3c4c622 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ This simply uses the FFI extension to enable _getch and _ungetch in Windows and linux. +[![pipeline status](https://repos.bgemi.net/sikofitt/getch/badges/1.x/pipeline.svg)](https://repos.bgemi.net/sikofitt/getch/-/commits/1.x) +[![coverage report](https://repos.bgemi.net/sikofitt/getch/badges/1.x/coverage.svg)](https://repos.bgemi.net/sikofitt/getch/-/commits/1.x) + ```shell script $ composer require sikofitt/getch:dev-master ``` diff --git a/composer.lock b/composer.lock index 935e256..b0b2ea0 100644 --- a/composer.lock +++ b/composer.lock @@ -377,16 +377,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.18.1", + "version": "v2.18.2", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "c68ff6231adb276857761e43b7ed082f164dce0b" + "reference": "18f8c9d184ba777380794a389fabc179896ba913" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c68ff6231adb276857761e43b7ed082f164dce0b", - "reference": "c68ff6231adb276857761e43b7ed082f164dce0b", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/18f8c9d184ba777380794a389fabc179896ba913", + "reference": "18f8c9d184ba777380794a389fabc179896ba913", "shasum": "" }, "require": { @@ -468,7 +468,7 @@ "description": "A tool to automatically fix PHP code style", "support": { "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.18.1" + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.18.2" }, "funding": [ { @@ -476,7 +476,7 @@ "type": "github" } ], - "time": "2021-01-21T18:50:42+00:00" + "time": "2021-01-26T00:22:21+00:00" }, { "name": "jetbrains/phpstorm-stubs", @@ -484,12 +484,12 @@ "source": { "type": "git", "url": "https://github.com/JetBrains/phpstorm-stubs.git", - "reference": "44640f75da5865d1c3b0e433cc72ee4cdc677926" + "reference": "b8cf707c050f775cdb7693afa6099bd227b383f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/44640f75da5865d1c3b0e433cc72ee4cdc677926", - "reference": "44640f75da5865d1c3b0e433cc72ee4cdc677926", + "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/b8cf707c050f775cdb7693afa6099bd227b383f4", + "reference": "b8cf707c050f775cdb7693afa6099bd227b383f4", "shasum": "" }, "require-dev": { @@ -524,7 +524,7 @@ "support": { "source": "https://github.com/JetBrains/phpstorm-stubs/tree/master" }, - "time": "2021-01-25T16:29:22+00:00" + "time": "2021-02-15T11:11:55+00:00" }, { "name": "myclabs/deep-copy", @@ -1351,16 +1351,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.1", + "version": "9.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360" + "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7bdf4085de85a825f4424eae52c99a1cec2f360", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f661659747f2f87f9e72095bb207bceb0f151cb4", + "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4", "shasum": "" }, "require": { @@ -1438,7 +1438,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2" }, "funding": [ { @@ -1450,7 +1450,7 @@ "type": "github" } ], - "time": "2021-01-17T07:42:25+00:00" + "time": "2021-02-02T14:45:58+00:00" }, { "name": "psr/container", @@ -2571,16 +2571,16 @@ }, { "name": "symfony/console", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "47c02526c532fb381374dab26df05e7313978976" + "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/47c02526c532fb381374dab26df05e7313978976", - "reference": "47c02526c532fb381374dab26df05e7313978976", + "url": "https://api.github.com/repos/symfony/console/zipball/89d4b176d12a2946a1ae4e34906a025b7b6b135a", + "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a", "shasum": "" }, "require": { @@ -2639,7 +2639,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "keywords": [ "cli", @@ -2648,7 +2648,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.1" + "source": "https://github.com/symfony/console/tree/v5.2.3" }, "funding": [ { @@ -2664,7 +2664,7 @@ "type": "tidelift" } ], - "time": "2020-12-18T08:03:05+00:00" + "time": "2021-01-28T22:06:19+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2735,16 +2735,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "1c93f7a1dff592c252574c79a8635a8a80856042" + "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1c93f7a1dff592c252574c79a8635a8a80856042", - "reference": "1c93f7a1dff592c252574c79a8635a8a80856042", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4f9760f8074978ad82e2ce854dff79a71fe45367", + "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367", "shasum": "" }, "require": { @@ -2797,10 +2797,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.1" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.3" }, "funding": [ { @@ -2816,7 +2816,7 @@ "type": "tidelift" } ], - "time": "2020-12-18T08:03:05+00:00" + "time": "2021-01-27T10:36:42+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2899,16 +2899,16 @@ }, { "name": "symfony/filesystem", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d" + "reference": "262d033b57c73e8b59cd6e68a45c528318b15038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/fa8f8cab6b65e2d99a118e082935344c5ba8c60d", - "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038", + "reference": "262d033b57c73e8b59cd6e68a45c528318b15038", "shasum": "" }, "require": { @@ -2938,10 +2938,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.2.1" + "source": "https://github.com/symfony/filesystem/tree/v5.2.3" }, "funding": [ { @@ -2957,20 +2957,20 @@ "type": "tidelift" } ], - "time": "2020-11-30T17:05:38+00:00" + "time": "2021-01-27T10:01:46+00:00" }, { "name": "symfony/finder", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "0b9231a5922fd7287ba5b411893c0ecd2733e5ba" + "reference": "4adc8d172d602008c204c2e16956f99257248e03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/0b9231a5922fd7287ba5b411893c0ecd2733e5ba", - "reference": "0b9231a5922fd7287ba5b411893c0ecd2733e5ba", + "url": "https://api.github.com/repos/symfony/finder/zipball/4adc8d172d602008c204c2e16956f99257248e03", + "reference": "4adc8d172d602008c204c2e16956f99257248e03", "shasum": "" }, "require": { @@ -2999,10 +2999,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.2.1" + "source": "https://github.com/symfony/finder/tree/v5.2.3" }, "funding": [ { @@ -3018,20 +3018,20 @@ "type": "tidelift" } ], - "time": "2020-12-08T17:02:38+00:00" + "time": "2021-01-28T22:06:19+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986" + "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/87a2a4a766244e796dd9cb9d6f58c123358cd986", - "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", + "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", "shasum": "" }, "require": { @@ -3063,7 +3063,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony OptionsResolver Component", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ "config", @@ -3071,7 +3071,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.2.1" + "source": "https://github.com/symfony/options-resolver/tree/v5.2.3" }, "funding": [ { @@ -3087,7 +3087,7 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:08:07+00:00" + "time": "2021-01-27T12:56:27+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3721,16 +3721,16 @@ }, { "name": "symfony/process", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "bd8815b8b6705298beaa384f04fabd459c10bedd" + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/bd8815b8b6705298beaa384f04fabd459c10bedd", - "reference": "bd8815b8b6705298beaa384f04fabd459c10bedd", + "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f", + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f", "shasum": "" }, "require": { @@ -3760,10 +3760,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.2.1" + "source": "https://github.com/symfony/process/tree/v5.2.3" }, "funding": [ { @@ -3779,7 +3779,7 @@ "type": "tidelift" } ], - "time": "2020-12-08T17:03:37+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { "name": "symfony/service-contracts", @@ -3862,16 +3862,16 @@ }, { "name": "symfony/stopwatch", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "2b105c0354f39a63038a1d8bf776ee92852813af" + "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2b105c0354f39a63038a1d8bf776ee92852813af", - "reference": "2b105c0354f39a63038a1d8bf776ee92852813af", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b12274acfab9d9850c52583d136a24398cdf1a0c", + "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c", "shasum": "" }, "require": { @@ -3901,10 +3901,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Stopwatch Component", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.2.1" + "source": "https://github.com/symfony/stopwatch/tree/v5.2.3" }, "funding": [ { @@ -3920,20 +3920,20 @@ "type": "tidelift" } ], - "time": "2020-11-01T16:14:45+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { "name": "symfony/string", - "version": "v5.2.1", + "version": "v5.2.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed" + "reference": "c95468897f408dd0aca2ff582074423dd0455122" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", - "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", + "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122", + "reference": "c95468897f408dd0aca2ff582074423dd0455122", "shasum": "" }, "require": { @@ -3976,7 +3976,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony String component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", @@ -3987,7 +3987,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.1" + "source": "https://github.com/symfony/string/tree/v5.2.3" }, "funding": [ { @@ -4003,7 +4003,7 @@ "type": "tidelift" } ], - "time": "2020-12-05T07:33:16+00:00" + "time": "2021-01-25T15:14:59+00:00" }, { "name": "theseer/tokenizer", diff --git a/functions.php b/functions.php index bf6a4bd..64443ca 100644 --- a/functions.php +++ b/functions.php @@ -31,12 +31,3 @@ if (!function_exists('ungetch')) { return $g->ungetch($char); } } - -if (!function_exists('ungetchString')) { - function ungetchString(string $string, string $linuxLibrary = null): bool - { - $g = new Getch($linuxLibrary); - - return $g->ungetchString($string); - } -} diff --git a/src/Console/Getch.php b/src/Console/Getch.php index 301c2e1..5a8f6c3 100644 --- a/src/Console/Getch.php +++ b/src/Console/Getch.php @@ -19,6 +19,36 @@ use RuntimeException; final class Getch { + // Special key codes + // Extended scan code used to indicate special keyboard functions + public const KEY_RESERVED = 0; + public const KEY_E0 = 0; + public const KEY_E1 = 224; + + // Supported scan scodes. + public const KEY_F1 = 59; + public const KEY_F2 = 60; + public const KEY_F3 = 61; + public const KEY_F4 = 62; + public const KEY_F5 = 63; + public const KEY_F6 = 64; + public const KEY_F7 = 65; + public const KEY_F8 = 66; + public const KEY_F9 = 67; + public const KEY_F10 = 68; + public const KEY_F11 = 87; + public const KEY_F12 = 88; + public const KEY_UP = 72; + public const KEY_LEFT = 75; + public const KEY_RIGHT = 77; + public const KEY_DOWN = 80; + public const KEY_DELETE = 83; + public const KEY_HOME = 71; + public const KEY_PAGEUP = 73; + public const KEY_END = 79; + public const KEY_PAGEDOWN = 81; + public const KEY_INSERT = 82; + private const LINUX_LIBRARY = __DIR__.'/Resources/libgetch.so'; private const WINDOWS_LIBRARY = 'ucrtbase.dll'; private const DECLARATIONS = <<_kbhit()) { + $result = $ffi->_getch(); + $ffi->_ungetch($result); + return $result; + } + return -1; + } + return $ffi->cinPeek(); + } + public function getch(): int { return self::$ffi->_getch(); diff --git a/src/Console/Resources/Makefile b/src/Console/Resources/Makefile index fa3a278..1bdfab1 100644 --- a/src/Console/Resources/Makefile +++ b/src/Console/Resources/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -shared -Wall -fPIC +CFLAGS = -shared -Wall -Wno-unknown-pragmas -fPIC all: @${CC} ${CFLAGS} getch.c -o libgetch.so diff --git a/src/Console/Resources/getch.c b/src/Console/Resources/getch.c index a0acfb0..398b063 100644 --- a/src/Console/Resources/getch.c +++ b/src/Console/Resources/getch.c @@ -1,58 +1,303 @@ +/*********************************************************************** + * This Source Code Form is subject to the terms of the Mozilla Public * + * License, v. 2.0. If a copy of the MPL was not distributed with this * + * file, You can obtain one at http://mozilla.org/MPL/2.0/. * + ***********************************************************************/ + #include #include #include #include #include -static struct termios oldattr; +#include +#include +#include -static char *strrev(char *str) +#define EVENT_DEVICE_GLOB "/dev/input/by-path/*-event-kbd" + +#define FKEY(k) ((k) >= KEY_F1 && (k) <= KEY_F10) || (k) == KEY_F12 || (k) == KEY_F11 +#define NOTNUMPAD(k) ((k) >= KEY_HOME && (k) <= KEY_DELETE) +#define XOR_SWAP(a,b) do\ + {\ + (a) ^= (b);\ + (b) ^= (a);\ + (a) ^= (b);\ + } while (0) + +static struct termios oldTermAttributes; + +inline static void reverseString(char * str) { - char *p1, *p2; + if (str) + { + char * end = str + strlen(str) - 1; - if (! str || ! *str) - return str; - for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) - { - *p1 ^= *p2; - *p2 ^= *p1; - *p1 ^= *p2; - } - return str; + while (str < end) + { + XOR_SWAP(*str, *end); + str++; + end--; + } + } } +static int discardRead(unsigned int length) +{ + char buffer[length]; + ssize_t bytesRead; + + int flags = fcntl(STDIN_FILENO, F_GETFL); + fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK); + + if( (bytesRead = fread(buffer, sizeof(char), length, stdin)) == -1) { + perror("discardRead"); + } + + fcntl(STDIN_FILENO, F_SETFL, flags); + + return (int)bytesRead; +} + +static int getEventDevice(char ** device) +{ + glob_t search; + + int globResult = glob( + EVENT_DEVICE_GLOB, + GLOB_NOSORT, + NULL, + &search + ); + + if(0 != globResult) { + globfree(&search); + return -1; + } + + if(search.gl_pathc == 0) { + globfree(&search); + return -1; + } + + size_t pathLength; + + for(int i = 0; ivaEJjNJZLJS1Ulo z)QZ;-Z?~M1q#-0Bt>XB!jt|b^kk-f!rVGZFQwcs8#j;Lil&edvP~%4-#0Vg z)xB4{NlyRiIju(L-fzC&_susm-^`uI-8*{Jzpk#r`!r-;c?w`xcg% z?N##XkJxE{h2j_M^qh`hN>{7X)#`LiGgKf1rqzlSn|_!GQ#o@U1vu$(t;dvoxEbjv zzuz{n`|5Rm+|~vn~ICV;c5F%{@_PVFK^iV%S)eg zFIsweaku+h-~Qq!m9+Fd4R}Hc(VvvTsUDsZ`cIb8Z!Uv30@q#FW^H>J{f07lXBqq^ zII2{=zF9`Ur40VhWpHW-4=!i;3V>4i{#6g;cX-OcoFBoWQi3VF+_D4iVBHkK_il$IB(F!Kqf_OX<42$+iJRXZ9Ph=O` z9SAk=4m1T@TE(u`Sfji_Eh2)UP^6* zyx*q)o(Gs8cu3(i#_|+HNY;Xrj6=?Xk7p3{vIW;&PD%?Fe6mHq)~A@8L3EWQ;IZK3 zO%AhsDn|M|ClNBwM|U9zm-DvdgjnoT5yI)>;Pa^%VeAV+3?JQHVZp8Q%n=LjfgXor zJ{6;SO<)kT$EO&=KcJ%W{5={7K25C!UrT`5wu?Ks+re z@<$|pig;QI<+~;S4DmFL<=Z9yB=NMQ$nTW=mx-sPM1GUxKS%r+;(e0;BjRa^ke?^{ z$B3t;LEazzm#~I((^|ozkqm}%JbclulEYkbOgK%#^L23go1H+ z)MO${3W;fGzVXnZ=DW3+ZEEtm?|fY9QhUAR;F13OqLpMMz2WCI=3W26q`jO2lwNOTx!f6hqtXeTWw+P~s?744FTnK~1t?iW7pr3ljNbksn zjr*p`3MWS8B2u)Ypj-lvw8JIGtS%|QC5Qbdjnp|=J6TfXcmhve?r!9m@;{Y4@RUeQ zQd)hvQ@<(}d(qg%{2-M#jkE_dou>HnruZVnef}QYMV?-lLA5vC&|`$&FwVV?PTOy! z#aWV4o&OjGg&FEE!<3{PX5nnKm;Y(#JWW`RFf?P@f6Pcxe{rTH>=<$}Qr{!}TGE$w z?aN*IC2BzqADor+_qfu2lnVtyWtNkIJhIQH#mnA3j!^d)-G zhq?9OK1zn1AH|4}EEGFu!n1#XF)sHk6(w!Tqm&1^BEJ-6(gOC4>QN6PMe1Mz29qz+ zos10`m;(c_J%em-hNd$1@33f5s+LOYyVKIl3;Rf#`g z6Q7zbRHu|rYwk@F?vUdN9-d9ZT6kds`bzc7P=)yr03$syOe96mRYNb(9SZ%VLNh>L z1^T-R-KJ3ZtziJ@X2oXHqc$RjLCes6ww7{`@$<58sC*Yt5z%w0Z>oU~&wX2gE1oJe zrTo|3r~TKHIkdp&z0D~+@V6bLHp6Um9W!*gs=_#ix?e^dtyawTZlVTV)%!6b5B9Dn zvar`6;_Y2S$L_EFqMDFO_LS#bkS|Ve6HxsF%JWbctr>nZX@ph% z`V1a?l<;a(^1xMB=a^o4-=LL zTb3+L+q=|OyE;qlmU8Wy`ux`_#Nh2jE5)FjXq6benWzv)kkd7h7z>-%bnnoa{6H6Nij6_xP|@5oQeUg)2Crc<<&LDg;_V-zzB1Gm@BRh1 z8af;4ASaYzo(a>OwoSI^32Rq0r!|!Hg!?p>f^uSe6;X3$lW{$@xldWP) zbKA9Hb6dZ2ZoBexs^5jQ|3b;R?G1?0idVRH=ID{>^CR3uag)vCXx@I$&Sjj z{s9^@$G~Aw%=Y;+XksyVuaR!(Px~{*G52#-73qcyO(ciMeSw4!WewMB?z5RxMbe+a za1J$NiGN`4Ym_;e+X*N3`OipGQsQG`1w4}_J#-Rcc<7E9RB((EeYy8!0yKAFxKAEa+Jn>WedSkxWk+ms-?^GZ z6Q75@`PcG|C?0s)AJ}#TRP&KA;(-wl{9o`uxTPZ$jEC##S0oB^!;w96TcYiqiMfqk zbJ~N6=DGLGiR_6)6LWSqhUwQIKQXH#ds;#f_2ZHJ1!_KKWty4Smgiqxc=60y$!z9^{>F`V*T%0c*dEM4$1lAv_#pqYf9xZT6NOKeDQy%g>CQUVlfN; z*(=2&tq~Uev{Laj; zu~_~Nzwal-VmEj(zNT(`?MK~X_f~g{hbAw(=e`+tae55jq91jmmm?geBC;LVv#4u| zz!l?b4ppqW<)(_qk%JH(K#$;>1Rs2W?6cgTtgIaEk~(Cg2iGY0XPZ^0a*I?Tc?Q>V z`0eX9`Jb8cEa||Xn9Yas*eT7c?+lRcWys}kKU{`wLvjz~=OI6BOByO+)$jK;8{GwzEU!uN#tQAm0r+wU_C`z>qu( zc?@#-TMXB)UdcU_2K}H3a{0S8%c%`gLv1hvI&Y${$u&3YRCG%twDejHxetA9JlPuw zBOVy>z=#J%JTT&c5f6-bV8jC>9{4pL;P2Y_J2os=WuR~8DBy=96=c)x|0xiauKQdtc!?$Ht6Q)RUNXiYgk{4gWSpVbA$yIaY#AF=(C z{BbSER#5Wvd*A4!93q-mX|AJ?==(>R~50CXNmVd@4Pwj1)lc% zXDh~8T^nC1&}!UaEE4+ql>46r#`m0+W3Y6@BfnJ~?$ahJg8C-<_j{b;MzC!{=KiU&M zUk|kMy%DgtHJ;KRvC8*Fz<#Xp4{KM;_e8+1XneEA<@+IEHe0C-LFWSDmy! zynk+y`0)O}9XMVJ4twrm+RlK_Udm5NeE4(mS>P2k8#~60F9O$1ZZq_iGWfH=JvL3n ze5Z{5yJhgpz#Wf@G*pRkxm0;3mBH^RgV#xX__>e`8h4%xX((fdzH}>9p7+Ysi+0>q2oJ&L|Xu7W}=Y}oVf|d z0(8zM*cu2YV)2eZuydaX#oF3iBZ)}ZyLchax0F<(b3HAAU_2h|3SgZQ@4{i8U|S>* z?rdx8LJ|uXfM&v`Do@{_OtIGR)=0c<1&-^qGEk}Kl2reWSfDu=4byR_)t}g~V*T1xK-X+&2>1=1&RAU!2q&Yo zg}|D18&|Ga7uZ-=x5d9Tuyw`Cb$%SAB3GDantVR<6cG*#{klU|>bR3#Uml*aL-JIW z9g@eb?2tN$WyhR{uZE=eo9ChQp(%MRYDi9Jo#Y8DJ0zPp7`KCtV`b_rmt94j$g*SR z`7Jwap58J~dfDaXQ7}79XU6P^I$CDOY$w9h2{pTd^<0=;LeG;0!2?9c=YESLa+XAN zfXv&`)s_f0f+pgMHgjxOwA0%h>}VF=a90#+isI0lmn8HEwZx(}CIDGH(i)@$I@aEr z5MJ3Ky@+~u#lYg28}9UMLn#&xCW68nY1U73a~MXLQ%Tihtx|9tIRx8ULdZUrAj>G6 zw9|;=dtQt!Z5S`?!_@zjqkTGlc8X#6R)Hg$^7@Zq-UqWft} z8Wx)oslvtSdH=>VqZL`s{^R;TtkY9DDDXayDZj6v^t6LL+dxaj?>Fy zXsAGCwT7Kwtm%2*$h6zxa9$oV-R(%v`%9)=Ue1$wrqqqC`SZSsX_Q~|Xak%t+uQF* z&+ns{@_QQAch3QE><5ekGuUz_G2&nII{=7fu|Cf-V z%30)WkK2)6f{_ZR=Y9OZTuOy+Bos04oSx}9NUZ6FPpi&Tv|qDKI33g1Ah4$I_bJVO zou2ia>6s_{q{HRU`jliqr{Aeq^X@TYD)J@><*(IEDW~_Ch>DOd1uJ!Q5h*WdgR^u6 v=>6;ixVSz%{?PNmWtR!{&9g*BUUUdKU)H5PnnOj3-qjPwd#tFbGyQ)9^yA9| literal 16440 zcmeHOZ)_CD6`!*)m_Tepp#}1X3kr&9%Z0yzG$lDOXBK3L<3Qyoq08Yr;}ds2>uwz| zY7|ROqgWOd%7-Sc_<)oW(R`_uN-0fSdLeC~m8wENM5t|4QZ?1NL9IhXQ(bbnDJR{^?@6i|eE?Z}nJm=>$X z2mQ3W&hV$1iXE|Jw}|Z)v7OKo_b0T`G^0A-b5ft?X9w#El^8w(I}RCZd^(Zvb#Z^S z#y7=&3T0N&_?mDKPyI%Raf;8-STm|W{K*SnfB2DqUO}~Lq5h*)BrgG*R|Ve#YI@8X!R=BVwTGs1gV zXN>VX8UGUF%C-;W^8I$vNfsR2R`&MYJMC1ukUo$pI_bjhoo(5Ce|mRve>QDo)g^Y% z;iTQ0=}%@eN7Ab36td}l<@6j#J4weWsGi)hI!*m^U> zI?Tm-Js+uN{uF2ge>6Yq)HtZj_XU~EtqS_*7#m!YnN8XkO=CB&Z{|<%WNeY^p7Bn` zyW<9s>sRSIN3;<^x$&^1fBBW7?4{Q#Z7e<2;T}d5cS}4 z&mcjo2iNyi0#R1QHBrOfosBL6>J}?*s-%C5iz?OW!RL7Juo^}n zj6fKHFalu&!U%*B2qW;>jDR(ArO_H|c;{ZFtZ$Z_y2=G>jWmc$b!&HtKsTEb3@YyK?pv~-;~q50Q|rzPve zG0ne9JT09khBf~R@wDum7|{F{@V&>t>tY?<^c?`!(T3-V+&|^qf#KZ1(>e`jUgdIc zZ>)LgGg_DYe>1?t{xNO8zKh6;8`kKg^~T>Hvfh}gw<2#@7jHPXqrqQtgDrsur?oe{ zX*SwXgBz@oO)tIY}4Psl{aAsWecahZ#7&75DEGd17GPQvpDr${B zRklW+Dl2D>HI^thzowok4bG(M`%lx^8cj?ZdanHFjY`Ej+F8+j5e2OBi2Z6DngA|4(Xq`bzK+gmHWTn=E^!?mj^K@eHP+I62Hfb| z@a*b!>JGzx7n0Qgd4JbNUsnC=CI4C_}pZuUwIR(1oYNc`>H1Z*yen9U8ZNwA*cFCa}G5QtB006@Wr)vFC#m$ z--=@qd}t(q3)#}Q+H1g161KUi<=MKnTW8jtKo6$vZvZ|5pMT_&zv{}51D}Vn+~JjX zYq9q2r@)&a*WX;F{ob4MKSI73@_W4Y`;DBwOJ2tjg}jO6VF@D;Mj(tp7=bVXVFbbm zgb@fM5Jn)3z-Kf9vOiMxL-OOC-g8MV&lSIU1Lm#F5A#0N#mvjT>f3~8x`e6hzq~yl zzjmXN*ARUws)2}h@W%?x3XPJJR>B~%I|LnH5wy#86x25}?d_H=AY;A1S%EFrHz`7NN33lt_ z>uJZW{BRx#?Gl!G;DNDEwqD76@#8a;#NCfKDEYno_)I0gyC0wB&JRD1*}>!XZZ_1L}NSru<~_eFcH>fyXxmwtSnsy09Bv2$Ja zMf>$}Gl+W;Q?JKc9hnbaX}wyg_EpvYyjM-v={lOBKEM}qf1&$Q9j^XTAfEINhVWn# z!d;BZeNI~2fJ(&U5A{c-9Y>@}d~(34eYtOvE|no15j8!|F9NUT{|T+XPz}dXLHVj7 z>J%uof0Fm7>vvwje!%z+PIA!ix`4gTc!clA`W+Xr^Ej&YTPvOyp&!Bgd4d8JDAn5e z6LiSW0q$4cef?GA)939K;E^ix;bVghnPOk7Xqhtg7fDtjZdG48-Ul*T|6*P zHUW>K?_hZhIEUUR$^x&JUou<+Kd$jPDwxmlQVspnz@xq%4RfxB{vT@KS2aF8zvPn| z`t$3mk84E@ybX9YKlcJ(R>gnSQ*esTAa-B%sGDgkwv)4aC~>8TG?7%^K9J4tPiF0u zlP?tQ|m)$v!&kCLWi>8cL@zX+3@{n8kBbx-O8=b5lZX1=5a8c8_1h zO&apUZeEa^UgVd%8Ag8Cn=TZPP|DMPkk;h4QnBJt&PnbEbqa>|N$o)YV5~1$>{GGS zP(RcRMIKa)Bu}Ob#Z126$Jme+(%B>#aBU##sF?nlVyMOr+_GW2F2xSid2}k z24LJdePuZ~T62q;tjC1X{g4W+Z>cKwvR+=vGO?F+n^0NTLroWMPFO`rW3|HZyD zYBYtmkkC_P+yc>hkk(4_{3p-NZvL()G8KEF-vi$4v6uCGOUxLE9-F)KNAPh7Xzob= zvd))0=Mwubaffi = \FFI::load(__DIR__.'/../test.h'); - $stdin = $this->ffi->stdin; + $file = $this->ffi->stdin; + $stdin = $this->ffi->fopen('/dev/stdin', 'a+'); foreach (range('D', 'A') as $character) { - $this->ffi->ungetc(ord($character), $stdin); + $this->ffi->ungetc(ord($character), $file); } - - parent::setUp(); // TODO: Change the autogenerated stub } /** * @preserveGlobalState disabled + * @backupStaticAttributes false + * @backupGlobals false */ public function testFailureOnInvalidLibrary() { + Getch::resetFFI(); $this->expectException(\RuntimeException::class); \getch(__DIR__.'/library.so'); } @@ -53,4 +56,28 @@ class GetchTest extends TestCase $this->expectException(\RuntimeException::class); \getch(); } + + public function testHomeKey() + { + self::markTestSkipped('This seems impossible to test, since it relies on someone actually pressing keys on the keyboard.'); + + $stdin = $this->ffi->stdin; + + foreach (str_split(strrev(self::HOME_KEY)) as $character) { + $this->ffi->ungetc(ord($character), $stdin); + } + $g = new Getch(); + self::assertEquals(0, $g->getch()); + self::assertEquals(71, $g->getch()); + } + + public function setPeek() + { + $g = new Getch(); + $g->ungetch('q'); + $peek = $g->peek(); + $getch = $g->getch(); + self::assertEquals($peek, $getch); + self::assertEquals(113, $peek); + } } diff --git a/tests/Ungetch/UngetchTest.php b/tests/Ungetch/UngetchTest.php index 1b5515a..e495257 100644 --- a/tests/Ungetch/UngetchTest.php +++ b/tests/Ungetch/UngetchTest.php @@ -34,14 +34,14 @@ class UngetchTest extends TestCase public function testForFun() { - foreach(\str_split(\strrev('Hello World!')) as $char) { + foreach (\str_split(\strrev('Hello World!')) as $char) { ungetch($char); } $result = ''; do { $ord = getch(); $result .= \chr($ord); - } while($ord !== ord('!')); + } while ($ord !== ord('!')); self::assertSame('Hello World!', $result); } } diff --git a/tests/test.h b/tests/test.h index 5f84fc9..9de0eca 100644 --- a/tests/test.h +++ b/tests/test.h @@ -13,5 +13,7 @@ typedef struct _iobuf FILE *stdin; +FILE *fopen(const char *filename, const char *mode); + int ungetc(int ch, FILE *stream);