mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Use linux /dev/input for global hotkeys instead of x11. This also works on wayland on any compositor
This commit is contained in:
@@ -20,6 +20,10 @@ These are the dependencies needed to build GPU Screen Recorder UI:
|
||||
|
||||
* x11 (libx11, libxrandr, libxrender, libxfixes, libxcomposite)
|
||||
* libglvnd (which provides libgl, libglx and libegl)
|
||||
* libevdev
|
||||
* libudev (systemd-libs)
|
||||
* libinput
|
||||
* libxkbcommon
|
||||
|
||||
## Runtime dependencies
|
||||
* [GPU Screen Recorder](https://git.dec05eba.com/gpu-screen-recorder/)
|
||||
|
||||
6
TODO
6
TODO
@@ -39,8 +39,6 @@ Update gsr info and validate saved config when monitors update. The selected mon
|
||||
Have different modes. Overlay, window and side menu. Overlay can be used on x11, window and side menu can be used on both x11 and wayland.
|
||||
Window mode should look similar to overlay.
|
||||
|
||||
Look at /home/dec05eba/git/global-hotkeys for hotkeys.
|
||||
|
||||
Show navigation breadcrumbs for settings and deeper navigation (such as selecting a directory to save videos).
|
||||
|
||||
Add option to hide stream key like a password input.
|
||||
@@ -96,3 +94,7 @@ Try to reproduce this and if it happens try grab cursor and keyboard instead of
|
||||
Run `systemctl status --user gpu-screen-recorder` when starting recording and give a notification warning if it returns 0 (running). Or run pidof gpu-screen-recorder.
|
||||
|
||||
Add option to select which gpu to record with, or list all monitors and automatically use the gpu associated with the monitor. Do the same in gtk application.
|
||||
|
||||
Remove all dependencies from tools/gsr-global-hotkeys and roll our own keyboard events code.
|
||||
|
||||
Test global hotkeys with azerty instead of qwerty.
|
||||
@@ -19,9 +19,10 @@ namespace gsr {
|
||||
GlobalHotkeys& operator=(const GlobalHotkeys&) = delete;
|
||||
virtual ~GlobalHotkeys() = default;
|
||||
|
||||
virtual bool bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) = 0;
|
||||
virtual void unbind_key_press(const std::string &id) = 0;
|
||||
virtual void unbind_all_keys() = 0;
|
||||
virtual bool bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) { (void)hotkey; (void)id; (void)callback; return false; }
|
||||
virtual void unbind_key_press(const std::string &id) { (void)id; }
|
||||
virtual void unbind_all_keys() {}
|
||||
virtual bool bind_action(const std::string &id, GlobalHotkeyCallback callback) { (void)id; (void)callback; return false; };
|
||||
virtual void poll_events() = 0;
|
||||
};
|
||||
}
|
||||
24
include/GlobalHotkeysLinux.hpp
Normal file
24
include/GlobalHotkeysLinux.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "GlobalHotkeys.hpp"
|
||||
#include <unordered_map>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace gsr {
|
||||
class GlobalHotkeysLinux : public GlobalHotkeys {
|
||||
public:
|
||||
GlobalHotkeysLinux();
|
||||
GlobalHotkeysLinux(const GlobalHotkeysLinux&) = delete;
|
||||
GlobalHotkeysLinux& operator=(const GlobalHotkeysLinux&) = delete;
|
||||
~GlobalHotkeysLinux() override;
|
||||
|
||||
bool start();
|
||||
bool bind_action(const std::string &id, GlobalHotkeyCallback callback) override;
|
||||
void poll_events() override;
|
||||
private:
|
||||
pid_t process_id = 0;
|
||||
int pipes[2];
|
||||
FILE *read_file = nullptr;
|
||||
std::unordered_map<std::string, GlobalHotkeyCallback> bound_actions_by_id;
|
||||
};
|
||||
}
|
||||
17
meson.build
17
meson.build
@@ -35,6 +35,7 @@ src = [
|
||||
'src/Process.cpp',
|
||||
'src/Overlay.cpp',
|
||||
'src/GlobalHotkeysX11.cpp',
|
||||
'src/GlobalHotkeysLinux.cpp',
|
||||
'src/main.cpp',
|
||||
]
|
||||
|
||||
@@ -65,10 +66,26 @@ executable(
|
||||
dependencies : [dependency('x11')],
|
||||
)
|
||||
|
||||
executable(
|
||||
'gsr-global-hotkeys',
|
||||
['tools/gsr-global-hotkeys/main.c'],
|
||||
install : true,
|
||||
dependencies : [
|
||||
dependency('libevdev'),
|
||||
dependency('libudev'),
|
||||
dependency('libinput'),
|
||||
dependency('xkbcommon')
|
||||
],
|
||||
)
|
||||
|
||||
install_subdir('images', install_dir : gsr_ui_resources_path)
|
||||
install_subdir('fonts', install_dir : gsr_ui_resources_path)
|
||||
install_subdir('scripts', install_dir : gsr_ui_resources_path, install_mode : 'rwxr-xr-x')
|
||||
|
||||
if get_option('systemd') == true
|
||||
install_data(files('extra/gpu-screen-recorder-ui.service'), install_dir : 'lib/systemd/user')
|
||||
endif
|
||||
|
||||
if get_option('capabilities') == true
|
||||
meson.add_install_script('meson_post_install.sh')
|
||||
endif
|
||||
@@ -1 +1,2 @@
|
||||
option('systemd', type : 'boolean', value : true, description : 'Install systemd service file')
|
||||
option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability on gsr-global-hotkeys binary to allow global hotkeys')
|
||||
4
meson_post_install.sh
Executable file
4
meson_post_install.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/sbin/setcap cap_sys_admin+ep ${MESON_INSTALL_DESTDIR_PREFIX}/bin/gsr-global-hotkeys \
|
||||
|| echo "\n!!! Please re-run install as root\n"
|
||||
@@ -8,7 +8,7 @@ platforms = ["posix"]
|
||||
version = "c++17"
|
||||
|
||||
[config]
|
||||
ignore_dirs = ["build", "gsr-window-name"]
|
||||
ignore_dirs = ["build", "tools"]
|
||||
|
||||
[dependencies]
|
||||
xcomposite = ">=0"
|
||||
108
src/GlobalHotkeysLinux.cpp
Normal file
108
src/GlobalHotkeysLinux.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "../include/GlobalHotkeysLinux.hpp"
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PIPE_READ 0
|
||||
#define PIPE_WRITE 1
|
||||
|
||||
namespace gsr {
|
||||
GlobalHotkeysLinux::GlobalHotkeysLinux() {
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
pipes[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalHotkeysLinux::~GlobalHotkeysLinux() {
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
if(pipes[i] > 0)
|
||||
close(pipes[i]);
|
||||
}
|
||||
|
||||
if(read_file)
|
||||
fclose(read_file);
|
||||
|
||||
if(process_id > 0) {
|
||||
kill(process_id, SIGKILL);
|
||||
int status;
|
||||
waitpid(process_id, &status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalHotkeysLinux::start() {
|
||||
if(process_id > 0)
|
||||
return false;
|
||||
|
||||
if(pipe(pipes) == -1)
|
||||
return false;
|
||||
|
||||
const pid_t pid = vfork();
|
||||
if(pid == -1) {
|
||||
perror("Failed to vfork");
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
close(pipes[i]);
|
||||
pipes[i] = -1;
|
||||
}
|
||||
return false;
|
||||
} else if(pid == 0) { /* child */
|
||||
dup2(pipes[PIPE_WRITE], STDOUT_FILENO);
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
close(pipes[i]);
|
||||
}
|
||||
|
||||
const char *args[] = { "gsr-global-hotkeys", NULL };
|
||||
execvp(args[0], (char* const*)args);
|
||||
perror("execvp");
|
||||
_exit(127);
|
||||
} else { /* parent */
|
||||
process_id = pid;
|
||||
close(pipes[PIPE_WRITE]);
|
||||
pipes[PIPE_WRITE] = -1;
|
||||
|
||||
const int fdl = fcntl(pipes[PIPE_READ], F_GETFL);
|
||||
fcntl(pipes[PIPE_READ], F_SETFL, fdl | O_NONBLOCK);
|
||||
|
||||
read_file = fdopen(pipes[PIPE_READ], "r");
|
||||
if(read_file)
|
||||
pipes[PIPE_READ] = -1;
|
||||
else
|
||||
fprintf(stderr, "fdopen failed, error: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GlobalHotkeysLinux::bind_action(const std::string &id, GlobalHotkeyCallback callback) {
|
||||
return bound_actions_by_id.insert(std::make_pair(id, callback)).second;
|
||||
}
|
||||
|
||||
void GlobalHotkeysLinux::poll_events() {
|
||||
if(process_id <= 0) {
|
||||
fprintf(stderr, "error: GlobalHotkeysLinux::poll_events failed, process has not been started yet. Use GlobalHotkeysLinux::start to start the process first\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!read_file) {
|
||||
fprintf(stderr, "error: GlobalHotkeysLinux::poll_events failed, read file hasn't opened\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
char *line = fgets(buffer, sizeof(buffer), read_file);
|
||||
if(!line)
|
||||
return;
|
||||
|
||||
const int line_len = strlen(line);
|
||||
if(line_len == 0)
|
||||
return;
|
||||
|
||||
if(line[line_len - 1] == '\n')
|
||||
line[line_len - 1] = '\0';
|
||||
|
||||
const std::string action = line;
|
||||
auto it = bound_actions_by_id.find(action);
|
||||
if(it != bound_actions_by_id.end())
|
||||
it->second(action);
|
||||
}
|
||||
}
|
||||
49
src/main.cpp
49
src/main.cpp
@@ -3,6 +3,7 @@
|
||||
#include "../include/window_texture.h"
|
||||
#include "../include/Overlay.hpp"
|
||||
#include "../include/GlobalHotkeysX11.hpp"
|
||||
#include "../include/GlobalHotkeysLinux.hpp"
|
||||
#include "../include/gui/Utils.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
@@ -98,33 +99,67 @@ int main(void) {
|
||||
auto overlay = std::make_unique<gsr::Overlay>(resources_path, gsr_info, egl_funcs);
|
||||
//overlay.show();
|
||||
|
||||
gsr::GlobalHotkeysX11 global_hotkeys;
|
||||
const bool show_hotkey_registered = global_hotkeys.bind_key_press({ XK_z, Mod1Mask }, "show/hide", [&](const std::string &id) {
|
||||
// gsr::GlobalHotkeysX11 global_hotkeys;
|
||||
// const bool show_hotkey_registered = global_hotkeys.bind_key_press({ XK_z, Mod1Mask }, "show_hide", [&](const std::string &id) {
|
||||
// fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
// overlay->toggle_show();
|
||||
// });
|
||||
|
||||
// const bool record_hotkey_registered = global_hotkeys.bind_key_press({ XK_F9, Mod1Mask }, "record", [&](const std::string &id) {
|
||||
// fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
// overlay->toggle_record();
|
||||
// });
|
||||
|
||||
// const bool pause_hotkey_registered = global_hotkeys.bind_key_press({ XK_F7, Mod1Mask }, "pause", [&](const std::string &id) {
|
||||
// fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
// overlay->toggle_pause();
|
||||
// });
|
||||
|
||||
// const bool stream_hotkey_registered = global_hotkeys.bind_key_press({ XK_F8, Mod1Mask }, "stream", [&](const std::string &id) {
|
||||
// fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
// overlay->toggle_stream();
|
||||
// });
|
||||
|
||||
// const bool replay_hotkey_registered = global_hotkeys.bind_key_press({ XK_F10, ShiftMask | Mod1Mask }, "replay start", [&](const std::string &id) {
|
||||
// fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
// overlay->toggle_replay();
|
||||
// });
|
||||
|
||||
// const bool replay_save_hotkey_registered = global_hotkeys.bind_key_press({ XK_F10, Mod1Mask }, "replay save", [&](const std::string &id) {
|
||||
// fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
// overlay->save_replay();
|
||||
// });
|
||||
|
||||
gsr::GlobalHotkeysLinux global_hotkeys;
|
||||
if(!global_hotkeys.start())
|
||||
fprintf(stderr, "error: failed to start global hotkeys\n");
|
||||
|
||||
const bool show_hotkey_registered = global_hotkeys.bind_action("show_hide", [&](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->toggle_show();
|
||||
});
|
||||
|
||||
const bool record_hotkey_registered = global_hotkeys.bind_key_press({ XK_F9, Mod1Mask }, "record", [&](const std::string &id) {
|
||||
const bool record_hotkey_registered = global_hotkeys.bind_action("record", [&](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->toggle_record();
|
||||
});
|
||||
|
||||
const bool pause_hotkey_registered = global_hotkeys.bind_key_press({ XK_F7, Mod1Mask }, "pause", [&](const std::string &id) {
|
||||
const bool pause_hotkey_registered = global_hotkeys.bind_action("pause", [&](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->toggle_pause();
|
||||
});
|
||||
|
||||
const bool stream_hotkey_registered = global_hotkeys.bind_key_press({ XK_F8, Mod1Mask }, "stream", [&](const std::string &id) {
|
||||
const bool stream_hotkey_registered = global_hotkeys.bind_action("stream", [&](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->toggle_stream();
|
||||
});
|
||||
|
||||
const bool replay_hotkey_registered = global_hotkeys.bind_key_press({ XK_F10, ShiftMask | Mod1Mask }, "replay start", [&](const std::string &id) {
|
||||
const bool replay_hotkey_registered = global_hotkeys.bind_action("replay_start", [&](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->toggle_replay();
|
||||
});
|
||||
|
||||
const bool replay_save_hotkey_registered = global_hotkeys.bind_key_press({ XK_F10, Mod1Mask }, "replay save", [&](const std::string &id) {
|
||||
const bool replay_save_hotkey_registered = global_hotkeys.bind_action("replay_save", [&](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->save_replay();
|
||||
});
|
||||
|
||||
237
tools/gsr-global-hotkeys/main.c
Normal file
237
tools/gsr-global-hotkeys/main.c
Normal file
@@ -0,0 +1,237 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <libudev.h>
|
||||
#include <libinput.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
typedef struct {
|
||||
struct xkb_context *xkb_context;
|
||||
struct xkb_keymap *xkb_keymap;
|
||||
struct xkb_state *xkb_state;
|
||||
} key_mapper;
|
||||
|
||||
typedef enum {
|
||||
MODKEY_ALT = 1 << 0,
|
||||
MODKEY_SUPER = 1 << 1,
|
||||
MODKEY_CTRL = 1 << 2,
|
||||
MODKEY_SHIFT = 1 << 3
|
||||
} modkeys;
|
||||
|
||||
typedef struct {
|
||||
uint32_t key;
|
||||
uint32_t modifiers; /* modkeys */
|
||||
const char *action;
|
||||
} global_hotkey;
|
||||
|
||||
#define NUM_GLOBAL_HOTKEYS 6
|
||||
static global_hotkey global_hotkeys[NUM_GLOBAL_HOTKEYS] = {
|
||||
{ .key = XKB_KEY_z, .modifiers = MODKEY_ALT, .action = "show_hide" },
|
||||
{ .key = XKB_KEY_F9, .modifiers = MODKEY_ALT, .action = "record" },
|
||||
{ .key = XKB_KEY_F7, .modifiers = MODKEY_ALT, .action = "pause" },
|
||||
{ .key = XKB_KEY_F8, .modifiers = MODKEY_ALT, .action = "stream" },
|
||||
{ .key = XKB_KEY_F10, .modifiers = MODKEY_ALT | MODKEY_SHIFT, .action = "replay_start" },
|
||||
{ .key = XKB_KEY_F10, .modifiers = MODKEY_ALT, .action = "replay_save" }
|
||||
};
|
||||
|
||||
static int open_restricted(const char *path, int flags, void *user_data) {
|
||||
(void)user_data;
|
||||
int fd = open(path, flags);
|
||||
if(fd < 0)
|
||||
fprintf(stderr, "Failed to open %s, error: %s\n", path, strerror(errno));
|
||||
return fd < 0 ? -errno : fd;
|
||||
}
|
||||
|
||||
static void close_restricted(int fd, void *user_data) {
|
||||
(void)user_data;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static const struct libinput_interface interface = {
|
||||
.open_restricted = open_restricted,
|
||||
.close_restricted = close_restricted,
|
||||
};
|
||||
|
||||
static bool is_mod_key(xkb_keycode_t xkb_key_code) {
|
||||
return xkb_key_code >= XKB_KEY_Shift_L && xkb_key_code <= XKB_KEY_Hyper_R;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *modname;
|
||||
modkeys key;
|
||||
} modname_to_modkey_map;
|
||||
|
||||
static uint32_t xkb_state_to_modifiers(struct xkb_state *xkb_state) {
|
||||
const modname_to_modkey_map modifier_keys[] = {
|
||||
{ .modname = XKB_MOD_NAME_ALT, .key = MODKEY_ALT },
|
||||
{ .modname = XKB_MOD_NAME_LOGO, .key = MODKEY_SUPER },
|
||||
{ .modname = XKB_MOD_NAME_SHIFT, .key = MODKEY_SHIFT },
|
||||
{ .modname = XKB_MOD_NAME_CTRL, .key = MODKEY_CTRL }
|
||||
};
|
||||
|
||||
uint32_t modifiers = 0;
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
if(xkb_state_mod_name_is_active(xkb_state, modifier_keys[i].modname, XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
modifiers |= modifier_keys[i].key;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
#define KEY_CODE_EV_TO_XKB(key) ((key) + 8)
|
||||
|
||||
static int print_key_event(struct libinput_event *event, key_mapper *mapper) {
|
||||
struct libinput_event_keyboard *keyboard = libinput_event_get_keyboard_event(event);
|
||||
const uint32_t key_code = libinput_event_keyboard_get_key(keyboard);
|
||||
enum libinput_key_state state_code = libinput_event_keyboard_get_key_state(keyboard);
|
||||
|
||||
const xkb_keycode_t xkb_key_code = KEY_CODE_EV_TO_XKB(key_code);
|
||||
xkb_state_update_key(mapper->xkb_state, xkb_key_code, state_code == LIBINPUT_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||
xkb_keysym_t xkb_key_sym = xkb_state_key_get_one_sym(mapper->xkb_state, xkb_key_code);
|
||||
// char main_key[128];
|
||||
// main_key[0] = '\0';
|
||||
|
||||
// if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
// strcat(main_key, "Super+");
|
||||
// if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
// strcat(main_key, "Ctrl+");
|
||||
// if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0 && strcmp(main_key, "Meta") != 0)
|
||||
// strcat(main_key, "Alt+");
|
||||
// if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
// strcat(main_key, "Shift+");
|
||||
|
||||
// if(!is_mod_key(xkb_key_sym)) {
|
||||
// char reg_key[64];
|
||||
// reg_key[0] = '\0';
|
||||
// xkb_keysym_get_name(xkb_key_sym, reg_key, sizeof(reg_key));
|
||||
// strcat(main_key, reg_key);
|
||||
// }
|
||||
|
||||
if(state_code != LIBINPUT_KEY_STATE_PRESSED)
|
||||
return 0;
|
||||
|
||||
const uint32_t current_modifiers = xkb_state_to_modifiers(mapper->xkb_state);
|
||||
for(int i = 0; i < NUM_GLOBAL_HOTKEYS; ++i) {
|
||||
if(xkb_key_sym == global_hotkeys[i].key && current_modifiers == global_hotkeys[i].modifiers) {
|
||||
puts(global_hotkeys[i].action);
|
||||
fflush(stdout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_events(struct libinput *libinput, key_mapper *mapper) {
|
||||
int result = -1;
|
||||
struct libinput_event *event;
|
||||
|
||||
if(libinput_dispatch(libinput) < 0)
|
||||
return result;
|
||||
|
||||
while((event = libinput_get_event(libinput)) != NULL) {
|
||||
if(libinput_event_get_type(event) == LIBINPUT_EVENT_KEYBOARD_KEY)
|
||||
print_key_event(event, mapper);
|
||||
|
||||
libinput_event_destroy(event);
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int run_mainloop(struct libinput *libinput, key_mapper *mapper) {
|
||||
struct pollfd fd;
|
||||
fd.fd = libinput_get_fd(libinput);
|
||||
fd.events = POLLIN;
|
||||
fd.revents = 0;
|
||||
|
||||
if(handle_events(libinput, mapper) != 0) {
|
||||
fprintf(stderr, "error: didn't receive device added events. Is this program not running as root?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while(poll(&fd, 1, -1) >= 0) {
|
||||
handle_events(libinput, mapper);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mapper_refresh_keymap(key_mapper *mapper) {
|
||||
if(mapper->xkb_keymap != NULL) {
|
||||
xkb_keymap_unref(mapper->xkb_keymap);
|
||||
mapper->xkb_keymap = NULL;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
struct xkb_rule_names names = {
|
||||
NULL, NULL,
|
||||
NULL,//keymap_is_default(mapper->layout) ? NULL : mapper->layout,
|
||||
NULL,//keymap_is_default(mapper->variant) ? NULL : mapper->variant,
|
||||
NULL
|
||||
};
|
||||
mapper->xkb_keymap = xkb_keymap_new_from_names(mapper->xkb_context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if(mapper->xkb_keymap == NULL) {
|
||||
fprintf(stderr, "Failed to create XKB keymap.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mapper->xkb_state != NULL) {
|
||||
xkb_state_unref(mapper->xkb_state);
|
||||
mapper->xkb_state = NULL;
|
||||
}
|
||||
|
||||
mapper->xkb_state = xkb_state_new(mapper->xkb_keymap);
|
||||
if(mapper->xkb_state == NULL) {
|
||||
fprintf(stderr, "Failed to create XKB state.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct udev *udev = udev_new();
|
||||
if(!udev) {
|
||||
fprintf(stderr, "error: udev_new failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct libinput *libinput = libinput_udev_create_context(&interface, NULL, udev);
|
||||
if(!libinput) {
|
||||
fprintf(stderr, "error: libinput_udev_create_context failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(libinput_udev_assign_seat(libinput, "seat0") != 0) {
|
||||
fprintf(stderr, "error: libinput_udev_assign_seat with seat0 failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
key_mapper mapper;
|
||||
mapper.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if(!mapper.xkb_context) {
|
||||
fprintf(stderr, "error: xkb_context_new failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!mapper_refresh_keymap(&mapper)) {
|
||||
fprintf(stderr, "error: key mapper failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(run_mainloop(libinput, &mapper) < 0) {
|
||||
fprintf(stderr, "error: failed to start main loop\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
libinput_unref(libinput);
|
||||
udev_unref(udev);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user