updated libgetch

This commit is contained in:
R. Eric Wheeler 2021-02-22 17:35:17 -08:00
parent a918fd2cac
commit c7459a7d47
5 changed files with 203 additions and 231 deletions

View File

@ -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 public function getch(): int
{ {
return self::$ffi->_getch(); return self::$ffi->_getch();

View File

@ -1,5 +1,5 @@
CC = gcc CC = gcc
CFLAGS = -shared -Wall -fPIC CFLAGS = -shared -Wall -Wno-unknown-pragmas -fPIC
all: all:
@${CC} ${CFLAGS} getch.c -o libgetch.so @${CC} ${CFLAGS} getch.c -o libgetch.so

View File

@ -3,14 +3,105 @@
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.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 struct termios oldTermAttributes;
static char *strrev(char *str)
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; char *p1, *p2;
@ -24,129 +115,114 @@ static char *strrev(char *str)
} }
return str; return str;
} }
*/ #pragma clang diagnostic pop
static void setNormalMode(void)
#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); cfmakeraw(&raw);
tcsetattr(STDIN_FILENO, TCSANOW, &raw); tcsetattr(STDIN_FILENO, TCSANOW, &raw);
} }
// F1 - 59 unsigned short resolveScanCode(unsigned short key)
// F2 - 60, {
// F3 - 61, switch(key) {
// F2 - 62 case KEY_DOWN: return KEY_KP2;
// F1-12 = 59 - 68 case KEY_LEFT: return KEY_KP4;
enum special_keycodes { case KEY_RIGHT: return KEY_KP6;
GETCH_F1 = 59, case KEY_UP: return KEY_KP8;
GETCH_F2, case KEY_HOME: return KEY_KP7;
GETCH_F3, case KEY_END: return KEY_KP1;
GETCH_F4, case KEY_INSERT: return KEY_KP0;
GETCH_F5, case KEY_DELETE: return KEY_KPDOT;
GETCH_F6, case KEY_PAGEUP: return KEY_KP9;
GETCH_F7, case KEY_PAGEDOWN: return KEY_KP3;
GETCH_F8, default: return key;
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 readKey(void) {
unsigned short scanCode;
int key = getchar(); int key = getchar();
if (key == 27) {
if (key == '\x1b') { scanCode = getScanCode();
char seq[4];
if ((seq[0] = getchar()) == EOF) return '\x1b'; // [
if ((seq[1] = getchar()) == EOF) return '\x1b';
if (seq[0] == '[') { if (scanCode == KEY_ESC) {
if(seq[1] == 'H') { _ungetc(GETCH_HOME); return 0; } return 27;
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]; } int returnResult = 0;
} else {
switch (seq[1]) { if (NOTNUMPAD(scanCode)) {
case 'A': _ungetc(GETCH_UP_ARROW); return 0; scanCode = resolveScanCode(scanCode);
case 'B': _ungetc(GETCH_DOWN_ARROW); return 0; returnResult = 224;
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;
} }
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);
} }
else if (seq[0] == 'O') {
switch (seq[1]) { pushStdin(scanCode);
case 'H': _ungetc(GETCH_HOME); return 0; return returnResult;
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; return key;
}
} }
int _getch(void) { int _getch(void) {
@ -161,129 +237,7 @@ int _getch(void) {
return key; return key;
} }
/* reads from keypress, doesn't echo */ int _ungetch(int ch)
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)
{ {
return ungetc(ch, stdin); return ungetc(ch, stdin);
} }

Binary file not shown.

View File

@ -59,6 +59,8 @@ class GetchTest extends TestCase
public function testHomeKey() 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; $stdin = $this->ffi->stdin;
foreach (str_split(strrev(self::HOME_KEY)) as $character) { foreach (str_split(strrev(self::HOME_KEY)) as $character) {
@ -66,6 +68,6 @@ class GetchTest extends TestCase
} }
$g = new Getch(); $g = new Getch();
self::assertEquals(0, $g->getch()); self::assertEquals(0, $g->getch());
self::assertEquals(102, $g->getch()); self::assertEquals(71, $g->getch());
} }
} }