Support libinput for keybinds

libinput_only
jackun 4 years ago
parent a82e27d978
commit ce02eec416
No known key found for this signature in database
GPG Key ID: 119DB3F1D05A9ED3

@ -157,8 +157,8 @@ build() {
if [[ ! -f "build/meson64/build.ninja" ]]; then
configure $@
fi
DESTDIR="$PWD/build/release" ninja -C build/meson32 install
DESTDIR="$PWD/build/release" ninja -C build/meson64 install
DESTDIR="$PWD/build/release" ninja -C build/meson32 install
}
package() {

@ -22,6 +22,13 @@ pre_args = [
'-DPACKAGE_VERSION="@0@"'.format(meson.project_version()),
]
sizeof_ptr = cc.sizeof('void*')
if sizeof_ptr == 8
pre_args += '-DMANGOHUD_ARCH="64bit"'
elif sizeof_ptr == 4
pre_args += '-DMANGOHUD_ARCH="32bit"'
endif
# Define DEBUG for debug builds only (debugoptimized is not included on this one)
if get_option('buildtype') == 'debug'
pre_args += '-DDEBUG'
@ -83,17 +90,34 @@ vulkan_wsi_deps = []
if is_unixy
dep_x11 = dependency('x11', required: get_option('with_x11'))
dep_wayland_client = dependency('wayland-client',
required: get_option('with_wayland'), version : '>=1.11')
# Not needed right now
#dep_wayland_client = dependency('wayland-client',
# required: get_option('with_wayland'), version : '>=1.11')
dep_wayland_client = null_dep
dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true)
dep_libdrm = dependency('libdrm', required: get_option('with_libdrm_amdgpu')).partial_dependency(compile_args : true, includes : true)
# dep_libdrm_amdgpu = dependency('libdrm_amdgpu', version : '>=2.4.79', required: get_option('with_libdrm_amdgpu')).partial_dependency(compile_args : true, includes : true)
# Support 32bit only if library is found
cc_input_dep = cc.find_library('libinput', required: false)
if sizeof_ptr == 8 or cc_input_dep.found()
libinput_dep = dependency('libinput', required: get_option('with_wayland')).partial_dependency(compile_args : true, includes : true)
libudev_dep = dependency('libudev', required: get_option('with_wayland')).partial_dependency(compile_args : true, includes : true)
else
libinput_dep = null_dep
libudev_dep = null_dep
endif
if not dep_x11.found() and not cc_input_dep.found()
message('Hotkey support is not enabled!')
endif
else
dep_x11 = null_dep
dep_wayland_client = null_dep
dbus_dep = null_dep
dep_libdrm = null_dep
# dep_libdrm_amdgpu = null_dep
libinput_dep = null_dep
libudev_dep = null_dep
endif
if dep_x11.found()
@ -105,10 +129,6 @@ if dep_wayland_client.found()
vulkan_wsi_deps += dep_wayland_client
endif
if is_unixy and not dep_x11.found() and not dep_wayland_client.found()
error('At least one of "with_x11" and "with_wayland" should be enabled')
endif
inc_common = [
include_directories('include'),
]
@ -218,13 +238,6 @@ util_files = files(
'src/mesa/util/os_time.c',
)
sizeof_ptr = cc.sizeof('void*')
if sizeof_ptr == 8
pre_args += '-DMANGOHUD_ARCH="64bit"'
elif sizeof_ptr == 4
pre_args += '-DMANGOHUD_ARCH="32bit"'
endif
dearimgui_sp = subproject('imgui', default_options: [
'default_library=static',
# use 'auto_features=disabled' once available: https://github.com/mesonbuild/meson/issues/5320

@ -10,7 +10,7 @@ option('include_doc', type : 'boolean', value : true, description: 'Include the
option('with_nvml', type : 'combo', value : 'enabled', choices: ['enabled', 'system', 'disabled'], description: 'Enable NVML support')
option('with_xnvctrl', type : 'feature', value : 'enabled', description: 'Enable XNVCtrl support')
option('with_x11', type : 'feature', value : 'enabled')
option('with_wayland', type : 'feature', value : 'disabled')
option('with_wayland', type : 'feature', value : 'enabled')
option('with_dbus', type : 'feature', value : 'enabled')
option('with_dlsym', type : 'feature', value : 'disabled')
option('with_libdrm_amdgpu', type : 'feature', value : 'enabled', description: 'Get amdgpu sensor info through libdrm_amdgpu')

@ -2,10 +2,77 @@
#include "timing.hpp"
#include "logging.h"
#include "keybinds.h"
#include "keybinds_libinput.h"
Clock::time_point last_f2_press, toggle_fps_limit_press , last_f12_press, reload_cfg_press, last_upload_press;
#ifdef HAVE_X11
bool x11_keys_are_pressed(const std::vector<KeySym>& keys) {
if (!init_x11()) {
return false;
}
char keys_return[32];
size_t pressed = 0;
g_x11->XQueryKeymap(get_xdisplay(), keys_return);
for (KeySym ks : keys) {
KeyCode kc2 = g_x11->XKeysymToKeycode(get_xdisplay(), ks);
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
if (isPressed)
pressed++;
}
if (pressed > 0 && pressed == keys.size()) {
return true;
}
return false;
}
#endif //HAVE_X11
#ifdef _WIN32
#include <windows.h>
bool win32_keys_are_pressed(const std::vector<KeySym>& keys) {
size_t pressed = 0;
for (KeySym ks : keys) {
if (GetAsyncKeyState(ks) & 0x8000)
pressed++;
}
if (pressed > 0 && pressed == keys.size()) {
return true;
}
return false;
}
#endif
bool keys_are_pressed(const std::vector<KeySym>& keys) {
#ifdef HAVE_X11
if (x11_keys_are_pressed(keys))
return true;
#endif
#ifdef HAVE_LIBINPUT
if (libinput_keys_are_pressed(keys))
return true;
#endif
#ifdef _WIN32
if (win32_keys_are_pressed(keys))
return true;
#endif
return false;
}
void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){
using namespace std::chrono_literals;
bool pressed = false; // FIXME just a placeholder until wayland support
auto now = Clock::now(); /* us */
auto elapsedF2 = now - last_f2_press;
auto elapsedFpsLimitToggle = now - toggle_fps_limit_press;
@ -16,12 +83,7 @@ void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& par
auto keyPressDelay = 500ms;
if (elapsedF2 >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.toggle_logging);
#else
pressed = false;
#endif
if (pressed && (now - logger->last_log_end() > 11s)) {
if (keys_are_pressed(params.toggle_logging) && (now - logger->last_log_end() > 11s)) {
last_f2_press = now;
if (logger->is_active()) {
logger->stop_logging();
@ -36,12 +98,7 @@ void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& par
}
if (elapsedFpsLimitToggle >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.toggle_fps_limit);
#else
pressed = false;
#endif
if (pressed){
if (keys_are_pressed(params.toggle_fps_limit)){
toggle_fps_limit_press = now;
for (size_t i = 0; i < params.fps_limit.size(); i++){
uint32_t fps_limit = params.fps_limit[i];
@ -61,47 +118,27 @@ void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& par
}
if (elapsedF12 >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.toggle_hud);
#else
pressed = false;
#endif
if (pressed){
if (keys_are_pressed(params.toggle_hud)){
last_f12_press = now;
params.no_display = !params.no_display;
}
}
if (elapsedReloadCfg >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.reload_cfg);
#else
pressed = false;
#endif
if (pressed){
if (keys_are_pressed(params.reload_cfg)){
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
reload_cfg_press = now;
}
}
if (params.permit_upload && elapsedUpload >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.upload_log);
#else
pressed = false;
#endif
if (pressed){
if (keys_are_pressed(params.upload_log)){
last_upload_press = now;
logger->upload_last_log();
}
}
if (params.permit_upload && elapsedUpload >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.upload_logs);
#else
pressed = false;
#endif
if (pressed){
if (keys_are_pressed(params.upload_logs)){
last_upload_press = now;
logger->upload_last_logs();
}

@ -11,52 +11,8 @@
typedef unsigned long KeySym;
#endif
Clock::time_point last_f2_press, toggle_fps_limit_press , last_f12_press, reload_cfg_press, last_upload_press;
extern Clock::time_point last_f2_press, toggle_fps_limit_press , last_f12_press, reload_cfg_press, last_upload_press;
#ifdef HAVE_X11
bool keys_are_pressed(const std::vector<KeySym>& keys) {
if (!init_x11())
return false;
char keys_return[32];
size_t pressed = 0;
g_x11->XQueryKeymap(get_xdisplay(), keys_return);
for (KeySym ks : keys) {
KeyCode kc2 = g_x11->XKeysymToKeycode(get_xdisplay(), ks);
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
if (isPressed)
pressed++;
}
if (pressed > 0 && pressed == keys.size()) {
return true;
}
return false;
}
#endif //HAVE_X11
#ifdef _WIN32
#include <windows.h>
bool keys_are_pressed(const std::vector<KeySym>& keys) {
size_t pressed = 0;
for (KeySym ks : keys) {
if (GetAsyncKeyState(ks) & 0x8000)
pressed++;
}
if (pressed > 0 && pressed == keys.size()) {
return true;
}
return false;
}
#endif
bool keys_are_pressed(const std::vector<KeySym>& keys);
#endif //MANGOHUD_KEYBINDS_H

@ -0,0 +1,376 @@
#include <thread>
#include <iostream>
#include <functional>
#include <memory>
#include <chrono>
#include <vector>
#include <map>
#include <mutex>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <linux/input-event-codes.h>
#include "loaders/loader_libudev.h"
#include "loaders/loader_libinput.h"
#include "keybinds_libinput.h"
struct key_state {
uint32_t key;
bool pressed;
int modifiers;
};
static std::mutex input_mutex;
static std::vector<std::vector<key_state>> key_states;
static std::unique_ptr<libudev_loader> libudev;
static std::unique_ptr<libinput_loader> libinput;
// FIXME User needs to have read access to devices in /dev/input, i.e. be in "input", "plugdev" or similar group etc.
// This can pose a security issue, i.e. key loggers
static int open_restricted(const char *path, int flags, void *user_data)
{
uint32_t *device_count = static_cast<uint32_t *>(user_data);
#ifndef NDEBUG
std::cerr << __func__ << ": " << path << "\n";
#endif
int fd = open(path, flags);
if (fd < 0)
{
fprintf(stderr, "MANGOHUD: libinput cannot open '%s': ", path);
perror("");
return -errno;
}
if (device_count)
++(*device_count);
return fd;
}
static void close_restricted(int fd, void *user_data)
{
uint32_t *device_count = static_cast<uint32_t *>(user_data);
if (device_count)
--(*device_count);
close(fd);
}
struct input_reader {
bool quit = false;
std::thread thread;
bool thread_started = false;
input_reader()
{
thread_started = true;
thread = std::thread(&input_reader::event_thread, this);
}
~input_reader()
{
quit = true;
if (thread.joinable())
thread.join();
// Clear keybinds so we don't falsely assume that their states are legit
std::lock_guard<std::mutex> lk(input_mutex);
key_states.clear();
}
void event_thread()
{
static struct libinput_interface interface;
interface.open_restricted = open_restricted;
interface.close_restricted = close_restricted;
struct libinput *li;
struct libinput_event *event;
struct udev *udev = libudev->udev_new();
const char* seat = getenv("XDG_SEAT");
if (!seat)
seat = "seat0";
uint32_t device_count = 0;
li = libinput->udev_create_context(&interface, &device_count, udev);
libinput->udev_assign_seat(li, seat);
while (!quit && device_count) {
libinput->dispatch(li);
while ((event = libinput->get_event(li)) != nullptr) {
auto event_type = libinput->event_get_type(event);
/*if (LIBINPUT_EVENT_DEVICE_ADDED == event_type) {
struct libinput_device *dev = libinput->event_get_device(event);
const char *name = libinput->device_get_name(dev);
std::cerr << __func__ << ": new device: " << name << "\n";
}*/
if (LIBINPUT_EVENT_KEYBOARD_KEY == event_type) {
auto kb_ev = libinput->event_get_keyboard_event(event);
uint32_t key = libinput->event_keyboard_get_key(kb_ev);
auto kb_key_state = libinput->event_keyboard_get_key_state(kb_ev);
std::lock_guard<std::mutex> lk(input_mutex);
for (auto& ks : key_states)
{
for (auto& k : ks) {
if (k.key == key)
{
k.pressed = (kb_key_state == LIBINPUT_KEY_STATE_PRESSED);
}
}
}
}
libinput->event_destroy(event);
libinput->dispatch(li);
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
libinput->unref(li);
libudev->udev_unref(udev);
thread_started = false;
std::cerr << "MANGOHUD: libinput thread quit\n";
}
};
static std::unique_ptr<input_reader> libinput_thread;
const std::map<uint16_t, uint8_t> input_map_x11_to_linux = {
{0x20, 0x39}, /* x11:32 (XK_space) -> linux:57 (KEY_SPACE) */
{0x21, 0x2}, /* x11:33 (XK_exclam) -> linux:2 (KEY_1) */
{0x22, 0x28}, /* x11:34 (XK_quotedbl) -> linux:40 (KEY_APOSTROPHE) */
{0x23, 0x4}, /* x11:35 (XK_numbersign) -> linux:4 (KEY_3) */
{0x24, 0x5}, /* x11:36 (XK_dollar) -> linux:5 (KEY_4) */
{0x25, 0x6}, /* x11:37 (XK_percent) -> linux:6 (KEY_5) */
{0x26, 0x8}, /* x11:38 (XK_ampersand) -> linux:8 (KEY_7) */
{0x27, 0x28}, /* x11:39 (XK_quotedbl) -> linux:40 (KEY_APOSTROPHE) */
{0x28, 0xa}, /* x11:40 (XK_parenleft) -> linux:10 (KEY_9) */
{0x29, 0xb}, /* x11:41 (XK_parenright) -> linux:11 (KEY_0) */
{0x2a, 0x9}, /* x11:42 (XK_asterisk) -> linux:9 (KEY_8) */
{0x2b, 0xd}, /* x11:43 (XK_plus) -> linux:13 (KEY_EQUAL) */
{0x2c, 0x33}, /* x11:44 (XK_less) -> linux:51 (KEY_COMMA) */
{0x2d, 0xc}, /* x11:45 (XK_underscore) -> linux:12 (KEY_MINUS) */
{0x2e, 0x34}, /* x11:46 (XK_greater) -> linux:52 (KEY_DOT) */
{0x2f, 0x35}, /* x11:47 (XK_question) -> linux:53 (KEY_SLASH) */
{0x30, 0xb}, /* x11:48 (XK_parenright) -> linux:11 (KEY_0) */
{0x31, 0x2}, /* x11:49 (XK_exclam) -> linux:2 (KEY_1) */
{0x32, 0x3}, /* x11:50 (XK_at) -> linux:3 (KEY_2) */
{0x33, 0x4}, /* x11:51 (XK_numbersign) -> linux:4 (KEY_3) */
{0x34, 0x5}, /* x11:52 (XK_dollar) -> linux:5 (KEY_4) */
{0x35, 0x6}, /* x11:53 (XK_percent) -> linux:6 (KEY_5) */
{0x36, 0x7}, /* x11:54 (XK_asciicircum) -> linux:7 (KEY_6) */
{0x37, 0x8}, /* x11:55 (XK_ampersand) -> linux:8 (KEY_7) */
{0x38, 0x9}, /* x11:56 (XK_asterisk) -> linux:9 (KEY_8) */
{0x39, 0xa}, /* x11:57 (XK_parenleft) -> linux:10 (KEY_9) */
{0x3a, 0x27}, /* x11:58 (XK_colon) -> linux:39 (KEY_SEMICOLON) */
{0x3b, 0x27}, /* x11:59 (XK_colon) -> linux:39 (KEY_SEMICOLON) */
{0x3c, 0x33}, /* x11:60 (XK_less) -> linux:51 (KEY_COMMA) */
{0x3d, 0xd}, /* x11:61 (XK_plus) -> linux:13 (KEY_EQUAL) */
{0x3e, 0x34}, /* x11:62 (XK_greater) -> linux:52 (KEY_DOT) */
{0x3f, 0x35}, /* x11:63 (XK_question) -> linux:53 (KEY_SLASH) */
{0x40, 0x3}, /* x11:64 (XK_at) -> linux:3 (KEY_2) */
{0x41, 0x1e}, /* x11:65 (XK_a) -> linux:30 (KEY_A) */
{0x42, 0x30}, /* x11:66 (XK_b) -> linux:48 (KEY_B) */
{0x43, 0x2e}, /* x11:67 (XK_c) -> linux:46 (KEY_C) */
{0x44, 0x20}, /* x11:68 (XK_d) -> linux:32 (KEY_D) */
{0x45, 0x12}, /* x11:69 (XK_e) -> linux:18 (KEY_E) */
{0x46, 0x21}, /* x11:70 (XK_f) -> linux:33 (KEY_F) */
{0x47, 0x22}, /* x11:71 (XK_g) -> linux:34 (KEY_G) */
{0x48, 0x23}, /* x11:72 (XK_h) -> linux:35 (KEY_H) */
{0x49, 0x17}, /* x11:73 (XK_i) -> linux:23 (KEY_I) */
{0x4a, 0x24}, /* x11:74 (XK_j) -> linux:36 (KEY_J) */
{0x4b, 0x25}, /* x11:75 (XK_k) -> linux:37 (KEY_K) */
{0x4c, 0x26}, /* x11:76 (XK_l) -> linux:38 (KEY_L) */
{0x4d, 0x32}, /* x11:77 (XK_m) -> linux:50 (KEY_M) */
{0x4e, 0x31}, /* x11:78 (XK_n) -> linux:49 (KEY_N) */
{0x4f, 0x18}, /* x11:79 (XK_o) -> linux:24 (KEY_O) */
{0x50, 0x19}, /* x11:80 (XK_p) -> linux:25 (KEY_P) */
{0x51, 0x10}, /* x11:81 (XK_q) -> linux:16 (KEY_Q) */
{0x52, 0x13}, /* x11:82 (XK_r) -> linux:19 (KEY_R) */
{0x53, 0x1f}, /* x11:83 (XK_s) -> linux:31 (KEY_S) */
{0x54, 0x14}, /* x11:84 (XK_t) -> linux:20 (KEY_T) */
{0x55, 0x16}, /* x11:85 (XK_u) -> linux:22 (KEY_U) */
{0x56, 0x2f}, /* x11:86 (XK_v) -> linux:47 (KEY_V) */
{0x57, 0x11}, /* x11:87 (XK_w) -> linux:17 (KEY_W) */
{0x58, 0x2d}, /* x11:88 (XK_x) -> linux:45 (KEY_X) */
{0x59, 0x15}, /* x11:89 (XK_y) -> linux:21 (KEY_Y) */
{0x5a, 0x2c}, /* x11:90 (XK_z) -> linux:44 (KEY_Z) */
{0x5b, 0x1a}, /* x11:91 (XK_braceleft) -> linux:26 (KEY_LEFTBRACE) */
{0x5c, 0x2b}, /* x11:92 (XK_bar) -> linux:43 (KEY_BACKSLASH) */
{0x5d, 0x1b}, /* x11:93 (XK_braceright) -> linux:27 (KEY_RIGHTBRACE) */
{0x5e, 0x7}, /* x11:94 (XK_asciicircum) -> linux:7 (KEY_6) */
{0x5f, 0xc}, /* x11:95 (XK_underscore) -> linux:12 (KEY_MINUS) */
{0x60, 0x29}, /* x11:96 (XK_asciitilde) -> linux:41 (KEY_GRAVE) */
{0x61, 0x1e}, /* x11:97 (XK_a) -> linux:30 (KEY_A) */
{0x62, 0x30}, /* x11:98 (XK_b) -> linux:48 (KEY_B) */
{0x63, 0x2e}, /* x11:99 (XK_c) -> linux:46 (KEY_C) */
{0x64, 0x20}, /* x11:100 (XK_d) -> linux:32 (KEY_D) */
{0x65, 0x12}, /* x11:101 (XK_e) -> linux:18 (KEY_E) */
{0x66, 0x21}, /* x11:102 (XK_f) -> linux:33 (KEY_F) */
{0x67, 0x22}, /* x11:103 (XK_g) -> linux:34 (KEY_G) */
{0x68, 0x23}, /* x11:104 (XK_h) -> linux:35 (KEY_H) */
{0x69, 0x17}, /* x11:105 (XK_i) -> linux:23 (KEY_I) */
{0x6a, 0x24}, /* x11:106 (XK_j) -> linux:36 (KEY_J) */
{0x6b, 0x25}, /* x11:107 (XK_k) -> linux:37 (KEY_K) */
{0x6c, 0x26}, /* x11:108 (XK_l) -> linux:38 (KEY_L) */
{0x6d, 0x32}, /* x11:109 (XK_m) -> linux:50 (KEY_M) */
{0x6e, 0x31}, /* x11:110 (XK_n) -> linux:49 (KEY_N) */
{0x6f, 0x18}, /* x11:111 (XK_o) -> linux:24 (KEY_O) */
{0x70, 0x19}, /* x11:112 (XK_p) -> linux:25 (KEY_P) */
{0x71, 0x10}, /* x11:113 (XK_q) -> linux:16 (KEY_Q) */
{0x72, 0x13}, /* x11:114 (XK_r) -> linux:19 (KEY_R) */
{0x73, 0x1f}, /* x11:115 (XK_s) -> linux:31 (KEY_S) */
{0x74, 0x14}, /* x11:116 (XK_t) -> linux:20 (KEY_T) */
{0x75, 0x16}, /* x11:117 (XK_u) -> linux:22 (KEY_U) */
{0x76, 0x2f}, /* x11:118 (XK_v) -> linux:47 (KEY_V) */
{0x77, 0x11}, /* x11:119 (XK_w) -> linux:17 (KEY_W) */
{0x78, 0x2d}, /* x11:120 (XK_x) -> linux:45 (KEY_X) */
{0x79, 0x15}, /* x11:121 (XK_y) -> linux:21 (KEY_Y) */
{0x7a, 0x2c}, /* x11:122 (XK_z) -> linux:44 (KEY_Z) */
{0x7b, 0x1a}, /* x11:123 (XK_braceleft) -> linux:26 (KEY_LEFTBRACE) */
{0x7c, 0x2b}, /* x11:124 (XK_bar) -> linux:43 (KEY_BACKSLASH) */
{0x7d, 0x1b}, /* x11:125 (XK_braceright) -> linux:27 (KEY_RIGHTBRACE) */
{0x7e, 0x29}, /* x11:126 (XK_asciitilde) -> linux:41 (KEY_GRAVE) */
{0xd7, 0x37}, /* x11:215 (XK_multiply) -> linux:55 (KEY_KPASTERISK) */
{0xff08, 0xe}, /* x11:65288 (XK_BackSpace) -> linux:14 (KEY_BACKSPACE) */
{0xff09, 0xf}, /* x11:65289 (XK_Tab) -> linux:15 (KEY_TAB) */
{0xff0d, 0x1c}, /* x11:65293 (XK_Return) -> linux:28 (KEY_ENTER) */
{0xff13, 0x77}, /* x11:65299 (XK_Pause) -> linux:119 (KEY_PAUSE) */
{0xff14, 0x46}, /* x11:65300 (XK_Scroll_Lock) -> linux:70 (KEY_SCROLLLOCK) */
{0xff15, 0x63}, /* x11:65301 (XK_Sys_Req) -> linux:99 (KEY_SYSRQ) */
{0xff1b, 0x1}, /* x11:65307 (XK_Escape) -> linux:1 (KEY_ESC) */
{0xff50, 0x66}, /* x11:65360 (XK_Home) -> linux:102 (KEY_HOME) */
{0xff51, 0x69}, /* x11:65361 (XK_Left) -> linux:105 (KEY_LEFT) */
{0xff52, 0x67}, /* x11:65362 (XK_Up) -> linux:103 (KEY_UP) */
{0xff53, 0x6a}, /* x11:65363 (XK_Right) -> linux:106 (KEY_RIGHT) */
{0xff54, 0x6c}, /* x11:65364 (XK_Down) -> linux:108 (KEY_DOWN) */
{0xff55, 0x68}, /* x11:65365 (XK_Page_Up) -> linux:104 (KEY_PAGEUP) */
{0xff56, 0x6d}, /* x11:65366 (XK_Page_Down) -> linux:109 (KEY_PAGEDOWN) */
{0xff57, 0x6b}, /* x11:65367 (XK_End) -> linux:107 (KEY_END) */
{0xff60, 0x161}, /* x11:65376 (XK_Select) -> linux:353 (KEY_SELECT) */
{0xff63, 0x6e}, /* x11:65379 (XK_Insert) -> linux:110 (KEY_INSERT) */
{0xff6a, 0x8a}, /* x11:65386 (XK_Help) -> linux:138 (KEY_HELP) */
{0xff7f, 0x45}, /* x11:65407 (XK_Num_Lock) -> linux:69 (KEY_NUMLOCK) */
{0xff8d, 0x60}, /* x11:65421 (XK_KP_Enter) -> linux:96 (KEY_KPENTER) */
{0xffab, 0x4e}, /* x11:65451 (XK_KP_Add) -> linux:78 (KEY_KPPLUS) */
{0xffac, 0x5f}, /* x11:65452 (XK_KP_Separator) -> linux:95 (KEY_KPJPCOMMA) */
{0xffad, 0x4a}, /* x11:65453 (XK_KP_Subtract) -> linux:74 (KEY_KPMINUS) */
{0xffae, 0x53}, /* x11:65454 (XK_KP_Decimal) -> linux:83 (KEY_KPDOT) */
{0xffaf, 0x62}, /* x11:65455 (XK_KP_Divide) -> linux:98 (KEY_KPSLASH) */
{0xffb0, 0x52}, /* x11:65456 (XK_KP_0) -> linux:82 (KEY_KP0) */
{0xffb1, 0x4f}, /* x11:65457 (XK_KP_1) -> linux:79 (KEY_KP1) */
{0xffb2, 0x50}, /* x11:65458 (XK_KP_2) -> linux:80 (KEY_KP2) */
{0xffb3, 0x51}, /* x11:65459 (XK_KP_3) -> linux:81 (KEY_KP3) */
{0xffb4, 0x4b}, /* x11:65460 (XK_KP_4) -> linux:75 (KEY_KP4) */
{0xffb5, 0x4c}, /* x11:65461 (XK_KP_5) -> linux:76 (KEY_KP5) */
{0xffb6, 0x4d}, /* x11:65462 (XK_KP_6) -> linux:77 (KEY_KP6) */
{0xffb7, 0x47}, /* x11:65463 (XK_KP_7) -> linux:71 (KEY_KP7) */
{0xffb8, 0x48}, /* x11:65464 (XK_KP_8) -> linux:72 (KEY_KP8) */
{0xffb9, 0x49}, /* x11:65465 (XK_KP_9) -> linux:73 (KEY_KP9) */
{0xffbd, 0x75}, /* x11:65469 (XK_KP_Equal) -> linux:117 (KEY_KPEQUAL) */
{0xffbe, 0x3b}, /* x11:65470 (XK_F1) -> linux:59 (KEY_F1) */
{0xffbf, 0x3c}, /* x11:65471 (XK_F2) -> linux:60 (KEY_F2) */
{0xffc0, 0x3d}, /* x11:65472 (XK_F3) -> linux:61 (KEY_F3) */
{0xffc1, 0x3e}, /* x11:65473 (XK_F4) -> linux:62 (KEY_F4) */
{0xffc2, 0x3f}, /* x11:65474 (XK_F5) -> linux:63 (KEY_F5) */
{0xffc3, 0x40}, /* x11:65475 (XK_F6) -> linux:64 (KEY_F6) */
{0xffc4, 0x41}, /* x11:65476 (XK_F7) -> linux:65 (KEY_F7) */
{0xffc5, 0x42}, /* x11:65477 (XK_F8) -> linux:66 (KEY_F8) */
{0xffc6, 0x43}, /* x11:65478 (XK_F9) -> linux:67 (KEY_F9) */
{0xffc7, 0x44}, /* x11:65479 (XK_F10) -> linux:68 (KEY_F10) */
{0xffc8, 0x57}, /* x11:65480 (XK_F11) -> linux:87 (KEY_F11) */
{0xffc9, 0x58}, /* x11:65481 (XK_F12) -> linux:88 (KEY_F12) */
{0xffe1, 0x2a}, /* x11:65505 (XK_Shift_L) -> linux:42 (KEY_LEFTSHIFT) */
{0xffe2, 0x36}, /* x11:65506 (XK_Shift_R) -> linux:54 (KEY_RIGHTSHIFT) */
{0xffe3, 0x1d}, /* x11:65507 (XK_Control_L) -> linux:29 (KEY_LEFTCTRL) */
{0xffe4, 0x61}, /* x11:65508 (XK_Control_R) -> linux:97 (KEY_RIGHTCTRL) */
{0xffe5, 0x3a}, /* x11:65509 (XK_Caps_Lock) -> linux:58 (KEY_CAPSLOCK) */
{0xffe7, 0x7d}, /* x11:65511 (XK_Meta_L) -> linux:125 (KEY_LEFTMETA) */
{0xffe8, 0x7e}, /* x11:65512 (XK_Meta_R) -> linux:126 (KEY_RIGHTMETA) */
{0xffe9, 0x38}, /* x11:65513 (XK_Alt_L) -> linux:56 (KEY_LEFTALT) */
{0xffea, 0x64}, /* x11:65514 (XK_Alt_R) -> linux:100 (KEY_RIGHTALT) */
{0xffff, 0x6f}, /* x11:65535 (XK_Delete) -> linux:111 (KEY_DELETE) */
};
std::vector<key_state> get_linux_key(const std::vector<KeySym>& x11_keys)
{
std::vector<key_state> keys;
for (const auto x11_key : x11_keys)
{
auto it = input_map_x11_to_linux.find(x11_key);
if (it != input_map_x11_to_linux.end())
keys.push_back({it->second, false, 0});
else
{
auto f = std::cerr.flags();
std::cerr << "MANGOHUD: cannot remap X11 key to libinput: " << std::hex << x11_key << "\n";
std::cerr.flags(f);
break;
}
}
if (keys.size() == x11_keys.size())
return keys;
return {};
}
void start_input(const overlay_params& params)
{
if (!libudev)
libudev = std::make_unique<libudev_loader>("libudev.so.1");
if (!libudev->IsLoaded())
return;
if (!libinput)
libinput = std::make_unique<libinput_loader>("libinput.so.10");
if (!libinput->IsLoaded())
return;
std::lock_guard<std::mutex> lk(input_mutex);
key_states.clear();
key_states.push_back(get_linux_key(params.toggle_hud));
key_states.push_back(get_linux_key(params.toggle_logging));
key_states.push_back(get_linux_key(params.reload_cfg));
key_states.push_back(get_linux_key(params.upload_log));
key_states.push_back(get_linux_key(params.upload_logs));
key_states.push_back(get_linux_key(params.toggle_fps_limit));
if (libinput_thread && libinput_thread->thread_started)
return;
libinput_thread = std::make_unique<input_reader>();
}
bool libinput_keys_are_pressed(const std::vector<KeySym>& x11_keybinds)
{
if (!libinput->IsLoaded() || !libinput_thread || !libinput_thread->thread_started)
return false;
std::lock_guard<std::mutex> lk(input_mutex);
auto keybinds = get_linux_key(x11_keybinds);
if (keybinds.empty())
return false;
for (const auto& ks : key_states)
{
if (ks.size() != keybinds.size())
continue;
size_t pressed = 0;
for (const auto k : ks)
{
for (const auto keybind : keybinds)
if (k.key == keybind.key && k.pressed)
++pressed;
}
if (pressed > 0 && pressed == x11_keybinds.size())
return true;
}
return false;
}

@ -0,0 +1,5 @@
#pragma once
#include "overlay_params.h"
bool libinput_keys_are_pressed(const std::vector<KeySym>& x11_keybinds);
void start_input(const overlay_params& params);

@ -0,0 +1,162 @@
#include "loader_libinput.h"
// Put these sanity checks here so that they fire at most once
// (to avoid cluttering the build output).
#if !defined(LIBRARY_LOADER_LIBINPUT_H_DLOPEN) && !defined(LIBRARY_LOADER_LIBINPUT_H_DT_NEEDED)
#error neither LIBRARY_LOADER_LIBINPUT_H_DLOPEN nor LIBRARY_LOADER_LIBINPUT_H_DT_NEEDED defined
#endif
#if defined(LIBRARY_LOADER_LIBINPUT_H_DLOPEN) && defined(LIBRARY_LOADER_LIBINPUT_H_DT_NEEDED)
#error both LIBRARY_LOADER_LIBINPUT_H_DLOPEN and LIBRARY_LOADER_LIBINPUT_H_DT_NEEDED defined
#endif
libinput_loader::libinput_loader() : loaded_(false) {
}
libinput_loader::~libinput_loader() {
CleanUp(loaded_);
}
bool libinput_loader::Load(const std::string& library_name) {
if (loaded_) {
return false;
}
#if defined(LIBRARY_LOADER_LIBINPUT_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_)
return false;
udev_create_context =
reinterpret_cast<decltype(this->udev_create_context)>(
dlsym(library_, "libinput_udev_create_context"));
if (!udev_create_context) {
CleanUp(true);
return false;
}
udev_assign_seat =
reinterpret_cast<decltype(this->udev_assign_seat)>(
dlsym(library_, "libinput_udev_assign_seat"));
if (!udev_assign_seat) {
CleanUp(true);
return false;
}
dispatch =
reinterpret_cast<decltype(this->dispatch)>(
dlsym(library_, "libinput_dispatch"));
if (!dispatch) {
CleanUp(true);
return false;
}
get_event =
reinterpret_cast<decltype(this->get_event)>(
dlsym(library_, "libinput_get_event"));
if (!get_event) {
CleanUp(true);
return false;
}
event_get_type =
reinterpret_cast<decltype(this->event_get_type)>(
dlsym(library_, "libinput_event_get_type"));
if (!event_get_type) {
CleanUp(true);
return false;
}
device_get_name =
reinterpret_cast<decltype(this->device_get_name)>(
dlsym(library_, "libinput_device_get_name"));
if (!device_get_name) {
CleanUp(true);
return false;
}
event_get_keyboard_event =
reinterpret_cast<decltype(this->event_get_keyboard_event)>(
dlsym(library_, "libinput_event_get_keyboard_event"));
if (!event_get_keyboard_event) {
CleanUp(true);
return false;
}
event_keyboard_get_key =
reinterpret_cast<decltype(this->event_keyboard_get_key)>(
dlsym(library_, "libinput_event_keyboard_get_key"));
if (!event_keyboard_get_key) {
CleanUp(true);
return false;
}
event_keyboard_get_key_state =
reinterpret_cast<decltype(this->event_keyboard_get_key_state)>(
dlsym(library_, "libinput_event_keyboard_get_key_state"));
if (!event_keyboard_get_key_state) {
CleanUp(true);
return false;
}
event_destroy =
reinterpret_cast<decltype(this->event_destroy)>(
dlsym(library_, "libinput_event_destroy"));
if (!event_destroy) {
CleanUp(true);
return false;
}
unref =
reinterpret_cast<decltype(this->unref)>(
dlsym(library_, "libinput_unref"));
if (!unref) {
CleanUp(true);
return false;
}
#endif
#if defined(LIBRARY_LOADER_LIBINPUT_H_DT_NEEDED)
libinput_udev_create_context = &::libinput_udev_create_context;
libinput_udev_assign_seat = &::libinput_udev_assign_seat;
libinput_dispatch = &::libinput_dispatch;
libinput_get_event = &::libinput_get_event;
libinput_event_get_type = &::libinput_event_get_type;
libinput_device_get_name = &::libinput_device_get_name;
libinput_event_get_keyboard_event = &::libinput_event_get_keyboard_event;
libinput_event_keyboard_get_key = &::libinput_event_keyboard_get_key;
libinput_event_keyboard_get_key_state = &::libinput_event_keyboard_get_key_state;
libinput_event_destroy = &::libinput_event_destroy;
libinput_dispatch = &::libinput_dispatch;
libinput_unref = &::libinput_unref;
#endif
loaded_ = true;
return true;
}
void libinput_loader::CleanUp(bool unload) {
#if defined(LIBRARY_LOADER_LIBINPUT_H_DLOPEN)
if (unload) {
dlclose(library_);
library_ = NULL;
}
#endif
loaded_ = false;
udev_create_context = NULL;
udev_assign_seat = NULL;
dispatch = NULL;
get_event = NULL;
event_get_type = NULL;
device_get_name = NULL;
event_get_keyboard_event = NULL;
event_keyboard_get_key = NULL;
event_keyboard_get_key_state = NULL;
event_destroy = NULL;
dispatch = NULL;
unref = NULL;
}

@ -0,0 +1,50 @@
#ifndef LIBRARY_LOADER_LIBINPUT_H
#define LIBRARY_LOADER_LIBINPUT_H
#include <libudev.h>
#include <libinput.h>
#define LIBRARY_LOADER_LIBINPUT_H_DLOPEN
#include <string>
#include <dlfcn.h>
class libinput_loader {
public:
libinput_loader();
libinput_loader(const std::string& library_name) : libinput_loader() {
Load(library_name);
}
~libinput_loader();
bool Load(const std::string& library_name);
bool IsLoaded() { return loaded_; }
decltype(&::libinput_udev_create_context) udev_create_context;
decltype(&::libinput_udev_assign_seat) udev_assign_seat;
decltype(&::libinput_dispatch) dispatch;
decltype(&::libinput_get_event) get_event;
decltype(&::libinput_event_get_type) event_get_type;
decltype(&::libinput_device_get_name) device_get_name;
decltype(&::libinput_event_get_keyboard_event) event_get_keyboard_event;
decltype(&::libinput_event_keyboard_get_key) event_keyboard_get_key;
decltype(&::libinput_event_keyboard_get_key_state) event_keyboard_get_key_state;
decltype(&::libinput_event_destroy) event_destroy;
decltype(&::libinput_unref) unref;
private:
void CleanUp(bool unload);
#if defined(LIBRARY_LOADER_LIBINPUT_H_DLOPEN)
void* library_;
#endif
bool loaded_;
// Disallow copy constructor and assignment operator.
libinput_loader(const libinput_loader&);
void operator=(const libinput_loader&);
};
#endif // LIBRARY_LOADER_LIBINPUT_H

@ -0,0 +1,70 @@
#include "loader_libudev.h"
// Put these sanity checks here so that they fire at most once
// (to avoid cluttering the build output).
#if !defined(LIBRARY_LOADER_LIBUDEV_H_DLOPEN) && !defined(LIBRARY_LOADER_LIBUDEV_H_DT_NEEDED)
#error neither LIBRARY_LOADER_LIBUDEV_H_DLOPEN nor LIBRARY_LOADER_LIBUDEV_H_DT_NEEDED defined
#endif
#if defined(LIBRARY_LOADER_LIBUDEV_H_DLOPEN) && defined(LIBRARY_LOADER_LIBUDEV_H_DT_NEEDED)
#error both LIBRARY_LOADER_LIBUDEV_H_DLOPEN and LIBRARY_LOADER_LIBUDEV_H_DT_NEEDED defined
#endif
libudev_loader::libudev_loader() : loaded_(false) {
}
libudev_loader::~libudev_loader() {
CleanUp(loaded_);
}
bool libudev_loader::Load(const std::string& library_name) {
if (loaded_) {
return false;
}
#if defined(LIBRARY_LOADER_LIBUDEV_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_)
return false;
udev_new =
reinterpret_cast<decltype(this->udev_new)>(
dlsym(library_, "udev_new"));
if (!udev_new) {
CleanUp(true);
return false;
}
udev_unref =
reinterpret_cast<decltype(this->udev_unref)>(
dlsym(library_, "udev_unref"));
if (!udev_unref) {
CleanUp(true);
return false;
}
#endif
#if defined(LIBRARY_LOADER_LIBUDEV_H_DT_NEEDED)
udev_new = &::udev_new;
udev_unref = &::udev_unref;
#endif
loaded_ = true;
return true;
}
void libudev_loader::CleanUp(bool unload) {
#if defined(LIBRARY_LOADER_LIBUDEV_H_DLOPEN)
if (unload) {
dlclose(library_);
library_ = NULL;
}
#endif
loaded_ = false;
udev_new = NULL;
udev_unref = NULL;
}

@ -0,0 +1,41 @@
#ifndef LIBRARY_LOADER_LIBUDEV_H
#define LIBRARY_LOADER_LIBUDEV_H
#include <libudev.h>
#define LIBRARY_LOADER_LIBUDEV_H_DLOPEN
#include <string>
#include <dlfcn.h>
class libudev_loader {
public:
libudev_loader();
libudev_loader(const std::string& library_name) : libudev_loader() {
Load(library_name);
}
~libudev_loader();
bool Load(const std::string& library_name);
bool IsLoaded() { return loaded_; }
decltype(&::udev_new) udev_new;
decltype(&::udev_unref) udev_unref;
private:
void CleanUp(bool unload);
#if defined(LIBRARY_LOADER_LIBUDEV_H_DLOPEN)
void* library_;
#endif
bool loaded_;
// Disallow copy constructor and assignment operator.
libudev_loader(const libudev_loader&);
void operator=(const libudev_loader&);
};
#endif // LIBRARY_LOADER_LIBUDEV_H

@ -159,6 +159,15 @@ if is_unixy
)
#endif
endif
if libinput_dep.found() and libudev_dep.found()
pre_args += '-DHAVE_LIBINPUT'
vklayer_files += files(
'loaders/loader_libudev.cpp',
'loaders/loader_libinput.cpp',
'keybinds_libinput.cpp',
)
endif
endif
link_args = cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL'])
@ -189,6 +198,8 @@ vklayer_mesa_overlay = shared_library(
spdlog_dep,
dep_libdrm,
#dep_libdrm_amdgpu,
libinput_dep,
libudev_dep,
dbus_dep,
dep_dl,
dep_rt,
@ -200,6 +211,7 @@ vklayer_mesa_overlay = shared_library(
install_dir : libdir_mangohud,
install : true
)
if is_unixy
mangohud_dlsym = shared_library(
'MangoHud_dlsym',

@ -33,6 +33,10 @@
#include "loaders/loader_x11.h"
#endif
#ifdef HAVE_LIBINPUT
#include "keybinds_libinput.h"
#endif
#ifdef HAVE_DBUS
#include "dbus_info.h"
#endif
@ -115,22 +119,26 @@ parse_float(const char *str)
return val;
}
#ifdef HAVE_X11
#if defined(HAVE_X11) || defined(HAVE_LIBINPUT)
static std::vector<KeySym>
parse_string_to_keysym_vec(const char *str)
{
std::vector<KeySym> keys;
if(g_x11->IsLoaded())
{
auto keyStrings = str_tokenize(str);
for (auto& ks : keyStrings) {
trim(ks);
KeySym xk = g_x11->XStringToKeysym(ks.c_str());
if (xk)
keys.push_back(xk);
else
SPDLOG_ERROR("Unrecognized key: '{}'", ks);
}
KeySym xk = 0;
auto keyStrings = str_tokenize(str);
for (auto& ks : keyStrings) {
trim(ks);
if (starts_with(ks, "0x"))
xk = strtol(ks.c_str() + 2, NULL, 16);
#ifdef HAVE_X11
else if(g_x11->IsLoaded())
xk = g_x11->XStringToKeysym(ks.c_str());
#endif
if (xk)
keys.push_back(xk);
else
SPDLOG_ERROR("Unrecognized key: '{}'", ks);
}
return keys;
}
@ -203,7 +211,7 @@ parse_load_color(const char *str)
load_colors.push_back(std::stoi(token, NULL, 16));
}
while (load_colors.size() != 3) {
load_colors.push_back(std::stoi("FFFFFF" , NULL, 16));
load_colors.push_back(0xFFFFFF);
}
return load_colors;
@ -627,13 +635,13 @@ parse_overlay_config(struct overlay_params *params,
#ifdef HAVE_X11
params->toggle_hud = { XK_Shift_R, XK_F12 };
params->toggle_fps_limit = { XK_Shift_L, XK_F1 };
params->toggle_logging = { XK_Shift_L, XK_F2 };
params->reload_cfg = { XK_Shift_L, XK_F4 };
params->upload_log = { XK_Shift_L, XK_F3 };
params->upload_logs = { XK_Control_L, XK_F3 };
#if defined(HAVE_X11) || defined(HAVE_LIBINPUT)
params->toggle_hud = { 0xFFE2, 0xFFC9 }; // XK_Shift_R, XK_F12
params->toggle_fps_limit = { 0xFFE1, 0xFFBE }; // XK_Shift_L, XK_F1
params->toggle_logging = { 0xFFE1, 0xFFBF }; // XK_Shift_L, XK_F2
params->reload_cfg = { 0xFFE1, 0xFFC1 }; // XK_Shift_L, XK_F4
params->upload_log = { 0xFFE1, 0xFFC0 }; // XK_Shift_L, XK_F3
params->upload_logs = { 0xFFE3, 0xFFC0 }; // XK_Control_L, XK_F3
#endif
#ifdef _WIN32
@ -800,6 +808,10 @@ parse_overlay_config(struct overlay_params *params,
// Needs ImGui context but it is null here for OpenGL so just note it and update somewhere else
HUDElements.colors.update = true;
#ifdef HAVE_LIBINPUT
start_input(*params);
#endif
if(!logger) logger = std::make_unique<Logger>(params);
if(params->autostart_log && !logger->is_active())
std::thread(autostart_log, params->autostart_log).detach();

Loading…
Cancel
Save