new function cinPeek(); named from c++ cin.peek()
This commit is contained in:
parent
6372eaddca
commit
876c303e5e
|
@ -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}
|
||||
)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
13
example.c
13
example.c
|
@ -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
97
getch.c
|
@ -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;
|
||||
}
|
5
getch.h
5
getch.h
|
@ -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
|
|
@ -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);
|
||||
|
||||
close(eventDevice);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue