Skip to content

Commit

Permalink
input: Introduce basic hyprland HID classes
Browse files Browse the repository at this point in the history
Implements an intermediary HID class for mice, keyboards and touch devices, removing the old structs from WLClasses.hpp

Yes, virtual ones are duplicated a bit, but will likely be de-duped once wlr_input_device is not used anymore.
  • Loading branch information
vaxerski committed May 3, 2024
1 parent 1d2acbe commit 1237732
Show file tree
Hide file tree
Showing 39 changed files with 1,443 additions and 566 deletions.
5 changes: 1 addition & 4 deletions src/Compositor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2418,10 +2418,7 @@ void CCompositor::warpCursorTo(const Vector2D& pos, bool force) {
if (*PNOWARPS && !force)
return;

if (!m_sSeat.mouse)
return;

wlr_cursor_warp(m_sWLRCursor, m_sSeat.mouse->mouse, pos.x, pos.y);
wlr_cursor_warp(m_sWLRCursor, nullptr, pos.x, pos.y);

const auto PMONITORNEW = getMonitorFromVector(pos);
if (PMONITORNEW != m_pLastMonitor)
Expand Down
53 changes: 29 additions & 24 deletions src/debug/HyprCtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include "../config/ConfigValue.hpp"
#include "../managers/CursorManager.hpp"
#include "../hyprerror/HyprError.hpp"
#include "../devices/IPointer.hpp"
#include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp"

static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
Expand Down Expand Up @@ -514,23 +517,24 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "{\n";
result += "\"mice\": [\n";

for (auto& m : g_pInputManager->m_lMice) {
for (auto& m : g_pInputManager->m_vPointers) {
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}",
"defaultSpeed": {:.5f}
}},)#",
(uintptr_t)&m, escapeJSONStrings(m.name),
wlr_input_device_is_libinput(m.mouse) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(m.mouse)) : 0.f);
(uintptr_t)m.get(), escapeJSONStrings(m->hlName),
wlr_input_device_is_libinput(&m->wlr()->base) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base)) :
0.f);
}

trimTrailingComma(result);
result += "\n],\n";

result += "\"keyboards\": [\n";
for (auto& k : g_pInputManager->m_lKeyboards) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
for (auto& k : g_pInputManager->m_vKeyboards) {
const auto KM = k->getActiveLayout();
result += std::format(
R"#( {{
"address": "0x{:x}",
Expand All @@ -543,9 +547,9 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"active_keymap": "{}",
"main": {}
}},)#",
(uintptr_t)&k, escapeJSONStrings(k.name), escapeJSONStrings(k.currentRules.rules), escapeJSONStrings(k.currentRules.model),
escapeJSONStrings(k.currentRules.layout), escapeJSONStrings(k.currentRules.variant), escapeJSONStrings(k.currentRules.options), escapeJSONStrings(KM),
(k.active ? "true" : "false"));
(uintptr_t)k.get(), escapeJSONStrings(k->hlName), escapeJSONStrings(k->currentRules.rules), escapeJSONStrings(k->currentRules.model),
escapeJSONStrings(k->currentRules.layout), escapeJSONStrings(k->currentRules.variant), escapeJSONStrings(k->currentRules.options), escapeJSONStrings(KM),
(k->active ? "true" : "false"));
}

trimTrailingComma(result);
Expand Down Expand Up @@ -590,13 +594,13 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {

result += "\"touch\": [\n";

for (auto& d : g_pInputManager->m_lTouchDevices) {
for (auto& d : g_pInputManager->m_vTouches) {
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, escapeJSONStrings(d.name));
(uintptr_t)d.get(), escapeJSONStrings(d->hlName));
}

trimTrailingComma(result);
Expand All @@ -621,19 +625,20 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
} else {
result += "mice:\n";

for (auto& m : g_pInputManager->m_lMice) {
result += std::format(
"\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)&m, m.name,
(wlr_input_device_is_libinput(m.mouse) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(m.mouse)) : 0.f));
for (auto& m : g_pInputManager->m_vPointers) {
result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)m.get(), m->hlName,
(wlr_input_device_is_libinput(&m->wlr()->base) ?
libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base)) :
0.f));
}

result += "\n\nKeyboards:\n";

