--- /dev/null
+cmake_minimum_required(VERSION 2.6)
+project(lfsLinBench)
+
+add_definitions("-std=gnu89 -Wall -Werror -D_GNU_SOURCE")
+
+find_package(Threads REQUIRED)
+find_package(X11 REQUIRED)
+include_directories(${X11_INCLUDE_DIRS})
+
+set(lfsLinBench_SRCS
+ appwindow.c
+ fpscounter.c
+ keyblogger.c
+ main.c)
+
+add_executable(lfsLinBench ${lfsLinBench_SRCS})
+target_link_libraries(lfsLinBench ${X11_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} m cap)
\ No newline at end of file
--- /dev/null
+/* This file is part of LFSBench for Linux/X11
+ *
+ * LFSBench for Linux/X11 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LFSBench for Linux/X11 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with LFSBench for Linux/X11. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "appwindow.h"
+#include "sharedstructs.h"
+
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#define TEXT_TOP_OFFSET 15
+#define TEXT_LEFT_OFFSET 10
+#define TEXT_LINE_OFFSET 12
+
+#ifdef LFSVER_6B
+#define LFS_VERSTR "0.6B"
+#elif defined LFSVER_6E12
+#define LFS_VERSTR "0.6E12"
+#else
+#define LFS_VERSTR "0.6F"
+#endif
+
+static Display* Xdisp;
+static GC Xgc;
+static Window Xwin;
+static KeyCode KC_Q;
+static int XblackColor;
+static int XwhiteColor;
+
+static char** window_text = NULL;
+static int last_text_y_pos;
+static int run_loop;
+static unsigned int window_text_count = 0;
+static pthread_mutex_t draw_mutex;
+static Atom wmDeleteWindow;
+
+static const struct timespec wait_usecs = { 0, 20000000 };
+
+static void redraw_window();
+
+/** Draws a new line of text into the window. */
+int append_window_text(char* text, int dont_flush)
+{
+ pthread_mutex_lock(&draw_mutex);
+ /* Append the text to the list */
+ window_text = (char**)realloc(window_text, ++window_text_count * sizeof(char**));
+ if (window_text == NULL) {
+ fprintf(stderr, "CRITICAL: Insufficient memory to reacollate text array!\n.");
+ run_loop = 0;
+ return ENOMEM;
+ }
+
+ window_text[window_text_count - 1] = (char*)malloc((strlen(text) + 1) * sizeof(char));
+ if (window_text[window_text_count - 1] == NULL) {
+ fprintf(stderr, "CRITICAL: Insufficient memory to append text!\n");
+ run_loop = 0;
+ return ENOMEM;
+ }
+ strcpy(window_text[window_text_count - 1], text);
+
+ /* Draw the text to the window */
+ XDrawString(Xdisp, Xwin, Xgc, TEXT_LEFT_OFFSET, last_text_y_pos, text, strlen(text));
+ last_text_y_pos += TEXT_LINE_OFFSET;
+
+ if (dont_flush != 1)
+ XFlush(Xdisp);
+
+ pthread_mutex_unlock(&draw_mutex);
+
+ return 0;
+}
+
+/** Creates and displays the main window
+ * of the application */
+int create_window()
+{
+ /* Initialize the draw_mutex.
+ * Multiple threads can draw to the window simultaneously,
+ * this mutex makes sure the drawing functions are thread-safe. */
+ if (pthread_mutex_init(&draw_mutex, NULL) != 0) {
+ fprintf(stderr, "CRITICAL: Cannot init draw mutex.\n");
+ return -1;
+ }
+
+ Xdisp = XOpenDisplay(NULL);
+ if (Xdisp == NULL) {
+ fprintf(stderr, "CRITICAL: Unable to open X display!\n");
+ return -1;
+ }
+
+ /* Get black and white color */
+ XblackColor = BlackPixel(Xdisp, DefaultScreen(Xdisp));
+ XwhiteColor = WhitePixel(Xdisp, DefaultScreen(Xdisp));
+
+ /* Quit key keycode */
+ KC_Q = XKeysymToKeycode(Xdisp, XK_Q);
+
+ /* Create the window */
+ Xwin = XCreateSimpleWindow(Xdisp, DefaultRootWindow(Xdisp),
+ 0, 0, 350, 250, 0, XblackColor, XwhiteColor);
+ XSetStandardProperties(Xdisp, Xwin, "LFS Benchmark", "LFSBench", None, NULL, 0, NULL);
+ wmDeleteWindow = XInternAtom(Xdisp, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(Xdisp, Xwin, &wmDeleteWindow, 1);
+ XMapWindow(Xdisp, Xwin);
+ Xgc = XCreateGC(Xdisp, Xwin, 0, NULL);
+ XSetForeground(Xdisp, Xgc, XblackColor);
+ XSetBackground(Xdisp, Xgc, XwhiteColor);
+ XClearWindow(Xdisp, Xwin);
+ XMapRaised(Xdisp, Xwin);
+ XFlush(Xdisp);
+ nanosleep(&wait_usecs, NULL);
+
+ /* Print welcome text */
+ last_text_y_pos = TEXT_TOP_OFFSET;
+ char* verinfo;
+ asprintf(&verinfo, "LFS %s benchmarking tool for Linux/X11", LFS_VERSTR);
+ append_window_text(verinfo, 1);
+ append_window_text("========================================", 1);
+ append_window_text("To start the benchmark, press F11 when", 1);
+ append_window_text("instructed in the replay.", 1);
+ append_window_text("", 1);
+ manual_xflush();
+ free(verinfo);
+
+ return 0;
+}
+
+/** Destroys the window and cleans up */
+void destroy_window()
+{
+ XFreeGC(Xdisp, Xgc);
+ XDestroyWindow(Xdisp, Xwin);
+ XCloseDisplay(Xdisp);
+}
+
+/** Manually flushes the drawing requests.
+ * Must be called after calling "append_window_text()" with "dont_flush = 1" */
+void manual_xflush()
+{
+ pthread_mutex_lock(&draw_mutex);
+ XFlush(Xdisp);
+ pthread_mutex_unlock(&draw_mutex);
+}
+
+/** Runs the main window's event loop */
+void* window_event_loop(void* args)
+{
+ run_loop = 1;
+ exit_thrdata* etd = (exit_thrdata*)args;
+
+ XSelectInput(Xdisp, Xwin, KeyPressMask | ExposureMask | StructureNotifyMask);
+ while(run_loop) {
+ XEvent Xev;
+ XNextEvent(Xdisp, &Xev);
+ switch (Xev.type) {
+ case KeyPress:
+ if (Xev.xkey.keycode == KC_Q) {
+ run_loop = 0;
+ pthread_mutex_lock(etd->exit_mutex);
+ *(etd->pexit_app) = 1;
+ pthread_mutex_unlock(etd->exit_mutex);
+ }
+ case Expose:
+ redraw_window();
+ break;
+ case ConfigureNotify:
+ redraw_window();
+ break;
+ case ClientMessage:
+ if (Xev.xclient.data.l[0] == wmDeleteWindow) {
+ run_loop = 0;
+ pthread_mutex_lock(etd->exit_mutex);
+ *(etd->pexit_app) = 1;
+ pthread_mutex_unlock(etd->exit_mutex);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Check if we should exit the loop */
+ pthread_mutex_lock(etd->exit_mutex);
+ if (*(etd->pexit_app) == 1)
+ run_loop = 0;
+ pthread_mutex_unlock(etd->exit_mutex);
+ }
+
+ return NULL;
+}
+
+/** Thread-safe redrawing routine.
+ * Called then the window is exposed, moved or resized. */
+static void redraw_window()
+{
+ pthread_mutex_lock(&draw_mutex);
+ int i;
+ int text_y_pos = TEXT_TOP_OFFSET;
+
+ for (i = 0; i < window_text_count; i++) {
+ XDrawString(Xdisp, Xwin, Xgc, TEXT_LEFT_OFFSET, text_y_pos, window_text[i], strlen(window_text[i]));
+ text_y_pos += TEXT_LINE_OFFSET;
+ }
+ XFlush(Xdisp);
+ pthread_mutex_unlock(&draw_mutex);
+}
--- /dev/null
+/* This file is part of LFSBench for Linux/X11
+ *
+ * Foobar is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LFSBench for Linux/X11 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with LFSBench for Linux/X11. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _APPWINDOW_H
+#define _APPWINDOW_H
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <X11/Xlib.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+int append_window_text(char *, int);
+int create_window();
+void destroy_window();
+void manual_xflush();
+void* window_event_loop();
+
+#endif
\ No newline at end of file
--- /dev/null
+/* This file is part of LFSBench for Linux/X11
+ *
+ * LFSBench for Linux/X11 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LFSBench for Linux/X11 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with LFSBench for Linux/X11. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "appwindow.h"
+#include "fpscounter.h"
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <math.h>
+#include <pthread.h>
+
+#define READ_BUF_SIZE 50
+#define FPS_OFFSET_6E 0x00988B6C /* Uncertain */
+#define FPS_OFFSET_6E12 0x00921DC4
+#define FPS_OFFSET_6F 0x923644
+#define LFS_EXEC_NAME "LFS.exe"
+#define LFS_EXEC_NAME_6E12 "LFS_6E12.exe"
+
+
+#define SEC_TO_USEC(n) (n * 1000000)
+#define USEC_TO_NSEC(n) (n * 1000)
+
+static pid_t lfs_pid = 0;
+static volatile int run_loop = 0;
+static pthread_t benchmarking_loop_thr;
+static int benchmark_running = 0;
+
+#ifdef LFSVER_6B
+static const off_t OFFSET = FPS_OFFSET_6B;
+static const char* EXEC_NAME = LFS_EXEC_NAME;
+#elif defined LFSVER_6E12
+static const off_t OFFSET = FPS_OFFSET_6E12;
+static const char* EXEC_NAME = LFS_EXEC_NAME_6E12;
+#else
+static const off_t OFFSET = FPS_OFFSET_6F;
+static const char* EXEC_NAME = LFS_EXEC_NAME;
+#endif
+
+static void* benchmarking_loop();
+static void create_fraps_output(const float, const long long, const float, const float, const float);
+static pid_t find_pid_by_name(const char*);
+static void ptrace_error_str(const int, char** const);
+static int start_process();
+static int stop_process();
+
+/** Checks if LFS is running and accesses it's memory
+ * using /proc/$PID/mem to read the FPS count. */
+int init_fps_counter(exit_thrdata* etd)
+{
+ char* noexe = strstr(EXEC_NAME, ".exe");
+ size_t len = noexe - EXEC_NAME;
+ noexe = malloc(len + 1);
+ memcpy(noexe, EXEC_NAME, len);
+ noexe[len] = 0;
+
+ /* Get PID of the LFS process */
+ append_window_text("Waiting for LFS to start...", 0);
+ while(lfs_pid == 0) {
+ lfs_pid = find_pid_by_name(EXEC_NAME);
+ if (lfs_pid == 0)
+ lfs_pid = find_pid_by_name(noexe);
+
+ pthread_mutex_lock(etd->exit_mutex);
+ if (*(etd->pexit_app) == 1) {
+ pthread_mutex_unlock(etd->exit_mutex);
+ goto out;
+ }
+ pthread_mutex_unlock(etd->exit_mutex);
+ sleep(1);
+ }
+ append_window_text("LFS process found.", 0);
+
+out:
+ free(noexe);
+ return 0;
+}
+
+/** Starts or stops the benchmarking loop */
+int start_stop_benchmark() {
+ if (benchmark_running == 0) {
+ run_loop = 1;
+ if (pthread_create(&benchmarking_loop_thr, NULL, &benchmarking_loop, NULL) != 0) {
+ append_window_text("CRITICAL: Cannot start benchmark!", 0);
+ run_loop = 0;
+ return -1;
+ }
+ benchmark_running = 1;
+ return 0;
+ } else {
+ run_loop = 0;
+ pthread_join(benchmarking_loop_thr, NULL);
+ benchmark_running = 0;
+ }
+ return 0;
+}
+
+/** Continouously reads the FPS count and calculates
+ * minimum, maximum and average FPS in the end. */
+static void* benchmarking_loop()
+{
+ const struct timespec RATE_NSEC = { 0, 50000000 };
+ const int ONE_SECOND = 1000000000;
+ double max_fps = 0.0;
+ double min_fps = 1001.0; /* LFS won't render faster than 1000 FPS */
+ double total_frames = 0.0;
+ double avg_fps;
+ struct timeval tp_start, tp_end;
+ long long start_usec, end_usec, diff;
+ long traceret;
+
+ traceret = ptrace(PTRACE_SEIZE, lfs_pid, NULL, 0);
+ if (traceret != 0) {
+ char* s = NULL;
+ char* ss = NULL;
+ ptrace_error_str(errno, &ss);
+ asprintf(&s, "Cannot ptrace LFS process, %s", ss);
+ append_window_text(s, 0);
+ free(s);
+ free(ss);
+ return NULL;
+ }
+ append_window_text("Benchmark started.", 0);
+
+ gettimeofday(&tp_start, NULL);
+
+ while(run_loop) {
+ if (stop_process())
+ goto out;
+ errno = 0;
+ traceret = ptrace(PTRACE_PEEKDATA, lfs_pid, OFFSET, NULL);
+ if (errno == 0) {
+ float _fps = *((float*)(&traceret));
+ double fps = _fps;
+ if (fps > max_fps)
+ max_fps = fps;
+ if (fps < min_fps)
+ min_fps = fps;
+ total_frames += (fps * RATE_NSEC.tv_nsec) / ONE_SECOND;
+ } else {
+ char* s = NULL;
+ char* ss = NULL;
+ ptrace_error_str(errno, &ss);
+ asprintf(&s, "CRITICAL: Error reading FPS, %s", ss);
+ append_window_text(s, 0);
+ free(s);
+ free(ss);
+ goto out;
+ }
+ if (start_process())
+ goto out;
+ nanosleep(&RATE_NSEC, NULL);
+ }
+ gettimeofday(&tp_end, NULL);
+ append_window_text("Benchmark completed.", 0);
+
+ start_usec = SEC_TO_USEC(tp_start.tv_sec) + tp_start.tv_usec;
+ end_usec = SEC_TO_USEC(tp_end.tv_sec) + tp_end.tv_usec;
+ diff = end_usec - start_usec;
+ avg_fps = (total_frames * ONE_SECOND) / USEC_TO_NSEC(diff);
+
+ char* s = NULL;
+
+ asprintf(&s, "TIME Start: %ld, %ld End: %ld, %ld", tp_start.tv_sec, tp_start.tv_usec, tp_end.tv_sec, tp_end.tv_usec);
+ append_window_text(s, 1);
+ free(s);
+
+ asprintf(&s, "TIME Diff: %lld", diff);
+ append_window_text(s, 1);
+ free(s);
+
+ asprintf(&s, "Total frames: %g", total_frames);
+ append_window_text(s, 1);
+ free(s);
+
+ asprintf(&s, "Max: %g, Min: %g, Avg: %g", max_fps, min_fps, avg_fps);
+ append_window_text(s, 1);
+ free(s);
+ manual_xflush();
+
+ create_fraps_output(total_frames, diff, avg_fps, min_fps, max_fps);
+
+out:
+ ptrace(PTRACE_DETACH, lfs_pid, 0, 0);
+ return NULL;
+}
+
+/** Stops benchmark if it's running */
+void stop_benchmark()
+{
+ if (run_loop == 1) {
+ run_loop = 0;
+ pthread_join(benchmarking_loop_thr, NULL);
+ }
+}
+
+/** Creates FRAPS-like output and writes in to a file */
+static void create_fraps_output(const float total_frames, const long long diff, const float avg_fps,
+ const float min_fps, const float max_fps)
+{
+ const char* filename = "lfsbench_fps.txt";
+ const int fraps_total_frames = roundf(total_frames);
+ const int fraps_min_fps = roundf(min_fps);
+ const int fraps_max_fps = roundf(max_fps);
+ const long fraps_time_msec = diff / 1000;
+ time_t curtime;
+ struct tm* t;
+
+ curtime = time(NULL);
+ t = localtime(&curtime);
+
+ FILE* fraps_output;
+ fraps_output = fopen(filename, "a+");
+ if (fraps_output == NULL) {
+ append_window_text("WARNING: Could not write results to log", 0);
+ return;
+ }
+
+ fprintf(fraps_output, "%d-%02d-%02d %02d:%02d:%02d - LFS\n", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+ fprintf(fraps_output, "Frames: %d - Time: %ldms - Avg: %f - Min: %d - Max: %d\n", fraps_total_frames, fraps_time_msec, avg_fps, fraps_min_fps, fraps_max_fps);
+ fprintf(fraps_output, "\n");
+
+ fclose(fraps_output);
+}
+
+/** Gets a PID from a process name.
+ * If there is more than one process of that name, PID of the first matching process is returned. */
+static pid_t find_pid_by_name(const char* pidName)
+{
+ DIR *dir;
+ struct dirent *next;
+
+ dir = opendir("/proc");
+ if (!dir) {
+ perror("Cannot open /proc");
+ return 0;
+ }
+
+ while ((next = readdir(dir)) != NULL) {
+ FILE* status;
+ char filename[READ_BUF_SIZE];
+ char buffer[READ_BUF_SIZE];
+ char name[READ_BUF_SIZE];
+
+ /* Must skip ".." since that is outside /proc */
+ if (strcmp(next->d_name, "..") == 0)
+ continue;
+
+ /* If it isn't a number, we don't want it */
+ if (!isdigit(*next->d_name))
+ continue;
+
+ sprintf(filename, "/proc/%s/status", next->d_name);
+ if (! (status = fopen(filename, "r")) ) {
+ continue;
+ }
+ if (fgets(buffer, READ_BUF_SIZE-1, status) == NULL) {
+ fclose(status);
+ continue;
+ }
+ fclose(status);
+
+ /* Buffer should contain a string like "Name: binary_name" */
+ sscanf(buffer, "%*s %s", name);
+ if (strcmp(name, pidName) == 0) {
+ long pid = strtol(next->d_name, NULL, 0);
+ closedir(dir);
+ return pid;
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+static void ptrace_error_str(const int err, char** const str)
+{
+ switch (err) {
+ case EPERM:
+ asprintf(str, "Bad permissions");
+ break;
+ case ESRCH:
+ asprintf(str, "No such process");
+ break;
+ case EIO:
+ asprintf(str, "Invalid memory address");
+ break;
+ case EFAULT:
+ asprintf(str, "Access to unmapped memory");
+ break;
+ case EINVAL:
+ asprintf(str, "Invalid operation");
+ break;
+ case EBUSY:
+ asprintf(str, "Debug register operation failed");
+ break;
+ default:
+ asprintf(str, "Unknown error");
+ }
+}
+
+static int start_process()
+{
+ long ret;
+
+ errno = 0;
+ ret = ptrace(PTRACE_CONT, lfs_pid, NULL, 0);
+ if (ret) {
+ char* s, *ss;
+ ptrace_error_str(errno, &ss);
+ asprintf(&s, "Cannot restart LFS process, %s", ss);
+ append_window_text(s, 0);
+ free(s);
+ free(ss);
+ return 1;
+ }
+ return 0;
+}
+
+static int stop_process()
+{
+ long ret;
+
+ errno = 0;
+ ret = ptrace(PTRACE_INTERRUPT, lfs_pid, NULL, 0);
+ if (ret) {
+ char* s, *ss;
+ ptrace_error_str(errno, &ss);
+ asprintf(&s, "Cannot stop LFS process, %s", ss);
+ append_window_text(s, 0);
+ free(s);
+ free(ss);
+ return 1;
+ }
+ waitpid(lfs_pid, NULL, 0);
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+#ifndef _FPSCOUNTER_H
+#define _FPSCOUNTER_H
+
+#include "sharedstructs.h"
+#include <fcntl.h>
+#include <unistd.h>
+
+int init_fps_counter(exit_thrdata* etd);
+int start_stop_benchmark();
+void stop_benchmark();
+
+#endif
\ No newline at end of file
--- /dev/null
+#include "appwindow.h"
+#include "keyblogger.h"
+#include "fpscounter.h"
+#include "sharedstructs.h"
+#include <linux/input.h>
+
+#define EV_RELEASED 0
+#define EV_PRESSED 1
+
+static int keybd_fd = 0;
+
+/** Accesses the keyboard to read raw keyboard events */
+int init_keyb_logging(const char* evdev_path)
+{
+ keybd_fd = open(evdev_path, O_RDONLY);
+
+ if (keybd_fd < 0) {
+ fprintf(stderr, "CRITICAL: Cannot open %s for reading. Check permissions.\n", evdev_path);
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Keyboard event loop */
+void* get_keyb_events(void* args)
+{
+ exit_thrdata* etd = (exit_thrdata*)args;
+ struct input_event ev;
+ int run_loop = 1;
+
+ while (run_loop) {
+ read(keybd_fd, &ev, sizeof(struct input_event));
+ if (ev.type == EV_KEY) {
+ if (ev.code == 87 && ev.value == EV_PRESSED) {
+ start_stop_benchmark();
+ }
+ }
+
+ pthread_mutex_lock(etd->exit_mutex);
+ if (*(etd->pexit_app) == 1)
+ run_loop = 0;
+ pthread_mutex_unlock(etd->exit_mutex);
+ }
+
+ return NULL;
+}
--- /dev/null
+#ifndef _KEYBLOGGER_H
+#define _KEYBLOGGER_H
+
+int init_keyb_logging(const char*);
+void* get_keyb_events(void*);
+
+#endif
\ No newline at end of file
--- /dev/null
+/* This file is part of LFSBench for Linux/X11
+ *
+ * LFSBench for Linux/X11 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LFSBench for Linux/X11 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with LFSBench for Linux/X11. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "appwindow.h"
+#include "fpscounter.h"
+#include "keyblogger.h"
+#include "sharedstructs.h"
+#include <sys/capability.h>
+#include <unistd.h>
+
+pthread_t win_event_loop;
+pthread_t kb_event_loop;
+pthread_mutex_t exit_mutex;
+
+char* evdev_path;
+int exit_app;
+
+int main(int argc, char** argv)
+{
+ if (geteuid() != 0) {
+ cap_t caps = cap_get_proc();
+ cap_flag_value_t capfv;
+
+ if (caps == NULL) {
+ fprintf(stderr, "CRITICAL: Cannot get advanced capabilites status.\n");
+ return -1;
+ }
+ if (cap_get_flag(caps, CAP_DAC_READ_SEARCH, CAP_EFFECTIVE, &capfv) != 0) {
+ fprintf(stderr, "CRITICAL: Cannot check capability flags.\n");
+ return -1;
+ }
+ if (capfv != CAP_SET) {
+ fprintf(stderr, "CRITICAL: Capability CAP_DAC_READ_SEARCH is not set, this capability is required.\n");
+ cap_free(caps);
+ return -1;
+ }
+ if (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &capfv) != 0) {
+ fprintf(stderr, "CRITICAL: Cannot check capability flags.\n");
+ return -1;
+ }
+ if (capfv != CAP_SET) {
+ fprintf(stderr, "CRITICAL: Capability CAP_SYS_PTRACE is not set, this capability is required.\n");
+ cap_free(caps);
+ return -1;
+ }
+ cap_free(caps);
+ }
+
+ /* Check arguments */
+ if (argc != 2) {
+ printf("No arguments given, assuming that \"/dev/input/event0\" is the keyboard.\n");
+ evdev_path = "/dev/input/event0";
+ } else {
+ evdev_path = malloc(strlen(argv[1])*sizeof(char) + 1);
+ strcpy(evdev_path, argv[1]);
+ }
+
+ /* Setup threading data */
+ exit_thrdata etd;
+ exit_app = 0;
+ if (pthread_mutex_init(&exit_mutex, NULL) != 0) {
+ fprintf(stderr, "CRITICAL: Cannot create exit_mutex!");
+ return -1;
+ }
+
+ etd.exit_mutex = &exit_mutex;
+ etd.pexit_app = &exit_app;
+
+ if (XInitThreads() <= 0) {
+ fprintf(stderr, "Unable to initialize X11 threads\n");
+ pthread_mutex_destroy(&exit_mutex);
+ return -1;
+ }
+ /* Create main window */
+ if (create_window() < 0)
+ return -1;
+ /* Start main window event loop */
+ if (pthread_create(&win_event_loop, NULL, &window_event_loop, &etd) != 0) {
+ fprintf(stderr, "CRITICAL: Cannot start main window event loop!\n");
+ return -1;
+ }
+
+ if (init_keyb_logging(evdev_path) != 0)
+ goto exit;
+
+ if (init_fps_counter(&etd) != 0)
+ goto exit;
+
+ if (pthread_create(&kb_event_loop, NULL, &get_keyb_events, &etd) != 0) {
+ fprintf(stderr, "CRITICAL: Cannot start boundkeys event loop!\n");
+ return -1;
+ }
+
+ pthread_join(win_event_loop, NULL);
+ stop_benchmark();
+ destroy_window();
+
+ pthread_join(kb_event_loop, NULL);
+
+ pthread_mutex_destroy(&exit_mutex);
+
+ return 0;
+
+ exit:
+ pthread_join(win_event_loop, NULL);
+ pthread_mutex_destroy(&exit_mutex);
+
+ return -1;
+}
--- /dev/null
+This tool needs elevated privileges to use ptrace and read raw keyboard events. It is possible to run this tool as root, but the preferred and safer approach is to use advances permission capabilities. This will allow you to run this tool as a regular user.
+
+To set the advanced capabilities, run:
+setcap cap_dac_read_search,cap_sys_ptrace+ep lfsLinBench
--- /dev/null
+#ifndef _SHAREDSTRUCTS_H
+#define _SHAREDSTRUCTS_H
+
+#include <pthread.h>
+
+typedef struct {
+ pthread_mutex_t* exit_mutex;
+ int* pexit_app;
+} exit_thrdata;
+
+#endif
\ No newline at end of file