Switched to libinput.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,7 +15,7 @@ _deps
|
|||||||
CMakeUserPresets.json
|
CMakeUserPresets.json
|
||||||
|
|
||||||
# Binary itself
|
# Binary itself
|
||||||
hyprevents
|
autotablet
|
||||||
|
|
||||||
# CLion
|
# CLion
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.2)
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
project(
|
project(
|
||||||
hyprevents
|
autotablet
|
||||||
DESCRIPTION "React on hyprland events"
|
DESCRIPTION "React on hyprland events"
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
@ -9,6 +9,10 @@ project(
|
|||||||
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
|
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS, 1)
|
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 "-Wall -Wextra -W")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||||
@ -17,7 +21,7 @@ set(CMAKE_CXX_STANDARD 23)
|
|||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
add_custom_target(run
|
add_custom_target(run
|
||||||
COMMAND hyprevents
|
COMMAND autotablet
|
||||||
DEPENDS hyprevents
|
DEPENDS autotablet
|
||||||
WORKING_DIRECTORY src
|
WORKING_DIRECTORY src
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
add_executable(hyprevents main.cpp)
|
add_executable(autotablet main.cpp)
|
||||||
install(TARGETS hyprevents)
|
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 <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <exception>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <format>
|
||||||
|
#include <iostream>
|
||||||
|
#include <libinput.h>
|
||||||
|
#include <libudev.h>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <string>
|
#include <signal.h>
|
||||||
#include <sys/socket.h>
|
#include <stdexcept>
|
||||||
#include <sys/un.h>
|
#include <sys/poll.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
std::string instanceSignature;
|
static volatile sig_atomic_t stop = 0;
|
||||||
int hypr_socket;
|
|
||||||
bool sigintReceived;
|
|
||||||
|
|
||||||
std::string getRuntimeDir() {
|
static void sighandler(int signal) {
|
||||||
const auto XDG = getenv("XDG_RUNTIME_DIR");
|
std::println("Received signal {}", signal);
|
||||||
|
stop = 1;
|
||||||
if (!XDG) {
|
|
||||||
const std::string USERID = std::to_string(getuid());
|
|
||||||
return "/run/user/" + USERID + "/hypr";
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string(XDG) + "/hypr";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int readLoop(int hypr_socket) {
|
void close_restricted(int fd, [[maybe_unused]] void *user_data) {
|
||||||
constexpr size_t BUFFER_SIZE = 8192;
|
auto file = fdopen(fd, "r+");
|
||||||
std::array<char, BUFFER_SIZE> buffer = {0};
|
std::fclose(file);
|
||||||
long sizeWritten = 0;
|
}
|
||||||
|
|
||||||
while (!sigintReceived) {
|
int open_restricted(const char *path, int flags,
|
||||||
sizeWritten = read(hypr_socket, buffer.data(), BUFFER_SIZE);
|
[[maybe_unused]] void *user_data) {
|
||||||
if (sizeWritten < 0 && errno != EAGAIN) {
|
auto fd = open(path, flags);
|
||||||
if (errno != EINTR)
|
if (fd < 0) {
|
||||||
std::println("Couldn't read (5): {}: {}", strerror(errno), errno);
|
perror(std::format("Cannot open {}", path).c_str());
|
||||||
close(hypr_socket);
|
return -errno;
|
||||||
return 5;
|
}
|
||||||
|
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)
|
if (libinput_udev_assign_seat(li, seat) == -1) {
|
||||||
break;
|
libinput_unref(li);
|
||||||
|
throw std::runtime_error("Cannot assign seat");
|
||||||
if (sizeWritten > 0) {
|
|
||||||
std::println("{}", std::string(buffer.data(), sizeWritten));
|
|
||||||
buffer.fill('\0');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
usleep(100000);
|
|
||||||
}
|
}
|
||||||
close(hypr_socket);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void signalHandler(int signal) {
|
~LibInputListener() {
|
||||||
std::println("Received {}", signal);
|
if (li) {
|
||||||
sigintReceived = true;
|
libinput_unref(li);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
void listen() {
|
||||||
auto runtimeDir = getRuntimeDir();
|
libinput_dispatch(li);
|
||||||
instanceSignature = std::string(std::getenv("HYPRLAND_INSTANCE_SIGNATURE"));
|
struct libinput_event *event;
|
||||||
if (instanceSignature.empty()) {
|
struct pollfd fds;
|
||||||
std::println("Hyprland instance isn't running.");
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
std::string deviceFilter(argv[1]);
|
||||||
|
|
||||||
auto socket_fpath = runtimeDir + "/" + instanceSignature + "/.socket2.sock";
|
struct udev *udev = udev_new();
|
||||||
std::println("Found socket file path: {}", socket_fpath);
|
|
||||||
|
|
||||||
struct sockaddr_un socket_address;
|
signal(SIGINT, sighandler);
|
||||||
socket_address.sun_family = AF_UNIX;
|
signal(SIGTERM, sighandler);
|
||||||
strncpy(socket_address.sun_path, socket_fpath.c_str(),
|
|
||||||
sizeof(socket_address.sun_path) - 1);
|
|
||||||
|
|
||||||
auto hypr_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
try {
|
||||||
if (hypr_socket < 1) {
|
LibInputListener listener(udev, "seat0", &deviceFilter);
|
||||||
std::println("Cannot create socket (1).");
|
listener.listen();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
udev_unref(udev);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
udev_unref(udev);
|
||||||
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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user