From d08ea692771caa8e385412c2f992089672773e30 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 3 May 2025 12:03:43 +0200 Subject: Keep keyboard led when turning on global hotkeys, move files --- include/CursorTracker.hpp | 23 - include/CursorTracker/CursorTracker.hpp | 23 + include/CursorTracker/CursorTrackerWayland.hpp | 43 ++ include/CursorTracker/CursorTrackerX11.hpp | 20 + include/CursorTrackerWayland.hpp | 43 -- include/CursorTrackerX11.hpp | 20 - include/GlobalHotkeys.hpp | 45 -- include/GlobalHotkeys/GlobalHotkeys.hpp | 45 ++ include/GlobalHotkeys/GlobalHotkeysJoystick.hpp | 70 +++ include/GlobalHotkeys/GlobalHotkeysLinux.hpp | 34 ++ include/GlobalHotkeys/GlobalHotkeysX11.hpp | 34 ++ include/GlobalHotkeysJoystick.hpp | 70 --- include/GlobalHotkeysLinux.hpp | 34 -- include/GlobalHotkeysX11.hpp | 34 -- include/Overlay.hpp | 4 +- meson.build | 10 +- src/Config.cpp | 2 +- src/CursorTracker/CursorTrackerWayland.cpp | 564 ++++++++++++++++++++++++ src/CursorTracker/CursorTrackerX11.cpp | 29 ++ src/CursorTrackerWayland.cpp | 564 ------------------------ src/CursorTrackerX11.cpp | 29 -- src/GlobalHotkeys/GlobalHotkeysJoystick.cpp | 317 +++++++++++++ src/GlobalHotkeys/GlobalHotkeysLinux.cpp | 275 ++++++++++++ src/GlobalHotkeys/GlobalHotkeysX11.cpp | 201 +++++++++ src/GlobalHotkeysJoystick.cpp | 317 ------------- src/GlobalHotkeysLinux.cpp | 275 ------------ src/GlobalHotkeysX11.cpp | 201 --------- src/Overlay.cpp | 19 +- src/gui/GlobalSettingsPage.cpp | 1 - tools/gsr-global-hotkeys/keyboard_event.c | 20 +- 30 files changed, 1685 insertions(+), 1681 deletions(-) delete mode 100644 include/CursorTracker.hpp create mode 100644 include/CursorTracker/CursorTracker.hpp create mode 100644 include/CursorTracker/CursorTrackerWayland.hpp create mode 100644 include/CursorTracker/CursorTrackerX11.hpp delete mode 100644 include/CursorTrackerWayland.hpp delete mode 100644 include/CursorTrackerX11.hpp delete mode 100644 include/GlobalHotkeys.hpp create mode 100644 include/GlobalHotkeys/GlobalHotkeys.hpp create mode 100644 include/GlobalHotkeys/GlobalHotkeysJoystick.hpp create mode 100644 include/GlobalHotkeys/GlobalHotkeysLinux.hpp create mode 100644 include/GlobalHotkeys/GlobalHotkeysX11.hpp delete mode 100644 include/GlobalHotkeysJoystick.hpp delete mode 100644 include/GlobalHotkeysLinux.hpp delete mode 100644 include/GlobalHotkeysX11.hpp create mode 100644 src/CursorTracker/CursorTrackerWayland.cpp create mode 100644 src/CursorTracker/CursorTrackerX11.cpp delete mode 100644 src/CursorTrackerWayland.cpp delete mode 100644 src/CursorTrackerX11.cpp create mode 100644 src/GlobalHotkeys/GlobalHotkeysJoystick.cpp create mode 100644 src/GlobalHotkeys/GlobalHotkeysLinux.cpp create mode 100644 src/GlobalHotkeys/GlobalHotkeysX11.cpp delete mode 100644 src/GlobalHotkeysJoystick.cpp delete mode 100644 src/GlobalHotkeysLinux.cpp delete mode 100644 src/GlobalHotkeysX11.cpp diff --git a/include/CursorTracker.hpp b/include/CursorTracker.hpp deleted file mode 100644 index ff7374f..0000000 --- a/include/CursorTracker.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace gsr { - struct CursorInfo { - mgl::vec2i position; - std::string monitor_name; - }; - - class CursorTracker { - public: - CursorTracker() = default; - CursorTracker(const CursorTracker&) = delete; - CursorTracker& operator=(const CursorTracker&) = delete; - virtual ~CursorTracker() = default; - - virtual void update() = 0; - virtual std::optional get_latest_cursor_info() = 0; - }; -} \ No newline at end of file diff --git a/include/CursorTracker/CursorTracker.hpp b/include/CursorTracker/CursorTracker.hpp new file mode 100644 index 0000000..ff7374f --- /dev/null +++ b/include/CursorTracker/CursorTracker.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace gsr { + struct CursorInfo { + mgl::vec2i position; + std::string monitor_name; + }; + + class CursorTracker { + public: + CursorTracker() = default; + CursorTracker(const CursorTracker&) = delete; + CursorTracker& operator=(const CursorTracker&) = delete; + virtual ~CursorTracker() = default; + + virtual void update() = 0; + virtual std::optional get_latest_cursor_info() = 0; + }; +} \ No newline at end of file diff --git a/include/CursorTracker/CursorTrackerWayland.hpp b/include/CursorTracker/CursorTrackerWayland.hpp new file mode 100644 index 0000000..1eeee83 --- /dev/null +++ b/include/CursorTracker/CursorTrackerWayland.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "CursorTracker.hpp" +#include +#include + +struct wl_display; +struct wl_registry; +struct wl_output; +struct zxdg_output_manager_v1; +struct zxdg_output_v1; + +namespace gsr { + struct WaylandOutput { + uint32_t wl_name; + struct wl_output *output; + struct zxdg_output_v1 *xdg_output; + mgl::vec2i pos; + mgl::vec2i size; + int32_t transform; + std::string name; + }; + + class CursorTrackerWayland : public CursorTracker { + public: + CursorTrackerWayland(const char *card_path); + CursorTrackerWayland(const CursorTrackerWayland&) = delete; + CursorTrackerWayland& operator=(const CursorTrackerWayland&) = delete; + ~CursorTrackerWayland(); + + void update() override; + std::optional get_latest_cursor_info() override; + + std::vector monitors; + struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; + private: + void set_monitor_outputs_from_xdg_output(struct wl_display *dpy); + private: + int drm_fd = -1; + mgl::vec2i latest_cursor_position; // Position of the cursor within the monitor + int latest_crtc_id = -1; + }; +} \ No newline at end of file diff --git a/include/CursorTracker/CursorTrackerX11.hpp b/include/CursorTracker/CursorTrackerX11.hpp new file mode 100644 index 0000000..66618c4 --- /dev/null +++ b/include/CursorTracker/CursorTrackerX11.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "CursorTracker.hpp" + +typedef struct _XDisplay Display; + +namespace gsr { + class CursorTrackerX11 : public CursorTracker { + public: + CursorTrackerX11(Display *dpy); + CursorTrackerX11(const CursorTrackerX11&) = delete; + CursorTrackerX11& operator=(const CursorTrackerX11&) = delete; + ~CursorTrackerX11() = default; + + void update() override {} + std::optional get_latest_cursor_info() override; + private: + Display *dpy = nullptr; + }; +} \ No newline at end of file diff --git a/include/CursorTrackerWayland.hpp b/include/CursorTrackerWayland.hpp deleted file mode 100644 index 1eeee83..0000000 --- a/include/CursorTrackerWayland.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "CursorTracker.hpp" -#include -#include - -struct wl_display; -struct wl_registry; -struct wl_output; -struct zxdg_output_manager_v1; -struct zxdg_output_v1; - -namespace gsr { - struct WaylandOutput { - uint32_t wl_name; - struct wl_output *output; - struct zxdg_output_v1 *xdg_output; - mgl::vec2i pos; - mgl::vec2i size; - int32_t transform; - std::string name; - }; - - class CursorTrackerWayland : public CursorTracker { - public: - CursorTrackerWayland(const char *card_path); - CursorTrackerWayland(const CursorTrackerWayland&) = delete; - CursorTrackerWayland& operator=(const CursorTrackerWayland&) = delete; - ~CursorTrackerWayland(); - - void update() override; - std::optional get_latest_cursor_info() override; - - std::vector monitors; - struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; - private: - void set_monitor_outputs_from_xdg_output(struct wl_display *dpy); - private: - int drm_fd = -1; - mgl::vec2i latest_cursor_position; // Position of the cursor within the monitor - int latest_crtc_id = -1; - }; -} \ No newline at end of file diff --git a/include/CursorTrackerX11.hpp b/include/CursorTrackerX11.hpp deleted file mode 100644 index 66618c4..0000000 --- a/include/CursorTrackerX11.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "CursorTracker.hpp" - -typedef struct _XDisplay Display; - -namespace gsr { - class CursorTrackerX11 : public CursorTracker { - public: - CursorTrackerX11(Display *dpy); - CursorTrackerX11(const CursorTrackerX11&) = delete; - CursorTrackerX11& operator=(const CursorTrackerX11&) = delete; - ~CursorTrackerX11() = default; - - void update() override {} - std::optional get_latest_cursor_info() override; - private: - Display *dpy = nullptr; - }; -} \ No newline at end of file diff --git a/include/GlobalHotkeys.hpp b/include/GlobalHotkeys.hpp deleted file mode 100644 index 2927fa7..0000000 --- a/include/GlobalHotkeys.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mgl { - class Event; -} - -namespace gsr { - enum HotkeyModifier : uint32_t { - HOTKEY_MOD_LSHIFT = 1 << 0, - HOTKEY_MOD_RSHIFT = 1 << 1, - HOTKEY_MOD_LCTRL = 1 << 2, - HOTKEY_MOD_RCTRL = 1 << 3, - HOTKEY_MOD_LALT = 1 << 4, - HOTKEY_MOD_RALT = 1 << 5, - HOTKEY_MOD_LSUPER = 1 << 6, - HOTKEY_MOD_RSUPER = 1 << 7 - }; - - struct Hotkey { - uint32_t key = 0; // X11 keysym - uint32_t modifiers = 0; // HotkeyModifier - }; - - using GlobalHotkeyCallback = std::function; - - class GlobalHotkeys { - public: - GlobalHotkeys() = default; - GlobalHotkeys(const GlobalHotkeys&) = delete; - GlobalHotkeys& operator=(const GlobalHotkeys&) = delete; - virtual ~GlobalHotkeys() = default; - - 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; - // Returns true if the event wasn't consumed (if the event didn't match a key that has been bound) - virtual bool on_event(mgl::Event &event) { (void)event; return true; } - }; -} \ No newline at end of file diff --git a/include/GlobalHotkeys/GlobalHotkeys.hpp b/include/GlobalHotkeys/GlobalHotkeys.hpp new file mode 100644 index 0000000..2927fa7 --- /dev/null +++ b/include/GlobalHotkeys/GlobalHotkeys.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +namespace mgl { + class Event; +} + +namespace gsr { + enum HotkeyModifier : uint32_t { + HOTKEY_MOD_LSHIFT = 1 << 0, + HOTKEY_MOD_RSHIFT = 1 << 1, + HOTKEY_MOD_LCTRL = 1 << 2, + HOTKEY_MOD_RCTRL = 1 << 3, + HOTKEY_MOD_LALT = 1 << 4, + HOTKEY_MOD_RALT = 1 << 5, + HOTKEY_MOD_LSUPER = 1 << 6, + HOTKEY_MOD_RSUPER = 1 << 7 + }; + + struct Hotkey { + uint32_t key = 0; // X11 keysym + uint32_t modifiers = 0; // HotkeyModifier + }; + + using GlobalHotkeyCallback = std::function; + + class GlobalHotkeys { + public: + GlobalHotkeys() = default; + GlobalHotkeys(const GlobalHotkeys&) = delete; + GlobalHotkeys& operator=(const GlobalHotkeys&) = delete; + virtual ~GlobalHotkeys() = default; + + 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; + // Returns true if the event wasn't consumed (if the event didn't match a key that has been bound) + virtual bool on_event(mgl::Event &event) { (void)event; return true; } + }; +} \ No newline at end of file diff --git a/include/GlobalHotkeys/GlobalHotkeysJoystick.hpp b/include/GlobalHotkeys/GlobalHotkeysJoystick.hpp new file mode 100644 index 0000000..4b266cb --- /dev/null +++ b/include/GlobalHotkeys/GlobalHotkeysJoystick.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "GlobalHotkeys.hpp" +#include "../Hotplug.hpp" +#include +#include +#include +#include + +namespace gsr { + static constexpr int max_js_poll_fd = 16; + + class GlobalHotkeysJoystick : public GlobalHotkeys { + class GlobalHotkeysJoystickHotplugDelegate; + public: + GlobalHotkeysJoystick() = default; + GlobalHotkeysJoystick(const GlobalHotkeysJoystick&) = delete; + GlobalHotkeysJoystick& operator=(const GlobalHotkeysJoystick&) = delete; + ~GlobalHotkeysJoystick() override; + + bool start(); + // Currently valid ids: + // save_replay + // save_1_min_replay + // save_10_min_replay + // take_screenshot + // toggle_record + // toggle_replay + // toggle_show + bool bind_action(const std::string &id, GlobalHotkeyCallback callback) override; + void poll_events() override; + private: + void read_events(); + void process_js_event(int fd, js_event &event); + bool add_device(const char *dev_input_filepath, bool print_error = true); + bool remove_device(const char *dev_input_filepath); + bool remove_poll_fd(int index); + // Returns -1 if not found + int get_poll_fd_index_by_dev_input_id(int dev_input_id) const; + private: + struct ExtraData { + int dev_input_id = 0; + }; + + std::unordered_map bound_actions_by_id; + std::thread read_thread; + + pollfd poll_fd[max_js_poll_fd]; + ExtraData extra_data[max_js_poll_fd]; + int num_poll_fd = 0; + int event_fd = -1; + int event_index = -1; + + bool playstation_button_pressed = false; + bool up_pressed = false; + bool down_pressed = false; + bool left_pressed = false; + bool right_pressed = false; + + bool save_replay = false; + bool save_1_min_replay = false; + bool save_10_min_replay = false; + bool take_screenshot = false; + bool toggle_record = false; + bool toggle_replay = false; + bool toggle_show = false; + int hotplug_poll_index = -1; + Hotplug hotplug; + }; +} \ No newline at end of file diff --git a/include/GlobalHotkeys/GlobalHotkeysLinux.hpp b/include/GlobalHotkeys/GlobalHotkeysLinux.hpp new file mode 100644 index 0000000..959d095 --- /dev/null +++ b/include/GlobalHotkeys/GlobalHotkeysLinux.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "GlobalHotkeys.hpp" +#include +#include + +namespace gsr { + class GlobalHotkeysLinux : public GlobalHotkeys { + public: + enum class GrabType { + ALL, + VIRTUAL + }; + + GlobalHotkeysLinux(GrabType grab_type); + GlobalHotkeysLinux(const GlobalHotkeysLinux&) = delete; + GlobalHotkeysLinux& operator=(const GlobalHotkeysLinux&) = delete; + ~GlobalHotkeysLinux() override; + + bool start(); + bool bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) override; + void unbind_all_keys() override; + void poll_events() override; + private: + void close_fds(); + private: + pid_t process_id = 0; + int read_pipes[2]; + int write_pipes[2]; + FILE *read_file = nullptr; + std::unordered_map bound_actions_by_id; + GrabType grab_type; + }; +} \ No newline at end of file diff --git a/include/GlobalHotkeys/GlobalHotkeysX11.hpp b/include/GlobalHotkeys/GlobalHotkeysX11.hpp new file mode 100644 index 0000000..610399a --- /dev/null +++ b/include/GlobalHotkeys/GlobalHotkeysX11.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "GlobalHotkeys.hpp" +#include +#include + +namespace gsr { + class GlobalHotkeysX11 : public GlobalHotkeys { + public: + GlobalHotkeysX11(); + GlobalHotkeysX11(const GlobalHotkeysX11&) = delete; + GlobalHotkeysX11& operator=(const GlobalHotkeysX11&) = delete; + ~GlobalHotkeysX11() override; + + // Hotkey key is a KeySym (XK_z for example) and modifiers is a bitmask of X11 modifier masks (for example ShiftMask | Mod1Mask) + bool bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) override; + void unbind_key_press(const std::string &id) override; + void unbind_all_keys() override; + void poll_events() override; + bool on_event(mgl::Event &event) override; + private: + // Returns true if a key bind has been registered for the hotkey + bool call_hotkey_callback(Hotkey hotkey) const; + private: + struct HotkeyData { + Hotkey hotkey; + GlobalHotkeyCallback callback; + }; + + Display *dpy = nullptr; + XEvent xev; + std::unordered_map bound_keys_by_id; + }; +} \ No newline at end of file diff --git a/include/GlobalHotkeysJoystick.hpp b/include/GlobalHotkeysJoystick.hpp deleted file mode 100644 index fde5be2..0000000 --- a/include/GlobalHotkeysJoystick.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include "GlobalHotkeys.hpp" -#include "Hotplug.hpp" -#include -#include -#include -#include - -namespace gsr { - static constexpr int max_js_poll_fd = 16; - - class GlobalHotkeysJoystick : public GlobalHotkeys { - class GlobalHotkeysJoystickHotplugDelegate; - public: - GlobalHotkeysJoystick() = default; - GlobalHotkeysJoystick(const GlobalHotkeysJoystick&) = delete; - GlobalHotkeysJoystick& operator=(const GlobalHotkeysJoystick&) = delete; - ~GlobalHotkeysJoystick() override; - - bool start(); - // Currently valid ids: - // save_replay - // save_1_min_replay - // save_10_min_replay - // take_screenshot - // toggle_record - // toggle_replay - // toggle_show - bool bind_action(const std::string &id, GlobalHotkeyCallback callback) override; - void poll_events() override; - private: - void read_events(); - void process_js_event(int fd, js_event &event); - bool add_device(const char *dev_input_filepath, bool print_error = true); - bool remove_device(const char *dev_input_filepath); - bool remove_poll_fd(int index); - // Returns -1 if not found - int get_poll_fd_index_by_dev_input_id(int dev_input_id) const; - private: - struct ExtraData { - int dev_input_id = 0; - }; - - std::unordered_map bound_actions_by_id; - std::thread read_thread; - - pollfd poll_fd[max_js_poll_fd]; - ExtraData extra_data[max_js_poll_fd]; - int num_poll_fd = 0; - int event_fd = -1; - int event_index = -1; - - bool playstation_button_pressed = false; - bool up_pressed = false; - bool down_pressed = false; - bool left_pressed = false; - bool right_pressed = false; - - bool save_replay = false; - bool save_1_min_replay = false; - bool save_10_min_replay = false; - bool take_screenshot = false; - bool toggle_record = false; - bool toggle_replay = false; - bool toggle_show = false; - int hotplug_poll_index = -1; - Hotplug hotplug; - }; -} \ No newline at end of file diff --git a/include/GlobalHotkeysLinux.hpp b/include/GlobalHotkeysLinux.hpp deleted file mode 100644 index 959d095..0000000 --- a/include/GlobalHotkeysLinux.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "GlobalHotkeys.hpp" -#include -#include - -namespace gsr { - class GlobalHotkeysLinux : public GlobalHotkeys { - public: - enum class GrabType { - ALL, - VIRTUAL - }; - - GlobalHotkeysLinux(GrabType grab_type); - GlobalHotkeysLinux(const GlobalHotkeysLinux&) = delete; - GlobalHotkeysLinux& operator=(const GlobalHotkeysLinux&) = delete; - ~GlobalHotkeysLinux() override; - - bool start(); - bool bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) override; - void unbind_all_keys() override; - void poll_events() override; - private: - void close_fds(); - private: - pid_t process_id = 0; - int read_pipes[2]; - int write_pipes[2]; - FILE *read_file = nullptr; - std::unordered_map bound_actions_by_id; - GrabType grab_type; - }; -} \ No newline at end of file diff --git a/include/GlobalHotkeysX11.hpp b/include/GlobalHotkeysX11.hpp deleted file mode 100644 index 610399a..0000000 --- a/include/GlobalHotkeysX11.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "GlobalHotkeys.hpp" -#include -#include - -namespace gsr { - class GlobalHotkeysX11 : public GlobalHotkeys { - public: - GlobalHotkeysX11(); - GlobalHotkeysX11(const GlobalHotkeysX11&) = delete; - GlobalHotkeysX11& operator=(const GlobalHotkeysX11&) = delete; - ~GlobalHotkeysX11() override; - - // Hotkey key is a KeySym (XK_z for example) and modifiers is a bitmask of X11 modifier masks (for example ShiftMask | Mod1Mask) - bool bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) override; - void unbind_key_press(const std::string &id) override; - void unbind_all_keys() override; - void poll_events() override; - bool on_event(mgl::Event &event) override; - private: - // Returns true if a key bind has been registered for the hotkey - bool call_hotkey_callback(Hotkey hotkey) const; - private: - struct HotkeyData { - Hotkey hotkey; - GlobalHotkeyCallback callback; - }; - - Display *dpy = nullptr; - XEvent xev; - std::unordered_map bound_keys_by_id; - }; -} \ No newline at end of file diff --git a/include/Overlay.hpp b/include/Overlay.hpp index 5af839e..4cfab1d 100644 --- a/include/Overlay.hpp +++ b/include/Overlay.hpp @@ -6,10 +6,10 @@ #include "Config.hpp" #include "window_texture.h" #include "WindowUtils.hpp" -#include "GlobalHotkeysJoystick.hpp" +#include "GlobalHotkeys/GlobalHotkeysJoystick.hpp" #include "AudioPlayer.hpp" #include "RegionSelector.hpp" -#include "CursorTracker.hpp" +#include "CursorTracker/CursorTracker.hpp" #include #include diff --git a/meson.build b/meson.build index 2b5cdb6..2f991ed 100644 --- a/meson.build +++ b/meson.build @@ -39,11 +39,11 @@ src = [ 'src/GsrInfo.cpp', 'src/Process.cpp', 'src/Overlay.cpp', - 'src/GlobalHotkeysX11.cpp', - 'src/GlobalHotkeysLinux.cpp', - 'src/GlobalHotkeysJoystick.cpp', - 'src/CursorTrackerX11.cpp', - 'src/CursorTrackerWayland.cpp', + 'src/GlobalHotkeys/GlobalHotkeysX11.cpp', + 'src/GlobalHotkeys/GlobalHotkeysLinux.cpp', + 'src/GlobalHotkeys/GlobalHotkeysJoystick.cpp', + 'src/CursorTracker/CursorTrackerX11.cpp', + 'src/CursorTracker/CursorTrackerWayland.cpp', 'src/AudioPlayer.cpp', 'src/Hotplug.cpp', 'src/Rpc.cpp', diff --git a/src/Config.cpp b/src/Config.cpp index e920bf0..2de6a96 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -1,7 +1,7 @@ #include "../include/Config.hpp" #include "../include/Utils.hpp" #include "../include/GsrInfo.hpp" -#include "../include/GlobalHotkeys.hpp" +#include "../include/GlobalHotkeys/GlobalHotkeys.hpp" #include #include #include diff --git a/src/CursorTracker/CursorTrackerWayland.cpp b/src/CursorTracker/CursorTrackerWayland.cpp new file mode 100644 index 0000000..b28b978 --- /dev/null +++ b/src/CursorTracker/CursorTrackerWayland.cpp @@ -0,0 +1,564 @@ +#include "../../include/CursorTracker/CursorTrackerWayland.hpp" +#include +#include +#include +#include +#include +#include +#include "xdg-output-unstable-v1-client-protocol.h" + +namespace gsr { + static const int MAX_CONNECTORS = 32; + static const int CONNECTOR_TYPE_COUNTS = 32; + static const uint32_t plane_property_all = 0xF; + + typedef struct { + int type; + int count; + } drm_connector_type_count; + + typedef enum { + PLANE_PROPERTY_CRTC_X = 1 << 0, + PLANE_PROPERTY_CRTC_Y = 1 << 1, + PLANE_PROPERTY_CRTC_ID = 1 << 2, + PLANE_PROPERTY_TYPE_CURSOR = 1 << 3, + } plane_property_mask; + + typedef struct { + uint64_t crtc_id; + mgl::vec2i size; + bool vrr_enabled; + } drm_connector; + + typedef struct { + drm_connector connectors[MAX_CONNECTORS]; + int num_connectors; + bool has_any_crtc_with_vrr_enabled; + } drm_connectors; + + /* Returns plane_property_mask */ + static uint32_t plane_get_properties(int drm_fd, uint32_t plane_id, int *crtc_x, int *crtc_y, int *crtc_id) { + *crtc_x = 0; + *crtc_y = 0; + *crtc_id = 0; + + uint32_t property_mask = 0; + + drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_fd, plane_id, DRM_MODE_OBJECT_PLANE); + if(!props) + return property_mask; + + for(uint32_t i = 0; i < props->count_props; ++i) { + drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]); + if(!prop) + continue; + + // SRC_* values are fixed 16.16 points + const uint32_t type = prop->flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE); + if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_X") == 0) { + *crtc_x = (int)props->prop_values[i]; + property_mask |= PLANE_PROPERTY_CRTC_X; + } else if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_Y") == 0) { + *crtc_y = (int)props->prop_values[i]; + property_mask |= PLANE_PROPERTY_CRTC_Y; + } else if((type & DRM_MODE_PROP_OBJECT) && strcmp(prop->name, "CRTC_ID") == 0) { + *crtc_id = (int)props->prop_values[i]; + property_mask |= PLANE_PROPERTY_CRTC_ID; + } else if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) { + const uint64_t current_enum_value = props->prop_values[i]; + for(int j = 0; j < prop->count_enums; ++j) { + if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Cursor") == 0) { + property_mask |= PLANE_PROPERTY_TYPE_CURSOR; + break; + } + } + } + + drmModeFreeProperty(prop); + } + + drmModeFreeObjectProperties(props); + return property_mask; + } + + static bool get_drm_property_by_name(int drm_fd, drmModeObjectPropertiesPtr props, const char *name, uint64_t *result) { + for(uint32_t i = 0; i < props->count_props; ++i) { + drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]); + if(!prop) + continue; + + if(strcmp(name, prop->name) == 0) { + *result = props->prop_values[i]; + drmModeFreeProperty(prop); + return true; + } + drmModeFreeProperty(prop); + } + return false; + } + + static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) { + drmModeObjectProperties properties; + properties.count_props = (uint32_t)props->count_props; + properties.props = props->props; + properties.prop_values = props->prop_values; + return get_drm_property_by_name(drm_fd, &properties, name, result); + } + + static drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) { + for(int i = 0; i < *num_type_counts; ++i) { + if(type_counts[i].type == connector_type) + return &type_counts[i]; + } + + if(*num_type_counts == CONNECTOR_TYPE_COUNTS) + return NULL; + + const int index = *num_type_counts; + type_counts[index].type = connector_type; + type_counts[index].count = 0; + ++*num_type_counts; + return &type_counts[index]; + } + + // Note: this monitor name logic is kept in sync with gpu screen recorder + static std::string get_monitor_name_from_crtc_id(int drm_fd, uint32_t crtc_id) { + std::string result; + drmModeResPtr resources = drmModeGetResources(drm_fd); + if(!resources) + return result; + + drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS]; + int num_type_counts = 0; + + for(int i = 0; i < resources->count_connectors; ++i) { + uint64_t connector_crtc_id = 0; + drmModeConnectorPtr connector = drmModeGetConnectorCurrent(drm_fd, resources->connectors[i]); + if(!connector) + continue; + + drm_connector_type_count *connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector->connector_type); + const char *connection_name = drmModeGetConnectorTypeName(connector->connector_type); + if(connector_type) + ++connector_type->count; + + if(connector->connection != DRM_MODE_CONNECTED) + goto next; + + if(connector_type && connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &connector_crtc_id) && connector_crtc_id == crtc_id) { + result = connection_name; + result += "-"; + result += std::to_string(connector_type->count); + drmModeFreeConnector(connector); + break; + } + + next: + drmModeFreeConnector(connector); + } + + drmModeFreeResources(resources); + return result; + } + + // Name is the crtc name. TODO: verify if this works on all wayland compositors + static const WaylandOutput* get_wayland_monitor_by_name(const std::vector &monitors, const std::string &name) { + for(const WaylandOutput &monitor : monitors) { + if(monitor.name == name) + return &monitor; + } + return nullptr; + } + + static WaylandOutput* get_wayland_monitor_by_output(CursorTrackerWayland &cursor_tracker_wayland, struct wl_output *output) { + for(WaylandOutput &monitor : cursor_tracker_wayland.monitors) { + if(monitor.output == output) + return &monitor; + } + return nullptr; + } + + static void output_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { + (void)wl_output; + (void)phys_width; + (void)phys_height; + (void)subpixel; + (void)make; + (void)model; + CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; + WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output); + if(!monitor) + return; + + monitor->pos.x = x; + monitor->pos.y = y; + monitor->transform = transform; + } + + static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + (void)wl_output; + (void)flags; + (void)refresh; + CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; + WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output); + if(!monitor) + return; + + monitor->size.x = width; + monitor->size.y = height; + } + + static void output_handle_done(void *data, struct wl_output *wl_output) { + (void)data; + (void)wl_output; + } + + static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) { + (void)data; + (void)wl_output; + (void)factor; + } + + static void output_handle_name(void *data, struct wl_output *wl_output, const char *name) { + (void)wl_output; + CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; + WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output); + if(!monitor) + return; + + monitor->name = name; + } + + static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) { + (void)data; + (void)wl_output; + (void)description; + } + + static const struct wl_output_listener output_listener = { + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, + output_handle_name, + output_handle_description, + }; + + static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { + (void)version; + CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; + if(strcmp(interface, wl_output_interface.name) == 0) { + if(version < 4) { + fprintf(stderr, "Warning: wl output interface version is < 4, expected >= 4\n"); + return; + } + + struct wl_output *output = (struct wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 4); + cursor_tracker_wayland->monitors.push_back( + WaylandOutput{ + name, + output, + nullptr, + mgl::vec2i{0, 0}, + mgl::vec2i{0, 0}, + 0, + "" + }); + wl_output_add_listener(output, &output_listener, cursor_tracker_wayland); + } else if(strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + if(version < 1) { + fprintf(stderr, "Warning: xdg output interface version is < 1, expected >= 1\n"); + return; + } + + if(cursor_tracker_wayland->xdg_output_manager) { + zxdg_output_manager_v1_destroy(cursor_tracker_wayland->xdg_output_manager); + cursor_tracker_wayland->xdg_output_manager = NULL; + } + cursor_tracker_wayland->xdg_output_manager = (struct zxdg_output_manager_v1*)wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 1); + } + } + + static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { + (void)data; + (void)registry; + (void)name; + // TODO: Remove output + } + + static struct wl_registry_listener registry_listener = { + registry_add_object, + registry_remove_object, + }; + + static void xdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y) { + (void)zxdg_output_v1; + WaylandOutput *monitor = (WaylandOutput*)data; + monitor->pos.x = x; + monitor->pos.y = y; + } + + static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { + (void)data; + (void)xdg_output; + (void)width; + (void)height; + } + + static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { + (void)data; + (void)xdg_output; + } + + static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { + (void)data; + (void)xdg_output; + (void)name; + } + + static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { + (void)data; + (void)xdg_output; + (void)description; + } + + static const struct zxdg_output_v1_listener xdg_output_listener = { + xdg_output_logical_position, + xdg_output_handle_logical_size, + xdg_output_handle_done, + xdg_output_handle_name, + xdg_output_handle_description, + }; + + /* Returns nullptr if not found */ + static drm_connector* get_drm_connector_by_crtc_id(drm_connectors *connectors, uint32_t crtc_id) { + for(int i = 0; i < connectors->num_connectors; ++i) { + if(connectors->connectors[i].crtc_id == crtc_id) + return &connectors->connectors[i]; + } + return nullptr; + } + + static void get_drm_connectors(int drm_fd, drm_connectors *drm_connectors) { + drm_connectors->num_connectors = 0; + drm_connectors->has_any_crtc_with_vrr_enabled = false; + + drmModeResPtr resources = drmModeGetResources(drm_fd); + if(!resources) + return; + + for(int i = 0; i < resources->count_connectors && drm_connectors->num_connectors < MAX_CONNECTORS; ++i) { + drmModeConnectorPtr connector = nullptr; + drmModeCrtcPtr crtc = nullptr; + + connector = drmModeGetConnectorCurrent(drm_fd, resources->connectors[i]); + if(!connector) + continue; + + uint64_t crtc_id = 0; + connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &crtc_id); + if(crtc_id == 0) + goto next_connector; + + crtc = drmModeGetCrtc(drm_fd, crtc_id); + if(!crtc) + goto next_connector; + + drm_connectors->connectors[drm_connectors->num_connectors].crtc_id = crtc_id; + drm_connectors->connectors[drm_connectors->num_connectors].size = mgl::vec2i{(int)crtc->width, (int)crtc->height}; + drm_connectors->connectors[drm_connectors->num_connectors].vrr_enabled = false; + ++drm_connectors->num_connectors; + + next_connector: + if(crtc) + drmModeFreeCrtc(crtc); + + if(connector) + drmModeFreeConnector(connector); + } + + for(int i = 0; i < resources->count_crtcs; ++i) { + drmModeCrtcPtr crtc = nullptr; + drmModeObjectPropertiesPtr properties = nullptr; + uint64_t vrr_enabled = 0; + drm_connector *connector = nullptr; + + crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]); + if(!crtc) + continue; + + properties = drmModeObjectGetProperties(drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC); + if(!properties) + goto next_crtc; + + if(!get_drm_property_by_name(drm_fd, properties, "VRR_ENABLED", &vrr_enabled)) + goto next_crtc; + + connector = get_drm_connector_by_crtc_id(drm_connectors, crtc->crtc_id); + if(!connector) + goto next_crtc; + + if(vrr_enabled) { + connector->vrr_enabled = true; + drm_connectors->has_any_crtc_with_vrr_enabled = true; + } + + next_crtc: + if(properties) + drmModeFreeObjectProperties(properties); + + if(crtc) + drmModeFreeCrtc(crtc); + } + + drmModeFreeResources(resources); + } + + CursorTrackerWayland::CursorTrackerWayland(const char *card_path) { + drm_fd = open(card_path, O_RDONLY); + if(drm_fd <= 0) { + fprintf(stderr, "Error: CursorTrackerWayland: failed to open %s\n", card_path); + return; + } + + drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1); + } + + CursorTrackerWayland::~CursorTrackerWayland() { + if(drm_fd > 0) + close(drm_fd); + } + + void CursorTrackerWayland::update() { + if(drm_fd <= 0) + return; + + drm_connectors connectors; + connectors.num_connectors = 0; + connectors.has_any_crtc_with_vrr_enabled = false; + get_drm_connectors(drm_fd, &connectors); + + drmModePlaneResPtr planes = drmModeGetPlaneResources(drm_fd); + if(!planes) + return; + + bool found_cursor = false; + for(uint32_t i = 0; i < planes->count_planes; ++i) { + drmModePlanePtr plane = nullptr; + const drm_connector *connector = nullptr; + int crtc_x = 0; + int crtc_y = 0; + int crtc_id = 0; + uint32_t property_mask = 0; + + plane = drmModeGetPlane(drm_fd, planes->planes[i]); + if(!plane) + goto next; + + if(!plane->fb_id) + goto next; + + property_mask = plane_get_properties(drm_fd, planes->planes[i], &crtc_x, &crtc_y, &crtc_id); + if(property_mask != plane_property_all || crtc_id <= 0) + goto next; + + connector = get_drm_connector_by_crtc_id(&connectors, crtc_id); + if(!connector) + goto next; + + if(crtc_x >= 0 && crtc_x <= connector->size.x && crtc_y >= 0 && crtc_y <= connector->size.y) { + latest_cursor_position.x = crtc_x; + latest_cursor_position.y = crtc_y; + latest_crtc_id = crtc_id; + found_cursor = true; + drmModeFreePlane(plane); + break; + } + + next: + drmModeFreePlane(plane); + } + + // On kde plasma wayland (and possibly other wayland compositors) it uses a software cursor only for the monitors with vrr enabled. + // In that case we cant know the cursor location and we instead want to fallback to getting focused monitor by using the hack of creating a window and getting the position. + if(!found_cursor && latest_crtc_id > 0 && connectors.has_any_crtc_with_vrr_enabled) + latest_crtc_id = -1; + + drmModeFreePlaneResources(planes); + } + + void CursorTrackerWayland::set_monitor_outputs_from_xdg_output(struct wl_display *dpy) { + if(!xdg_output_manager) { + fprintf(stderr, "Warning: CursorTrackerWayland::set_monitor_outputs_from_xdg_output: zxdg_output_manager not found. Registered monitor positions might be incorrect\n"); + return; + } + + for(WaylandOutput &monitor : monitors) { + monitor.xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, monitor.output); + zxdg_output_v1_add_listener(monitor.xdg_output, &xdg_output_listener, &monitor); + } + + // Fetch xdg_output + wl_display_roundtrip(dpy); + } + + std::optional CursorTrackerWayland::get_latest_cursor_info() { + if(drm_fd <= 0 || latest_crtc_id == -1) + return std::nullopt; + + std::string monitor_name = get_monitor_name_from_crtc_id(drm_fd, latest_crtc_id); + if(monitor_name.empty()) + return std::nullopt; + + struct wl_display *dpy = wl_display_connect(nullptr); + if(!dpy) { + fprintf(stderr, "Error: CursorTrackerWayland::get_latest_cursor_info: failed to connect to the wayland server\n"); + return std::nullopt; + } + + monitors.clear(); + struct wl_registry *registry = wl_display_get_registry(dpy); + wl_registry_add_listener(registry, ®istry_listener, this); + + // Fetch globals + wl_display_roundtrip(dpy); + + // Fetch wl_output + wl_display_roundtrip(dpy); + + set_monitor_outputs_from_xdg_output(dpy); + + mgl::vec2i cursor_position = latest_cursor_position; + const WaylandOutput *wayland_monitor = get_wayland_monitor_by_name(monitors, monitor_name); + if(!wayland_monitor) + return std::nullopt; + + cursor_position = wayland_monitor->pos + latest_cursor_position; + for(WaylandOutput &monitor : monitors) { + if(monitor.output) { + wl_output_destroy(monitor.output); + monitor.output = nullptr; + } + + if(monitor.xdg_output) { + zxdg_output_v1_destroy(monitor.xdg_output); + monitor.xdg_output = nullptr; + } + } + monitors.clear(); + + if(xdg_output_manager) { + zxdg_output_manager_v1_destroy(xdg_output_manager); + xdg_output_manager = nullptr; + } + + wl_registry_destroy(registry); + wl_display_disconnect(dpy); + + return CursorInfo{ cursor_position, std::move(monitor_name) }; + } +} \ No newline at end of file diff --git a/src/CursorTracker/CursorTrackerX11.cpp b/src/CursorTracker/CursorTrackerX11.cpp new file mode 100644 index 0000000..7c98f4d --- /dev/null +++ b/src/CursorTracker/CursorTrackerX11.cpp @@ -0,0 +1,29 @@ +#include "../../include/CursorTracker/CursorTrackerX11.hpp" +#include "../../include/WindowUtils.hpp" + +namespace gsr { + CursorTrackerX11::CursorTrackerX11(Display *dpy) : dpy(dpy) { + + } + + std::optional CursorTrackerX11::get_latest_cursor_info() { + Window window = None; + const auto cursor_pos = get_cursor_position(dpy, &window); + const auto monitors = get_monitors(dpy); + std::string monitor_name; + + for(const auto &monitor : monitors) { + if(cursor_pos.x >= monitor.position.x && cursor_pos.x <= monitor.position.x + monitor.size.x + && cursor_pos.y >= monitor.position.y && cursor_pos.y <= monitor.position.y + monitor.size.y) + { + monitor_name = monitor.name; + break; + } + } + + if(monitor_name.empty()) + return std::nullopt; + + return CursorInfo{ cursor_pos, std::move(monitor_name) }; + } +} \ No newline at end of file diff --git a/src/CursorTrackerWayland.cpp b/src/CursorTrackerWayland.cpp deleted file mode 100644 index 9a0f442..0000000 --- a/src/CursorTrackerWayland.cpp +++ /dev/null @@ -1,564 +0,0 @@ -#include "../include/CursorTrackerWayland.hpp" -#include -#include -#include -#include -#include -#include -#include "xdg-output-unstable-v1-client-protocol.h" - -namespace gsr { - static const int MAX_CONNECTORS = 32; - static const int CONNECTOR_TYPE_COUNTS = 32; - static const uint32_t plane_property_all = 0xF; - - typedef struct { - int type; - int count; - } drm_connector_type_count; - - typedef enum { - PLANE_PROPERTY_CRTC_X = 1 << 0, - PLANE_PROPERTY_CRTC_Y = 1 << 1, - PLANE_PROPERTY_CRTC_ID = 1 << 2, - PLANE_PROPERTY_TYPE_CURSOR = 1 << 3, - } plane_property_mask; - - typedef struct { - uint64_t crtc_id; - mgl::vec2i size; - bool vrr_enabled; - } drm_connector; - - typedef struct { - drm_connector connectors[MAX_CONNECTORS]; - int num_connectors; - bool has_any_crtc_with_vrr_enabled; - } drm_connectors; - - /* Returns plane_property_mask */ - static uint32_t plane_get_properties(int drm_fd, uint32_t plane_id, int *crtc_x, int *crtc_y, int *crtc_id) { - *crtc_x = 0; - *crtc_y = 0; - *crtc_id = 0; - - uint32_t property_mask = 0; - - drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_fd, plane_id, DRM_MODE_OBJECT_PLANE); - if(!props) - return property_mask; - - for(uint32_t i = 0; i < props->count_props; ++i) { - drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]); - if(!prop) - continue; - - // SRC_* values are fixed 16.16 points - const uint32_t type = prop->flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE); - if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_X") == 0) { - *crtc_x = (int)props->prop_values[i]; - property_mask |= PLANE_PROPERTY_CRTC_X; - } else if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_Y") == 0) { - *crtc_y = (int)props->prop_values[i]; - property_mask |= PLANE_PROPERTY_CRTC_Y; - } else if((type & DRM_MODE_PROP_OBJECT) && strcmp(prop->name, "CRTC_ID") == 0) { - *crtc_id = (int)props->prop_values[i]; - property_mask |= PLANE_PROPERTY_CRTC_ID; - } else if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) { - const uint64_t current_enum_value = props->prop_values[i]; - for(int j = 0; j < prop->count_enums; ++j) { - if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Cursor") == 0) { - property_mask |= PLANE_PROPERTY_TYPE_CURSOR; - break; - } - } - } - - drmModeFreeProperty(prop); - } - - drmModeFreeObjectProperties(props); - return property_mask; - } - - static bool get_drm_property_by_name(int drm_fd, drmModeObjectPropertiesPtr props, const char *name, uint64_t *result) { - for(uint32_t i = 0; i < props->count_props; ++i) { - drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]); - if(!prop) - continue; - - if(strcmp(name, prop->name) == 0) { - *result = props->prop_values[i]; - drmModeFreeProperty(prop); - return true; - } - drmModeFreeProperty(prop); - } - return false; - } - - static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) { - drmModeObjectProperties properties; - properties.count_props = (uint32_t)props->count_props; - properties.props = props->props; - properties.prop_values = props->prop_values; - return get_drm_property_by_name(drm_fd, &properties, name, result); - } - - static drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) { - for(int i = 0; i < *num_type_counts; ++i) { - if(type_counts[i].type == connector_type) - return &type_counts[i]; - } - - if(*num_type_counts == CONNECTOR_TYPE_COUNTS) - return NULL; - - const int index = *num_type_counts; - type_counts[index].type = connector_type; - type_counts[index].count = 0; - ++*num_type_counts; - return &type_counts[index]; - } - - // Note: this monitor name logic is kept in sync with gpu screen recorder - static std::string get_monitor_name_from_crtc_id(int drm_fd, uint32_t crtc_id) { - std::string result; - drmModeResPtr resources = drmModeGetResources(drm_fd); - if(!resources) - return result; - - drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS]; - int num_type_counts = 0; - - for(int i = 0; i < resources->count_connectors; ++i) { - uint64_t connector_crtc_id = 0; - drmModeConnectorPtr connector = drmModeGetConnectorCurrent(drm_fd, resources->connectors[i]); - if(!connector) - continue; - - drm_connector_type_count *connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector->connector_type); - const char *connection_name = drmModeGetConnectorTypeName(connector->connector_type); - if(connector_type) - ++connector_type->count; - - if(connector->connection != DRM_MODE_CONNECTED) - goto next; - - if(connector_type && connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &connector_crtc_id) && connector_crtc_id == crtc_id) { - result = connection_name; - result += "-"; - result += std::to_string(connector_type->count); - drmModeFreeConnector(connector); - break; - } - - next: - drmModeFreeConnector(connector); - } - - drmModeFreeResources(resources); - return result; - } - - // Name is the crtc name. TODO: verify if this works on all wayland compositors - static const WaylandOutput* get_wayland_monitor_by_name(const std::vector &monitors, const std::string &name) { - for(const WaylandOutput &monitor : monitors) { - if(monitor.name == name) - return &monitor; - } - return nullptr; - } - - static WaylandOutput* get_wayland_monitor_by_output(CursorTrackerWayland &cursor_tracker_wayland, struct wl_output *output) { - for(WaylandOutput &monitor : cursor_tracker_wayland.monitors) { - if(monitor.output == output) - return &monitor; - } - return nullptr; - } - - static void output_handle_geometry(void *data, struct wl_output *wl_output, - int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, - int32_t subpixel, const char *make, const char *model, - int32_t transform) { - (void)wl_output; - (void)phys_width; - (void)phys_height; - (void)subpixel; - (void)make; - (void)model; - CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; - WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output); - if(!monitor) - return; - - monitor->pos.x = x; - monitor->pos.y = y; - monitor->transform = transform; - } - - static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - (void)wl_output; - (void)flags; - (void)refresh; - CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; - WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output); - if(!monitor) - return; - - monitor->size.x = width; - monitor->size.y = height; - } - - static void output_handle_done(void *data, struct wl_output *wl_output) { - (void)data; - (void)wl_output; - } - - static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) { - (void)data; - (void)wl_output; - (void)factor; - } - - static void output_handle_name(void *data, struct wl_output *wl_output, const char *name) { - (void)wl_output; - CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; - WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output); - if(!monitor) - return; - - monitor->name = name; - } - - static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) { - (void)data; - (void)wl_output; - (void)description; - } - - static const struct wl_output_listener output_listener = { - output_handle_geometry, - output_handle_mode, - output_handle_done, - output_handle_scale, - output_handle_name, - output_handle_description, - }; - - static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - (void)version; - CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data; - if(strcmp(interface, wl_output_interface.name) == 0) { - if(version < 4) { - fprintf(stderr, "Warning: wl output interface version is < 4, expected >= 4\n"); - return; - } - - struct wl_output *output = (struct wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 4); - cursor_tracker_wayland->monitors.push_back( - WaylandOutput{ - name, - output, - nullptr, - mgl::vec2i{0, 0}, - mgl::vec2i{0, 0}, - 0, - "" - }); - wl_output_add_listener(output, &output_listener, cursor_tracker_wayland); - } else if(strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { - if(version < 1) { - fprintf(stderr, "Warning: xdg output interface version is < 1, expected >= 1\n"); - return; - } - - if(cursor_tracker_wayland->xdg_output_manager) { - zxdg_output_manager_v1_destroy(cursor_tracker_wayland->xdg_output_manager); - cursor_tracker_wayland->xdg_output_manager = NULL; - } - cursor_tracker_wayland->xdg_output_manager = (struct zxdg_output_manager_v1*)wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 1); - } - } - - static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { - (void)data; - (void)registry; - (void)name; - // TODO: Remove output - } - - static struct wl_registry_listener registry_listener = { - registry_add_object, - registry_remove_object, - }; - - static void xdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y) { - (void)zxdg_output_v1; - WaylandOutput *monitor = (WaylandOutput*)data; - monitor->pos.x = x; - monitor->pos.y = y; - } - - static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { - (void)data; - (void)xdg_output; - (void)width; - (void)height; - } - - static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { - (void)data; - (void)xdg_output; - } - - static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { - (void)data; - (void)xdg_output; - (void)name; - } - - static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { - (void)data; - (void)xdg_output; - (void)description; - } - - static const struct zxdg_output_v1_listener xdg_output_listener = { - xdg_output_logical_position, - xdg_output_handle_logical_size, - xdg_output_handle_done, - xdg_output_handle_name, - xdg_output_handle_description, - }; - - /* Returns nullptr if not found */ - static drm_connector* get_drm_connector_by_crtc_id(drm_connectors *connectors, uint32_t crtc_id) { - for(int i = 0; i < connectors->num_connectors; ++i) { - if(connectors->connectors[i].crtc_id == crtc_id) - return &connectors->connectors[i]; - } - return nullptr; - } - - static void get_drm_connectors(int drm_fd, drm_connectors *drm_connectors) { - drm_connectors->num_connectors = 0; - drm_connectors->has_any_crtc_with_vrr_enabled = false; - - drmModeResPtr resources = drmModeGetResources(drm_fd); - if(!resources) - return; - - for(int i = 0; i < resources->count_connectors && drm_connectors->num_connectors < MAX_CONNECTORS; ++i) { - drmModeConnectorPtr connector = nullptr; - drmModeCrtcPtr crtc = nullptr; - - connector = drmModeGetConnectorCurrent(drm_fd, resources->connectors[i]); - if(!connector) - continue; - - uint64_t crtc_id = 0; - connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &crtc_id); - if(crtc_id == 0) - goto next_connector; - - crtc = drmModeGetCrtc(drm_fd, crtc_id); - if(!crtc) - goto next_connector; - - drm_connectors->connectors[drm_connectors->num_connectors].crtc_id = crtc_id; - drm_connectors->connectors[drm_connectors->num_connectors].size = mgl::vec2i{(int)crtc->width, (int)crtc->height}; - drm_connectors->connectors[drm_connectors->num_connectors].vrr_enabled = false; - ++drm_connectors->num_connectors; - - next_connector: - if(crtc) - drmModeFreeCrtc(crtc); - - if(connector) - drmModeFreeConnector(connector); - } - - for(int i = 0; i < resources->count_crtcs; ++i) { - drmModeCrtcPtr crtc = nullptr; - drmModeObjectPropertiesPtr properties = nullptr; - uint64_t vrr_enabled = 0; - drm_connector *connector = nullptr; - - crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]); - if(!crtc) - continue; - - properties = drmModeObjectGetProperties(drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC); - if(!properties) - goto next_crtc; - - if(!get_drm_property_by_name(drm_fd, properties, "VRR_ENABLED", &vrr_enabled)) - goto next_crtc; - - connector = get_drm_connector_by_crtc_id(drm_connectors, crtc->crtc_id); - if(!connector) - goto next_crtc; - - if(vrr_enabled) { - connector->vrr_enabled = true; - drm_connectors->has_any_crtc_with_vrr_enabled = true; - } - - next_crtc: - if(properties) - drmModeFreeObjectProperties(properties); - - if(crtc) - drmModeFreeCrtc(crtc); - } - - drmModeFreeResources(resources); - } - - CursorTrackerWayland::CursorTrackerWayland(const char *card_path) { - drm_fd = open(card_path, O_RDONLY); - if(drm_fd <= 0) { - fprintf(stderr, "Error: CursorTrackerWayland: failed to open %s\n", card_path); - return; - } - - drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); - drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1); - } - - CursorTrackerWayland::~CursorTrackerWayland() { - if(drm_fd > 0) - close(drm_fd); - } - - void CursorTrackerWayland::update() { - if(drm_fd <= 0) - return; - - drm_connectors connectors; - connectors.num_connectors = 0; - connectors.has_any_crtc_with_vrr_enabled = false; - get_drm_connectors(drm_fd, &connectors); - - drmModePlaneResPtr planes = drmModeGetPlaneResources(drm_fd); - if(!planes) - return; - - bool found_cursor = false; - for(uint32_t i = 0; i < planes->count_planes; ++i) { - drmModePlanePtr plane = nullptr; - const drm_connector *connector = nullptr; - int crtc_x = 0; - int crtc_y = 0; - int crtc_id = 0; - uint32_t property_mask = 0; - - plane = drmModeGetPlane(drm_fd, planes->planes[i]); - if(!plane) - goto next; - - if(!plane->fb_id) - goto next; - - property_mask = plane_get_properties(drm_fd, planes->planes[i], &crtc_x, &crtc_y, &crtc_id); - if(property_mask != plane_property_all || crtc_id <= 0) - goto next; - - connector = get_drm_connector_by_crtc_id(&connectors, crtc_id); - if(!connector) - goto next; - - if(crtc_x >= 0 && crtc_x <= connector->size.x && crtc_y >= 0 && crtc_y <= connector->size.y) { - latest_cursor_position.x = crtc_x; - latest_cursor_position.y = crtc_y; - latest_crtc_id = crtc_id; - found_cursor = true; - drmModeFreePlane(plane); - break; - } - - next: - drmModeFreePlane(plane); - } - - // On kde plasma wayland (and possibly other wayland compositors) it uses a software cursor only for the monitors with vrr enabled. - // In that case we cant know the cursor location and we instead want to fallback to getting focused monitor by using the hack of creating a window and getting the position. - if(!found_cursor && latest_crtc_id > 0 && connectors.has_any_crtc_with_vrr_enabled) - latest_crtc_id = -1; - - drmModeFreePlaneResources(planes); - } - - void CursorTrackerWayland::set_monitor_outputs_from_xdg_output(struct wl_display *dpy) { - if(!xdg_output_manager) { - fprintf(stderr, "Warning: CursorTrackerWayland::set_monitor_outputs_from_xdg_output: zxdg_output_manager not found. Registered monitor positions might be incorrect\n"); - return; - } - - for(WaylandOutput &monitor : monitors) { - monitor.xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, monitor.output); - zxdg_output_v1_add_listener(monitor.xdg_output, &xdg_output_listener, &monitor); - } - - // Fetch xdg_output - wl_display_roundtrip(dpy); - } - - std::optional CursorTrackerWayland::get_latest_cursor_info() { - if(drm_fd <= 0 || latest_crtc_id == -1) - return std::nullopt; - - std::string monitor_name = get_monitor_name_from_crtc_id(drm_fd, latest_crtc_id); - if(monitor_name.empty()) - return std::nullopt; - - struct wl_display *dpy = wl_display_connect(nullptr); - if(!dpy) { - fprintf(stderr, "Error: CursorTrackerWayland::get_latest_cursor_info: failed to connect to the wayland server\n"); - return std::nullopt; - } - - monitors.clear(); - struct wl_registry *registry = wl_display_get_registry(dpy); - wl_registry_add_listener(registry, ®istry_listener, this); - - // Fetch globals - wl_display_roundtrip(dpy); - - // Fetch wl_output - wl_display_roundtrip(dpy); - - set_monitor_outputs_from_xdg_output(dpy); - - mgl::vec2i cursor_position = latest_cursor_position; - const WaylandOutput *wayland_monitor = get_wayland_monitor_by_name(monitors, monitor_name); - if(!wayland_monitor) - return std::nullopt; - - cursor_position = wayland_monitor->pos + latest_cursor_position; - for(WaylandOutput &monitor : monitors) { - if(monitor.output) { - wl_output_destroy(monitor.output); - monitor.output = nullptr; - } - - if(monitor.xdg_output) { - zxdg_output_v1_destroy(monitor.xdg_output); - monitor.xdg_output = nullptr; - } - } - monitors.clear(); - - if(xdg_output_manager) { - zxdg_output_manager_v1_destroy(xdg_output_manager); - xdg_output_manager = nullptr; - } - - wl_registry_destroy(registry); - wl_display_disconnect(dpy); - - return CursorInfo{ cursor_position, std::move(monitor_name) }; - } -} \ No newline at end of file diff --git a/src/CursorTrackerX11.cpp b/src/CursorTrackerX11.cpp deleted file mode 100644 index 7c40cea..0000000 --- a/src/CursorTrackerX11.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "../include/CursorTrackerX11.hpp" -#include "../include/WindowUtils.hpp" - -namespace gsr { - CursorTrackerX11::CursorTrackerX11(Display *dpy) : dpy(dpy) { - - } - - std::optional CursorTrackerX11::get_latest_cursor_info() { - Window window = None; - const auto cursor_pos = get_cursor_position(dpy, &window); - const auto monitors = get_monitors(dpy); - std::string monitor_name; - - for(const auto &monitor : monitors) { - if(cursor_pos.x >= monitor.position.x && cursor_pos.x <= monitor.position.x + monitor.size.x - && cursor_pos.y >= monitor.position.y && cursor_pos.y <= monitor.position.y + monitor.size.y) - { - monitor_name = monitor.name; - break; - } - } - - if(monitor_name.empty()) - return std::nullopt; - - return CursorInfo{ cursor_pos, std::move(monitor_name) }; - } -} \ No newline at end of file diff --git a/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp b/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp new file mode 100644 index 0000000..b3b21c8 --- /dev/null +++ b/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp @@ -0,0 +1,317 @@ +#include "../../include/GlobalHotkeys/GlobalHotkeysJoystick.hpp" +#include +#include +#include +#include +#include + +namespace gsr { + static constexpr int button_pressed = 1; + static constexpr int cross_button = 0; + static constexpr int triangle_button = 2; + static constexpr int options_button = 9; + static constexpr int playstation_button = 10; + static constexpr int axis_up_down = 7; + static constexpr int axis_left_right = 6; + + // Returns -1 on error + static int get_js_dev_input_id_from_filepath(const char *dev_input_filepath) { + if(strncmp(dev_input_filepath, "/dev/input/js", 13) != 0) + return -1; + + int dev_input_id = -1; + if(sscanf(dev_input_filepath + 13, "%d", &dev_input_id) == 1) + return dev_input_id; + return -1; + } + + GlobalHotkeysJoystick::~GlobalHotkeysJoystick() { + if(event_fd > 0) { + const uint64_t exit = 1; + write(event_fd, &exit, sizeof(exit)); + } + + if(read_thread.joinable()) + read_thread.join(); + + if(event_fd > 0) + close(event_fd); + + for(int i = 0; i < num_poll_fd; ++i) { + close(poll_fd[i].fd); + } + } + + bool GlobalHotkeysJoystick::start() { + if(num_poll_fd > 0) + return false; + + event_fd = eventfd(0, 0); + if(event_fd <= 0) + return false; + + event_index = num_poll_fd; + poll_fd[num_poll_fd] = { + event_fd, + POLLIN, + 0 + }; + extra_data[num_poll_fd] = { + -1 + }; + ++num_poll_fd; + + if(!hotplug.start()) { + fprintf(stderr, "Warning: failed to setup hotplugging\n"); + } else { + hotplug_poll_index = num_poll_fd; + poll_fd[num_poll_fd] = { + hotplug.steal_fd(), + POLLIN, + 0 + }; + extra_data[num_poll_fd] = { + -1 + }; + ++num_poll_fd; + } + + char dev_input_path[128]; + for(int i = 0; i < 8; ++i) { + snprintf(dev_input_path, sizeof(dev_input_path), "/dev/input/js%d", i); + add_device(dev_input_path, false); + } + + if(num_poll_fd == 0) + fprintf(stderr, "Info: no joysticks found, assuming they might be connected later\n"); + + read_thread = std::thread(&GlobalHotkeysJoystick::read_events, this); + return true; + } + + bool GlobalHotkeysJoystick::bind_action(const std::string &id, GlobalHotkeyCallback callback) { + if(num_poll_fd == 0) + return false; + return bound_actions_by_id.insert(std::make_pair(id, std::move(callback))).second; + } + + void GlobalHotkeysJoystick::poll_events() { + if(num_poll_fd == 0) + return; + + if(save_replay) { + save_replay = false; + auto it = bound_actions_by_id.find("save_replay"); + if(it != bound_actions_by_id.end()) + it->second("save_replay"); + } + + if(save_1_min_replay) { + save_1_min_replay = false; + auto it = bound_actions_by_id.find("save_1_min_replay"); + if(it != bound_actions_by_id.end()) + it->second("save_1_min_replay"); + } + + if(save_10_min_replay) { + save_10_min_replay = false; + auto it = bound_actions_by_id.find("save_10_min_replay"); + if(it != bound_actions_by_id.end()) + it->second("save_10_min_replay"); + } + + if(take_screenshot) { + take_screenshot = false; + auto it = bound_actions_by_id.find("take_screenshot"); + if(it != bound_actions_by_id.end()) + it->second("take_screenshot"); + } + + if(toggle_record) { + toggle_record = false; + auto it = bound_actions_by_id.find("toggle_record"); + if(it != bound_actions_by_id.end()) + it->second("toggle_record"); + } + + if(toggle_replay) { + toggle_replay = false; + auto it = bound_actions_by_id.find("toggle_replay"); + if(it != bound_actions_by_id.end()) + it->second("toggle_replay"); + } + + if(toggle_show) { + toggle_show = false; + auto it = bound_actions_by_id.find("toggle_show"); + if(it != bound_actions_by_id.end()) + it->second("toggle_show"); + } + } + + void GlobalHotkeysJoystick::read_events() { + js_event event; + while(poll(poll_fd, num_poll_fd, -1) > 0) { + for(int i = 0; i < num_poll_fd; ++i) { + if(poll_fd[i].revents & (POLLHUP|POLLERR|POLLNVAL)) { + if(i == event_index) + goto done; + + if(remove_poll_fd(i)) + --i; // This item was removed so we want to repeat the same index to continue to the next item + + continue; + } + + if(!(poll_fd[i].revents & POLLIN)) + continue; + + if(i == event_index) { + goto done; + } else if(i == hotplug_poll_index) { + hotplug.process_event_data(poll_fd[i].fd, [&](HotplugAction hotplug_action, const char *devname) { + char dev_input_filepath[1024]; + snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/%s", devname); + switch(hotplug_action) { + case HotplugAction::ADD: { + // Cant open the /dev/input device immediately or it fails. + // TODO: Remove this hack when a better solution is found. + usleep(50 * 1000); + add_device(dev_input_filepath); + break; + } + case HotplugAction::REMOVE: { + if(remove_device(dev_input_filepath)) + --i; // This item was removed so we want to repeat the same index to continue to the next item + break; + } + } + }); + } else { + process_js_event(poll_fd[i].fd, event); + } + } + } + + done: + ; + } + + void GlobalHotkeysJoystick::process_js_event(int fd, js_event &event) { + if(read(fd, &event, sizeof(event)) != sizeof(event)) + return; + + if((event.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) { + switch(event.number) { + case playstation_button: { + playstation_button_pressed = event.value == button_pressed; + break; + } + case options_button: { + if(playstation_button_pressed && event.value == button_pressed) + toggle_show = true; + break; + } + case cross_button: { + if(playstation_button_pressed && event.value == button_pressed) + save_1_min_replay = true; + break; + } + case triangle_button: { + if(playstation_button_pressed && event.value == button_pressed) + save_10_min_replay = true; + break; + } + } + } else if((event.type & JS_EVENT_AXIS) == JS_EVENT_AXIS && playstation_button_pressed) { + const int trigger_threshold = 16383; + const bool prev_up_pressed = up_pressed; + const bool prev_down_pressed = down_pressed; + const bool prev_left_pressed = left_pressed; + const bool prev_right_pressed = right_pressed; + + if(event.number == axis_up_down) { + up_pressed = event.value <= -trigger_threshold; + down_pressed = event.value >= trigger_threshold; + } else if(event.number == axis_left_right) { + left_pressed = event.value <= -trigger_threshold; + right_pressed = event.value >= trigger_threshold; + } + + if(up_pressed && !prev_up_pressed) + take_screenshot = true; + else if(down_pressed && !prev_down_pressed) + save_replay = true; + else if(left_pressed && !prev_left_pressed) + toggle_record = true; + else if(right_pressed && !prev_right_pressed) + toggle_replay = true; + } + } + + bool GlobalHotkeysJoystick::add_device(const char *dev_input_filepath, bool print_error) { + if(num_poll_fd >= max_js_poll_fd) { + fprintf(stderr, "Warning: failed to add joystick device %s, too many joysticks have been added\n", dev_input_filepath); + return false; + } + + const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath); + if(dev_input_id == -1) + return false; + + const int fd = open(dev_input_filepath, O_RDONLY); + if(fd <= 0) { + if(print_error) + fprintf(stderr, "Error: failed to add joystick %s, error: %s\n", dev_input_filepath, strerror(errno)); + return false; + } + + poll_fd[num_poll_fd] = { + fd, + POLLIN, + 0 + }; + + extra_data[num_poll_fd] = { + dev_input_id + }; + + ++num_poll_fd; + fprintf(stderr, "Info: added joystick: %s\n", dev_input_filepath); + return true; + } + + bool GlobalHotkeysJoystick::remove_device(const char *dev_input_filepath) { + const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath); + if(dev_input_id == -1) + return false; + + const int poll_fd_index = get_poll_fd_index_by_dev_input_id(dev_input_id); + if(poll_fd_index == -1) + return false; + + fprintf(stderr, "Info: removed joystick: %s\n", dev_input_filepath); + return remove_poll_fd(poll_fd_index); + } + + bool GlobalHotkeysJoystick::remove_poll_fd(int index) { + if(index < 0 || index >= num_poll_fd) + return false; + + close(poll_fd[index].fd); + for(int i = index + 1; i < num_poll_fd; ++i) { + poll_fd[i - 1] = poll_fd[i]; + extra_data[i - 1] = extra_data[i]; + } + --num_poll_fd; + return true; + } + + int GlobalHotkeysJoystick::get_poll_fd_index_by_dev_input_id(int dev_input_id) const { + for(int i = 0; i < num_poll_fd; ++i) { + if(dev_input_id == extra_data[i].dev_input_id) + return i; + } + return -1; + } +} diff --git a/src/GlobalHotkeys/GlobalHotkeysLinux.cpp b/src/GlobalHotkeys/GlobalHotkeysLinux.cpp new file mode 100644 index 0000000..a56bbc6 --- /dev/null +++ b/src/GlobalHotkeys/GlobalHotkeysLinux.cpp @@ -0,0 +1,275 @@ +#include "../../include/GlobalHotkeys/GlobalHotkeysLinux.hpp" +#include +#include +#include +#include + +extern "C" { +#include +} +#include +#include +#include + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +namespace gsr { + static const char* grab_type_to_arg(GlobalHotkeysLinux::GrabType grab_type) { + switch(grab_type) { + case GlobalHotkeysLinux::GrabType::ALL: return "--all"; + case GlobalHotkeysLinux::GrabType::VIRTUAL: return "--virtual"; + } + return "--all"; + } + + static inline uint8_t x11_keycode_to_linux_keycode(uint8_t code) { + return code - 8; + } + + static std::vector modifiers_to_linux_keys(uint32_t modifiers) { + std::vector result; + if(modifiers & HOTKEY_MOD_LSHIFT) + result.push_back(KEY_LEFTSHIFT); + if(modifiers & HOTKEY_MOD_RSHIFT) + result.push_back(KEY_RIGHTSHIFT); + if(modifiers & HOTKEY_MOD_LCTRL) + result.push_back(KEY_LEFTCTRL); + if(modifiers & HOTKEY_MOD_RCTRL) + result.push_back(KEY_RIGHTCTRL); + if(modifiers & HOTKEY_MOD_LALT) + result.push_back(KEY_LEFTALT); + if(modifiers & HOTKEY_MOD_RALT) + result.push_back(KEY_RIGHTALT); + if(modifiers & HOTKEY_MOD_LSUPER) + result.push_back(KEY_LEFTMETA); + if(modifiers & HOTKEY_MOD_RSUPER) + result.push_back(KEY_RIGHTMETA); + return result; + } + + static std::string linux_keys_to_command_string(const uint8_t *keys, size_t size) { + std::string result; + for(size_t i = 0; i < size; ++i) { + if(!result.empty()) + result += "+"; + result += std::to_string(keys[i]); + } + return result; + } + + static bool x11_key_is_alpha_numerical(KeySym keysym) { + return (keysym >= XK_A && keysym <= XK_Z) || (keysym >= XK_a && keysym <= XK_z) || (keysym >= XK_0 && keysym <= XK_9); + } + + GlobalHotkeysLinux::GlobalHotkeysLinux(GrabType grab_type) : grab_type(grab_type) { + for(int i = 0; i < 2; ++i) { + read_pipes[i] = -1; + write_pipes[i] = -1; + } + } + + GlobalHotkeysLinux::~GlobalHotkeysLinux() { + if(write_pipes[PIPE_WRITE] > 0) { + char command[32]; + const int command_size = snprintf(command, sizeof(command), "exit\n"); + if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) { + fprintf(stderr, "Error: GlobalHotkeysLinux::~GlobalHotkeysLinux: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno)); + close_fds(); + } + } else { + close_fds(); + } + + if(process_id > 0) { + int status; + waitpid(process_id, &status, 0); + } + + close_fds(); + } + + void GlobalHotkeysLinux::close_fds() { + for(int i = 0; i < 2; ++i) { + if(read_pipes[i] > 0) { + close(read_pipes[i]); + read_pipes[i] = -1; + } + + if(write_pipes[i] > 0) { + close(write_pipes[i]); + write_pipes[i] = -1; + } + } + + if(read_file) { + fclose(read_file); + read_file = nullptr; + } + } + + bool GlobalHotkeysLinux::start() { + const char *grab_type_arg = grab_type_to_arg(grab_type); + const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; + const char *user_homepath = getenv("HOME"); + if(!user_homepath) + user_homepath = "/tmp"; + + if(process_id > 0) + return false; + + if(pipe(read_pipes) == -1) + return false; + + if(pipe(write_pipes) == -1) { + for(int i = 0; i < 2; ++i) { + close(read_pipes[i]); + read_pipes[i] = -1; + } + return false; + } + + const pid_t pid = vfork(); + if(pid == -1) { + perror("Failed to vfork"); + for(int i = 0; i < 2; ++i) { + close(read_pipes[i]); + close(write_pipes[i]); + read_pipes[i] = -1; + write_pipes[i] = -1; + } + return false; + } else if(pid == 0) { /* child */ + dup2(read_pipes[PIPE_WRITE], STDOUT_FILENO); + for(int i = 0; i < 2; ++i) { + close(read_pipes[i]); + } + + dup2(write_pipes[PIPE_READ], STDIN_FILENO); + for(int i = 0; i < 2; ++i) { + close(write_pipes[i]); + } + + if(inside_flatpak) { + const char *args[] = { "flatpak-spawn", "--host", "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy", "launch-gsr-global-hotkeys", user_homepath, grab_type_arg, nullptr }; + execvp(args[0], (char* const*)args); + } else { + const char *args[] = { "gsr-global-hotkeys", grab_type_arg, nullptr }; + execvp(args[0], (char* const*)args); + } + + perror("gsr-global-hotkeys"); + _exit(127); + } else { /* parent */ + process_id = pid; + + close(read_pipes[PIPE_WRITE]); + read_pipes[PIPE_WRITE] = -1; + + close(write_pipes[PIPE_READ]); + write_pipes[PIPE_READ] = -1; + + fcntl(read_pipes[PIPE_READ], F_SETFL, fcntl(read_pipes[PIPE_READ], F_GETFL) | O_NONBLOCK); + read_file = fdopen(read_pipes[PIPE_READ], "r"); + if(read_file) + read_pipes[PIPE_READ] = -1; + else + fprintf(stderr, "fdopen failed for read, error: %s\n", strerror(errno)); + } + + return true; + } + + bool GlobalHotkeysLinux::bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) { + if(process_id <= 0) + return false; + + if(bound_actions_by_id.find(id) != bound_actions_by_id.end()) + return false; + + if(id.find(' ') != std::string::npos || id.find('\n') != std::string::npos) { + fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: id \"%s\" contains either space or newline\n", id.c_str()); + return false; + } + + if(hotkey.key == 0) { + //fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a key\n"); + return false; + } + + if(hotkey.modifiers == 0 && x11_key_is_alpha_numerical(hotkey.key)) { + //fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a modifier\n"); + return false; + } + + mgl_context *context = mgl_get_context(); + Display *display = (Display*)context->connection; + const uint8_t keycode = x11_keycode_to_linux_keycode(XKeysymToKeycode(display, hotkey.key)); + const std::vector modifiers = modifiers_to_linux_keys(hotkey.modifiers); + const std::string modifiers_command = linux_keys_to_command_string(modifiers.data(), modifiers.size()); + + char command[256]; + int command_size = 0; + if(modifiers_command.empty()) + command_size = snprintf(command, sizeof(command), "bind %s %d\n", id.c_str(), (int)keycode); + else + command_size = snprintf(command, sizeof(command), "bind %s %d+%s\n", id.c_str(), (int)keycode, modifiers_command.c_str()); + + if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) { + fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno)); + return false; + } + + bound_actions_by_id[id] = std::move(callback); + return true; + } + + void GlobalHotkeysLinux::unbind_all_keys() { + if(process_id <= 0) + return; + + if(bound_actions_by_id.empty()) + return; + + char command[32]; + const int command_size = snprintf(command, sizeof(command), "unbind_all\n"); + if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) { + fprintf(stderr, "Error: GlobalHotkeysLinux::unbind_all_keys: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno)); + } + bound_actions_by_id.clear(); + } + + 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; + } + + std::string action; + char buffer[256]; + while(true) { + char *line = fgets(buffer, sizeof(buffer), read_file); + if(!line) + break; + + int line_len = strlen(line); + if(line_len == 0) + continue; + + if(line[line_len - 1] == '\n') { + line[line_len - 1] = '\0'; + --line_len; + } + + action = line; + auto it = bound_actions_by_id.find(action); + if(it != bound_actions_by_id.end()) + it->second(action); + } + } +} diff --git a/src/GlobalHotkeys/GlobalHotkeysX11.cpp b/src/GlobalHotkeys/GlobalHotkeysX11.cpp new file mode 100644 index 0000000..bc79ce8 --- /dev/null +++ b/src/GlobalHotkeys/GlobalHotkeysX11.cpp @@ -0,0 +1,201 @@ +#include "../../include/GlobalHotkeys/GlobalHotkeysX11.hpp" +#include +#include +#include + +namespace gsr { + static bool x_failed = false; + static int xerror_grab_error(Display*, XErrorEvent*) { + x_failed = true; + return 0; + } + + static unsigned int x11_get_numlock_mask(Display *dpy) { + unsigned int numlockmask = 0; + KeyCode numlock_keycode = XKeysymToKeycode(dpy, XK_Num_Lock); + XModifierKeymap *modmap = XGetModifierMapping(dpy); + if(modmap) { + for(int i = 0; i < 8; ++i) { + for(int j = 0; j < modmap->max_keypermod; ++j) { + if(modmap->modifiermap[i * modmap->max_keypermod + j] == numlock_keycode) + numlockmask = (1 << i); + } + } + XFreeModifiermap(modmap); + } + return numlockmask; + } + + static KeySym mgl_key_to_key_sym(mgl::Keyboard::Key key) { + switch(key) { + case mgl::Keyboard::Z: return XK_z; + case mgl::Keyboard::F7: return XK_F7; + case mgl::Keyboard::F8: return XK_F8; + case mgl::Keyboard::F9: return XK_F9; + case mgl::Keyboard::F10: return XK_F10; + default: return None; + } + } + + static uint32_t mgl_key_modifiers_to_x11_modifier_mask(const mgl::Event::KeyEvent &key_event) { + uint32_t mask = 0; + if(key_event.shift) + mask |= ShiftMask; + if(key_event.control) + mask |= ControlMask; + if(key_event.alt) + mask |= Mod1Mask; + if(key_event.system) + mask |= Mod4Mask; + return mask; + } + + static uint32_t modifiers_to_x11_modifiers(uint32_t modifiers) { + uint32_t result = 0; + if(modifiers & HOTKEY_MOD_LSHIFT) + result |= ShiftMask; + if(modifiers & HOTKEY_MOD_RSHIFT) + result |= ShiftMask; + if(modifiers & HOTKEY_MOD_LCTRL) + result |= ControlMask; + if(modifiers & HOTKEY_MOD_RCTRL) + result |= ControlMask; + if(modifiers & HOTKEY_MOD_LALT) + result |= Mod1Mask; + if(modifiers & HOTKEY_MOD_RALT) + result |= Mod5Mask; + if(modifiers & HOTKEY_MOD_LSUPER) + result |= Mod4Mask; + if(modifiers & HOTKEY_MOD_RSUPER) + result |= Mod4Mask; + return result; + } + + GlobalHotkeysX11::GlobalHotkeysX11() { + dpy = XOpenDisplay(NULL); + if(!dpy) + fprintf(stderr, "GlobalHotkeysX11 error: failed to connect to X11 server, global hotkeys wont be available\n"); + } + + GlobalHotkeysX11::~GlobalHotkeysX11() { + if(dpy) { + XCloseDisplay(dpy); + dpy = nullptr; + } + } + + bool GlobalHotkeysX11::bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) { + if(!dpy) + return false; + + auto it = bound_keys_by_id.find(id); + if(it != bound_keys_by_id.end()) + return false; + + x_failed = false; + XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error); + + const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(hotkey.modifiers); + unsigned int numlock_mask = x11_get_numlock_mask(dpy); + unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask }; + for(int i = 0; i < 4; ++i) { + XGrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy), False, GrabModeAsync, GrabModeAsync); + } + XSync(dpy, False); + + if(x_failed) { + for(int i = 0; i < 4; ++i) { + XUngrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy)); + } + XSync(dpy, False); + XSetErrorHandler(prev_xerror); + return false; + } else { + XSetErrorHandler(prev_xerror); + bound_keys_by_id[id] = { hotkey, std::move(callback) }; + return true; + } + } + + void GlobalHotkeysX11::unbind_key_press(const std::string &id) { + if(!dpy) + return; + + auto it = bound_keys_by_id.find(id); + if(it == bound_keys_by_id.end()) + return; + + x_failed = false; + XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error); + + const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(it->second.hotkey.modifiers); + unsigned int numlock_mask = x11_get_numlock_mask(dpy); + unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask }; + for(int i = 0; i < 4; ++i) { + XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy)); + } + XSync(dpy, False); + + XSetErrorHandler(prev_xerror); + bound_keys_by_id.erase(id); + } + + void GlobalHotkeysX11::unbind_all_keys() { + if(!dpy) + return; + + x_failed = false; + XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error); + + unsigned int numlock_mask = x11_get_numlock_mask(dpy); + unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask }; + for(auto it = bound_keys_by_id.begin(); it != bound_keys_by_id.end();) { + const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(it->second.hotkey.modifiers); + for(int i = 0; i < 4; ++i) { + XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy)); + } + } + bound_keys_by_id.clear(); + XSync(dpy, False); + + XSetErrorHandler(prev_xerror); + } + + void GlobalHotkeysX11::poll_events() { + if(!dpy) + return; + + while(XPending(dpy)) { + XNextEvent(dpy, &xev); + if(xev.type == KeyPress) { + const KeySym key_sym = XLookupKeysym(&xev.xkey, 0); + call_hotkey_callback({ (uint32_t)key_sym, xev.xkey.state }); + } + } + } + + bool GlobalHotkeysX11::on_event(mgl::Event &event) { + if(event.type != mgl::Event::KeyPressed) + return true; + + // Note: not all keys are mapped in mgl_key_to_key_sym. If more hotkeys are added or changed then add the key mapping there + const KeySym key_sym = mgl_key_to_key_sym(event.key.code); + const uint32_t modifiers = mgl_key_modifiers_to_x11_modifier_mask(event.key); + return !call_hotkey_callback(Hotkey{(uint32_t)key_sym, modifiers}); + } + + static unsigned int key_state_without_locks(unsigned int key_state) { + return key_state & ~(Mod2Mask|LockMask); + } + + bool GlobalHotkeysX11::call_hotkey_callback(Hotkey hotkey) const { + const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(hotkey.modifiers); + for(const auto &[key, val] : bound_keys_by_id) { + if(val.hotkey.key == hotkey.key && key_state_without_locks(modifiers_to_x11_modifiers(val.hotkey.modifiers)) == key_state_without_locks(modifiers_x11)) { + val.callback(key); + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/GlobalHotkeysJoystick.cpp b/src/GlobalHotkeysJoystick.cpp deleted file mode 100644 index 822a73a..0000000 --- a/src/GlobalHotkeysJoystick.cpp +++ /dev/null @@ -1,317 +0,0 @@ -#include "../include/GlobalHotkeysJoystick.hpp" -#include -#include -#include -#include -#include - -namespace gsr { - static constexpr int button_pressed = 1; - static constexpr int cross_button = 0; - static constexpr int triangle_button = 2; - static constexpr int options_button = 9; - static constexpr int playstation_button = 10; - static constexpr int axis_up_down = 7; - static constexpr int axis_left_right = 6; - - // Returns -1 on error - static int get_js_dev_input_id_from_filepath(const char *dev_input_filepath) { - if(strncmp(dev_input_filepath, "/dev/input/js", 13) != 0) - return -1; - - int dev_input_id = -1; - if(sscanf(dev_input_filepath + 13, "%d", &dev_input_id) == 1) - return dev_input_id; - return -1; - } - - GlobalHotkeysJoystick::~GlobalHotkeysJoystick() { - if(event_fd > 0) { - const uint64_t exit = 1; - write(event_fd, &exit, sizeof(exit)); - } - - if(read_thread.joinable()) - read_thread.join(); - - if(event_fd > 0) - close(event_fd); - - for(int i = 0; i < num_poll_fd; ++i) { - close(poll_fd[i].fd); - } - } - - bool GlobalHotkeysJoystick::start() { - if(num_poll_fd > 0) - return false; - - event_fd = eventfd(0, 0); - if(event_fd <= 0) - return false; - - event_index = num_poll_fd; - poll_fd[num_poll_fd] = { - event_fd, - POLLIN, - 0 - }; - extra_data[num_poll_fd] = { - -1 - }; - ++num_poll_fd; - - if(!hotplug.start()) { - fprintf(stderr, "Warning: failed to setup hotplugging\n"); - } else { - hotplug_poll_index = num_poll_fd; - poll_fd[num_poll_fd] = { - hotplug.steal_fd(), - POLLIN, - 0 - }; - extra_data[num_poll_fd] = { - -1 - }; - ++num_poll_fd; - } - - char dev_input_path[128]; - for(int i = 0; i < 8; ++i) { - snprintf(dev_input_path, sizeof(dev_input_path), "/dev/input/js%d", i); - add_device(dev_input_path, false); - } - - if(num_poll_fd == 0) - fprintf(stderr, "Info: no joysticks found, assuming they might be connected later\n"); - - read_thread = std::thread(&GlobalHotkeysJoystick::read_events, this); - return true; - } - - bool GlobalHotkeysJoystick::bind_action(const std::string &id, GlobalHotkeyCallback callback) { - if(num_poll_fd == 0) - return false; - return bound_actions_by_id.insert(std::make_pair(id, std::move(callback))).second; - } - - void GlobalHotkeysJoystick::poll_events() { - if(num_poll_fd == 0) - return; - - if(save_replay) { - save_replay = false; - auto it = bound_actions_by_id.find("save_replay"); - if(it != bound_actions_by_id.end()) - it->second("save_replay"); - } - - if(save_1_min_replay) { - save_1_min_replay = false; - auto it = bound_actions_by_id.find("save_1_min_replay"); - if(it != bound_actions_by_id.end()) - it->second("save_1_min_replay"); - } - - if(save_10_min_replay) { - save_10_min_replay = false; - auto it = bound_actions_by_id.find("save_10_min_replay"); - if(it != bound_actions_by_id.end()) - it->second("save_10_min_replay"); - } - - if(take_screenshot) { - take_screenshot = false; - auto it = bound_actions_by_id.find("take_screenshot"); - if(it != bound_actions_by_id.end()) - it->second("take_screenshot"); - } - - if(toggle_record) { - toggle_record = false; - auto it = bound_actions_by_id.find("toggle_record"); - if(it != bound_actions_by_id.end()) - it->second("toggle_record"); - } - - if(toggle_replay) { - toggle_replay = false; - auto it = bound_actions_by_id.find("toggle_replay"); - if(it != bound_actions_by_id.end()) - it->second("toggle_replay"); - } - - if(toggle_show) { - toggle_show = false; - auto it = bound_actions_by_id.find("toggle_show"); - if(it != bound_actions_by_id.end()) - it->second("toggle_show"); - } - } - - void GlobalHotkeysJoystick::read_events() { - js_event event; - while(poll(poll_fd, num_poll_fd, -1) > 0) { - for(int i = 0; i < num_poll_fd; ++i) { - if(poll_fd[i].revents & (POLLHUP|POLLERR|POLLNVAL)) { - if(i == event_index) - goto done; - - if(remove_poll_fd(i)) - --i; // This item was removed so we want to repeat the same index to continue to the next item - - continue; - } - - if(!(poll_fd[i].revents & POLLIN)) - continue; - - if(i == event_index) { - goto done; - } else if(i == hotplug_poll_index) { - hotplug.process_event_data(poll_fd[i].fd, [&](HotplugAction hotplug_action, const char *devname) { - char dev_input_filepath[1024]; - snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/%s", devname); - switch(hotplug_action) { - case HotplugAction::ADD: { - // Cant open the /dev/input device immediately or it fails. - // TODO: Remove this hack when a better solution is found. - usleep(50 * 1000); - add_device(dev_input_filepath); - break; - } - case HotplugAction::REMOVE: { - if(remove_device(dev_input_filepath)) - --i; // This item was removed so we want to repeat the same index to continue to the next item - break; - } - } - }); - } else { - process_js_event(poll_fd[i].fd, event); - } - } - } - - done: - ; - } - - void GlobalHotkeysJoystick::process_js_event(int fd, js_event &event) { - if(read(fd, &event, sizeof(event)) != sizeof(event)) - return; - - if((event.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) { - switch(event.number) { - case playstation_button: { - playstation_button_pressed = event.value == button_pressed; - break; - } - case options_button: { - if(playstation_button_pressed && event.value == button_pressed) - toggle_show = true; - break; - } - case cross_button: { - if(playstation_button_pressed && event.value == button_pressed) - save_1_min_replay = true; - break; - } - case triangle_button: { - if(playstation_button_pressed && event.value == button_pressed) - save_10_min_replay = true; - break; - } - } - } else if((event.type & JS_EVENT_AXIS) == JS_EVENT_AXIS && playstation_button_pressed) { - const int trigger_threshold = 16383; - const bool prev_up_pressed = up_pressed; - const bool prev_down_pressed = down_pressed; - const bool prev_left_pressed = left_pressed; - const bool prev_right_pressed = right_pressed; - - if(event.number == axis_up_down) { - up_pressed = event.value <= -trigger_threshold; - down_pressed = event.value >= trigger_threshold; - } else if(event.number == axis_left_right) { - left_pressed = event.value <= -trigger_threshold; - right_pressed = event.value >= trigger_threshold; - } - - if(up_pressed && !prev_up_pressed) - take_screenshot = true; - else if(down_pressed && !prev_down_pressed) - save_replay = true; - else if(left_pressed && !prev_left_pressed) - toggle_record = true; - else if(right_pressed && !prev_right_pressed) - toggle_replay = true; - } - } - - bool GlobalHotkeysJoystick::add_device(const char *dev_input_filepath, bool print_error) { - if(num_poll_fd >= max_js_poll_fd) { - fprintf(stderr, "Warning: failed to add joystick device %s, too many joysticks have been added\n", dev_input_filepath); - return false; - } - - const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath); - if(dev_input_id == -1) - return false; - - const int fd = open(dev_input_filepath, O_RDONLY); - if(fd <= 0) { - if(print_error) - fprintf(stderr, "Error: failed to add joystick %s, error: %s\n", dev_input_filepath, strerror(errno)); - return false; - } - - poll_fd[num_poll_fd] = { - fd, - POLLIN, - 0 - }; - - extra_data[num_poll_fd] = { - dev_input_id - }; - - ++num_poll_fd; - fprintf(stderr, "Info: added joystick: %s\n", dev_input_filepath); - return true; - } - - bool GlobalHotkeysJoystick::remove_device(const char *dev_input_filepath) { - const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath); - if(dev_input_id == -1) - return false; - - const int poll_fd_index = get_poll_fd_index_by_dev_input_id(dev_input_id); - if(poll_fd_index == -1) - return false; - - fprintf(stderr, "Info: removed joystick: %s\n", dev_input_filepath); - return remove_poll_fd(poll_fd_index); - } - - bool GlobalHotkeysJoystick::remove_poll_fd(int index) { - if(index < 0 || index >= num_poll_fd) - return false; - - close(poll_fd[index].fd); - for(int i = index + 1; i < num_poll_fd; ++i) { - poll_fd[i - 1] = poll_fd[i]; - extra_data[i - 1] = extra_data[i]; - } - --num_poll_fd; - return true; - } - - int GlobalHotkeysJoystick::get_poll_fd_index_by_dev_input_id(int dev_input_id) const { - for(int i = 0; i < num_poll_fd; ++i) { - if(dev_input_id == extra_data[i].dev_input_id) - return i; - } - return -1; - } -} diff --git a/src/GlobalHotkeysLinux.cpp b/src/GlobalHotkeysLinux.cpp deleted file mode 100644 index d780916..0000000 --- a/src/GlobalHotkeysLinux.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#include "../include/GlobalHotkeysLinux.hpp" -#include -#include -#include -#include - -extern "C" { -#include -} -#include -#include -#include - -#define PIPE_READ 0 -#define PIPE_WRITE 1 - -namespace gsr { - static const char* grab_type_to_arg(GlobalHotkeysLinux::GrabType grab_type) { - switch(grab_type) { - case GlobalHotkeysLinux::GrabType::ALL: return "--all"; - case GlobalHotkeysLinux::GrabType::VIRTUAL: return "--virtual"; - } - return "--all"; - } - - static inline uint8_t x11_keycode_to_linux_keycode(uint8_t code) { - return code - 8; - } - - static std::vector modifiers_to_linux_keys(uint32_t modifiers) { - std::vector result; - if(modifiers & HOTKEY_MOD_LSHIFT) - result.push_back(KEY_LEFTSHIFT); - if(modifiers & HOTKEY_MOD_RSHIFT) - result.push_back(KEY_RIGHTSHIFT); - if(modifiers & HOTKEY_MOD_LCTRL) - result.push_back(KEY_LEFTCTRL); - if(modifiers & HOTKEY_MOD_RCTRL) - result.push_back(KEY_RIGHTCTRL); - if(modifiers & HOTKEY_MOD_LALT) - result.push_back(KEY_LEFTALT); - if(modifiers & HOTKEY_MOD_RALT) - result.push_back(KEY_RIGHTALT); - if(modifiers & HOTKEY_MOD_LSUPER) - result.push_back(KEY_LEFTMETA); - if(modifiers & HOTKEY_MOD_RSUPER) - result.push_back(KEY_RIGHTMETA); - return result; - } - - static std::string linux_keys_to_command_string(const uint8_t *keys, size_t size) { - std::string result; - for(size_t i = 0; i < size; ++i) { - if(!result.empty()) - result += "+"; - result += std::to_string(keys[i]); - } - return result; - } - - static bool x11_key_is_alpha_numerical(KeySym keysym) { - return (keysym >= XK_A && keysym <= XK_Z) || (keysym >= XK_a && keysym <= XK_z) || (keysym >= XK_0 && keysym <= XK_9); - } - - GlobalHotkeysLinux::GlobalHotkeysLinux(GrabType grab_type) : grab_type(grab_type) { - for(int i = 0; i < 2; ++i) { - read_pipes[i] = -1; - write_pipes[i] = -1; - } - } - - GlobalHotkeysLinux::~GlobalHotkeysLinux() { - if(write_pipes[PIPE_WRITE] > 0) { - char command[32]; - const int command_size = snprintf(command, sizeof(command), "exit\n"); - if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) { - fprintf(stderr, "Error: GlobalHotkeysLinux::~GlobalHotkeysLinux: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno)); - close_fds(); - } - } else { - close_fds(); - } - - if(process_id > 0) { - int status; - waitpid(process_id, &status, 0); - } - - close_fds(); - } - - void GlobalHotkeysLinux::close_fds() { - for(int i = 0; i < 2; ++i) { - if(read_pipes[i] > 0) { - close(read_pipes[i]); - read_pipes[i] = -1; - } - - if(write_pipes[i] > 0) { - close(write_pipes[i]); - write_pipes[i] = -1; - } - } - - if(read_file) { - fclose(read_file); - read_file = nullptr; - } - } - - bool GlobalHotkeysLinux::start() { - const char *grab_type_arg = grab_type_to_arg(grab_type); - const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; - const char *user_homepath = getenv("HOME"); - if(!user_homepath) - user_homepath = "/tmp"; - - if(process_id > 0) - return false; - - if(pipe(read_pipes) == -1) - return false; - - if(pipe(write_pipes) == -1) { - for(int i = 0; i < 2; ++i) { - close(read_pipes[i]); - read_pipes[i] = -1; - } - return false; - } - - const pid_t pid = vfork(); - if(pid == -1) { - perror("Failed to vfork"); - for(int i = 0; i < 2; ++i) { - close(read_pipes[i]); - close(write_pipes[i]); - read_pipes[i] = -1; - write_pipes[i] = -1; - } - return false; - } else if(pid == 0) { /* child */ - dup2(read_pipes[PIPE_WRITE], STDOUT_FILENO); - for(int i = 0; i < 2; ++i) { - close(read_pipes[i]); - } - - dup2(write_pipes[PIPE_READ], STDIN_FILENO); - for(int i = 0; i < 2; ++i) { - close(write_pipes[i]); - } - - if(inside_flatpak) { - const char *args[] = { "flatpak-spawn", "--host", "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy", "launch-gsr-global-hotkeys", user_homepath, grab_type_arg, nullptr }; - execvp(args[0], (char* const*)args); - } else { - const char *args[] = { "gsr-global-hotkeys", grab_type_arg, nullptr }; - execvp(args[0], (char* const*)args); - } - - perror("gsr-global-hotkeys"); - _exit(127); - } else { /* parent */ - process_id = pid; - - close(read_pipes[PIPE_WRITE]); - read_pipes[PIPE_WRITE] = -1; - - close(write_pipes[PIPE_READ]); - write_pipes[PIPE_READ] = -1; - - fcntl(read_pipes[PIPE_READ], F_SETFL, fcntl(read_pipes[PIPE_READ], F_GETFL) | O_NONBLOCK); - read_file = fdopen(read_pipes[PIPE_READ], "r"); - if(read_file) - read_pipes[PIPE_READ] = -1; - else - fprintf(stderr, "fdopen failed for read, error: %s\n", strerror(errno)); - } - - return true; - } - - bool GlobalHotkeysLinux::bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) { - if(process_id <= 0) - return false; - - if(bound_actions_by_id.find(id) != bound_actions_by_id.end()) - return false; - - if(id.find(' ') != std::string::npos || id.find('\n') != std::string::npos) { - fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: id \"%s\" contains either space or newline\n", id.c_str()); - return false; - } - - if(hotkey.key == 0) { - //fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a key\n"); - return false; - } - - if(hotkey.modifiers == 0 && x11_key_is_alpha_numerical(hotkey.key)) { - //fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a modifier\n"); - return false; - } - - mgl_context *context = mgl_get_context(); - Display *display = (Display*)context->connection; - const uint8_t keycode = x11_keycode_to_linux_keycode(XKeysymToKeycode(display, hotkey.key)); - const std::vector modifiers = modifiers_to_linux_keys(hotkey.modifiers); - const std::string modifiers_command = linux_keys_to_command_string(modifiers.data(), modifiers.size()); - - char command[256]; - int command_size = 0; - if(modifiers_command.empty()) - command_size = snprintf(command, sizeof(command), "bind %s %d\n", id.c_str(), (int)keycode); - else - command_size = snprintf(command, sizeof(command), "bind %s %d+%s\n", id.c_str(), (int)keycode, modifiers_command.c_str()); - - if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) { - fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno)); - return false; - } - - bound_actions_by_id[id] = std::move(callback); - return true; - } - - void GlobalHotkeysLinux::unbind_all_keys() { - if(process_id <= 0) - return; - - if(bound_actions_by_id.empty()) - return; - - char command[32]; - const int command_size = snprintf(command, sizeof(command), "unbind_all\n"); - if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) { - fprintf(stderr, "Error: GlobalHotkeysLinux::unbind_all_keys: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno)); - } - bound_actions_by_id.clear(); - } - - 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; - } - - std::string action; - char buffer[256]; - while(true) { - char *line = fgets(buffer, sizeof(buffer), read_file); - if(!line) - break; - - int line_len = strlen(line); - if(line_len == 0) - continue; - - if(line[line_len - 1] == '\n') { - line[line_len - 1] = '\0'; - --line_len; - } - - action = line; - auto it = bound_actions_by_id.find(action); - if(it != bound_actions_by_id.end()) - it->second(action); - } - } -} diff --git a/src/GlobalHotkeysX11.cpp b/src/GlobalHotkeysX11.cpp deleted file mode 100644 index 9af2607..0000000 --- a/src/GlobalHotkeysX11.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "../include/GlobalHotkeysX11.hpp" -#include -#include -#include - -namespace gsr { - static bool x_failed = false; - static int xerror_grab_error(Display*, XErrorEvent*) { - x_failed = true; - return 0; - } - - static unsigned int x11_get_numlock_mask(Display *dpy) { - unsigned int numlockmask = 0; - KeyCode numlock_keycode = XKeysymToKeycode(dpy, XK_Num_Lock); - XModifierKeymap *modmap = XGetModifierMapping(dpy); - if(modmap) { - for(int i = 0; i < 8; ++i) { - for(int j = 0; j < modmap->max_keypermod; ++j) { - if(modmap->modifiermap[i * modmap->max_keypermod + j] == numlock_keycode) - numlockmask = (1 << i); - } - } - XFreeModifiermap(modmap); - } - return numlockmask; - } - - static KeySym mgl_key_to_key_sym(mgl::Keyboard::Key key) { - switch(key) { - case mgl::Keyboard::Z: return XK_z; - case mgl::Keyboard::F7: return XK_F7; - case mgl::Keyboard::F8: return XK_F8; - case mgl::Keyboard::F9: return XK_F9; - case mgl::Keyboard::F10: return XK_F10; - default: return None; - } - } - - static uint32_t mgl_key_modifiers_to_x11_modifier_mask(const mgl::Event::KeyEvent &key_event) { - uint32_t mask = 0; - if(key_event.shift) - mask |= ShiftMask; - if(key_event.control) - mask |= ControlMask; - if(key_event.alt) - mask |= Mod1Mask; - if(key_event.system) - mask |= Mod4Mask; - return mask; - } - - static uint32_t modifiers_to_x11_modifiers(uint32_t modifiers) { - uint32_t result = 0; - if(modifiers & HOTKEY_MOD_LSHIFT) - result |= ShiftMask; - if(modifiers & HOTKEY_MOD_RSHIFT) - result |= ShiftMask; - if(modifiers & HOTKEY_MOD_LCTRL) - result |= ControlMask; - if(modifiers & HOTKEY_MOD_RCTRL) - result |= ControlMask; - if(modifiers & HOTKEY_MOD_LALT) - result |= Mod1Mask; - if(modifiers & HOTKEY_MOD_RALT) - result |= Mod5Mask; - if(modifiers & HOTKEY_MOD_LSUPER) - result |= Mod4Mask; - if(modifiers & HOTKEY_MOD_RSUPER) - result |= Mod4Mask; - return result; - } - - GlobalHotkeysX11::GlobalHotkeysX11() { - dpy = XOpenDisplay(NULL); - if(!dpy) - fprintf(stderr, "GlobalHotkeysX11 error: failed to connect to X11 server, global hotkeys wont be available\n"); - } - - GlobalHotkeysX11::~GlobalHotkeysX11() { - if(dpy) { - XCloseDisplay(dpy); - dpy = nullptr; - } - } - - bool GlobalHotkeysX11::bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) { - if(!dpy) - return false; - - auto it = bound_keys_by_id.find(id); - if(it != bound_keys_by_id.end()) - return false; - - x_failed = false; - XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error); - - const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(hotkey.modifiers); - unsigned int numlock_mask = x11_get_numlock_mask(dpy); - unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask }; - for(int i = 0; i < 4; ++i) { - XGrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy), False, GrabModeAsync, GrabModeAsync); - } - XSync(dpy, False); - - if(x_failed) { - for(int i = 0; i < 4; ++i) { - XUngrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy)); - } - XSync(dpy, False); - XSetErrorHandler(prev_xerror); - return false; - } else { - XSetErrorHandler(prev_xerror); - bound_keys_by_id[id] = { hotkey, std::move(callback) }; - return true; - } - } - - void GlobalHotkeysX11::unbind_key_press(const std::string &id) { - if(!dpy) - return; - - auto it = bound_keys_by_id.find(id); - if(it == bound_keys_by_id.end()) - return; - - x_failed = false; - XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error); - - const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(it->second.hotkey.modifiers); - unsigned int numlock_mask = x11_get_numlock_mask(dpy); - unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask }; - for(int i = 0; i < 4; ++i) { - XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy)); - } - XSync(dpy, False); - - XSetErrorHandler(prev_xerror); - bound_keys_by_id.erase(id); - } - - void GlobalHotkeysX11::unbind_all_keys() { - if(!dpy) - return; - - x_failed = false; - XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error); - - unsigned int numlock_mask = x11_get_numlock_mask(dpy); - unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask }; - for(auto it = bound_keys_by_id.begin(); it != bound_keys_by_id.end();) { - const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(it->second.hotkey.modifiers); - for(int i = 0; i < 4; ++i) { - XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy)); - } - } - bound_keys_by_id.clear(); - XSync(dpy, False); - - XSetErrorHandler(prev_xerror); - } - - void GlobalHotkeysX11::poll_events() { - if(!dpy) - return; - - while(XPending(dpy)) { - XNextEvent(dpy, &xev); - if(xev.type == KeyPress) { - const KeySym key_sym = XLookupKeysym(&xev.xkey, 0); - call_hotkey_callback({ (uint32_t)key_sym, xev.xkey.state }); - } - } - } - - bool GlobalHotkeysX11::on_event(mgl::Event &event) { - if(event.type != mgl::Event::KeyPressed) - return true; - - // Note: not all keys are mapped in mgl_key_to_key_sym. If more hotkeys are added or changed then add the key mapping there - const KeySym key_sym = mgl_key_to_key_sym(event.key.code); - const uint32_t modifiers = mgl_key_modifiers_to_x11_modifier_mask(event.key); - return !call_hotkey_callback(Hotkey{(uint32_t)key_sym, modifiers}); - } - - static unsigned int key_state_without_locks(unsigned int key_state) { - return key_state & ~(Mod2Mask|LockMask); - } - - bool GlobalHotkeysX11::call_hotkey_callback(Hotkey hotkey) const { - const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(hotkey.modifiers); - for(const auto &[key, val] : bound_keys_by_id) { - if(val.hotkey.key == hotkey.key && key_state_without_locks(modifiers_to_x11_modifiers(val.hotkey.modifiers)) == key_state_without_locks(modifiers_x11)) { - val.callback(key); - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/src/Overlay.cpp b/src/Overlay.cpp index 487b6bc..c423125 100644 --- a/src/Overlay.cpp +++ b/src/Overlay.cpp @@ -12,10 +12,10 @@ #include "../include/gui/Utils.hpp" #include "../include/gui/PageStack.hpp" #include "../include/WindowUtils.hpp" -#include "../include/GlobalHotkeys.hpp" -#include "../include/GlobalHotkeysLinux.hpp" -#include "../include/CursorTrackerX11.hpp" -#include "../include/CursorTrackerWayland.hpp" +#include "../include/GlobalHotkeys/GlobalHotkeys.hpp" +#include "../include/GlobalHotkeys/GlobalHotkeysLinux.hpp" +#include "../include/CursorTracker/CursorTrackerX11.hpp" +#include "../include/CursorTracker/CursorTrackerWayland.hpp" #include #include @@ -207,24 +207,21 @@ namespace gsr { return false; }*/ - // Returns the first monitor if not found. Assumes there is at least one monitor connected. static const Monitor* find_monitor_at_position(const std::vector &monitors, mgl::vec2i pos) { assert(!monitors.empty()); for(const Monitor &monitor : monitors) { if(mgl::IntRect(monitor.position, monitor.size).contains(pos)) return &monitor; } - return &monitors.front(); + return nullptr; } - // Returns the first monitor if not found. Assumes there is at least one monitor connected. static const Monitor* find_monitor_by_name(const std::vector &monitors, const std::string &name) { - assert(!monitors.empty()); for(const Monitor &monitor : monitors) { if(monitor.name == name) return &monitor; } - return &monitors.front(); + return nullptr; } static std::string get_power_supply_online_filepath() { @@ -894,10 +891,14 @@ namespace gsr { const Monitor *focused_monitor = nullptr; if(cursor_info) { focused_monitor = find_monitor_by_name(monitors, cursor_info->monitor_name); + if(!focused_monitor) + focused_monitor = &monitors.front(); cursor_position = cursor_info->position; } else { const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display); focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value); + if(!focused_monitor) + focused_monitor = &monitors.front(); } // Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused. diff --git a/src/gui/GlobalSettingsPage.cpp b/src/gui/GlobalSettingsPage.cpp index ccebb92..5444ae5 100644 --- a/src/gui/GlobalSettingsPage.cpp +++ b/src/gui/GlobalSettingsPage.cpp @@ -1,7 +1,6 @@ #include "../../include/gui/GlobalSettingsPage.hpp" #include "../../include/Overlay.hpp" -#include "../../include/GlobalHotkeys.hpp" #include "../../include/Theme.hpp" #include "../../include/Process.hpp" #include "../../include/gui/GsrPage.hpp" diff --git a/tools/gsr-global-hotkeys/keyboard_event.c b/tools/gsr-global-hotkeys/keyboard_event.c index bcfae6b..c7a54c3 100644 --- a/tools/gsr-global-hotkeys/keyboard_event.c +++ b/tools/gsr-global-hotkeys/keyboard_event.c @@ -404,8 +404,10 @@ static void keyboard_event_remove_event(keyboard_event *self, int index) { if(index < 0 || index >= self->num_event_polls) return; - ioctl(self->event_polls[index].fd, EVIOCGRAB, 0); - close(self->event_polls[index].fd); + if(self->event_polls[index].fd > 0) { + ioctl(self->event_polls[index].fd, EVIOCGRAB, 0); + close(self->event_polls[index].fd); + } free(self->event_extra_data[index].key_states); free(self->event_extra_data[index].key_presses_grabbed); @@ -435,7 +437,7 @@ static int setup_virtual_keyboard_input(const char *name) { success &= (ioctl(fd, UI_SET_EVBIT, EV_KEY) != -1); success &= (ioctl(fd, UI_SET_EVBIT, EV_REP) != -1); success &= (ioctl(fd, UI_SET_EVBIT, EV_REL) != -1); - success &= (ioctl(fd, UI_SET_EVBIT, EV_LED) != -1); + //success &= (ioctl(fd, UI_SET_EVBIT, EV_LED) != -1); success &= (ioctl(fd, UI_SET_MSCBIT, MSC_SCAN) != -1); for(int i = 1; i < KEY_MAX; ++i) { @@ -445,9 +447,9 @@ static int setup_virtual_keyboard_input(const char *name) { for(int i = 0; i < REL_MAX; ++i) { success &= (ioctl(fd, UI_SET_RELBIT, i) != -1); } - for(int i = 0; i < LED_MAX; ++i) { - success &= (ioctl(fd, UI_SET_LEDBIT, i) != -1); - } + // for(int i = 0; i < LED_MAX; ++i) { + // success &= (ioctl(fd, UI_SET_LEDBIT, i) != -1); + // } // success &= (ioctl(fd, UI_SET_EVBIT, EV_ABS) != -1); // success &= (ioctl(fd, UI_SET_ABSBIT, ABS_X) != -1); @@ -566,8 +568,10 @@ void keyboard_event_deinit(keyboard_event *self) { } for(int i = 0; i < self->num_event_polls; ++i) { - ioctl(self->event_polls[i].fd, EVIOCGRAB, 0); - close(self->event_polls[i].fd); + if(self->event_polls[i].fd > 0) { + ioctl(self->event_polls[i].fd, EVIOCGRAB, 0); + close(self->event_polls[i].fd); + } free(self->event_extra_data[i].key_states); free(self->event_extra_data[i].key_presses_grabbed); } -- cgit v1.2.3-70-g09d2