Skip to content

Commit

Permalink
Device change detection
Browse files Browse the repository at this point in the history
  • Loading branch information
houmain committed Jan 22, 2021
1 parent 7cbd0fa commit afaefb7
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 46 deletions.
179 changes: 145 additions & 34 deletions src/linux/server/keyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/inotify.h>

const auto EVDEV_MINORS = 32;

Expand Down Expand Up @@ -40,6 +41,13 @@ namespace {
return true;
}

std::string get_device_name(int fd) {
auto name = std::array<char, 256>();
if (::ioctl(fd, EVIOCGNAME(name.size()), name.data()) >= 0)
return name.data();
return "";
}

bool wait_until_keys_released(int fd) {
const auto retries = 1000;
const auto sleep_ms = 5;
Expand All @@ -59,7 +67,7 @@ namespace {
return false;
}

bool grab_keyboard(int fd, bool grab) {
bool grab_event_device(int fd, bool grab) {
return (::ioctl(fd, EVIOCGRAB, (grab ? 1 : 0)) == 0);
}

Expand All @@ -77,6 +85,18 @@ namespace {
return -1;
}

int create_event_device_monitor() {
auto fd = ::inotify_init();
if (fd >= 0) {
auto ret = ::inotify_add_watch(fd, "/dev/input", IN_CREATE | IN_DELETE);
if (ret == -1) {
::close(fd);
fd = -1;
}
}
return fd;
}

bool read_all(int fd, char* buffer, size_t length) {
while (length != 0) {
auto ret = ::read(fd, buffer, length);
Expand All @@ -89,44 +109,29 @@ namespace {
}
return true;
}
} // namespace

std::vector<int> grab_keyboards() {
auto fds = std::vector<int>();
for (auto i = 0; i < EVDEV_MINORS; ++i) {
const auto fd = open_event_device(i);
if (fd >= 0) {
if (is_keyboard(fd) &&
wait_until_keys_released(fd) &&
grab_keyboard(fd, true)) {
fds.push_back(fd);
}
else {
::close(fd);
}
bool read_event(const std::vector<int>& fds, int cancel_fd,
int* type, int* code, int* value) {

auto rfds = fd_set{ };
FD_ZERO(&rfds);
auto max_fd = 0;
for (auto fd : fds) {
max_fd = std::max(max_fd, fd);
FD_SET(fd, &rfds);
}
}
return fds;
}

void release_keyboards(const std::vector<int>& fds) {
for (auto fd : fds)
if (fd >= 0) {
grab_keyboard(fd, false);
::close(fd);
if (cancel_fd >= 0) {
max_fd = std::max(max_fd, cancel_fd);
FD_SET(cancel_fd, &rfds);
}
}

bool read_event(const std::vector<int>& fds, int* type, int* code, int* value) {
auto rfds = fd_set{ };
FD_ZERO(&rfds);
auto max_fd = 0;
for (auto fd : fds) {
max_fd = std::max(max_fd, fd);
FD_SET(fd, &rfds);
}
if (::select(max_fd + 1, &rfds, nullptr, nullptr, nullptr) == -1)
return false;

if (cancel_fd >= 0 && FD_ISSET(cancel_fd, &rfds))
return false;

if (::select(max_fd + 1, &rfds, nullptr, nullptr, nullptr) > 0)
for (auto fd : fds)
if (FD_ISSET(fd, &rfds)) {
auto ev = input_event{ };
Expand All @@ -138,5 +143,111 @@ bool read_event(const std::vector<int>& fds, int* type, int* code, int* value) {
return true;
}

return false;
return false;
}
} // namespace

class GrabbedKeyboards {
private:
const char* m_ignore_device_name = "";
int m_device_monitor_fd{ -1 };
std::vector<int> m_event_fds;
std::vector<int> m_grabbed_keyboard_fds;

public:
~GrabbedKeyboards() {
for (auto keyboard_fd : m_grabbed_keyboard_fds) {
grab_event_device(keyboard_fd, false);
::close(keyboard_fd);
}

if (m_device_monitor_fd >= 0)
::close(m_device_monitor_fd);
}

int device_monitor_fd() const {
return m_device_monitor_fd;
}

const std::vector<int>& grabbed_keyboard_fds() const {
return m_grabbed_keyboard_fds;
}

bool initialize(const char* ignore_device_name) {
m_ignore_device_name = ignore_device_name;
m_event_fds.resize(EVDEV_MINORS, -1);
reset_device_monitor();
return update();
}

void reset_device_monitor() {
if (m_device_monitor_fd >= 0)
::close(m_device_monitor_fd);
m_device_monitor_fd = create_event_device_monitor();
}

bool update() {
// update grabbed keyboards
auto event_id = 0;
for (auto& event_fd : m_event_fds) {
const auto fd = open_event_device(event_id);
if (fd >= 0 && is_keyboard(fd)) {
// keyboard, grab new ones
if (event_fd < 0 &&
get_device_name(fd) != m_ignore_device_name &&
wait_until_keys_released(fd) &&
grab_event_device(fd, true)) {
event_fd = ::dup(fd);
}
}
else {
// no keyboard, ungrab previously grabbed
if (event_fd >= 0) {
grab_event_device(event_fd, false);
::close(event_fd);
event_fd = -1;
}
}
if (fd >= 0)
::close(fd);
++event_id;
}

// collect grabbed keyboard fds
auto grabbed_keyboard_fds = std::vector<int>();
for (auto event_fd : m_event_fds)
if (event_fd >= 0)
grabbed_keyboard_fds.push_back(event_fd);

// check if they differ from previous list
if (grabbed_keyboard_fds != m_grabbed_keyboard_fds) {
m_grabbed_keyboard_fds = std::move(grabbed_keyboard_fds);
reset_device_monitor();
return true;
}
return false;
}
};

void FreeGrabbedKeyboards::operator()(GrabbedKeyboards* keyboards) {
delete keyboards;
}

GrabbedKeyboardsPtr grab_keyboards(const char* ignore_device_name) {
auto keyboards = GrabbedKeyboardsPtr(new GrabbedKeyboards());
if (!keyboards->initialize(ignore_device_name))
return nullptr;
return keyboards;
}

bool read_keyboard_event(GrabbedKeyboards& keyboards, int* type, int* code, int* value) {
for (;;) {
if (read_event(keyboards.grabbed_keyboard_fds(),
keyboards.device_monitor_fd(), type, code, value))
return true;

// cancelled because a device was detected?
if (!keyboards.update())
return false;
}
}
11 changes: 7 additions & 4 deletions src/linux/server/keyboard.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#pragma once

#include <vector>
#include <memory>

std::vector<int> grab_keyboards();
void release_keyboards(const std::vector<int>& fds);
bool read_event(const std::vector<int>& fds, int* type, int* code, int* value);
class GrabbedKeyboards;
struct FreeGrabbedKeyboards { void operator()(GrabbedKeyboards* keyboards); };
using GrabbedKeyboardsPtr = std::unique_ptr<GrabbedKeyboards, FreeGrabbedKeyboards>;

GrabbedKeyboardsPtr grab_keyboards(const char* ignore_device_name);
bool read_keyboard_event(GrabbedKeyboards& keyboards, int* type, int* code, int* value);
17 changes: 9 additions & 8 deletions src/linux/server/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

#include "ipc.h"
#include "keyboard.h"
#include "GrabbedKeyboards.h"
#include "uinput_keyboard.h"
#include "runtime/Stage.h"
#include <linux/uinput.h>
Expand All @@ -11,24 +11,26 @@ namespace {
}

int main() {
// wait for client connection loop
for (;;) {
const auto ipc_fd = initialize_ipc(ipc_fifo_filename);
if (ipc_fd < 0)
return 1;

const auto stage = read_config(ipc_fd);
if (stage) {
const auto event_fds = grab_keyboards();
if (!event_fds.empty()) {
const auto uinput_fd = create_uinput_keyboard(uinput_keyboard_name);
if (uinput_fd >= 0) {
// client connected
const auto uinput_fd = create_uinput_keyboard(uinput_keyboard_name);
if (uinput_fd >= 0) {
const auto grabbed_keyboards = grab_keyboards(uinput_keyboard_name);
if (grabbed_keyboards) {
// main loop
for (;;) {
// wait for next key event
auto type = 0;
auto code = 0;
auto value = 0;
if (!read_event(event_fds, &type, &code, &value))
if (!read_keyboard_event(*grabbed_keyboards, &type, &code, &value))
break;

// let client update configuration
Expand All @@ -52,10 +54,9 @@ int main() {
send_event(uinput_fd, type, code, value);
}
}
destroy_uinput_keyboard(uinput_fd);
}
release_keyboards(event_fds);
}
destroy_uinput_keyboard(uinput_fd);
}
shutdown_ipc(ipc_fd);
}
Expand Down

0 comments on commit afaefb7

Please sign in to comment.