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_C_STANDARD 11)
|
||||||
set(CMAKE_COLOR_MAKEFILE ON)
|
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(example example.c )
|
||||||
add_executable(getchTest tests/test.c tests/getchTests.c)
|
add_executable(getchTest tests/test.c tests/getchTests.c)
|
||||||
add_test(NAME getchTests
|
|
||||||
COMMAND getchTest
|
add_test(NAME defaultTests
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
COMMAND "getchTest"
|
||||||
target_link_libraries(getchTest -L./build getch)
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
target_link_libraries(example -L./build getch)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(getchTest -L./build libgetch)
|
||||||
|
target_link_libraries(example -L./build libgetch)
|
||||||
|
|
||||||
if (ENABLE_TESTS EQUAL 1)
|
if (ENABLE_TESTS EQUAL 1)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
endif ()
|
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.
|
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
|
### Note
|
||||||
Make sure that the user calling the function is in the group "input"
|
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();
|
key = _getch();
|
||||||
printf("Special Key : %d\n", key);
|
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 <glob.h>
|
||||||
#include "getch.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 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;
|
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)
|
static int discardRead(unsigned int length)
|
||||||
{
|
{
|
||||||
char buffer[length];
|
char buffer[length];
|
||||||
|
@ -38,7 +59,7 @@ static int discardRead(unsigned int length)
|
||||||
return (int)bytesRead;
|
return (int)bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getEventDevice(const char * device)
|
static int getEventDevice(char ** device)
|
||||||
{
|
{
|
||||||
glob_t search;
|
glob_t search;
|
||||||
|
|
||||||
|
@ -64,7 +85,8 @@ static int getEventDevice(const char * device)
|
||||||
for(int i = 0; i<search.gl_pathc;i++) {
|
for(int i = 0; i<search.gl_pathc;i++) {
|
||||||
if(access(search.gl_pathv[i], F_OK) == 0) {
|
if(access(search.gl_pathv[i], F_OK) == 0) {
|
||||||
pathLength = strlen(search.gl_pathv[i]) + 1;
|
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);
|
globfree(&search);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -76,30 +98,33 @@ static int getEventDevice(const char * device)
|
||||||
|
|
||||||
static unsigned short getScanCode()
|
static unsigned short getScanCode()
|
||||||
{
|
{
|
||||||
struct input_event inputEvent[3];
|
struct input_event inputEvent[5];
|
||||||
int eventDevice;
|
int eventDevice;
|
||||||
|
|
||||||
const char device[FILENAME_MAX];
|
char *device;
|
||||||
if(getEventDevice(device) == -1) {
|
if(getEventDevice(&device) == -1) {
|
||||||
perror("getEventDevice");
|
perror("getEventDevice");
|
||||||
|
free(device);
|
||||||
return KEY_RESERVED;
|
return KEY_RESERVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ( eventDevice = open(device, O_RDONLY)) == -1 ) {
|
if( ( eventDevice = open(device, O_RDONLY)) == -1 ) {
|
||||||
|
free(device);
|
||||||
perror("open");
|
perror("open");
|
||||||
return KEY_RESERVED;
|
return KEY_RESERVED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
free(device);
|
||||||
|
|
||||||
if( read(eventDevice, &inputEvent, sizeof(inputEvent)) == -1) {
|
if( read(eventDevice, &inputEvent, sizeof(inputEvent)) == -1) {
|
||||||
close(eventDevice);
|
close(eventDevice);
|
||||||
|
perror("read");
|
||||||
return KEY_RESERVED;
|
return KEY_RESERVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(eventDevice);
|
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) {
|
if(inputEvent[i].type == EV_KEY && inputEvent[i].code != KEY_ENTER) {
|
||||||
return inputEvent[i].code;
|
return inputEvent[i].code;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +178,7 @@ static int readKey(void) {
|
||||||
|
|
||||||
unsigned short scanCode = getScanCode();
|
unsigned short scanCode = getScanCode();
|
||||||
|
|
||||||
if (scanCode == KEY_ESC || scanCode == KEY_RESERVED) {
|
if (scanCode == KEY_ESC ) {
|
||||||
return 27;
|
return 27;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +196,6 @@ static int readKey(void) {
|
||||||
case KEY_F2:
|
case KEY_F2:
|
||||||
case KEY_F3:
|
case KEY_F3:
|
||||||
case KEY_F4:
|
case KEY_F4:
|
||||||
|
|
||||||
case KEY_KP1: // END
|
case KEY_KP1: // END
|
||||||
case KEY_KP2: // DOWN
|
case KEY_KP2: // DOWN
|
||||||
case KEY_KP4: // LEFT
|
case KEY_KP4: // LEFT
|
||||||
|
@ -199,6 +223,7 @@ static int readKey(void) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
discardBytes = 0;
|
discardBytes = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discardBytes != 0) {
|
if (discardBytes != 0) {
|
||||||
|
@ -225,7 +250,55 @@ int _getch(void) {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getch(void)
|
||||||
|
{
|
||||||
|
return _getch();
|
||||||
|
}
|
||||||
|
|
||||||
int _ungetch(int ch)
|
int _ungetch(int ch)
|
||||||
{
|
{
|
||||||
return ungetc(ch, stdin);
|
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
|
#define GETCH_H
|
||||||
|
|
||||||
int _getch(void);
|
int _getch(void);
|
||||||
|
int getch(void);
|
||||||
int _ungetch(int ch);
|
int _ungetch(int ch);
|
||||||
|
int ungetch(int ch);
|
||||||
|
int cinPeek();
|
||||||
|
|
||||||
#endif //GETCH_H
|
#endif
|
|
@ -5,15 +5,23 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <libevdev-1.0/libevdev/libevdev.h>
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
|
|
||||||
#include "../getch.h"
|
#include "../getch.h"
|
||||||
|
#define XOR_SWAP(a,b) do\
|
||||||
|
{\
|
||||||
|
(a) ^= (b);\
|
||||||
|
(b) ^= (a);\
|
||||||
|
(a) ^= (b);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define EV_PRESSED 1
|
#define EV_PRESSED 1
|
||||||
#define EV_RELEASED 0
|
#define EV_RELEASED 0
|
||||||
|
|
||||||
inline static void writeToEventDevice(__u16 code)
|
inline static void writeToEventDevice(__u16 code)
|
||||||
{
|
{
|
||||||
|
struct libevdev *dev = NULL;
|
||||||
glob_t globResult;
|
glob_t globResult;
|
||||||
glob("/dev/input/by-path/*-event-kbd", GLOB_NOSORT, NULL, &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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
char device[strlen(globResult.gl_pathv[0])];
|
char device[strlen(globResult.gl_pathv[0])+1];
|
||||||
strncpy(device, globResult.gl_pathv[0], strlen(globResult.gl_pathv[0]) + 1);
|
strcpy(device, globResult.gl_pathv[0]);
|
||||||
globfree(&globResult);
|
globfree(&globResult);
|
||||||
|
|
||||||
struct input_event inputEvent[2];
|
struct input_event inputEvent[3];
|
||||||
int eventDevice = open(device, O_WRONLY);
|
int eventDevice = open(device, O_WRONLY|O_NONBLOCK);
|
||||||
|
|
||||||
if(eventDevice == -1) {
|
if(eventDevice == -1) {
|
||||||
perror("open");
|
perror("open");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i=0; i<2;i++) {
|
inputEvent[0].time.tv_usec = 0;
|
||||||
gettimeofday(&inputEvent[i].time, NULL);
|
inputEvent[0].time.tv_sec = 0;
|
||||||
inputEvent[i].type = EV_KEY;
|
inputEvent[0].type = 4;
|
||||||
inputEvent[i].code = code;
|
inputEvent[0].code = 4;
|
||||||
inputEvent[i].value = i ? EV_RELEASED : EV_PRESSED;
|
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);
|
close(eventDevice);
|
||||||
|
|
||||||
sync();
|
|
||||||
|
|
||||||
fflush(NULL);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void reverseString(char * str)
|
inline static void reverseString(char * str)
|
||||||
|
@ -59,24 +78,12 @@ inline static void reverseString(char * str)
|
||||||
{
|
{
|
||||||
char * end = str + strlen(str) - 1;
|
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)
|
while (str < end)
|
||||||
{
|
{
|
||||||
XOR_SWAP(*str, *end);
|
XOR_SWAP(*str, *end);
|
||||||
str++;
|
str++;
|
||||||
end--;
|
end--;
|
||||||
}
|
}
|
||||||
# undef XOR_SWAP
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,12 +101,12 @@ inline static void writeStringToStdin(char * string)
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *asciiCodes[3] = {
|
static char *asciiCodes[3] = {
|
||||||
"\x1bOPP",
|
"\x1bOP",
|
||||||
"\x1b[H",
|
"\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)
|
CTEST(getchTests, simpleGetchSetReturn_q)
|
||||||
{
|
{
|
||||||
|
@ -113,9 +120,8 @@ CTEST(getchTests, simpleGetchSetReturn_q)
|
||||||
|
|
||||||
CTEST(getchTests, SpecialKey)
|
CTEST(getchTests, SpecialKey)
|
||||||
{
|
{
|
||||||
|
|
||||||
writeStringToStdin(asciiCodes[0]);
|
|
||||||
writeToEventDevice(scanCodes[0]);
|
writeToEventDevice(scanCodes[0]);
|
||||||
|
writeStringToStdin(asciiCodes[0]);
|
||||||
|
|
||||||
int key = _getch();
|
int key = _getch();
|
||||||
int code = _getch();
|
int code = _getch();
|
||||||
|
@ -126,10 +132,8 @@ CTEST(getchTests, SpecialKey)
|
||||||
|
|
||||||
CTEST(specialKeyTests, Home)
|
CTEST(specialKeyTests, Home)
|
||||||
{
|
{
|
||||||
|
writeToEventDevice(scanCodes[1]);
|
||||||
writeStringToStdin(asciiCodes[1]);
|
writeStringToStdin(asciiCodes[1]);
|
||||||
writeToEventDevice(KEY_HOME);
|
|
||||||
|
|
||||||
int key = _getch();
|
int key = _getch();
|
||||||
int code = _getch();
|
int code = _getch();
|
||||||
|
|
||||||
|
@ -139,8 +143,10 @@ CTEST(specialKeyTests, Home)
|
||||||
|
|
||||||
CTEST(escapeReturnsTest, Esc)
|
CTEST(escapeReturnsTest, Esc)
|
||||||
{
|
{
|
||||||
ungetc(27, stdin);
|
|
||||||
writeToEventDevice(KEY_ESC);
|
writeToEventDevice(KEY_ESC);
|
||||||
|
ungetc(27, stdin);
|
||||||
|
|
||||||
|
|
||||||
int key = _getch();
|
int key = _getch();
|
||||||
|
|
||||||
ASSERT_EQUAL(27, key);
|
ASSERT_EQUAL(27, key);
|
||||||
|
|
Loading…
Reference in New Issue