From c7459a7d477e161edb26fcd3131e13dcd28f8f2a Mon Sep 17 00:00:00 2001
From: "R. Eric Wheeler" <sikofitt@gmail.com>
Date: Mon, 22 Feb 2021 17:35:17 -0800
Subject: [PATCH] updated libgetch

---
 src/Console/Getch.php             |  16 ++
 src/Console/Resources/Makefile    |   2 +-
 src/Console/Resources/getch.c     | 412 +++++++++++++-----------------
 src/Console/Resources/libgetch.so | Bin 16616 -> 17264 bytes
 tests/Getch/GetchTest.php         |   4 +-
 5 files changed, 203 insertions(+), 231 deletions(-)

diff --git a/src/Console/Getch.php b/src/Console/Getch.php
index 4825458..5479c35 100644
--- a/src/Console/Getch.php
+++ b/src/Console/Getch.php
@@ -80,6 +80,22 @@ final class Getch
         }
     }
 
+/*    public function keyCode(string $device): array
+    {
+
+        $arrayType = \FFI::arrayType(self::$ffi->type('int'), [3]);
+        $res = \FFI::new($arrayType);
+
+        $arrayType = self::$ffi->keyCode($device);
+
+        return [
+            'type' => $arrayType[0],
+            'code' => $arrayType[1],
+            'value' => $arrayType[2],
+            'keyCode' => $arrayType[3],
+        ];
+    }*/
+
     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 e30ed66..54a6105 100644
--- a/src/Console/Resources/getch.c
+++ b/src/Console/Resources/getch.c
@@ -3,14 +3,105 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <glob.h>
 
-#define CTRL_KEY(k) ((k) & 0x1f)
+// #define FKEY(k) ((k) >= KEY_F1 && (k) <= KEY_F10) || (k) == KEY_F12 || (k) == KEY_F11
+#define NOTNUMPAD(k) ((k) >= KEY_HOME && (k) <= KEY_DELETE)
 
-static struct termios oldattr;
+#define EVENT_DEVICE_GLOB "/dev/input/by-path/*-event-kbd"
 
-/*
-static char *strrev(char *str)
+static struct termios oldTermAttributes;
+
+static void setRawMode(void);
+static void setNormalMode(void);
+
+inline static int discardRead(unsigned int length)
+{
+    char buffer[length];
+    ssize_t bytes_read;
+
+    int flags = fcntl(STDIN_FILENO, F_GETFL);
+    fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK);
+
+    if( (bytes_read = fread(buffer, sizeof(char), length, stdin)) == -1) {
+        perror("discardRead");
+    }
+
+    fcntl(STDIN_FILENO, F_SETFL, flags);
+
+    return (int)bytes_read;
+}
+
+int getEventDevice(const 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; i<search.gl_pathc;i++) {
+        if(access(search.gl_pathv[i], F_OK) == 0) {
+            pathLength = strlen(search.gl_pathv[i]) + 1;
+            strncpy((char *)device, search.gl_pathv[i], pathLength);
+            globfree(&search);
+            return 1;
+        }
+    }
+
+    globfree(&search);
+    return -1;
+}
+
+unsigned short getScanCode()
+{
+    struct input_event inputEvent[5];
+    int fd;
+
+    const char device[FILENAME_MAX];
+    if(-1 == getEventDevice(device)) {
+        return KEY_RESERVED;
+    }
+
+    if( (fd = open(device, O_RDONLY)) == -1 ) {
+        return KEY_RESERVED;
+    };
+
+    if( read(fd, &inputEvent, sizeof (inputEvent)) == -1 ) {
+        return KEY_RESERVED;
+    }
+
+    close(fd);
+
+    for(int i = 0; i<=5; i++) {
+        if(inputEvent[i].type == EV_KEY) {
+            return inputEvent[i].code;
+        }
+    }
+
+    return KEY_RESERVED;
+}
+
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions"
+inline static char *reverseString(char *str)
 {
       char *p1, *p2;
 
@@ -24,129 +115,114 @@ static char *strrev(char *str)
       }
       return str;
 }
-*/
-static void setNormalMode(void)
+#pragma clang diagnostic pop
+
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "bugprone-reserved-identifier"
+inline static void pushStdin(int c) {
+    ungetc(c, stdin);
+}
+#pragma clang diagnostic pop
+
+inline static void setNormalMode(void)
 {
-    tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
+    tcsetattr(STDIN_FILENO, TCSANOW, &oldTermAttributes);
 }
 