for (auto& k : g_pInputManager->m_lKeyboards) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
for (auto& k : g_pInputManager->m_vKeyboards) {
const auto KM = k->getActiveLayout();
result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\tmain: {}\n",
(uintptr_t)&k, k.name, k.currentRules.rules, k.currentRules.model, k.currentRules.layout, k.currentRules.variant, k.currentRules.options, KM,
(k.active ? "yes" : "no"));
(uintptr_t)k.get(), k->hlName, k->currentRules.rules, k->currentRules.model, k->currentRules.layout, k->currentRules.variant,
k->currentRules.options, KM, (k->active ? "yes" : "no"));
}

result += "\n\nTablets:\n";
Expand All @@ -652,8 +657,8 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {

result += "\n\nTouch:\n";

for (auto& d : g_pInputManager->m_lTouchDevices) {
result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.name);
for (auto& d : g_pInputManager->m_vTouches) {
result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)d.get(), d->hlName);
}

result += "\n\nSwitches:\n";
Expand Down Expand Up @@ -1069,13 +1074,13 @@ std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string requ
const auto CMD = vars[2];

// get kb
const auto PKEYBOARD = std::find_if(g_pInputManager->m_lKeyboards.begin(), g_pInputManager->m_lKeyboards.end(),
[&](const SKeyboard& other) { return other.name == g_pInputManager->deviceNameToInternalString(KB); });
const auto PKEYBOARD = std::find_if(g_pInputManager->m_vKeyboards.begin(), g_pInputManager->m_vKeyboards.end(),
[&](const auto& other) { return other->hlName == g_pInputManager->deviceNameToInternalString(KB); });

if (PKEYBOARD == g_pInputManager->m_lKeyboards.end())
if (PKEYBOARD == g_pInputManager->m_vKeyboards.end())
return "device not found";

const auto PWLRKEYBOARD = wlr_keyboard_from_input_device(PKEYBOARD->keyboard);
const auto PWLRKEYBOARD = (*PKEYBOARD)->wlr();
const auto LAYOUTS = xkb_keymap_num_layouts(PWLRKEYBOARD->keymap);
xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) {
Expand Down
6 changes: 3 additions & 3 deletions src/desktop/LayerSurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ void CLayerSurface::onMap() {

const bool GRABSFOCUS = layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained());
(g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained());

if (GRABSFOCUS) {
g_pInputManager->releaseAllMouseButtons();
Expand Down Expand Up @@ -383,15 +383,15 @@ void CLayerSurface::onCommit() {
realSize.setValueAndWarp(geometry.size());
}

if (layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained()) // don't focus if constrained
if (layerSurface->current.keyboard_interactive && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained
&& !keyboardExclusive && mapped) {
g_pCompositor->focusSurface(layerSurface->surface);

const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, layerSurface->surface, LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
g_pInputManager->m_bEmptyFocusCursorSet = false;
} else if (!layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained()) && keyboardExclusive) {
} else if (!layerSurface->current.keyboard_interactive && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) {
g_pInputManager->refocus();
}

Expand Down
26 changes: 26 additions & 0 deletions src/devices/IHID.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <cstdint>
#include <string>
#include "../helpers/signal/Signal.hpp"

enum eHIDCapabilityType : uint32_t {
HID_INPUT_CAPABILITY_KEYBOARD = (1 << 0),
HID_INPUT_CAPABILITY_POINTER = (1 << 1),
HID_INPUT_CAPABILITY_TOUCH = (1 << 2),
};

/*
Base class for a HID device.
This could be a keyboard, a mouse, or a touchscreen.
*/
class IHID {
public:
virtual uint32_t getCapabilities() = 0;

struct {
CSignal destroy;
} events;

std::string deviceName;
};
134 changes: 134 additions & 0 deletions src/devices/IKeyboard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#include "IKeyboard.hpp"
#include "../defines.hpp"
#include "../helpers/VarList.hpp"
#include "../managers/input/InputManager.hpp"

uint32_t IKeyboard::getCapabilities() {
return HID_INPUT_CAPABILITY_KEYBOARD;
}

IKeyboard::~IKeyboard() {
events.destroy.emit();

if (!xkbTranslationState)
return;

xkb_state_unref(xkbTranslationState);
xkbTranslationState = nullptr;
}

void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {

if (xkbTranslationState)
xkb_state_unref(xkbTranslationState);

if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
xkbTranslationState = xkb_state_new(keymap);
return;
}

const auto WLRKB = wlr();
const auto KEYMAP = WLRKB->keymap;
const auto STATE = WLRKB->xkb_state;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);

