new function cinPeek(); named from c++ cin.peek()

This commit is contained in:
R. Eric Wheeler 2021-03-01 09:37:48 -08:00
parent 6372eaddca
commit 876c303e5e
6 changed files with 179 additions and 58 deletions

View File

@ -4,15 +4,39 @@ project(getch C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_COLOR_MAKEFILE ON)
add_library(getch SHARED getch.c getch.h)
add_library(libgetch SHARED getch.c getch.h)
add_library(libgetch_static STATIC getch.c getch.h)
add_executable(example example.c )
add_executable(getchTest tests/test.c tests/getchTests.c)
add_test(NAME getchTests
COMMAND getchTest
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(getchTest -L./build getch)
target_link_libraries(example -L./build getch)
add_test(NAME defaultTests
COMMAND "getchTest"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(getchTest -L./build libgetch)
target_link_libraries(example -L./build libgetch)
if (ENABLE_TESTS EQUAL 1)
enable_testing()
endif ()
add_custom_target(escapeTest getchTest escapeReturnsTest
COMMAND getchTest specialKeyTest
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
USES_TERMINAL
)
set_target_properties(libgetch PROPERTIES PUBLIC_HEADER getch.h)
set_target_properties(libgetch PROPERTIES OUTPUT_NAME getch)
set_target_properties(libgetch_static PROPERTIES OUTPUT_NAME getch_static)
include(GNUInstallDirs)
install(TARGETS libgetch libgetch_static
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER
DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}
)

View File

@ -10,6 +10,8 @@ See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getch-getwc
Difference is that _getch will read CTRL+C, or any control characters.
There is also an extra function `int cinPeek()` that returns the next character in stdin without removing it.
### Note
Make sure that the user calling the function is in the group "input"

View File

@ -11,4 +11,17 @@ int main() {
key = _getch();
printf("Special Key : %d\n", key);
}
do {
key = _getch();
printf("Key : %d\n", key);
} while(key != 113);
_ungetch('A');
int res = cinPeek();
int getch = _getch();
printf("%c : %c\n\r", res, getch);
}

97
getch.c
View File