-static void setRawMode(void)
+inline static void setRawMode(void)
 {
-    tcgetattr(STDIN_FILENO, &oldattr);
+    tcgetattr(STDIN_FILENO, &oldTermAttributes);
 
-    struct termios raw = oldattr;
+    struct termios raw = oldTermAttributes;
 
     cfmakeraw(&raw);
     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);
+unsigned short resolveScanCode(unsigned short key)
+{
+    switch(key) {
+        case KEY_DOWN: return KEY_KP2;
+        case KEY_LEFT: return KEY_KP4;
+        case KEY_RIGHT: return KEY_KP6;
+        case KEY_UP: return KEY_KP8;
+        case KEY_HOME: return KEY_KP7;
+        case KEY_END: return KEY_KP1;
+        case KEY_INSERT: return KEY_KP0;
+        case KEY_DELETE: return KEY_KPDOT;
+        case KEY_PAGEUP: return KEY_KP9;
+        case KEY_PAGEDOWN: return KEY_KP3;
+        default: return key;
+    };
 }
 
 int readKey(void) {
 
-
-
-
-
+    unsigned short scanCode;
     int key = getchar();
 
+    if (key == 27) {
 
-    if (key == '\x1b') {
-        char seq[4];
-        if ((seq[0] = getchar()) == EOF) return '\x1b'; // [
-        if ((seq[1] = getchar()) == EOF) return '\x1b';
+        scanCode = getScanCode();
 
-        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;
-                }
-            }
+        if (scanCode == KEY_ESC) {
+            return 27;
         }
-        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;
-            }
+
+        int returnResult = 0;
+
+        if (NOTNUMPAD(scanCode)) {
+            scanCode = resolveScanCode(scanCode);
+            returnResult = 224;
         }
-        return '\x1b';
-    } else {
-        return key;
+
+        u_short discardBytes;
+
+        switch (scanCode) {
+            case KEY_F1:
+            case KEY_F2:
+            case KEY_F3:
+            case KEY_F4:
+
+            case KEY_KP1: // END
+            case KEY_KP2: // DOWN
+            case KEY_KP4: // LEFT
+            case KEY_KP6: // RIGHT
+            case KEY_KP7: // HOME
+            case KEY_KP8: // UP
+                discardBytes = 2;
+                break;
+            case KEY_KP0: // INSERT
+            case KEY_KPDOT: // DELETE
+            case KEY_KP9: // PAGEUP
+            case KEY_KP3: // PAGEDOWN
+                discardBytes = 3;
+                break;
+            case KEY_F5:
+            case KEY_F6:
+            case KEY_F7:
+            case KEY_F8:
+            case KEY_F9:
+            case KEY_F10:
+            case KEY_F11:
+            case KEY_F12:
+                discardBytes = 4;
+                break;
+
+            default:
+                discardBytes = 0;
+        }
+
+        if (discardBytes != 0) {
+            discardRead(discardBytes);
+        }
+
+        pushStdin(scanCode);
+        return returnResult;
     }
+
+    return key;
 }
 
 int _getch(void) {
@@ -161,129 +237,7 @@ int _getch(void) {
     return key;
 }
 
-/* reads from keypress, doesn't echo */
-int old_getch(void)
-{
-    int ch;
-
-    setRawMode();
-    atexit(setNormalMode);
-
-    ch = getchar();
-
-    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;
-}
-
-int _ungetch(char ch)
+int _ungetch(int ch)
 {
     return ungetc(ch, stdin);
 }
\ No newline at end of file
diff --git a/src/Console/Resources/libgetch.so b/src/Console/Resources/libgetch.so
index 3142cc7e347e1feede842fcd6e56bc20525b9480..90d8939257245cb6ef4f0a8d9e998f299d5c2899 100755
GIT binary patch
literal 17264
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
TiCijDboMT#@iSJ`)NTLY{z})9

literal 16616
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

diff --git a/tests/Getch/GetchTest.php b/tests/Getch/GetchTest.php
index bde782b..b71ee8c 100644
--- a/tests/Getch/GetchTest.php
+++ b/tests/Getch/GetchTest.php
@@ -59,6 +59,8 @@ class GetchTest extends TestCase
 
     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) {
@@ -66,6 +68,6 @@ class GetchTest extends TestCase
         }
         $g = new Getch();
         self::assertEquals(0, $g->getch());
-        self::assertEquals(102, $g->getch());
+        self::assertEquals(71, $g->getch());
     }
 }