Switched to libinput.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,7 +15,7 @@ _deps
|
||||
CMakeUserPresets.json
|
||||
|
||||
# Binary itself
|
||||
hyprevents
|
||||
autotablet
|
||||
|
||||
# CLion
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
project(
|
||||
hyprevents
|
||||
autotablet
|
||||
DESCRIPTION "React on hyprland events"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
@ -9,6 +9,10 @@ project(
|
||||
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS, 1)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -W")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||
@ -17,7 +21,7 @@ set(CMAKE_CXX_STANDARD 23)
|
||||
add_subdirectory(src)
|
||||
|
||||
add_custom_target(run
|
||||
COMMAND hyprevents
|
||||
DEPENDS hyprevents
|
||||
COMMAND autotablet
|
||||
DEPENDS autotablet
|
||||
WORKING_DIRECTORY src
|
||||
)
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
add_executable(hyprevents main.cpp)
|
||||
install(TARGETS hyprevents)
|
||||
add_executable(autotablet main.cpp)
|
||||
target_link_libraries(autotablet -ludev -linput)
|
||||
install(TARGETS autotablet)
|
||||
|
||||
212
src/main.cpp
212
src/main.cpp
@ -1,102 +1,160 @@
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <fcntl.h>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <libinput.h>
|
||||
#include <libudev.h>
|
||||
#include <print>
|
||||
#include <string>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <signal.h>
|
||||
#include <stdexcept>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
std::string instanceSignature;
|
||||
int hypr_socket;
|
||||
bool sigintReceived;
|
||||
static volatile sig_atomic_t stop = 0;
|
||||
|
||||
std::string getRuntimeDir() {
|
||||
const auto XDG = getenv("XDG_RUNTIME_DIR");
|
||||
|
||||
if (!XDG) {
|
||||
const std::string USERID = std::to_string(getuid());
|
||||
return "/run/user/" + USERID + "/hypr";
|
||||
}
|
||||
|
||||
return std::string(XDG) + "/hypr";
|
||||
static void sighandler(int signal) {
|
||||
std::println("Received signal {}", signal);
|
||||
stop = 1;
|
||||
}
|
||||
|
||||
int readLoop(int hypr_socket) {
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
std::array<char, BUFFER_SIZE> buffer = {0};
|
||||
long sizeWritten = 0;
|
||||
void close_restricted(int fd, [[maybe_unused]] void *user_data) {
|
||||
auto file = fdopen(fd, "r+");
|
||||
std::fclose(file);
|
||||
}
|
||||
|
||||
while (!sigintReceived) {
|
||||
sizeWritten = read(hypr_socket, buffer.data(), BUFFER_SIZE);
|
||||
if (sizeWritten < 0 && errno != EAGAIN) {
|
||||
if (errno != EINTR)
|
||||
std::println("Couldn't read (5): {}: {}", strerror(errno), errno);
|
||||
close(hypr_socket);
|
||||
return 5;
|
||||
int open_restricted(const char *path, int flags,
|
||||
[[maybe_unused]] void *user_data) {
|
||||
auto fd = open(path, flags);
|
||||
if (fd < 0) {
|
||||
perror(std::format("Cannot open {}", path).c_str());
|
||||
return -errno;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
const static struct libinput_interface interface = {
|
||||
.open_restricted = open_restricted,
|
||||
.close_restricted = close_restricted,
|
||||
};
|
||||
|
||||
class LibInputListener {
|
||||
public:
|
||||
LibInputListener(struct udev *udev, const char *seat, std::string *filter) {
|
||||
deviceFilter = filter;
|
||||
keyboard_pid = -1;
|
||||
li = libinput_udev_create_context(&interface, nullptr, udev);
|
||||
if (!li) {
|
||||
throw std::runtime_error("Cannot create udev context");
|
||||
}
|
||||
|
||||
if (sizeWritten == 0)
|
||||
break;
|
||||
|
||||
if (sizeWritten > 0) {
|
||||
std::println("{}", std::string(buffer.data(), sizeWritten));
|
||||
buffer.fill('\0');
|
||||
if (libinput_udev_assign_seat(li, seat) == -1) {
|
||||
libinput_unref(li);
|
||||
throw std::runtime_error("Cannot assign seat");
|
||||
}
|
||||
|
||||
usleep(100000);
|
||||
}
|
||||
close(hypr_socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void signalHandler(int signal) {
|
||||
std::println("Received {}", signal);
|
||||
sigintReceived = true;
|
||||
}
|
||||
~LibInputListener() {
|
||||
if (li) {
|
||||
libinput_unref(li);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto runtimeDir = getRuntimeDir();
|
||||
instanceSignature = std::string(std::getenv("HYPRLAND_INSTANCE_SIGNATURE"));
|
||||
if (instanceSignature.empty()) {
|
||||
std::println("Hyprland instance isn't running.");
|
||||
void listen() {
|
||||
libinput_dispatch(li);
|
||||
struct libinput_event *event;
|
||||
struct pollfd fds;
|
||||
fds.fd = libinput_get_fd(li);
|
||||
fds.events = POLLIN;
|
||||
fds.revents = 0;
|
||||
|
||||
while (!stop && poll(&fds, 1, -1) > -1) {
|
||||
libinput_dispatch(li);
|
||||
while ((event = libinput_get_event(li))) {
|
||||
handleEvent(event);
|
||||
|
||||
libinput_event_destroy(event);
|
||||
libinput_dispatch(li);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
struct libinput *li;
|
||||
std::string *deviceFilter;
|
||||
pid_t keyboard_pid;
|
||||
|
||||
void handleLaptopMode() {
|
||||
if (keyboard_pid > 0) {
|
||||
std::println("Stopping on-screen keyboard with PID {}", keyboard_pid);
|
||||
kill(keyboard_pid, SIGTERM);
|
||||
keyboard_pid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void handleTabletMode() {
|
||||
keyboard_pid = fork();
|
||||
if (keyboard_pid == 0) {
|
||||
std::println("Starting on-screen keyboard");
|
||||
const char *binary = "squeekboard";
|
||||
const char *const argv[] = {nullptr};
|
||||
auto ret = execvp(binary, const_cast<char *const *>(argv));
|
||||
if (ret == -1) {
|
||||
std::cerr << "execv failed: " << strerror(errno) << std::endl;
|
||||
}
|
||||
std::println("I was supposed to execve, but got {}", ret);
|
||||
exit(ret);
|
||||
} else if (keyboard_pid < 0) {
|
||||
std::cerr << "Fork failed: " << strerror(errno) << std::endl;
|
||||
} else {
|
||||
std::println("Started on-screen keyboard with PID {}", keyboard_pid);
|
||||
}
|
||||
}
|
||||
|
||||
void handleEvent(struct libinput_event *event) {
|
||||
auto ty = libinput_event_get_type(event);
|
||||
if (ty != LIBINPUT_EVENT_SWITCH_TOGGLE) {
|
||||
return;
|
||||
}
|
||||
auto device = libinput_event_get_device(event);
|
||||
auto device_name = std::string(libinput_device_get_name(device));
|
||||
if (device_name.compare(*deviceFilter) != 0) {
|
||||
return;
|
||||
}
|
||||
struct libinput_event_switch *event_switch =
|
||||
libinput_event_get_switch_event(event);
|
||||
auto switch_state = libinput_event_switch_get_switch_state(event_switch);
|
||||
if (switch_state == LIBINPUT_SWITCH_STATE_ON) {
|
||||
handleTabletMode();
|
||||
} else {
|
||||
handleLaptopMode();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
std::println("Yo, give me proper tablet switch device name.");
|
||||
return 1;
|
||||
}
|
||||
std::string deviceFilter(argv[1]);
|
||||
|
||||
auto socket_fpath = runtimeDir + "/" + instanceSignature + "/.socket2.sock";
|
||||
std::println("Found socket file path: {}", socket_fpath);
|
||||
struct udev *udev = udev_new();
|
||||
|
||||
struct sockaddr_un socket_address;
|
||||
socket_address.sun_family = AF_UNIX;
|
||||
strncpy(socket_address.sun_path, socket_fpath.c_str(),
|
||||
sizeof(socket_address.sun_path) - 1);
|
||||
signal(SIGINT, sighandler);
|
||||
signal(SIGTERM, sighandler);
|
||||
|
||||
auto hypr_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (hypr_socket < 1) {
|
||||
std::println("Cannot create socket (1).");
|
||||
try {
|
||||
LibInputListener listener(udev, "seat0", &deviceFilter);
|
||||
listener.listen();
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
udev_unref(udev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::signal(SIGINT, signalHandler);
|
||||
std::signal(SIGTERM, signalHandler);
|
||||
|
||||
auto timeout = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
if (setsockopt(hypr_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout,
|
||||
sizeof(struct timeval)) < 0) {
|
||||
std::println("Cannot set timeout for a socket (2).");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (connect(hypr_socket, (struct sockaddr *)&socket_address,
|
||||
SUN_LEN(&socket_address)) < 0) {
|
||||
perror("connect");
|
||||
close(hypr_socket);
|
||||
std::println("Cannot connect to {} (3).", socket_fpath);
|
||||
return 3;
|
||||
}
|
||||
|
||||
readLoop(hypr_socket);
|
||||
|
||||
udev_unref(udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user