Merge 1.x into master #2
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
Binary file not shown.
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue