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/src/Console/Getch.php b/src/Console/Getch.php index 301c2e1..4825458 100644 --- a/src/Console/Getch.php +++ b/src/Console/Getch.php @@ -19,6 +19,31 @@ use RuntimeException; final class Getch { + // Special key codes + public const GETCH_SPECIAL = 0; + public const GETCH_F1 = 59; + public const GETCH_F2 = 60; + public const GETCH_F3 = 61; + public const GETCH_F4 = 62; + public const GETCH_F5 = 63; + public const GETCH_F6 = 64; + public const GETCH_F7 = 65; + public const GETCH_F8 = 66; + public const GETCH_F9 = 67; + public const GETCH_F10 = 68; + public const GETCH_F11 = 87; + public const GETCH_F12 = 88; + public const GETCH_UP_ARROW = 72; + public const GETCH_LEFT_ARROW = 75; + public const GETCH_RIGHT_ARROW = 77; + public const GETCH_DOWN_ARROW = 80; + public const GETCH_DELETE = 83; + public const GETCH_HOME = 102; + public const GETCH_PGUP = 104; + public const GETCH_END = 107; + public const GETCH_PGDN = 109; + public const GETCH_INSERT = 110; + private const LINUX_LIBRARY = __DIR__.'/Resources/libgetch.so'; private const WINDOWS_LIBRARY = 'ucrtbase.dll'; private const DECLARATIONS = << #include #include -static struct termios oldattr; +#include +#include +#define CTRL_KEY(k) ((k) & 0x1f) + +static struct termios oldattr; +static int stdin_flags; + +/* static char *strrev(char *str) { char *p1, *p2; @@ -19,26 +26,152 @@ static char *strrev(char *str) } return str; } - - -static void setRawMode(void) -{ - struct termios newattr; - - tcgetattr(STDIN_FILENO, &oldattr); - newattr = oldattr; - cfmakeraw(&newattr); - tcsetattr(STDIN_FILENO, TCSANOW, &newattr); - -} +*/ static void setNormalMode(void) { tcsetattr(STDIN_FILENO, TCSANOW, &oldattr); + fcntl(STDIN_FILENO, F_SETFL, stdin_flags); } +static void setRawMode(void) +{ + tcgetattr(STDIN_FILENO, &oldattr); + atexit(setNormalMode); + + struct termios raw = oldattr; + + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + raw.c_oflag &= ~(OPOST); + raw.c_cflag |= (CS8); + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 1; + + tcsetattr(STDIN_FILENO, TCSANOW, &raw); + +} + +// F1 - 59 +// F2 - 60, +// F3 - 61, +// F2 - 62 +// F1-12 = 59 - 68 +enum special_keycodes { + GETCH_F1 = 59, + GETCH_F2, + GETCH_F3, + GETCH_F4, + GETCH_F5, + GETCH_F6, + GETCH_F7, + GETCH_F8, + GETCH_F9, + GETCH_F10, + GETCH_UP_ARROW = 72, + GETCH_LEFT_ARROW = 75, + GETCH_RIGHT_ARROW = 77, + GETCH_DOWN_ARROW = 80, + GETCH_DELETE = 83, + GETCH_F11 = 87, + GETCH_F12, + GETCH_HOME = 102, + GETCH_PGUP = 104, + GETCH_END = 107, + GETCH_PGDOWN = 109, + GETCH_INSERT +}; + +void _ungetc(int c) { + ungetc(c, stdin); +} + +int readKey(void) { + +int bytesRead; + + char key; + + while ((key = fgetc(stdin)) == EOF) { + } + + if (key == '\x1b') { + char seq[4]; + if ((seq[0] = getchar()) == EOF) return '\x1b'; // [ + if ((seq[1] = getchar()) == EOF) return '\x1b'; + + if (seq[0] == '[') { + if(seq[1] == 'H') { _ungetc(GETCH_HOME); return 0; } + if(seq[1] == 'F') { _ungetc(GETCH_END); return 0; } + + if (seq[1] >= '0' && seq[1] <= '9') { + if ((seq[2] = getchar()) == EOF) return '\x1b'; + if (seq[2] == '~') { + switch (seq[1]) { + case '1': _ungetc(GETCH_HOME); return 0; + case '2': _ungetc(GETCH_INSERT); return 0; + case '3': _ungetc(GETCH_DELETE); return 0; + case '4': _ungetc(GETCH_END); return 0; + case '5': _ungetc(GETCH_PGUP); return 0; + case '6': _ungetc(GETCH_PGDOWN); return 0; + case '7': _ungetc(GETCH_HOME); return 0; + case '8': _ungetc(GETCH_END); return 0; + } + } else if(seq[2] >= '0' && seq[2] <= '9') { + if ((seq[3] = getchar()) == EOF) return '\x1b'; + if(seq[3] != '~') return seq[3]; + + switch(seq[2]) { + case '5': _ungetc(GETCH_F5); return 0; + case '7': _ungetc(GETCH_F6); return 0; + case '8': _ungetc(GETCH_F7); return 0; + case '9': _ungetc(GETCH_F8); return 0; + case '0': _ungetc(GETCH_F9); return 0; + case '1': _ungetc(GETCH_F10); return 0; + case '3': _ungetc(GETCH_F11); return 0; + case '4': _ungetc(GETCH_F12); return 0; + } + + } else { return seq[2]; } + } else { + switch (seq[1]) { + case 'A': _ungetc(GETCH_UP_ARROW); return 0; + case 'B': _ungetc(GETCH_DOWN_ARROW); return 0; + case 'C': _ungetc(GETCH_RIGHT_ARROW); return 0; + case 'D': _ungetc(GETCH_LEFT_ARROW); return 0; + case 'H': _ungetc(GETCH_HOME); return 0; + case 'F': _ungetc(GETCH_END); return 0; + } + } + } + else if (seq[0] == 'O') { + switch (seq[1]) { + case 'H': _ungetc(GETCH_HOME); return 0; + case 'F': _ungetc(GETCH_END); return 0; + case 'P': _ungetc(GETCH_F1); return 0; + case 'Q': _ungetc(GETCH_F2); return 0; + case 'R': _ungetc(GETCH_F3); return 0; + case 'S': _ungetc(GETCH_F4); return 0; + } + } + return '\x1b'; + } else { + return key; + } +} + +int _getch(void) { + + setRawMode(); + + int key = readKey(); + + setNormalMode(); + + return key; +} /* reads from keypress, doesn't echo */ -int _getch(void) +int old_getch(void) { int ch; @@ -49,6 +182,113 @@ int _getch(void) setNormalMode(); +if(ch == 27) { + +char sequence[4]; + +if (read(STDIN_FILENO, &sequence[0], 1) != 1) return 27; // [ +if (read(STDIN_FILENO, &sequence[1], 1) != 1) return 27; // 0-9 + + switch(ch) + { + case 79: // F1-F4 + ch = getchar(); + switch(ch) { + case 80: // F1, [ESC]OP + _ungetc(GETCH_F1); + return 0; + break; + case 81: // F2, [ESC]OQ + _ungetc(GETCH_F2); + return 0; + break; + case 82: // F3,, [ESC]OR + _ungetc(61); + return 0; + case 83: // F4, , [ESC]OS + _ungetc(62); + return 0; + } + case 91: // [, Everything else + ch = getchar(); + switch(ch) { + case 49: // 1, F5-F8, HOME + ch = getchar(); + switch(ch) { + case 53: // 5, F5 + getchar(); // get the ~ + _ungetc(63); + return 0; + case 55: // 7, F6 + getchar(); + _ungetc(64); + return 0; + case 56: // 8, F7 + getchar(); + _ungetc(65); + return 0; + case 57: // 9, F8 + getchar(); + _ungetc(66); + return 0; + case 126: // ~, HOME + _ungetc(102); + return 0; + } + case 50: // 2, F9-F12, INSERT + ch = getchar(); + switch(ch) { + case 48: // 0, F9, [ESC][20~ + getchar(); + _ungetc(67); + return 0; + case 49: // 1, F10, [ESC][21~ + getchar(); + _ungetc(68); + return 0; + case 51: // 3, F11, [ESC][23~ + getchar(); + _ungetc(87); + return 0; + case 52: // 4, F12, [ESC][24~ + getchar(); + _ungetc(88); + return 0; + case 126: // ~, INSERT, [ESC][2~ + _ungetc(110); + return 0; + } + case 51: // 3, DELETE, [ESC][3~ + getchar(); // ~ + _ungetc(83); + return 0; + case 52: // 4, END, [ESC][4~ + getchar(); // ~ + _ungetc(107); + return 0; + case 53: // 5, PGUP, [ESC][5~ + getchar(); // ~ + _ungetc(104); + return 0; + case 54: // 6, PGDN, [ESC][6~ + getchar(); // ~ + _ungetc(109); + return 0; + case 65: // A, UP_ARROW, [ESC][A //72 + _ungetc(72); + return 0; + case 66: // B, DOWN_ARROW, [ESC][B // 80 + _ungetc(80); + return 0; + case 67: // C, RIGHT_ARROW, [ESC][C /77 + _ungetc(77); + return 0; + case 68: // D, LEFT_ARROW, [ESC][D //75 + _ungetc(75); + return 0; + } + } + } return ch; } diff --git a/src/Console/Resources/keycodes.txt b/src/Console/Resources/keycodes.txt new file mode 100644 index 0000000..58ce75a --- /dev/null +++ b/src/Console/Resources/keycodes.txt @@ -0,0 +1,16 @@ +F1-10 = 59..68 +UP_ARROW = 72 +LEFT_ARROW = 75 +RIGHT_ARROW = 77 +DOWN_ARROW = 80 +DELETE = 83 +F11-12 = 87..88 +HOME = 102 +PGUP = 104 +END = 107 +PGDN = 109 +INSERT = 110 + + + + diff --git a/src/Console/Resources/libgetch.so b/src/Console/Resources/libgetch.so index 8750c7b..82dae79 100755 Binary files a/src/Console/Resources/libgetch.so and b/src/Console/Resources/libgetch.so differ diff --git a/tests/Getch/GetchTest.php b/tests/Getch/GetchTest.php index 2ac28c6..bde782b 100644 --- a/tests/Getch/GetchTest.php +++ b/tests/Getch/GetchTest.php @@ -8,24 +8,27 @@ use Sikofitt\Console\Getch; class GetchTest extends TestCase { private \FFI $ffi; + private const HOME_KEY = "\x1b[H"; public function setUp(): void { $this->ffi = \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,16 @@ class GetchTest extends TestCase $this->expectException(\RuntimeException::class); \getch(); } + + public function testHomeKey() + { + $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(102, $g->getch()); + } } 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);