@ -14,13 +14,34 @@
#include <glob.h>
#include "getch.h"
#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 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)
{
if (str)
{
char * end = str + strlen(str) - 1;
while (str < end)
{
XOR_SWAP(*str, *end);
str++;
end--;
}
}
}
static int discardRead(unsigned int length)
{
char buffer[length];
@ -38,7 +59,7 @@ static int discardRead(unsigned int length)
return (int)bytesRead;
}
static int getEventDevice(const char * device)
static int getEventDevice(char ** device)
{
glob_t search;
@ -64,7 +85,8 @@ static int getEventDevice(const char * device)
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);
*device = (char *)malloc(pathLength);
strcpy(*device, search.gl_pathv[i]);
globfree(&search);
return 1;
}
@ -76,30 +98,33 @@ static int getEventDevice(const char * device)
static unsigned short getScanCode()
{
struct input_event inputEvent[3];
struct input_event inputEvent[5];
int eventDevice;
const char device[FILENAME_MAX];
if(getEventDevice(device) == -1) {
char *device;
if(getEventDevice(&device) == -1) {
perror("getEventDevice");
free(device);
return KEY_RESERVED;
}
if( ( eventDevice = open(device, O_RDONLY)) == -1 ) {
free(device);
perror("open");
return KEY_RESERVED;
};
free(device);
if( read(eventDevice, &inputEvent, sizeof(inputEvent)) == -1) {
close(eventDevice);
perror("read");
return KEY_RESERVED;
}
close(eventDevice);
for(int i = 0;i<3;i++) {
for(int i = 0;i<5;i++) {
if(inputEvent[i].type == EV_KEY && inputEvent[i].code != KEY_ENTER) {
return inputEvent[i].code;
}
@ -153,7 +178,7 @@ static int readKey(void) {
unsigned short scanCode = getScanCode();
if (scanCode == KEY_ESC || scanCode == KEY_RESERVED) {
if (scanCode == KEY_ESC ) {
return 27;
}
@ -171,7 +196,6 @@ static int readKey(void) {
case KEY_F2:
case KEY_F3:
case KEY_F4:
case KEY_KP1: // END
case KEY_KP2: // DOWN
case KEY_KP4: // LEFT
@ -199,6 +223,7 @@ static int readKey(void) {
default:
discardBytes = 0;
break;
}
if (discardBytes != 0) {
@ -225,7 +250,55 @@ int _getch(void) {
return key;
}
int getch(void)
{
return _getch();
}
int _ungetch(int ch)
{
return ungetc(ch, stdin);
}
int ungetch(int ch)
{
return _ungetch(ch);
}
int *cinPeekCount(ushort count)
{
char buffer[count];
int flags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK);
size_t byteCount = fread(&buffer, sizeof(char), count, stdin);
fcntl(STDIN_FILENO, F_SETFL, flags);
int *res = (int*)malloc(byteCount);
for(int i=0;i<byteCount;i++)
{
res[i] = (int)buffer[i];
}
reverseString(buffer);
for(int i=0;i<byteCount;i++) {
pushStdin(buffer[i]);
}
return res;
}
int cinPeek()
{
int flags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK);
int result = fgetc(stdin);
fcntl(STDIN_FILENO, F_SETFL, flags);
ungetc(result, stdin);
return result;
}

View File

@ -8,6 +8,9 @@
#define GETCH_H
int _getch(void);
int getch(void);
int _ungetch(int ch);
int ungetch(int ch);
int cinPeek();
#endif //GETCH_H
#endif

View File

@ -5,15 +5,23 @@
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <libevdev-1.0/libevdev/libevdev.h>
#include <glob.h>
#include "../getch.h"
#define XOR_SWAP(a,b) do\
{\
(a) ^= (b);\
(b) ^= (a);\
(a) ^= (b);\
} while (0)
#define EV_PRESSED 1
#define EV_RELEASED 0
inline static void writeToEventDevice(__u16 code)
{
struct libevdev *dev = NULL;
glob_t globResult;
glob("/dev/input/by-path/*-event-kbd", GLOB_NOSORT, NULL, &globResult);
@ -23,34 +31,45 @@ inline static void writeToEventDevice(__u16 code)
exit(EXIT_FAILURE);
}
char device[strlen(globResult.gl_pathv[0])];
strncpy(device, globResult.gl_pathv[0], strlen(globResult.gl_pathv[0]) + 1);
char device[strlen(globResult.gl_pathv[0])+1];
strcpy(device, globResult.gl_pathv[0]);
globfree(&globResult);
struct input_event inputEvent[2];
int eventDevice = open(device, O_WRONLY);
struct input_event inputEvent[3];
int eventDevice = open(device, O_WRONLY|O_NONBLOCK);
if(eventDevice == -1) {
perror("open");
exit(EXIT_FAILURE);
}
for(int i=0; i<2;i++) {
gettimeofday(&inputEvent[i].time, NULL);
inputEvent[i].type = EV_KEY;
inputEvent[i].code = code;
inputEvent[i].value = i ? EV_RELEASED : EV_PRESSED;
inputEvent[0].time.tv_usec = 0;
inputEvent[0].time.tv_sec = 0;
inputEvent[0].type = 4;
inputEvent[0].code = 4;
inputEvent[0].value = code;
inputEvent[1].time.tv_usec = 0;
inputEvent[1].time.tv_sec = 0;
inputEvent[1].type = EV_KEY;
inputEvent[1].code = code;
inputEvent[1].value = EV_PRESSED;
inputEvent[2].time.tv_usec = 0;
inputEvent[2].time.tv_sec = 0;
inputEvent[2].type = EV_KEY;
inputEvent[2].code = code;
inputEvent[2].value = EV_RELEASED;
int res;
res = write(eventDevice, &inputEvent, sizeof(inputEvent));
if(res == -1) {
perror("Write");
}
write(eventDevice, &inputEvent, sizeof(inputEvent));
close(eventDevice);
sync();
fflush(NULL);
}
inline static void reverseString(char * str)
@ -59,24 +78,12 @@ inline static void reverseString(char * str)
{
char * end = str + strlen(str) - 1;
// swap the values in the two given variables
// XXX: fails when a and b refer to same memory location
# define XOR_SWAP(a,b) do\
{\
(a) ^= (b);\
(b) ^= (a);\
(a) ^= (b);\
} while (0)
// walk inwards from both ends of the string,
// swapping until we get to the middle
while (str < end)
{
XOR_SWAP(*str, *end);
str++;
end--;
}
# undef XOR_SWAP
}
}
@ -94,12 +101,12 @@ inline static void writeStringToStdin(char * string)
}
static char *asciiCodes[3] = {
"\x1bOPP",
"\x1bOP",
"\x1b[H",
"\x1bOJ"
"\x1bOQ"
};
static int scanCodes[3] = { KEY_F1, KEY_HOME, KEY_F5 };
static int scanCodes[3] = { KEY_F1, KEY_HOME, KEY_F2 };
CTEST(getchTests, simpleGetchSetReturn_q)
{
@ -113,9 +120,8 @@ CTEST(getchTests, simpleGetchSetReturn_q)
CTEST(getchTests, SpecialKey)
{
writeStringToStdin(asciiCodes[0]);
writeToEventDevice(scanCodes[0]);
writeStringToStdin(asciiCodes[0]);
int key = _getch();
int code = _getch();
@ -126,10 +132,8 @@ CTEST(getchTests, SpecialKey)
CTEST(specialKeyTests, Home)
{
writeToEventDevice(scanCodes[1]);
writeStringToStdin(asciiCodes[1]);
writeToEventDevice(KEY_HOME);
int key = _getch();
int code = _getch();
@ -139,8 +143,10 @@ CTEST(specialKeyTests, Home)
CTEST(escapeReturnsTest, Esc)
{
ungetc(27, stdin);
writeToEventDevice(KEY_ESC);
ungetc(27, stdin);
int key = _getch();
ASSERT_EQUAL(27, key);