const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS);

for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE)) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from an active index {}", (uintptr_t)this, i);

CVarList keyboardLayouts(currentRules.layout, 0, ',');
CVarList keyboardModels(currentRules.model, 0, ',');
CVarList keyboardVariants(currentRules.variant, 0, ',');

xkb_rule_names rules = {.rules = "", .model = "", .layout = "", .variant = "", .options = ""};

std::string layout, model, variant;
layout = keyboardLayouts[i % keyboardLayouts.size()];
model = keyboardModels[i % keyboardModels.size()];
variant = keyboardVariants[i % keyboardVariants.size()];

rules.layout = layout.c_str();
rules.model = model.c_str();
rules.variant = variant.c_str();

auto KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);

if (!KEYMAP) {
Debug::log(ERR, "updateXKBTranslationState: keymap failed 1, fallback without model/variant");
rules.model = "";
rules.variant = "";
KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}

if (!KEYMAP) {
Debug::log(ERR, "updateXKBTranslationState: keymap failed 2, fallback to us");
rules.layout = "us";
KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}

xkbTranslationState = xkb_state_new(KEYMAP);

xkb_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT);

return;
}
}

Debug::log(LOG, "Updating keyboard {:x}'s translation state from an unknown index", (uintptr_t)this);

xkb_rule_names rules = {
.rules = currentRules.rules.c_str(),
.model = currentRules.model.c_str(),
.layout = currentRules.layout.c_str(),
.variant = currentRules.variant.c_str(),
.options = currentRules.options.c_str(),
};

const auto NEWKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);

xkbTranslationState = xkb_state_new(NEWKEYMAP);

xkb_keymap_unref(NEWKEYMAP);
xkb_context_unref(PCONTEXT);
}

std::string IKeyboard::getActiveLayout() {
const auto WLRKB = wlr();
const auto KEYMAP = WLRKB->keymap;
const auto STATE = WLRKB->xkb_state;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);

for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE)) {
const auto LAYOUTNAME = xkb_keymap_layout_get_name(KEYMAP, i);

if (LAYOUTNAME)
return std::string(LAYOUTNAME);
return "error";
}
}

return "none";
}

void IKeyboard::updateLEDs() {
auto keyboard = wlr();

if (keyboard->xkb_state == nullptr)
return;

uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->led_indexes[i]))
leds |= (1 << i);
}

if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock()))
return;

wlr_keyboard_led_update(wlr(), leds);
}
61 changes: 61 additions & 0 deletions src/devices/IKeyboard.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#pragma once

#include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
#include "../helpers/Vector2D.hpp"

#include <xkbcommon/xkbcommon.h>

struct wlr_keyboard;

class IKeyboard : public IHID {
public:
virtual ~IKeyboard();
virtual uint32_t getCapabilities();
virtual bool isVirtual() = 0;
virtual wlr_keyboard* wlr() = 0;

struct SKeyEvent {
uint32_t timeMs = 0;
uint32_t keycode = 0;
bool updateMods = false;
wl_keyboard_key_state state = WL_KEYBOARD_KEY_STATE_PRESSED;
};

struct {
CSignal key;
CSignal modifiers;
CSignal keymap;
CSignal repeatInfo;
} keyboardEvents;

struct SStringRuleNames {
std::string layout = "";
std::string model = "";
std::string variant = "";
std::string options = "";
std::string rules = "";
};

void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
std::string getActiveLayout();
void updateLEDs();

bool active = false;
bool enabled = true;

xkb_layout_index_t activeLayout = 0;
xkb_state* xkbTranslationState = nullptr;

std::string hlName = "";
std::string xkbFilePath = "";

SStringRuleNames currentRules;
int repeatRate = 0;
int repeatDelay = 0;
int numlockOn = -1;
bool resolveBindsBySym = false;

WP<IKeyboard> self;
};
5 changes: 5 additions & 0 deletions src/devices/IPointer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "IPointer.hpp"

uint32_t IPointer::getCapabilities() {
return HID_INPUT_CAPABILITY_POINTER;
}
Loading

0 comments on commit 1237732

Please sign in to comment.