Last active
December 13, 2018 12:25
-
-
Save lierdakil/af9af445c76dc4624199b09fef4566e4 to your computer and use it in GitHub Desktop.
Linux key polling and counting KPM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Licensed under WTFPL, Version 2. | |
// See http://www.wtfpl.net/about/ for more information | |
// system imports | |
#include <linux/input.h> | |
#include <sys/ioctl.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <poll.h> | |
#include <signal.h> | |
// c++stdlib imports | |
#include <iostream> | |
#include <filesystem> | |
#include <atomic> | |
#include <thread> | |
#include <numeric> | |
// for CHAR_BIT | |
#include <climits> | |
bool supportsKeyEvents(const int &fd) { | |
unsigned long evbit; | |
ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit); | |
if (evbit & (1 << EV_KEY)) { | |
unsigned char keybits[KEY_MAX/CHAR_BIT+1]; | |
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits); | |
// check if device reports any of the standard 255 keypresses | |
for(std::size_t n = 0; n < 0x100/CHAR_BIT; ++n) { | |
if (keybits[n] > 0) return true; | |
} | |
} | |
return false; | |
} | |
bool shouldExit = false; | |
void sigintHandler(int) { | |
shouldExit = true; | |
} | |
void eventListenThreadWorker(std::atomic<unsigned int>* counter) { | |
// find all /dev/input/event* which report key events for keycodes 0..255 | |
const char eventPathStart[] = "event"; | |
std::vector<pollfd> poll_fds; | |
for (const auto &i : std::filesystem::directory_iterator("/dev/input")) { | |
if(i.is_character_file()) { | |
const auto &path = i.path(); | |
const auto &filename = path.filename(); | |
if (filename.string().compare(0, sizeof(eventPathStart)-1, eventPathStart) == 0) { | |
const int evfile = open(path.c_str(), O_RDONLY | O_NONBLOCK); | |
if(supportsKeyEvents(evfile)) { | |
std::cerr << "Listening for events on " << filename << std::endl; | |
poll_fds.push_back({evfile, POLLIN, 0}); | |
} else { | |
close(evfile); | |
} | |
} | |
} | |
} | |
// poll for events | |
input_event ev; | |
while(!shouldExit) { | |
poll(poll_fds.data(), poll_fds.size(), -1); | |
for (const auto &pfd : poll_fds) { | |
if (pfd.revents & POLLIN) { | |
if(read(pfd.fd, &ev, sizeof(ev)) == sizeof(ev)) { | |
if(ev.type == EV_KEY && ev.value == 1 && ev.code < 0x100) { | |
(*counter)++; | |
} | |
} | |
} | |
} | |
} | |
// cleanup | |
for (auto &pfd : poll_fds) { | |
close(pfd.fd); | |
} | |
} | |
int main() { | |
signal(SIGINT, sigintHandler); | |
signal(SIGTERM, sigintHandler); | |
std::ios_base::sync_with_stdio(false); | |
std::atomic<unsigned int> counter = 0; | |
std::thread eventThread(eventListenThreadWorker, &counter); | |
using namespace std::chrono_literals; | |
// time between querying number of keypresses since last time | |
constexpr auto sleepTime = 100ms; | |
// number of periods that are smoothed over | |
constexpr std::size_t smoothingNum = 10; | |
// ring buffer to store a few recent values | |
std::array<unsigned int, smoothingNum> keysPerSleepTimeBuf = {0}; | |
// constant to normalize keys/time to keys/minute | |
constexpr double ratio = 1min/(sleepTime*smoothingNum); | |
std::cerr << ratio << std::endl; | |
std::size_t ringIdx = 0; | |
auto now = std::chrono::system_clock::now(); | |
while(!shouldExit) { | |
std::this_thread::sleep_until(now+sleepTime); | |
now = std::chrono::system_clock::now(); | |
keysPerSleepTimeBuf[ringIdx] = counter.exchange(0); | |
ringIdx = (ringIdx + 1) % smoothingNum; | |
double kpm = ratio*std::accumulate(keysPerSleepTimeBuf.cbegin(), keysPerSleepTimeBuf.cend(), 0); | |
std::cout << "Current speed is " << kpm << " kpm " << "\r"; | |
std::cout.flush(); | |
} | |
eventThread.join(); | |
std::cout << "\nfinished!\n"; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: | |
clang++ -std=c++17 keypoll.cpp -o keypoll -lstdc++fs -lpthread |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment